@involvex/youtube-music-cli 0.0.47 → 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.
Files changed (111) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/cli.js.map +3 -3
  3. package/dist/youtube-music-cli +0 -0
  4. package/package.json +1 -1
  5. package/dist/eslint.config.js +0 -55
  6. package/dist/package.json +0 -120
  7. package/dist/scripts/build-cli.js +0 -46
  8. package/dist/source/app.js +0 -17
  9. package/dist/source/cli.js +0 -504
  10. package/dist/source/components/common/ErrorBoundary.js +0 -22
  11. package/dist/source/components/common/Help.js +0 -18
  12. package/dist/source/components/common/ShortcutsBar.js +0 -89
  13. package/dist/source/components/config/ConfigLayout.js +0 -84
  14. package/dist/source/components/config/KeybindingsLayout.js +0 -107
  15. package/dist/source/components/export/ExportLayout.js +0 -111
  16. package/dist/source/components/import/ImportLayout.js +0 -119
  17. package/dist/source/components/import/ImportProgress.js +0 -73
  18. package/dist/source/components/layouts/ExploreLayout.js +0 -72
  19. package/dist/source/components/layouts/HistoryLayout.js +0 -37
  20. package/dist/source/components/layouts/LyricsLayout.js +0 -89
  21. package/dist/source/components/layouts/MainLayout.js +0 -190
  22. package/dist/source/components/layouts/MiniPlayerLayout.js +0 -20
  23. package/dist/source/components/layouts/PlayerLayout.js +0 -9
  24. package/dist/source/components/layouts/PluginsLayout.js +0 -77
  25. package/dist/source/components/layouts/SearchLayout.js +0 -193
  26. package/dist/source/components/layouts/TrendingLayout.js +0 -59
  27. package/dist/source/components/player/NowPlaying.js +0 -45
  28. package/dist/source/components/player/PlayerControls.js +0 -83
  29. package/dist/source/components/player/ProgressBar.js +0 -19
  30. package/dist/source/components/player/QueueList.js +0 -36
  31. package/dist/source/components/player/Suggestions.js +0 -50
  32. package/dist/source/components/playlist/PlaylistList.js +0 -138
  33. package/dist/source/components/plugins/PluginInstallDialog.js +0 -41
  34. package/dist/source/components/plugins/PluginsAvailable.js +0 -55
  35. package/dist/source/components/plugins/PluginsList.js +0 -18
  36. package/dist/source/components/search/SearchBar.js +0 -55
  37. package/dist/source/components/search/SearchHistory.js +0 -35
  38. package/dist/source/components/search/SearchResults.js +0 -280
  39. package/dist/source/components/settings/Settings.js +0 -211
  40. package/dist/source/components/theme/ThemeSwitcher.js +0 -11
  41. package/dist/source/config/themes.config.js +0 -123
  42. package/dist/source/contexts/theme.context.js +0 -29
  43. package/dist/source/hooks/useKeyboard.js +0 -188
  44. package/dist/source/hooks/useKeyboardBlocker.js +0 -45
  45. package/dist/source/hooks/useNavigation.js +0 -5
  46. package/dist/source/hooks/usePlayer.js +0 -43
  47. package/dist/source/hooks/usePlaylist.js +0 -65
  48. package/dist/source/hooks/useSearch.js +0 -76
  49. package/dist/source/hooks/useSleepTimer.js +0 -48
  50. package/dist/source/hooks/useTerminalSize.js +0 -24
  51. package/dist/source/hooks/useTheme.js +0 -5
  52. package/dist/source/hooks/useYouTubeMusic.js +0 -112
  53. package/dist/source/main.js +0 -127
  54. package/dist/source/services/cache/cache.service.js +0 -67
  55. package/dist/source/services/completions/completions.service.js +0 -313
  56. package/dist/source/services/config/config.service.js +0 -191
  57. package/dist/source/services/discord/discord-rpc.service.js +0 -95
  58. package/dist/source/services/download/download.service.js +0 -350
  59. package/dist/source/services/export/export.service.js +0 -131
  60. package/dist/source/services/history/history.service.js +0 -83
  61. package/dist/source/services/import/import.service.js +0 -272
  62. package/dist/source/services/import/spotify.service.js +0 -171
  63. package/dist/source/services/import/track-matcher.service.js +0 -271
  64. package/dist/source/services/import/youtube-import.service.js +0 -84
  65. package/dist/source/services/logger/logger.service.js +0 -52
  66. package/dist/source/services/lyrics/lyrics.service.js +0 -93
  67. package/dist/source/services/mpris/mpris.service.js +0 -78
  68. package/dist/source/services/notification/notification.service.js +0 -57
  69. package/dist/source/services/player/dependency-check.service.js +0 -140
  70. package/dist/source/services/player/player.service.js +0 -478
  71. package/dist/source/services/player-state/player-state.service.js +0 -123
  72. package/dist/source/services/plugin/plugin-audio-api.js +0 -36
  73. package/dist/source/services/plugin/plugin-context.js +0 -256
  74. package/dist/source/services/plugin/plugin-hooks.service.js +0 -135
  75. package/dist/source/services/plugin/plugin-installer.service.js +0 -248
  76. package/dist/source/services/plugin/plugin-loader.service.js +0 -161
  77. package/dist/source/services/plugin/plugin-permissions.service.js +0 -194
  78. package/dist/source/services/plugin/plugin-registry.service.js +0 -215
  79. package/dist/source/services/plugin/plugin-ui-api.js +0 -46
  80. package/dist/source/services/plugin/plugin-updater.service.js +0 -206
  81. package/dist/source/services/scrobbling/scrobbling.service.js +0 -115
  82. package/dist/source/services/sleep-timer/sleep-timer.service.js +0 -45
  83. package/dist/source/services/version-check/version-check.service.js +0 -121
  84. package/dist/source/services/web/static-file.service.js +0 -185
  85. package/dist/source/services/web/web-server-manager.js +0 -507
  86. package/dist/source/services/web/web-streaming.service.js +0 -292
  87. package/dist/source/services/web/websocket.server.js +0 -267
  88. package/dist/source/services/youtube-music/api.js +0 -649
  89. package/dist/source/services/youtube-music/search.service.js +0 -38
  90. package/dist/source/stores/history.store.js +0 -64
  91. package/dist/source/stores/navigation.store.js +0 -90
  92. package/dist/source/stores/player.store.js +0 -789
  93. package/dist/source/stores/plugins.store.js +0 -177
  94. package/dist/source/types/actions.js +0 -1
  95. package/dist/source/types/cli.types.js +0 -1
  96. package/dist/source/types/config.types.js +0 -1
  97. package/dist/source/types/history.types.js +0 -1
  98. package/dist/source/types/import.types.js +0 -2
  99. package/dist/source/types/keyboard.types.js +0 -1
  100. package/dist/source/types/navigation.types.js +0 -1
  101. package/dist/source/types/player.types.js +0 -1
  102. package/dist/source/types/playlist.types.js +0 -1
  103. package/dist/source/types/plugin.types.js +0 -1
  104. package/dist/source/types/theme.types.js +0 -1
  105. package/dist/source/types/web.types.js +0 -2
  106. package/dist/source/types/youtube-music.types.js +0 -1
  107. package/dist/source/types/youtubei.types.js +0 -3
  108. package/dist/source/utils/constants.js +0 -135
  109. package/dist/source/utils/format.js +0 -24
  110. package/dist/source/utils/icons.js +0 -28
  111. package/dist/source/utils/search-filters.js +0 -100
@@ -1,292 +0,0 @@
1
- import { logger } from "../logger/logger.service.js";
2
- class WebStreamingService {
3
- clients = new Map();
4
- clientInfo = new Map();
5
- messageHandlers = new Set();
6
- prevState = null;
7
- UPDATE_THROTTLE_MS = 250; // Throttle updates to 4/sec
8
- lastUpdateTime = 0;
9
- pendingUpdate = null;
10
- updateTimer = null;
11
- /**
12
- * Add a client connection
13
- */
14
- addClient(clientId, ws, authenticated = true) {
15
- this.clients.set(clientId, ws);
16
- this.clientInfo.set(clientId, {
17
- id: clientId,
18
- authenticated,
19
- connectedAt: Date.now(),
20
- lastHeartbeat: Date.now(),
21
- });
22
- logger.info('WebStreamingService', 'Client connected', {
23
- clientId,
24
- authenticated,
25
- });
26
- // Send initial state if available
27
- if (this.prevState) {
28
- this.sendToClient(clientId, {
29
- type: 'state-update',
30
- state: this.prevState,
31
- });
32
- }
33
- // Send connection event
34
- this.broadcast({
35
- type: 'event',
36
- event: 'client-connected',
37
- data: { clientId, clientCount: this.clients.size },
38
- });
39
- }
40
- /**
41
- * Remove a client connection
42
- */
43
- removeClient(clientId) {
44
- const hadClient = this.clients.has(clientId);
45
- this.clients.delete(clientId);
46
- this.clientInfo.delete(clientId);
47
- if (hadClient) {
48
- logger.info('WebStreamingService', 'Client disconnected', {
49
- clientId,
50
- remainingClients: this.clients.size,
51
- });
52
- this.broadcast({
53
- type: 'event',
54
- event: 'client-disconnected',
55
- data: { clientId, clientCount: this.clients.size },
56
- });
57
- }
58
- }
59
- /**
60
- * Check if a client exists
61
- */
62
- hasClient(clientId) {
63
- return this.clients.has(clientId);
64
- }
65
- /**
66
- * Get client count
67
- */
68
- getClientCount() {
69
- return this.clients.size;
70
- }
71
- /**
72
- * Get all connected clients
73
- */
74
- getClients() {
75
- return Array.from(this.clients.keys());
76
- }
77
- /**
78
- * Handle incoming message from a client
79
- */
80
- handleClientMessage(clientId, message) {
81
- try {
82
- const msg = message;
83
- // Update client heartbeat
84
- const info = this.clientInfo.get(clientId);
85
- if (info) {
86
- info.lastHeartbeat = Date.now();
87
- this.clientInfo.set(clientId, info);
88
- }
89
- // Emit to all handlers
90
- for (const handler of this.messageHandlers) {
91
- handler(msg);
92
- }
93
- }
94
- catch (error) {
95
- logger.error('WebStreamingService', 'Failed to handle client message', {
96
- clientId,
97
- error: error instanceof Error ? error.message : String(error),
98
- });
99
- }
100
- }
101
- /**
102
- * Register a handler for incoming client messages
103
- */
104
- onMessage(handler) {
105
- this.messageHandlers.add(handler);
106
- return () => {
107
- this.messageHandlers.delete(handler);
108
- };
109
- }
110
- /**
111
- * Send a message to a specific client
112
- */
113
- sendToClient(clientId, message) {
114
- const ws = this.clients.get(clientId);
115
- if (!ws || ws.readyState !== WebSocket.OPEN) {
116
- return;
117
- }
118
- try {
119
- ws.send(JSON.stringify(message));
120
- }
121
- catch (error) {
122
- logger.error('WebStreamingService', 'Failed to send to client', {
123
- clientId,
124
- error: error instanceof Error ? error.message : String(error),
125
- });
126
- }
127
- }
128
- /**
129
- * Broadcast a message to all connected clients
130
- */
131
- broadcast(message) {
132
- const data = JSON.stringify(message);
133
- for (const [clientId, ws] of this.clients) {
134
- if (ws.readyState === WebSocket.OPEN) {
135
- try {
136
- ws.send(data);
137
- }
138
- catch (error) {
139
- logger.error('WebStreamingService', 'Failed to broadcast to client', {
140
- clientId,
141
- error: error instanceof Error ? error.message : String(error),
142
- });
143
- }
144
- }
145
- }
146
- }
147
- /**
148
- * Compute partial state delta (only changed fields)
149
- */
150
- computeDelta(newState) {
151
- if (!this.prevState) {
152
- return newState;
153
- }
154
- const delta = {};
155
- // Check each field for changes
156
- const fields = [
157
- 'currentTrack',
158
- 'isPlaying',
159
- 'volume',
160
- 'speed',
161
- 'progress',
162
- 'duration',
163
- 'queue',
164
- 'queuePosition',
165
- 'repeat',
166
- 'shuffle',
167
- 'autoplay',
168
- 'isLoading',
169
- 'error',
170
- ];
171
- for (const field of fields) {
172
- const prevValue = this.prevState[field];
173
- const newValue = newState[field];
174
- // Deep comparison for objects/arrays
175
- if (typeof prevValue === 'object' && prevValue !== null) {
176
- if (JSON.stringify(prevValue) !== JSON.stringify(newValue)) {
177
- delta[field] = newValue;
178
- }
179
- }
180
- else if (prevValue !== newValue) {
181
- delta[field] = newValue;
182
- }
183
- }
184
- return delta;
185
- }
186
- /**
187
- * Update and broadcast player state (throttled)
188
- */
189
- onStateChange(state) {
190
- const now = Date.now();
191
- // Compute delta against OLD prevState BEFORE updating it
192
- const delta = this.computeDelta(state);
193
- this.prevState = { ...state };
194
- // Skip if no changes
195
- if (Object.keys(delta).length === 0) {
196
- return;
197
- }
198
- // Merge with pending update
199
- this.pendingUpdate = { ...(this.pendingUpdate || {}), ...delta };
200
- // Clear existing timer
201
- if (this.updateTimer) {
202
- clearTimeout(this.updateTimer);
203
- }
204
- // If throttle period passed, send immediately
205
- if (now - this.lastUpdateTime >= this.UPDATE_THROTTLE_MS) {
206
- this.sendPendingUpdate();
207
- }
208
- else {
209
- // Otherwise, schedule update
210
- this.updateTimer = setTimeout(() => {
211
- this.sendPendingUpdate();
212
- }, this.UPDATE_THROTTLE_MS - (now - this.lastUpdateTime));
213
- }
214
- }
215
- /**
216
- * Send pending state update to all clients
217
- */
218
- sendPendingUpdate() {
219
- if (!this.pendingUpdate)
220
- return;
221
- this.broadcast({
222
- type: 'state-update',
223
- state: this.pendingUpdate,
224
- });
225
- this.pendingUpdate = null;
226
- this.lastUpdateTime = Date.now();
227
- }
228
- /**
229
- * Broadcast import progress
230
- */
231
- onImportProgress(progress) {
232
- this.broadcast({
233
- type: 'import-progress',
234
- data: progress,
235
- });
236
- }
237
- /**
238
- * Broadcast import result
239
- */
240
- onImportResult(result) {
241
- this.broadcast({
242
- type: 'import-result',
243
- data: result,
244
- });
245
- }
246
- /**
247
- * Broadcast error message
248
- */
249
- sendError(error, code) {
250
- this.broadcast({
251
- type: 'error',
252
- error,
253
- code,
254
- });
255
- }
256
- /**
257
- * Disconnect all clients
258
- */
259
- disconnectAll() {
260
- for (const [, ws] of this.clients) {
261
- try {
262
- ws.close();
263
- }
264
- catch {
265
- // Ignore
266
- }
267
- }
268
- this.clients.clear();
269
- this.clientInfo.clear();
270
- logger.info('WebStreamingService', 'All clients disconnected');
271
- }
272
- /**
273
- * Get server statistics
274
- */
275
- getStats() {
276
- const now = Date.now();
277
- const oldestClient = Array.from(this.clientInfo.values()).sort((a, b) => a.connectedAt - b.connectedAt)[0];
278
- return {
279
- clients: this.clients.size,
280
- totalConnections: this.clientInfo.size,
281
- uptime: oldestClient ? now - oldestClient.connectedAt : 0,
282
- };
283
- }
284
- }
285
- // Singleton instance
286
- let webStreamingServiceInstance = null;
287
- export function getWebStreamingService() {
288
- if (!webStreamingServiceInstance) {
289
- webStreamingServiceInstance = new WebStreamingService();
290
- }
291
- return webStreamingServiceInstance;
292
- }
@@ -1,267 +0,0 @@
1
- // WebSocket server for web UI
2
- import { createServer as createHttpServer, } from 'node:http';
3
- import { WebSocketServer, WebSocket } from 'ws';
4
- import { getWebStreamingService } from "./web-streaming.service.js";
5
- import { getStaticFileService } from "./static-file.service.js";
6
- import { logger } from "../logger/logger.service.js";
7
- class WebSocketServerClass {
8
- httpServer = null;
9
- wsServer = null;
10
- config;
11
- streamingService = getWebStreamingService();
12
- staticFileService = getStaticFileService();
13
- onCommand;
14
- onImportRequest;
15
- onSearchRequest;
16
- onConfigUpdate;
17
- constructor() {
18
- this.config = {
19
- enabled: false,
20
- host: 'localhost',
21
- port: 8080,
22
- enableCors: true,
23
- allowedOrigins: ['*'],
24
- auth: { enabled: false },
25
- };
26
- }
27
- /**
28
- * Start the WebSocket server
29
- */
30
- async start(options) {
31
- this.config = options.config;
32
- this.onCommand = options.onCommand;
33
- this.onImportRequest = options.onImportRequest;
34
- this.onSearchRequest = options.onSearchRequest;
35
- this.onConfigUpdate = options.onConfigUpdate;
36
- logger.info('WebSocketServer', 'Starting server', {
37
- host: this.config.host,
38
- port: this.config.port,
39
- auth: this.config.auth.enabled,
40
- });
41
- // Create HTTP server
42
- this.httpServer = createHttpServer((req, res) => {
43
- this.handleHttpRequest(req, res);
44
- });
45
- // Create WebSocket server
46
- this.wsServer = new WebSocketServer({
47
- server: this.httpServer,
48
- path: '/ws',
49
- });
50
- // Handle WebSocket connections
51
- this.wsServer.on('connection', (ws, req) => {
52
- this.handleWebSocketConnection(ws, req);
53
- });
54
- // Handle HTTP server errors
55
- this.httpServer.on('error', error => {
56
- logger.error('WebSocketServer', 'HTTP server error', {
57
- error: error instanceof Error ? error.message : String(error),
58
- });
59
- });
60
- // Handle WebSocket server errors
61
- this.wsServer.on('error', error => {
62
- logger.error('WebSocketServer', 'WebSocket server error', {
63
- error: error instanceof Error ? error.message : String(error),
64
- });
65
- });
66
- // Start listening
67
- return new Promise((resolve, reject) => {
68
- this.httpServer.listen({
69
- host: this.config.host,
70
- port: this.config.port,
71
- }, () => {
72
- logger.info('WebSocketServer', 'Server started', {
73
- url: `http://${this.config.host}:${this.config.port}`,
74
- });
75
- resolve();
76
- });
77
- this.httpServer.on('error', reject);
78
- });
79
- }
80
- /**
81
- * Handle HTTP requests (for static file serving)
82
- */
83
- handleHttpRequest(req, res) {
84
- // Set CORS headers if enabled
85
- if (this.config.enableCors) {
86
- const origin = req.headers.origin;
87
- if (origin &&
88
- (this.config.allowedOrigins.includes('*') ||
89
- this.config.allowedOrigins.includes(origin))) {
90
- res.setHeader('Access-Control-Allow-Origin', origin);
91
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
92
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
93
- }
94
- }
95
- // Handle preflight requests
96
- if (req.method === 'OPTIONS') {
97
- res.writeHead(204);
98
- res.end();
99
- return;
100
- }
101
- // Serve static files
102
- const url = req.url ?? '/';
103
- this.staticFileService.serve(url, req, res);
104
- }
105
- /**
106
- * Handle WebSocket connection
107
- */
108
- handleWebSocketConnection(ws, req) {
109
- const clientId = this.generateClientId();
110
- logger.info('WebSocketServer', 'New connection', {
111
- clientId,
112
- ip: req.socket.remoteAddress,
113
- });
114
- // Handle authentication if enabled
115
- if (this.config.auth.enabled) {
116
- const token = this.extractAuthToken(req);
117
- if (!token || token !== this.config.auth.token) {
118
- logger.warn('WebSocketServer', 'Authentication failed', { clientId });
119
- ws.close(1008, 'Authentication failed');
120
- return;
121
- }
122
- }
123
- // Add client to streaming service
124
- this.streamingService.addClient(clientId, ws, true);
125
- // Send welcome message
126
- this.sendToClient(ws, {
127
- type: 'auth',
128
- success: true,
129
- message: 'Connected to YouTube Music CLI',
130
- });
131
- // Handle incoming messages
132
- ws.on('message', (data) => {
133
- try {
134
- const message = JSON.parse(data.toString());
135
- this.streamingService.handleClientMessage(clientId, message);
136
- this.handleClientCommand(clientId, message);
137
- }
138
- catch (error) {
139
- logger.error('WebSocketServer', 'Failed to parse message', {
140
- clientId,
141
- error: error instanceof Error ? error.message : String(error),
142
- });
143
- }
144
- });
145
- // Handle close
146
- ws.on('close', () => {
147
- this.streamingService.removeClient(clientId);
148
- });
149
- // Handle errors
150
- ws.on('error', error => {
151
- logger.error('WebSocketServer', 'WebSocket error', {
152
- error: error instanceof Error ? error.message : String(error),
153
- });
154
- });
155
- }
156
- /**
157
- * Handle commands from clients
158
- */
159
- handleClientCommand(_clientId, message) {
160
- switch (message.type) {
161
- case 'command':
162
- if (this.onCommand) {
163
- this.onCommand(message.action);
164
- }
165
- break;
166
- case 'import-request':
167
- if (this.onImportRequest) {
168
- this.onImportRequest(message.source, message.url, message.name);
169
- }
170
- break;
171
- case 'search-request':
172
- if (this.onSearchRequest) {
173
- this.onSearchRequest(message.query, message.searchType);
174
- }
175
- break;
176
- case 'config-update':
177
- if (this.onConfigUpdate) {
178
- this.onConfigUpdate(message.config);
179
- }
180
- break;
181
- case 'auth-request':
182
- // Already handled in connection phase
183
- break;
184
- }
185
- }
186
- /**
187
- * Send message to a specific WebSocket client
188
- */
189
- sendToClient(ws, message) {
190
- if (ws.readyState === WebSocket.OPEN) {
191
- try {
192
- ws.send(JSON.stringify(message));
193
- }
194
- catch (error) {
195
- logger.error('WebSocketServer', 'Failed to send message', {
196
- error: error instanceof Error ? error.message : String(error),
197
- });
198
- }
199
- }
200
- }
201
- /**
202
- * Generate a unique client ID
203
- */
204
- generateClientId() {
205
- return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
206
- }
207
- /**
208
- * Extract auth token from request
209
- */
210
- extractAuthToken(req) {
211
- // Check Authorization header
212
- const authHeader = req.headers.authorization;
213
- if (authHeader?.startsWith('Bearer ')) {
214
- return authHeader.substring(7);
215
- }
216
- // Check query parameter
217
- const url = req.url ?? '';
218
- const urlObj = new URL(url, `http://${req.headers.host}`);
219
- return urlObj.searchParams.get('token');
220
- }
221
- /**
222
- * Stop the server
223
- */
224
- async stop() {
225
- logger.info('WebSocketServer', 'Stopping server');
226
- // Disconnect all clients
227
- this.streamingService.disconnectAll();
228
- // Close WebSocket server
229
- if (this.wsServer) {
230
- this.wsServer.close();
231
- this.wsServer = null;
232
- }
233
- // Close HTTP server
234
- if (this.httpServer) {
235
- return new Promise(resolve => {
236
- this.httpServer.close(() => {
237
- this.httpServer = null;
238
- logger.info('WebSocketServer', 'Server stopped');
239
- resolve();
240
- });
241
- });
242
- }
243
- }
244
- /**
245
- * Check if server is running
246
- */
247
- isRunning() {
248
- return this.httpServer !== null;
249
- }
250
- /**
251
- * Get server URL
252
- */
253
- getServerUrl() {
254
- if (!this.isRunning()) {
255
- throw new Error('Server is not running');
256
- }
257
- return `http://${this.config.host}:${this.config.port}`;
258
- }
259
- }
260
- // Singleton instance
261
- let webSocketServerInstance = null;
262
- export function getWebSocketServer() {
263
- if (!webSocketServerInstance) {
264
- webSocketServerInstance = new WebSocketServerClass();
265
- }
266
- return webSocketServerInstance;
267
- }