@involvex/youtube-music-cli 0.0.46 → 0.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/cli.js.map +1004 -0
- package/dist/source/hooks/usePlayer.d.ts +1 -0
- package/dist/source/services/player-state/player-state.service.d.ts +1 -0
- package/dist/source/stores/player.store.d.ts +1 -0
- package/dist/source/types/actions.d.ts +4 -0
- package/dist/source/types/player.types.d.ts +3 -2
- package/dist/source/utils/constants.d.ts +1 -0
- package/dist/source/utils/icons.d.ts +1 -0
- package/dist/youtube-music-cli +0 -0
- package/package.json +1 -1
- package/dist/eslint.config.js +0 -55
- package/dist/package.json +0 -120
- package/dist/scripts/build-cli.js +0 -46
- package/dist/source/app.js +0 -17
- package/dist/source/cli.js +0 -504
- package/dist/source/components/common/ErrorBoundary.js +0 -22
- package/dist/source/components/common/Help.js +0 -18
- package/dist/source/components/common/ShortcutsBar.js +0 -80
- package/dist/source/components/config/ConfigLayout.js +0 -84
- package/dist/source/components/config/KeybindingsLayout.js +0 -107
- package/dist/source/components/export/ExportLayout.js +0 -111
- package/dist/source/components/import/ImportLayout.js +0 -119
- package/dist/source/components/import/ImportProgress.js +0 -73
- package/dist/source/components/layouts/ExploreLayout.js +0 -72
- package/dist/source/components/layouts/HistoryLayout.js +0 -37
- package/dist/source/components/layouts/LyricsLayout.js +0 -89
- package/dist/source/components/layouts/MainLayout.js +0 -190
- package/dist/source/components/layouts/MiniPlayerLayout.js +0 -20
- package/dist/source/components/layouts/PlayerLayout.js +0 -9
- package/dist/source/components/layouts/PluginsLayout.js +0 -77
- package/dist/source/components/layouts/SearchLayout.js +0 -193
- package/dist/source/components/layouts/TrendingLayout.js +0 -59
- package/dist/source/components/player/NowPlaying.js +0 -45
- package/dist/source/components/player/PlayerControls.js +0 -83
- package/dist/source/components/player/ProgressBar.js +0 -19
- package/dist/source/components/player/QueueList.js +0 -36
- package/dist/source/components/player/Suggestions.js +0 -50
- package/dist/source/components/playlist/PlaylistList.js +0 -138
- package/dist/source/components/plugins/PluginInstallDialog.js +0 -41
- package/dist/source/components/plugins/PluginsAvailable.js +0 -55
- package/dist/source/components/plugins/PluginsList.js +0 -18
- package/dist/source/components/search/SearchBar.js +0 -55
- package/dist/source/components/search/SearchHistory.js +0 -35
- package/dist/source/components/search/SearchResults.js +0 -280
- package/dist/source/components/settings/Settings.js +0 -211
- package/dist/source/components/theme/ThemeSwitcher.js +0 -11
- package/dist/source/config/themes.config.js +0 -123
- package/dist/source/contexts/theme.context.js +0 -29
- package/dist/source/hooks/useKeyboard.js +0 -188
- package/dist/source/hooks/useKeyboardBlocker.js +0 -45
- package/dist/source/hooks/useNavigation.js +0 -5
- package/dist/source/hooks/usePlayer.js +0 -43
- package/dist/source/hooks/usePlaylist.js +0 -65
- package/dist/source/hooks/useSearch.js +0 -76
- package/dist/source/hooks/useSleepTimer.js +0 -48
- package/dist/source/hooks/useTerminalSize.js +0 -24
- package/dist/source/hooks/useTheme.js +0 -5
- package/dist/source/hooks/useYouTubeMusic.js +0 -112
- package/dist/source/main.js +0 -127
- package/dist/source/services/cache/cache.service.js +0 -67
- package/dist/source/services/completions/completions.service.js +0 -313
- package/dist/source/services/config/config.service.js +0 -191
- package/dist/source/services/discord/discord-rpc.service.js +0 -95
- package/dist/source/services/download/download.service.js +0 -350
- package/dist/source/services/export/export.service.js +0 -131
- package/dist/source/services/history/history.service.js +0 -83
- package/dist/source/services/import/import.service.js +0 -272
- package/dist/source/services/import/spotify.service.js +0 -171
- package/dist/source/services/import/track-matcher.service.js +0 -271
- package/dist/source/services/import/youtube-import.service.js +0 -84
- package/dist/source/services/logger/logger.service.js +0 -52
- package/dist/source/services/lyrics/lyrics.service.js +0 -93
- package/dist/source/services/mpris/mpris.service.js +0 -78
- package/dist/source/services/notification/notification.service.js +0 -57
- package/dist/source/services/player/dependency-check.service.js +0 -140
- package/dist/source/services/player/player.service.js +0 -478
- package/dist/source/services/player-state/player-state.service.js +0 -122
- package/dist/source/services/plugin/plugin-audio-api.js +0 -36
- package/dist/source/services/plugin/plugin-context.js +0 -256
- package/dist/source/services/plugin/plugin-hooks.service.js +0 -135
- package/dist/source/services/plugin/plugin-installer.service.js +0 -248
- package/dist/source/services/plugin/plugin-loader.service.js +0 -161
- package/dist/source/services/plugin/plugin-permissions.service.js +0 -194
- package/dist/source/services/plugin/plugin-registry.service.js +0 -215
- package/dist/source/services/plugin/plugin-ui-api.js +0 -46
- package/dist/source/services/plugin/plugin-updater.service.js +0 -206
- package/dist/source/services/scrobbling/scrobbling.service.js +0 -115
- package/dist/source/services/sleep-timer/sleep-timer.service.js +0 -45
- package/dist/source/services/version-check/version-check.service.js +0 -121
- package/dist/source/services/web/static-file.service.js +0 -185
- package/dist/source/services/web/web-server-manager.js +0 -506
- package/dist/source/services/web/web-streaming.service.js +0 -290
- package/dist/source/services/web/websocket.server.js +0 -267
- package/dist/source/services/youtube-music/api.js +0 -649
- package/dist/source/services/youtube-music/search.service.js +0 -38
- package/dist/source/stores/history.store.js +0 -64
- package/dist/source/stores/navigation.store.js +0 -90
- package/dist/source/stores/player.store.js +0 -724
- package/dist/source/stores/plugins.store.js +0 -177
- package/dist/source/types/actions.js +0 -1
- package/dist/source/types/cli.types.js +0 -1
- package/dist/source/types/config.types.js +0 -1
- package/dist/source/types/history.types.js +0 -1
- package/dist/source/types/import.types.js +0 -2
- package/dist/source/types/keyboard.types.js +0 -1
- package/dist/source/types/navigation.types.js +0 -1
- package/dist/source/types/player.types.js +0 -1
- package/dist/source/types/playlist.types.js +0 -1
- package/dist/source/types/plugin.types.js +0 -1
- package/dist/source/types/theme.types.js +0 -1
- package/dist/source/types/web.types.js +0 -2
- package/dist/source/types/youtube-music.types.js +0 -1
- package/dist/source/types/youtubei.types.js +0 -3
- package/dist/source/utils/constants.js +0 -134
- package/dist/source/utils/format.js +0 -24
- package/dist/source/utils/icons.js +0 -26
- package/dist/source/utils/search-filters.js +0 -100
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
import { getWebSocketServer } from "./websocket.server.js";
|
|
2
|
-
import { getWebStreamingService } from "./web-streaming.service.js";
|
|
3
|
-
import { getConfigService } from "../config/config.service.js";
|
|
4
|
-
import { getImportService } from "../import/import.service.js";
|
|
5
|
-
import { getPlayerService } from "../player/player.service.js";
|
|
6
|
-
import { getSearchService } from "../youtube-music/search.service.js";
|
|
7
|
-
import { logger } from "../logger/logger.service.js";
|
|
8
|
-
class WebServerManager {
|
|
9
|
-
config;
|
|
10
|
-
isRunning = false;
|
|
11
|
-
cleanupHooks = [];
|
|
12
|
-
// Internal state for web-only mode (when PlayerProvider is not mounted)
|
|
13
|
-
internalState = {
|
|
14
|
-
currentTrack: null,
|
|
15
|
-
isPlaying: false,
|
|
16
|
-
volume: 70,
|
|
17
|
-
speed: 1,
|
|
18
|
-
progress: 0,
|
|
19
|
-
duration: 0,
|
|
20
|
-
queue: [],
|
|
21
|
-
queuePosition: 0,
|
|
22
|
-
repeat: 'off',
|
|
23
|
-
shuffle: false,
|
|
24
|
-
isLoading: false,
|
|
25
|
-
error: null,
|
|
26
|
-
playRequestId: 0,
|
|
27
|
-
};
|
|
28
|
-
constructor() {
|
|
29
|
-
// Load config or use defaults
|
|
30
|
-
const configService = getConfigService();
|
|
31
|
-
const savedConfig = configService.get('webServer');
|
|
32
|
-
this.config = savedConfig ?? {
|
|
33
|
-
enabled: false,
|
|
34
|
-
host: 'localhost',
|
|
35
|
-
port: 8080,
|
|
36
|
-
enableCors: true,
|
|
37
|
-
allowedOrigins: ['*'],
|
|
38
|
-
auth: { enabled: false },
|
|
39
|
-
};
|
|
40
|
-
// Save default config if not present
|
|
41
|
-
if (!savedConfig) {
|
|
42
|
-
configService.set('webServer', this.config);
|
|
43
|
-
}
|
|
44
|
-
// Initialize volume from config
|
|
45
|
-
this.internalState.volume = configService.get('volume') ?? 70;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Start the web server
|
|
49
|
-
*/
|
|
50
|
-
async start(options) {
|
|
51
|
-
if (this.isRunning) {
|
|
52
|
-
logger.warn('WebServerManager', 'Server already running');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
// Apply CLI options
|
|
56
|
-
const finalConfig = { ...this.config };
|
|
57
|
-
if (options) {
|
|
58
|
-
if (options.host !== undefined) {
|
|
59
|
-
finalConfig.host = options.host;
|
|
60
|
-
}
|
|
61
|
-
if (options.port !== undefined) {
|
|
62
|
-
finalConfig.port = options.port;
|
|
63
|
-
}
|
|
64
|
-
if (options.auth !== undefined) {
|
|
65
|
-
finalConfig.auth.enabled = true;
|
|
66
|
-
finalConfig.auth.token = options.auth;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
logger.info('WebServerManager', 'Starting web server', finalConfig);
|
|
70
|
-
try {
|
|
71
|
-
const wsServer = getWebSocketServer();
|
|
72
|
-
// Set up command handler
|
|
73
|
-
const cleanupCommand = this.setupCommandHandler();
|
|
74
|
-
this.cleanupHooks.push(cleanupCommand);
|
|
75
|
-
// Set up import handler
|
|
76
|
-
const cleanupImport = this.setupImportHandler();
|
|
77
|
-
this.cleanupHooks.push(cleanupImport);
|
|
78
|
-
// Start the server
|
|
79
|
-
await wsServer.start({
|
|
80
|
-
config: finalConfig,
|
|
81
|
-
onCommand: this.handleCommand.bind(this),
|
|
82
|
-
onImportRequest: this.handleImportRequest.bind(this),
|
|
83
|
-
onSearchRequest: this.handleSearchRequest.bind(this),
|
|
84
|
-
onConfigUpdate: this.handleConfigUpdate.bind(this),
|
|
85
|
-
});
|
|
86
|
-
this.isRunning = true;
|
|
87
|
-
// Set up graceful shutdown
|
|
88
|
-
this.setupShutdownHooks();
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
logger.error('WebServerManager', 'Failed to start server', {
|
|
92
|
-
error: error instanceof Error ? error.message : String(error),
|
|
93
|
-
});
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Stop the web server
|
|
99
|
-
*/
|
|
100
|
-
async stop() {
|
|
101
|
-
if (!this.isRunning) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
logger.info('WebServerManager', 'Stopping web server');
|
|
105
|
-
// Clean up hooks
|
|
106
|
-
for (const cleanup of this.cleanupHooks) {
|
|
107
|
-
cleanup();
|
|
108
|
-
}
|
|
109
|
-
this.cleanupHooks = [];
|
|
110
|
-
// Stop the WebSocket server
|
|
111
|
-
const wsServer = getWebSocketServer();
|
|
112
|
-
await wsServer.stop();
|
|
113
|
-
this.isRunning = false;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Set up player command handler
|
|
117
|
-
*/
|
|
118
|
-
setupCommandHandler() {
|
|
119
|
-
const streamingService = getWebStreamingService();
|
|
120
|
-
const unsubscribe = streamingService.onMessage(message => {
|
|
121
|
-
if (message.type === 'command') {
|
|
122
|
-
this.handleCommand(message.action);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
return unsubscribe;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Set up import progress handler
|
|
129
|
-
*/
|
|
130
|
-
setupImportHandler() {
|
|
131
|
-
const importService = getImportService();
|
|
132
|
-
const streamingService = getWebStreamingService();
|
|
133
|
-
const unsubscribe = importService.onProgress(progress => {
|
|
134
|
-
streamingService.onImportProgress(progress);
|
|
135
|
-
});
|
|
136
|
-
return unsubscribe;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Handle command from web client
|
|
140
|
-
*/
|
|
141
|
-
handleCommand(action) {
|
|
142
|
-
logger.debug('WebServerManager', 'Executing command from client', { action });
|
|
143
|
-
const playerService = getPlayerService();
|
|
144
|
-
const config = getConfigService();
|
|
145
|
-
// Execute command and update internal state
|
|
146
|
-
switch (action.category) {
|
|
147
|
-
case 'PLAY': {
|
|
148
|
-
if (action.track) {
|
|
149
|
-
this.internalState.currentTrack = action.track;
|
|
150
|
-
this.internalState.isPlaying = true;
|
|
151
|
-
this.internalState.progress = 0;
|
|
152
|
-
this.internalState.error = null;
|
|
153
|
-
const youtubeUrl = `https://www.youtube.com/watch?v=${action.track.videoId}`;
|
|
154
|
-
void playerService.play(youtubeUrl, {
|
|
155
|
-
volume: this.internalState.volume,
|
|
156
|
-
volumeFadeDuration: config.get('volumeFadeDuration'),
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
case 'PAUSE':
|
|
162
|
-
this.internalState.isPlaying = false;
|
|
163
|
-
playerService.pause();
|
|
164
|
-
break;
|
|
165
|
-
case 'RESUME':
|
|
166
|
-
this.internalState.isPlaying = true;
|
|
167
|
-
playerService.resume();
|
|
168
|
-
break;
|
|
169
|
-
case 'STOP':
|
|
170
|
-
this.internalState.isPlaying = false;
|
|
171
|
-
this.internalState.progress = 0;
|
|
172
|
-
this.internalState.currentTrack = null;
|
|
173
|
-
playerService.stop();
|
|
174
|
-
break;
|
|
175
|
-
case 'NEXT': {
|
|
176
|
-
if (this.internalState.queue.length === 0)
|
|
177
|
-
break;
|
|
178
|
-
if (this.internalState.shuffle && this.internalState.queue.length > 1) {
|
|
179
|
-
let randomIndex;
|
|
180
|
-
do {
|
|
181
|
-
randomIndex = Math.floor(Math.random() * this.internalState.queue.length);
|
|
182
|
-
} while (randomIndex === this.internalState.queuePosition);
|
|
183
|
-
this.internalState.queuePosition = randomIndex;
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
const nextPosition = this.internalState.queuePosition + 1;
|
|
187
|
-
if (nextPosition >= this.internalState.queue.length) {
|
|
188
|
-
if (this.internalState.repeat === 'all') {
|
|
189
|
-
this.internalState.queuePosition = 0;
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
this.internalState.queuePosition = nextPosition;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
this.internalState.currentTrack =
|
|
200
|
-
this.internalState.queue[this.internalState.queuePosition] ?? null;
|
|
201
|
-
this.internalState.isPlaying = true;
|
|
202
|
-
this.internalState.progress = 0;
|
|
203
|
-
if (this.internalState.currentTrack) {
|
|
204
|
-
const youtubeUrl = `https://www.youtube.com/watch?v=${this.internalState.currentTrack.videoId}`;
|
|
205
|
-
void playerService.play(youtubeUrl, {
|
|
206
|
-
volume: this.internalState.volume,
|
|
207
|
-
volumeFadeDuration: config.get('volumeFadeDuration'),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
case 'PREVIOUS': {
|
|
213
|
-
const prevPosition = this.internalState.queuePosition - 1;
|
|
214
|
-
if (prevPosition < 0)
|
|
215
|
-
break;
|
|
216
|
-
if (this.internalState.progress > 3) {
|
|
217
|
-
this.internalState.progress = 0;
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
this.internalState.queuePosition = prevPosition;
|
|
221
|
-
this.internalState.currentTrack =
|
|
222
|
-
this.internalState.queue[prevPosition] ?? null;
|
|
223
|
-
this.internalState.progress = 0;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
case 'SEEK':
|
|
227
|
-
if (action.position !== undefined) {
|
|
228
|
-
this.internalState.progress = Math.max(0, Math.min(action.position, this.internalState.duration));
|
|
229
|
-
// Note: Seeking via mpv IPC would require additional implementation
|
|
230
|
-
}
|
|
231
|
-
break;
|
|
232
|
-
case 'SET_VOLUME':
|
|
233
|
-
if (action.volume !== undefined) {
|
|
234
|
-
this.internalState.volume = Math.max(0, Math.min(100, action.volume));
|
|
235
|
-
playerService.setVolume(this.internalState.volume);
|
|
236
|
-
}
|
|
237
|
-
break;
|
|
238
|
-
case 'VOLUME_UP':
|
|
239
|
-
this.internalState.volume = Math.min(100, this.internalState.volume + 10);
|
|
240
|
-
playerService.setVolume(this.internalState.volume);
|
|
241
|
-
break;
|
|
242
|
-
case 'VOLUME_DOWN':
|
|
243
|
-
this.internalState.volume = Math.max(0, this.internalState.volume - 10);
|
|
244
|
-
playerService.setVolume(this.internalState.volume);
|
|
245
|
-
break;
|
|
246
|
-
case 'VOLUME_FINE_UP':
|
|
247
|
-
this.internalState.volume = Math.min(100, this.internalState.volume + 1);
|
|
248
|
-
playerService.setVolume(this.internalState.volume);
|
|
249
|
-
break;
|
|
250
|
-
case 'VOLUME_FINE_DOWN':
|
|
251
|
-
this.internalState.volume = Math.max(0, this.internalState.volume - 1);
|
|
252
|
-
playerService.setVolume(this.internalState.volume);
|
|
253
|
-
break;
|
|
254
|
-
case 'TOGGLE_SHUFFLE':
|
|
255
|
-
this.internalState.shuffle = !this.internalState.shuffle;
|
|
256
|
-
break;
|
|
257
|
-
case 'TOGGLE_REPEAT': {
|
|
258
|
-
const repeatModes = ['off', 'all', 'one'];
|
|
259
|
-
const currentIndex = repeatModes.indexOf(this.internalState.repeat);
|
|
260
|
-
this.internalState.repeat =
|
|
261
|
-
repeatModes[(currentIndex + 1) % 3] ?? 'off';
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
case 'SET_QUEUE':
|
|
265
|
-
if (action.queue) {
|
|
266
|
-
this.internalState.queue = action.queue;
|
|
267
|
-
this.internalState.queuePosition = 0;
|
|
268
|
-
}
|
|
269
|
-
break;
|
|
270
|
-
case 'ADD_TO_QUEUE':
|
|
271
|
-
if (action.track) {
|
|
272
|
-
this.internalState.queue = [
|
|
273
|
-
...this.internalState.queue,
|
|
274
|
-
action.track,
|
|
275
|
-
];
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
case 'REMOVE_FROM_QUEUE':
|
|
279
|
-
if (action.index !== undefined) {
|
|
280
|
-
const newQueue = [...this.internalState.queue];
|
|
281
|
-
newQueue.splice(action.index, 1);
|
|
282
|
-
this.internalState.queue = newQueue;
|
|
283
|
-
}
|
|
284
|
-
break;
|
|
285
|
-
case 'CLEAR_QUEUE':
|
|
286
|
-
this.internalState.queue = [];
|
|
287
|
-
this.internalState.queuePosition = 0;
|
|
288
|
-
this.internalState.isPlaying = false;
|
|
289
|
-
break;
|
|
290
|
-
case 'SET_QUEUE_POSITION':
|
|
291
|
-
if (action.position >= 0 &&
|
|
292
|
-
action.position < this.internalState.queue.length) {
|
|
293
|
-
this.internalState.queuePosition = action.position;
|
|
294
|
-
this.internalState.currentTrack =
|
|
295
|
-
this.internalState.queue[action.position] ?? null;
|
|
296
|
-
this.internalState.progress = 0;
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
|
-
case 'SET_SPEED':
|
|
300
|
-
if (action.speed !== undefined) {
|
|
301
|
-
const clampedSpeed = Math.max(0.25, Math.min(4.0, action.speed));
|
|
302
|
-
this.internalState.speed = clampedSpeed;
|
|
303
|
-
playerService.setSpeed(clampedSpeed);
|
|
304
|
-
}
|
|
305
|
-
break;
|
|
306
|
-
case 'UPDATE_PROGRESS':
|
|
307
|
-
if (action.progress !== undefined) {
|
|
308
|
-
this.internalState.progress = Math.max(0, Math.min(action.progress, this.internalState.duration || action.progress));
|
|
309
|
-
}
|
|
310
|
-
break;
|
|
311
|
-
case 'SET_DURATION':
|
|
312
|
-
if (action.duration !== undefined) {
|
|
313
|
-
this.internalState.duration = action.duration;
|
|
314
|
-
}
|
|
315
|
-
break;
|
|
316
|
-
case 'SET_LOADING':
|
|
317
|
-
if (action.loading !== undefined) {
|
|
318
|
-
this.internalState.isLoading = action.loading;
|
|
319
|
-
}
|
|
320
|
-
break;
|
|
321
|
-
case 'SET_ERROR':
|
|
322
|
-
if (action.error !== undefined) {
|
|
323
|
-
this.internalState.error = action.error;
|
|
324
|
-
this.internalState.isLoading = false;
|
|
325
|
-
}
|
|
326
|
-
break;
|
|
327
|
-
default:
|
|
328
|
-
logger.debug('WebServerManager', 'Unhandled command category', {
|
|
329
|
-
category: action.category,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
// Broadcast updated state after command
|
|
333
|
-
this.broadcastState();
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Handle import request from web client
|
|
337
|
-
*/
|
|
338
|
-
async handleImportRequest(source, url, name) {
|
|
339
|
-
logger.info('WebServerManager', 'Import request from client', {
|
|
340
|
-
source,
|
|
341
|
-
url,
|
|
342
|
-
name,
|
|
343
|
-
});
|
|
344
|
-
try {
|
|
345
|
-
const importService = getImportService();
|
|
346
|
-
await importService.importPlaylist(source, url, name);
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
logger.error('WebServerManager', 'Import failed', {
|
|
350
|
-
source,
|
|
351
|
-
url,
|
|
352
|
-
error: error instanceof Error ? error.message : String(error),
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Handle search request from web client
|
|
358
|
-
*/
|
|
359
|
-
async handleSearchRequest(query, searchType) {
|
|
360
|
-
logger.info('WebServerManager', 'Search request from client', {
|
|
361
|
-
query,
|
|
362
|
-
searchType,
|
|
363
|
-
});
|
|
364
|
-
try {
|
|
365
|
-
const searchService = getSearchService();
|
|
366
|
-
const response = await searchService.search(query, { type: searchType });
|
|
367
|
-
const streamingService = getWebStreamingService();
|
|
368
|
-
streamingService.broadcast({
|
|
369
|
-
type: 'search-results',
|
|
370
|
-
results: response.results,
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
catch (error) {
|
|
374
|
-
logger.error('WebServerManager', 'Search failed', {
|
|
375
|
-
query,
|
|
376
|
-
searchType,
|
|
377
|
-
error: error instanceof Error ? error.message : String(error),
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Handle config update from web client
|
|
383
|
-
*/
|
|
384
|
-
handleConfigUpdate(config) {
|
|
385
|
-
logger.info('WebServerManager', 'Config update from client', { config });
|
|
386
|
-
try {
|
|
387
|
-
const configService = getConfigService();
|
|
388
|
-
// Apply each config key
|
|
389
|
-
for (const [key, value] of Object.entries(config)) {
|
|
390
|
-
configService.set(key, value);
|
|
391
|
-
}
|
|
392
|
-
// Broadcast updated config to all clients
|
|
393
|
-
const streamingService = getWebStreamingService();
|
|
394
|
-
streamingService.broadcast({
|
|
395
|
-
type: 'config-update',
|
|
396
|
-
config,
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
catch (error) {
|
|
400
|
-
logger.error('WebServerManager', 'Config update failed', {
|
|
401
|
-
config,
|
|
402
|
-
error: error instanceof Error ? error.message : String(error),
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Update player state (call this when player state changes)
|
|
408
|
-
* This is called by PlayerProvider in normal mode to sync state
|
|
409
|
-
*/
|
|
410
|
-
updateState(state) {
|
|
411
|
-
if (!this.isRunning)
|
|
412
|
-
return;
|
|
413
|
-
// Update internal state to stay in sync
|
|
414
|
-
this.internalState = { ...state };
|
|
415
|
-
const streamingService = getWebStreamingService();
|
|
416
|
-
streamingService.onStateChange(state);
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Broadcast current state to all connected clients
|
|
420
|
-
*/
|
|
421
|
-
broadcastState() {
|
|
422
|
-
if (!this.isRunning)
|
|
423
|
-
return;
|
|
424
|
-
const streamingService = getWebStreamingService();
|
|
425
|
-
streamingService.onStateChange(this.internalState);
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Get current internal state
|
|
429
|
-
*/
|
|
430
|
-
getState() {
|
|
431
|
-
return { ...this.internalState };
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Set internal state directly (for sync from external sources)
|
|
435
|
-
*/
|
|
436
|
-
setState(state) {
|
|
437
|
-
this.internalState = { ...this.internalState, ...state };
|
|
438
|
-
this.broadcastState();
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Set up graceful shutdown hooks
|
|
442
|
-
*/
|
|
443
|
-
setupShutdownHooks() {
|
|
444
|
-
const shutdown = async () => {
|
|
445
|
-
await this.stop();
|
|
446
|
-
};
|
|
447
|
-
process.on('beforeExit', shutdown);
|
|
448
|
-
process.on('SIGINT', shutdown);
|
|
449
|
-
process.on('SIGTERM', shutdown);
|
|
450
|
-
this.cleanupHooks.push(() => {
|
|
451
|
-
process.off('beforeExit', shutdown);
|
|
452
|
-
process.off('SIGINT', shutdown);
|
|
453
|
-
process.off('SIGTERM', shutdown);
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Check if server is running
|
|
458
|
-
*/
|
|
459
|
-
isServerRunning() {
|
|
460
|
-
return this.isRunning;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Get server URL
|
|
464
|
-
*/
|
|
465
|
-
getServerUrl() {
|
|
466
|
-
const wsServer = getWebSocketServer();
|
|
467
|
-
return wsServer.getServerUrl();
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Get server statistics
|
|
471
|
-
*/
|
|
472
|
-
getStats() {
|
|
473
|
-
if (!this.isRunning) {
|
|
474
|
-
return { running: false };
|
|
475
|
-
}
|
|
476
|
-
const streamingService = getWebStreamingService();
|
|
477
|
-
const stats = streamingService.getStats();
|
|
478
|
-
return {
|
|
479
|
-
running: true,
|
|
480
|
-
url: this.getServerUrl(),
|
|
481
|
-
clients: stats.clients,
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Update configuration
|
|
486
|
-
*/
|
|
487
|
-
updateConfig(config) {
|
|
488
|
-
this.config = { ...this.config, ...config };
|
|
489
|
-
const configService = getConfigService();
|
|
490
|
-
configService.set('webServer', this.config);
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Get current configuration
|
|
494
|
-
*/
|
|
495
|
-
getConfig() {
|
|
496
|
-
return { ...this.config };
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
// Singleton instance
|
|
500
|
-
let webServerManagerInstance = null;
|
|
501
|
-
export function getWebServerManager() {
|
|
502
|
-
if (!webServerManagerInstance) {
|
|
503
|
-
webServerManagerInstance = new WebServerManager();
|
|
504
|
-
}
|
|
505
|
-
return webServerManagerInstance;
|
|
506
|
-
}
|