playlist-data-service.js
1 import { createEventEmitter, createLocalEventSubscriptionManager } from '../utils/event/index.js'; 2 import { MenuEventData, MenuEvents } from '../services/menu-events.js'; 3 import * as videoSources from '../services/video/sources.js'; 4 import filterSourceList from '../utils/playlist/filter-source-list.js'; 5 6 export class PlaylistDataService { 7 constructor() { 8 this.events = createEventEmitter(); 9 10 this.subscriptions = createLocalEventSubscriptionManager(); 11 12 this._filteredListCache = new Map(); 13 14 this._currentSearchQuery = ''; 15 16 this._operationInProgress = false; 17 } 18 19 getEventEmitter() { 20 return this.events; 21 } 22 23 get currentSearchQuery() { 24 return this._currentSearchQuery; 25 } 26 27 set currentSearchQuery(query) { 28 if (query !== this._currentSearchQuery) { 29 this._filteredListCache.clear(); 30 } 31 this._currentSearchQuery = query; 32 } 33 34 cleanup() { 35 if (this._unsubscribeMenuCloseRequested) { 36 this._unsubscribeMenuCloseRequested(); 37 this._unsubscribeMenuCloseRequested = null; 38 } 39 40 if (this._readyCheckInterval) { 41 clearInterval(this._readyCheckInterval); 42 this._readyCheckInterval = null; 43 } 44 45 if (this._readyCheckTimeout) { 46 clearTimeout(this._readyCheckTimeout); 47 this._readyCheckTimeout = null; 48 } 49 50 const eventData = MenuEventData.createMenuCloseReadyData('right', 'playlist-data'); 51 52 this.events.publish(MenuEvents.MENU_CLOSE_READY, eventData); 53 54 this.subscriptions.unsubscribeAll(); 55 56 this.events.clearAllEvents(); 57 } 58 59 subscribeToMenuEvents(menuManagerEvents) { 60 if (menuManagerEvents) { 61 this.subscriptions.subscribe( 62 menuManagerEvents, 63 MenuEvents.MENU_CLOSE_REQUESTED, 64 data => { 65 if (data && data.side === 'right') { 66 this._signalReadyForClose(); 67 } 68 }, 69 this, 70 ); 71 } else { 72 console.warn( 73 'Menu manager events not available, playlist data service will not respond to menu events', 74 ); 75 } 76 } 77 78 _signalReadyForClose() { 79 if (!this._operationInProgress) { 80 const eventData = MenuEventData.createMenuCloseReadyData('right', 'playlist-data'); 81 82 this.events.publish(MenuEvents.MENU_CLOSE_READY, eventData); 83 } else { 84 this._checkAndSignalWhenReady(); 85 } 86 } 87 88 _checkAndSignalWhenReady() { 89 if (this._readyCheckInterval) { 90 clearInterval(this._readyCheckInterval); 91 } 92 93 if (this._readyCheckTimeout) { 94 clearTimeout(this._readyCheckTimeout); 95 } 96 97 this._readyCheckInterval = setInterval(() => { 98 if (!this._operationInProgress) { 99 const eventData = MenuEventData.createMenuCloseReadyData('right', 'playlist-data'); 100 101 this.events.publish(MenuEvents.MENU_CLOSE_READY, eventData); 102 103 clearInterval(this._readyCheckInterval); 104 this._readyCheckInterval = null; 105 106 if (this._readyCheckTimeout) { 107 clearTimeout(this._readyCheckTimeout); 108 this._readyCheckTimeout = null; 109 } 110 } 111 }, 100); 112 113 this._readyCheckTimeout = setTimeout(() => { 114 console.warn('Forcing playlist data service ready signal after timeout'); 115 116 if (this._readyCheckInterval) { 117 clearInterval(this._readyCheckInterval); 118 this._readyCheckInterval = null; 119 } 120 121 const eventData = MenuEventData.createMenuCloseReadyData('right', 'playlist-data'); 122 123 this.events.publish(MenuEvents.MENU_CLOSE_READY, eventData); 124 125 this._readyCheckTimeout = null; 126 }, 1000); 127 } 128 129 getSourceList() { 130 return videoSources.getSourceList(); 131 } 132 133 getSourceCount() { 134 return videoSources.getSourceCount(); 135 } 136 137 getVideoInfoByIndex(index) { 138 return videoSources.getVideoInfoByIndex(index); 139 } 140 141 async shuffleSources() { 142 this._operationInProgress = true; 143 144 try { 145 this._filteredListCache.clear(); 146 147 videoSources.shuffleSources(); 148 149 this.events.publish(MenuEvents.PLAYLIST_RESHUFFLED); 150 } finally { 151 this._operationInProgress = false; 152 } 153 } 154 155 async filterSourceList(query = null) { 156 this._operationInProgress = true; 157 158 try { 159 const searchQuery = query !== null ? query : this._currentSearchQuery; 160 161 if (!searchQuery) { 162 return this.getSourceList(); 163 } 164 165 if (this._filteredListCache.has(searchQuery)) { 166 return this._filteredListCache.get(searchQuery); 167 } 168 169 try { 170 const filteredList = filterSourceList(searchQuery, videoSources.getSourceList); 171 172 this._filteredListCache.set(searchQuery, filteredList); 173 174 return filteredList; 175 } catch (error) { 176 console.error('Error filtering source list:', error); 177 return []; 178 } 179 } finally { 180 this._operationInProgress = false; 181 } 182 } 183 } 184 185 export default new PlaylistDataService();