@involvex/youtube-music-cli 0.0.47 → 0.0.49

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 +8 -0
  2. package/dist/cli.js.map +6 -6
  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,504 +0,0 @@
1
- #!/usr/bin/env node
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import App from "./app.js";
4
- import { render } from 'ink';
5
- import meow from 'meow';
6
- import { getPluginInstallerService } from "./services/plugin/plugin-installer.service.js";
7
- import { getPluginUpdaterService } from "./services/plugin/plugin-updater.service.js";
8
- import { getPluginRegistryService } from "./services/plugin/plugin-registry.service.js";
9
- import { getImportService } from "./services/import/import.service.js";
10
- import { getWebServerManager } from "./services/web/web-server-manager.js";
11
- import { getWebStreamingService } from "./services/web/web-streaming.service.js";
12
- import { getVersionCheckService } from "./services/version-check/version-check.service.js";
13
- import { generateCompletion, } from "./services/completions/completions.service.js";
14
- import { getConfigService } from "./services/config/config.service.js";
15
- import { getPlayerService } from "./services/player/player.service.js";
16
- import { APP_VERSION } from "./utils/constants.js";
17
- import { ensurePlaybackDependencies } from "./services/player/dependency-check.service.js";
18
- import { getMusicService } from "./services/youtube-music/api.js";
19
- const isStandalone = process
20
- .isStandaloneExecutable ||
21
- globalThis.Bun?.isStandalone;
22
- const argv = isStandalone ? process.argv.slice(1) : process.argv.slice(2);
23
- const cli = meow(`
24
- youtube-music-cli@${APP_VERSION}
25
-
26
- Usage
27
- $ youtube-music-cli
28
- $ youtube-music-cli play <track-id|youtube-url>
29
- $ youtube-music-cli search <query>
30
- $ youtube-music-cli playlist <playlist-id>
31
- $ youtube-music-cli suggestions
32
- $ youtube-music-cli pause
33
- $ youtube-music-cli resume
34
- $ youtube-music-cli skip
35
- $ youtube-music-cli back
36
-
37
- Plugin Commands
38
- $ youtube-music-cli plugins list
39
- $ youtube-music-cli plugins install <name|url>
40
- $ youtube-music-cli plugins remove <name>
41
- $ youtube-music-cli plugins update <name>
42
- $ youtube-music-cli plugins enable <name>
43
- $ youtube-music-cli plugins disable <name>
44
-
45
- Import Commands
46
- $ youtube-music-cli import spotify <url-or-id>
47
- $ youtube-music-cli import youtube <url-or-id>
48
-
49
- Options
50
- --theme, -t Theme to use (dark, light, midnight, matrix)
51
- --volume, -v Initial volume (0-100)
52
- --shuffle, -s Enable shuffle mode
53
- --repeat, -r Repeat mode (off, all, one)
54
- --headless Run without TUI (just play)
55
- --web Enable web UI server
56
- --web-host Web server host (default: localhost)
57
- --web-port Web server port (default: 8080)
58
- --web-only Run web server without CLI UI
59
- --web-auth Authentication token for web server
60
- --name Custom name for imported playlist
61
- --help, -h Show this help
62
-
63
- Shell Completions
64
- $ youtube-music-cli completions bash
65
- $ youtube-music-cli completions zsh
66
- $ youtube-music-cli completions powershell
67
- $ youtube-music-cli completions fish
68
-
69
- Examples
70
- $ youtube-music-cli
71
- $ youtube-music-cli play dQw4w9WgXcQ
72
- $ youtube-music-cli search "Rick Astley"
73
- $ youtube-music-cli play dQw4w9WgXcQ --headless
74
- $ youtube-music-cli plugins install adblock
75
- $ youtube-music-cli import spotify "https://open.spotify.com/playlist/..."
76
- $ youtube-music-cli --web --web-port 3000
77
- $ youtube-music-cli completions powershell | Out-File $PROFILE
78
- `, {
79
- importMeta: import.meta,
80
- argv,
81
- flags: {
82
- theme: {
83
- type: 'string',
84
- shortFlag: 't',
85
- },
86
- volume: {
87
- type: 'number',
88
- shortFlag: 'v',
89
- },
90
- shuffle: {
91
- type: 'boolean',
92
- shortFlag: 's',
93
- default: false,
94
- },
95
- repeat: {
96
- type: 'string',
97
- shortFlag: 'r',
98
- },
99
- headless: {
100
- type: 'boolean',
101
- default: false,
102
- },
103
- web: {
104
- type: 'boolean',
105
- default: false,
106
- },
107
- webHost: {
108
- type: 'string',
109
- },
110
- webPort: {
111
- type: 'number',
112
- },
113
- webOnly: {
114
- type: 'boolean',
115
- default: false,
116
- },
117
- webAuth: {
118
- type: 'string',
119
- },
120
- name: {
121
- type: 'string',
122
- },
123
- help: {
124
- type: 'boolean',
125
- shortFlag: 'h',
126
- default: false,
127
- },
128
- },
129
- autoVersion: true,
130
- autoHelp: false,
131
- });
132
- if (cli.flags.help) {
133
- cli.showHelp(0);
134
- }
135
- // Handle plugin commands
136
- const command = cli.input[0];
137
- const args = cli.input.slice(1);
138
- const isInteractiveTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY);
139
- function requiresImmediatePlayback(flags) {
140
- return Boolean(flags.playTrack || flags.searchQuery || flags.playPlaylist);
141
- }
142
- function shouldCheckPlaybackDependencies(commandName, flags) {
143
- if (flags.webOnly) {
144
- return false;
145
- }
146
- if (requiresImmediatePlayback(flags)) {
147
- return true;
148
- }
149
- return (commandName === undefined ||
150
- commandName === 'suggestions' ||
151
- Boolean(flags.web));
152
- }
153
- async function runDirectPlaybackCommand(flags) {
154
- const musicService = getMusicService();
155
- const playerService = getPlayerService();
156
- const config = getConfigService();
157
- const playbackOptions = {
158
- volume: flags.volume ?? config.get('volume'),
159
- audioNormalization: config.get('audioNormalization'),
160
- volumeFadeDuration: config.get('volumeFadeDuration'),
161
- };
162
- let track;
163
- if (flags.playTrack) {
164
- track = await musicService.getTrack(flags.playTrack);
165
- if (!track) {
166
- throw new Error(`Track not found: ${flags.playTrack}`);
167
- }
168
- }
169
- else if (flags.searchQuery) {
170
- const response = await musicService.search(flags.searchQuery, {
171
- type: 'songs',
172
- limit: 1,
173
- });
174
- const firstSong = response.results.find(result => result.type === 'song');
175
- if (!firstSong) {
176
- throw new Error(`No playable tracks found for: "${flags.searchQuery}"`);
177
- }
178
- track = firstSong.data;
179
- }
180
- else if (flags.playPlaylist) {
181
- const playlist = await musicService.getPlaylist(flags.playPlaylist);
182
- track = playlist.tracks[0];
183
- if (!track) {
184
- throw new Error(`No playable tracks found in playlist: ${flags.playPlaylist}`);
185
- }
186
- }
187
- if (!track) {
188
- throw new Error('No track resolved for playback command.');
189
- }
190
- const artists = track.artists.length > 0
191
- ? track.artists.map(artist => artist.name).join(', ')
192
- : 'Unknown Artist';
193
- console.log(`Playing: ${track.title} — ${artists}`);
194
- const youtubeUrl = `https://www.youtube.com/watch?v=${track.videoId}`;
195
- await playerService.play(youtubeUrl, playbackOptions);
196
- }
197
- if (command === 'plugins') {
198
- const subCommand = args[0];
199
- const pluginArg = args[1];
200
- void (async () => {
201
- const installer = getPluginInstallerService();
202
- const updater = getPluginUpdaterService();
203
- const registry = getPluginRegistryService();
204
- // Load existing plugins
205
- await registry.loadAllPlugins();
206
- switch (subCommand) {
207
- case 'list': {
208
- const plugins = registry.getAllPlugins();
209
- if (plugins.length === 0) {
210
- console.log('No plugins installed.');
211
- }
212
- else {
213
- console.log('Installed plugins:');
214
- for (const plugin of plugins) {
215
- const status = plugin.enabled ? '●' : '○';
216
- console.log(` ${status} ${plugin.manifest.name} v${plugin.manifest.version}`);
217
- }
218
- }
219
- process.exit(0);
220
- break;
221
- }
222
- case 'install': {
223
- if (!pluginArg) {
224
- console.error('Usage: youtube-music-cli plugins install <name|url>');
225
- process.exit(1);
226
- }
227
- console.log(`Installing ${pluginArg}...`);
228
- let result;
229
- if (pluginArg.startsWith('http')) {
230
- result = await installer.installFromGitHub(pluginArg);
231
- }
232
- else {
233
- result = await installer.installFromDefaultRepo(pluginArg);
234
- }
235
- if (result.success) {
236
- console.log(`✓ Successfully installed ${result.pluginId}`);
237
- }
238
- else {
239
- console.error(`✗ Failed: ${result.error}`);
240
- process.exit(1);
241
- }
242
- process.exit(0);
243
- break;
244
- }
245
- case 'remove':
246
- case 'uninstall': {
247
- if (!pluginArg) {
248
- console.error('Usage: youtube-music-cli plugins remove <name>');
249
- process.exit(1);
250
- }
251
- console.log(`Removing ${pluginArg}...`);
252
- try {
253
- await registry.unloadPlugin(pluginArg);
254
- }
255
- catch {
256
- // Plugin may not be loaded
257
- }
258
- const result = await installer.uninstall(pluginArg);
259
- if (result.success) {
260
- console.log(`✓ Successfully removed ${pluginArg}`);
261
- }
262
- else {
263
- console.error(`✗ Failed: ${result.error}`);
264
- process.exit(1);
265
- }
266
- process.exit(0);
267
- break;
268
- }
269
- case 'update': {
270
- if (!pluginArg) {
271
- console.error('Usage: youtube-music-cli plugins update <name>');
272
- process.exit(1);
273
- }
274
- console.log(`Updating ${pluginArg}...`);
275
- const result = await updater.updatePlugin(pluginArg);
276
- if (result.success) {
277
- console.log(`✓ Updated ${pluginArg} from ${result.oldVersion} to ${result.newVersion}`);
278
- }
279
- else {
280
- console.error(`✗ Failed: ${result.error}`);
281
- process.exit(1);
282
- }
283
- process.exit(0);
284
- break;
285
- }
286
- case 'enable': {
287
- if (!pluginArg) {
288
- console.error('Usage: youtube-music-cli plugins enable <name>');
289
- process.exit(1);
290
- }
291
- try {
292
- await registry.enablePlugin(pluginArg);
293
- console.log(`✓ Enabled ${pluginArg}`);
294
- }
295
- catch (error) {
296
- console.error(`✗ Failed: ${error instanceof Error ? error.message : String(error)}`);
297
- process.exit(1);
298
- }
299
- process.exit(0);
300
- break;
301
- }
302
- case 'disable': {
303
- if (!pluginArg) {
304
- console.error('Usage: youtube-music-cli plugins disable <name>');
305
- process.exit(1);
306
- }
307
- try {
308
- await registry.disablePlugin(pluginArg);
309
- console.log(`✓ Disabled ${pluginArg}`);
310
- }
311
- catch (error) {
312
- console.error(`✗ Failed: ${error instanceof Error ? error.message : String(error)}`);
313
- process.exit(1);
314
- }
315
- process.exit(0);
316
- break;
317
- }
318
- default:
319
- console.error('Usage: youtube-music-cli plugins <list|install|remove|update|enable|disable>');
320
- process.exit(1);
321
- }
322
- })();
323
- }
324
- else {
325
- // Handle other direct commands
326
- if (command === 'completions') {
327
- const shell = args[0];
328
- const validShells = ['bash', 'zsh', 'powershell', 'fish'];
329
- if (!shell || !validShells.includes(shell)) {
330
- console.error('Usage: youtube-music-cli completions <bash|zsh|powershell|fish>');
331
- process.exit(1);
332
- }
333
- console.log(generateCompletion(shell));
334
- process.exit(0);
335
- }
336
- else if (command === 'play' && args[0]) {
337
- // Play specific track
338
- cli.flags.playTrack = args[0];
339
- }
340
- else if (command === 'search' && args[0]) {
341
- // Search for query
342
- cli.flags.searchQuery = args.join(' ');
343
- }
344
- else if (command === 'playlist' && args[0]) {
345
- // Play specific playlist
346
- cli.flags.playPlaylist = args[0];
347
- }
348
- else if (command === 'suggestions') {
349
- // Show suggestions
350
- cli.flags.showSuggestions = true;
351
- }
352
- else if (command === 'pause') {
353
- cli.flags.action = 'pause';
354
- }
355
- else if (command === 'resume') {
356
- cli.flags.action = 'resume';
357
- }
358
- else if (command === 'skip') {
359
- cli.flags.action = 'next';
360
- }
361
- else if (command === 'back') {
362
- cli.flags.action = 'previous';
363
- }
364
- const flags = cli.flags;
365
- const shouldRunDirectPlayback = requiresImmediatePlayback(flags) &&
366
- (flags.headless || !isInteractiveTerminal);
367
- if (shouldRunDirectPlayback) {
368
- void (async () => {
369
- const dependencyCheck = await ensurePlaybackDependencies({
370
- interactive: isInteractiveTerminal,
371
- });
372
- if (!dependencyCheck.ready) {
373
- process.exit(1);
374
- return;
375
- }
376
- try {
377
- await runDirectPlaybackCommand(flags);
378
- process.exit(0);
379
- }
380
- catch (error) {
381
- console.error(`✗ Playback failed: ${error instanceof Error ? error.message : String(error)}`);
382
- process.exit(1);
383
- }
384
- })();
385
- }
386
- else if (command === 'import') {
387
- // Handle import commands
388
- void (async () => {
389
- const source = args[0];
390
- const url = args[1];
391
- if (!source || !url) {
392
- console.error('Usage: youtube-music-cli import <spotify|youtube> <url-or-id>');
393
- process.exit(1);
394
- }
395
- if (source !== 'spotify' && source !== 'youtube') {
396
- console.error('Invalid source. Use "spotify" or "youtube".');
397
- process.exit(1);
398
- }
399
- const importService = getImportService();
400
- const customName = cli.flags.name;
401
- try {
402
- console.log(`Importing ${source} playlist...`);
403
- const result = await importService.importPlaylist(source, url, customName);
404
- console.log(`\n✓ Import completed!`);
405
- console.log(` Playlist: ${result.playlistName}`);
406
- console.log(` Matched: ${result.matched}/${result.total} tracks`);
407
- if (result.errors.length > 0) {
408
- console.log(`\nErrors:`);
409
- for (const error of result.errors.slice(0, 10)) {
410
- console.log(` - ${error}`);
411
- }
412
- if (result.errors.length > 10) {
413
- console.log(` ... and ${result.errors.length - 10} more`);
414
- }
415
- }
416
- process.exit(0);
417
- }
418
- catch (error) {
419
- console.error(`✗ Import failed: ${error instanceof Error ? error.message : String(error)}`);
420
- process.exit(1);
421
- }
422
- })();
423
- }
424
- else if (cli.flags.web || cli.flags.webOnly) {
425
- // Handle web server flags
426
- void (async () => {
427
- const webManager = getWebServerManager();
428
- try {
429
- if (shouldCheckPlaybackDependencies(command, flags)) {
430
- const dependencyCheck = await ensurePlaybackDependencies({
431
- interactive: isInteractiveTerminal,
432
- });
433
- if (!dependencyCheck.ready && requiresImmediatePlayback(flags)) {
434
- process.exit(1);
435
- return;
436
- }
437
- }
438
- await webManager.start({
439
- enabled: true,
440
- host: cli.flags.webHost ?? 'localhost',
441
- port: cli.flags.webPort ?? 8080,
442
- webOnly: cli.flags.webOnly,
443
- auth: cli.flags.webAuth,
444
- });
445
- const serverUrl = webManager.getServerUrl();
446
- console.log(`Web UI server running at: ${serverUrl}`);
447
- // Set up import progress streaming
448
- const streamingService = getWebStreamingService();
449
- const importService = getImportService();
450
- importService.onProgress(progress => {
451
- streamingService.onImportProgress(progress);
452
- });
453
- // If web-only mode, just keep the server running
454
- if (cli.flags.webOnly) {
455
- console.log('Running in web-only mode. Press Ctrl+C to exit.');
456
- // Keep process alive
457
- process.on('SIGINT', () => {
458
- console.log('\nShutting down web server...');
459
- void webManager.stop().then(() => process.exit(0));
460
- });
461
- }
462
- else {
463
- // Also render the CLI UI
464
- render(_jsx(App, { flags: flags }));
465
- }
466
- }
467
- catch (error) {
468
- console.error(`Failed to start web server: ${error instanceof Error ? error.message : String(error)}`);
469
- process.exit(1);
470
- }
471
- })();
472
- }
473
- else {
474
- void (async () => {
475
- if (shouldCheckPlaybackDependencies(command, flags)) {
476
- const dependencyCheck = await ensurePlaybackDependencies({
477
- interactive: isInteractiveTerminal,
478
- });
479
- if (!dependencyCheck.ready && requiresImmediatePlayback(flags)) {
480
- process.exit(1);
481
- return;
482
- }
483
- }
484
- // Check for updates before rendering the app (skip in web-only mode)
485
- if (!cli.flags.webOnly) {
486
- const versionCheck = getVersionCheckService();
487
- const config = getConfigService();
488
- const lastCheck = config.getLastVersionCheck();
489
- if (versionCheck.shouldCheck(lastCheck)) {
490
- const result = await versionCheck.checkForUpdates(APP_VERSION);
491
- config.setLastVersionCheck(versionCheck.markChecked());
492
- if (result.hasUpdate) {
493
- console.log('');
494
- console.log(` Update available: ${APP_VERSION} → ${result.latestVersion}`);
495
- console.log('Run: npm install -g @involvex/youtube-music-cli');
496
- console.log('');
497
- }
498
- }
499
- }
500
- // Render the app
501
- render(_jsx(App, { flags: flags }));
502
- })();
503
- }
504
- }
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Error boundary component for robust error handling
3
- import { Component } from 'react';
4
- import { Box, Text } from 'ink';
5
- export class ErrorBoundary extends Component {
6
- state = {
7
- hasError: false,
8
- error: null,
9
- };
10
- static getDerivedStateFromError(error) {
11
- return { hasError: true, error };
12
- }
13
- componentDidCatch(error, errorInfo) {
14
- console.error('Uncaught error:', error, errorInfo);
15
- }
16
- render() {
17
- if (this.state.hasError) {
18
- return (_jsxs(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "red", children: [_jsx(Text, { color: "red", bold: true, children: "Something went wrong!" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "white", children: this.state.error?.message }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "dim", children: "Press Ctrl+C to exit and restart the CLI." }) })] }));
19
- }
20
- return this.props.children;
21
- }
22
- }
@@ -1,18 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Help component for keyboard shortcuts
3
- import { Box, Text } from 'ink';
4
- import { useTheme } from "../../hooks/useTheme.js";
5
- import { useNavigation } from "../../hooks/useNavigation.js";
6
- import { useKeyBinding } from "../../hooks/useKeyboard.js";
7
- import { KEYBINDINGS } from "../../utils/constants.js";
8
- import { useCallback } from 'react';
9
- export default function Help() {
10
- const { theme } = useTheme();
11
- const { dispatch } = useNavigation();
12
- const closeHelp = useCallback(() => {
13
- dispatch({ category: 'GO_BACK' });
14
- }, [dispatch]);
15
- useKeyBinding(KEYBINDINGS.BACK, closeHelp);
16
- useKeyBinding(KEYBINDINGS.SELECT, closeHelp);
17
- return (_jsxs(Box, { flexDirection: "column", gap: 1, padding: 1, children: [_jsx(Box, { borderStyle: "single", borderColor: theme.colors.secondary, paddingX: 1, children: _jsx(Text, { bold: true, color: theme.colors.primary, children: "Keyboard Shortcuts" }) }), _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Global" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "q" }), " - Quit", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "?" }), " - Help", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "/" }), " - Search", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+P" }), " - Playlists", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "p" }), " - Plugins", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "g" }), " - Suggestions", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "," }), " - Settings", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+Q" }), " - Background Play", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+R" }), " - Resume Control"] }) }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: "Player" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "Space" }), " - Play/Pause", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "n" }), " - Next", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "b" }), " - Previous", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "+/=" }), " - Volume Up", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "-" }), " - Volume Down", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+S" }), " - Toggle Shuffle", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "r" }), " - Toggle Repeat"] }) }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: "Navigation" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "Up" }), " /", _jsx(Text, { children: " " }), _jsx(Text, { color: theme.colors.text, children: "k" }), " - Move Up", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Down" }), " /", _jsx(Text, { children: " " }), _jsx(Text, { color: theme.colors.text, children: "j" }), " - Move Down", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Enter" }), " - Select", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Esc" }), " - Go Back"] }) }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: "Search" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "Tab" }), " - Switch Search Type", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "m" }), " - Create Mix Playlist", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+D" }), " - Download selection", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Esc" }), " - Clear Search", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "[ / ]" }), " - Results Limit"] }) }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: "Playlist" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "a" }), " - Add to Playlist", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "d" }), " - Remove from Playlist", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "c" }), " - Create Playlist", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "Shift+D" }), " - Download Playlist", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "D" }), " - Delete Playlist"] }) }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: "View" }), _jsx(Box, { paddingX: 2, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.text, children: "M" }), " - Toggle Mini Player", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "l" }), " - Lyrics", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "T" }), " - Trending", _jsx(Text, { children: " | " }), _jsx(Text, { color: theme.colors.text, children: "e" }), " - Explore"] }) }), _jsxs(Text, { color: theme.colors.dim, children: ["Press ", _jsx(Text, { color: theme.colors.text, children: "Esc" }), ",", ' ', _jsx(Text, { color: theme.colors.text, children: "Enter" }), ",", ' ', _jsx(Text, { color: theme.colors.text, children: "q" }), ", or", ' ', _jsx(Text, { color: theme.colors.text, children: "?" }), " to close"] })] })] }));
18
- }
@@ -1,89 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- // Shortcuts bar component
3
- import { useState } from 'react';
4
- import { Box, Text } from 'ink';
5
- import { usePlayer } from "../../hooks/usePlayer.js";
6
- import { useTheme } from "../../hooks/useTheme.js";
7
- import { useKeyBinding } from "../../hooks/useKeyboard.js";
8
- import { KEYBINDINGS } from "../../utils/constants.js";
9
- import { ICONS } from "../../utils/icons.js";
10
- const FLASH_DURATION_MS = 300;
11
- export default function ShortcutsBar() {
12
- const { theme } = useTheme();
13
- const { state: playerState, pause, resume, next, previous, volumeUp, volumeDown, volumeFineUp, volumeFineDown, toggleShuffle, toggleRepeat, toggleAutoplay, } = usePlayer();
14
- const [flashState, setFlashState] = useState({});
15
- const flash = (key) => {
16
- setFlashState(prev => ({ ...prev, [key]: true }));
17
- setTimeout(() => {
18
- setFlashState(prev => ({ ...prev, [key]: false }));
19
- }, FLASH_DURATION_MS);
20
- };
21
- const shortcutColor = (key) => flashState[key] ? theme.colors.success : theme.colors.text;
22
- // Register key bindings globally
23
- const handlePlayPause = () => {
24
- flash('playPause');
25
- if (playerState.isPlaying) {
26
- pause();
27
- }
28
- else {
29
- resume();
30
- }
31
- };
32
- useKeyBinding(KEYBINDINGS.PLAY_PAUSE, handlePlayPause);
33
- useKeyBinding(KEYBINDINGS.NEXT, () => {
34
- flash('next');
35
- next();
36
- });
37
- useKeyBinding(KEYBINDINGS.PREVIOUS, () => {
38
- flash('prev');
39
- previous();
40
- });
41
- useKeyBinding(KEYBINDINGS.VOLUME_UP, () => {
42
- flash('volume');
43
- volumeUp();
44
- });
45
- useKeyBinding(KEYBINDINGS.VOLUME_DOWN, () => {
46
- flash('volume');
47
- volumeDown();
48
- });
49
- useKeyBinding(KEYBINDINGS.VOLUME_FINE_UP, () => {
50
- flash('volume');
51
- volumeFineUp();
52
- });
53
- useKeyBinding(KEYBINDINGS.VOLUME_FINE_DOWN, () => {
54
- flash('volume');
55
- volumeFineDown();
56
- });
57
- useKeyBinding(KEYBINDINGS.SHUFFLE, () => {
58
- flash('shuffle');
59
- toggleShuffle();
60
- });
61
- useKeyBinding(KEYBINDINGS.REPEAT, () => {
62
- flash('repeat');
63
- toggleRepeat();
64
- });
65
- useKeyBinding(KEYBINDINGS.AUTOPLAY_TOGGLE, () => {
66
- flash('autoplay');
67
- toggleAutoplay();
68
- });
69
- // Note: SETTINGS keybinding handled by MainLayout to avoid double-dispatch
70
- const shuffleColor = flashState['shuffle']
71
- ? theme.colors.success
72
- : playerState.shuffle
73
- ? theme.colors.primary
74
- : theme.colors.dim;
75
- const repeatColor = flashState['repeat']
76
- ? theme.colors.success
77
- : playerState.repeat !== 'off'
78
- ? theme.colors.secondary
79
- : theme.colors.dim;
80
- const volumeColor = flashState['volume']
81
- ? theme.colors.success
82
- : theme.colors.primary;
83
- const autoplayColor = flashState['autoplay']
84
- ? theme.colors.success
85
- : playerState.autoplay
86
- ? theme.colors.primary
87
- : theme.colors.dim;
88
- return (_jsxs(Box, { borderStyle: "single", borderColor: theme.colors.dim, paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { color: theme.colors.dim, children: [_jsxs(Text, { color: shortcutColor('playPause'), children: [playerState.isPlaying ? ICONS.PAUSE : ICONS.PLAY_PAUSE_ON, " [Space]"] }), ' ', "| ", _jsxs(Text, { color: shortcutColor('prev'), children: [ICONS.PREV, " [B/\u2190]"] }), " |", ' ', _jsxs(Text, { color: shortcutColor('next'), children: [ICONS.NEXT, " [N/\u2192]"] }), " |", ' ', _jsxs(Text, { color: shuffleColor, children: [ICONS.SHUFFLE, " [Shift+S]"] }), " |", ' ', _jsxs(Text, { color: repeatColor, children: [playerState.repeat === 'one' ? ICONS.REPEAT_ONE : ICONS.REPEAT_ALL, ' ', "[R]"] }), ' ', "| ", _jsxs(Text, { color: autoplayColor, children: [ICONS.AUTOPLAY, " [Shift+A]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.PLAYLIST, " [Shift+P]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.DOWNLOAD, " [Shift+D]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.SEARCH, " [/]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.HELP, " [?]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.BG_PLAY, " [Shift+Q]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.RESUME, " [Shift+R]"] }), " |", ' ', _jsxs(Text, { color: theme.colors.text, children: [ICONS.QUIT, " [Q]"] })] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: shuffleColor, children: ICONS.SHUFFLE }), ' ', _jsx(Text, { color: repeatColor, children: playerState.repeat === 'one' ? ICONS.REPEAT_ONE : ICONS.REPEAT_ALL }), ' ', _jsx(Text, { color: autoplayColor, children: ICONS.AUTOPLAY }), ' ', _jsxs(Text, { color: theme.colors.dim, children: [ICONS.VOLUME, " [+/-]"] }), ' ', _jsxs(Text, { color: volumeColor, children: [playerState.volume, "%"] })] })] }));
89
- }