@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.
Files changed (118) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/cli.js.map +1004 -0
  3. package/dist/source/hooks/usePlayer.d.ts +1 -0
  4. package/dist/source/services/player-state/player-state.service.d.ts +1 -0
  5. package/dist/source/stores/player.store.d.ts +1 -0
  6. package/dist/source/types/actions.d.ts +4 -0
  7. package/dist/source/types/player.types.d.ts +3 -2
  8. package/dist/source/utils/constants.d.ts +1 -0
  9. package/dist/source/utils/icons.d.ts +1 -0
  10. package/dist/youtube-music-cli +0 -0
  11. package/package.json +1 -1
  12. package/dist/eslint.config.js +0 -55
  13. package/dist/package.json +0 -120
  14. package/dist/scripts/build-cli.js +0 -46
  15. package/dist/source/app.js +0 -17
  16. package/dist/source/cli.js +0 -504
  17. package/dist/source/components/common/ErrorBoundary.js +0 -22
  18. package/dist/source/components/common/Help.js +0 -18
  19. package/dist/source/components/common/ShortcutsBar.js +0 -80
  20. package/dist/source/components/config/ConfigLayout.js +0 -84
  21. package/dist/source/components/config/KeybindingsLayout.js +0 -107
  22. package/dist/source/components/export/ExportLayout.js +0 -111
  23. package/dist/source/components/import/ImportLayout.js +0 -119
  24. package/dist/source/components/import/ImportProgress.js +0 -73
  25. package/dist/source/components/layouts/ExploreLayout.js +0 -72
  26. package/dist/source/components/layouts/HistoryLayout.js +0 -37
  27. package/dist/source/components/layouts/LyricsLayout.js +0 -89
  28. package/dist/source/components/layouts/MainLayout.js +0 -190
  29. package/dist/source/components/layouts/MiniPlayerLayout.js +0 -20
  30. package/dist/source/components/layouts/PlayerLayout.js +0 -9
  31. package/dist/source/components/layouts/PluginsLayout.js +0 -77
  32. package/dist/source/components/layouts/SearchLayout.js +0 -193
  33. package/dist/source/components/layouts/TrendingLayout.js +0 -59
  34. package/dist/source/components/player/NowPlaying.js +0 -45
  35. package/dist/source/components/player/PlayerControls.js +0 -83
  36. package/dist/source/components/player/ProgressBar.js +0 -19
  37. package/dist/source/components/player/QueueList.js +0 -36
  38. package/dist/source/components/player/Suggestions.js +0 -50
  39. package/dist/source/components/playlist/PlaylistList.js +0 -138
  40. package/dist/source/components/plugins/PluginInstallDialog.js +0 -41
  41. package/dist/source/components/plugins/PluginsAvailable.js +0 -55
  42. package/dist/source/components/plugins/PluginsList.js +0 -18
  43. package/dist/source/components/search/SearchBar.js +0 -55
  44. package/dist/source/components/search/SearchHistory.js +0 -35
  45. package/dist/source/components/search/SearchResults.js +0 -280
  46. package/dist/source/components/settings/Settings.js +0 -211
  47. package/dist/source/components/theme/ThemeSwitcher.js +0 -11
  48. package/dist/source/config/themes.config.js +0 -123
  49. package/dist/source/contexts/theme.context.js +0 -29
  50. package/dist/source/hooks/useKeyboard.js +0 -188
  51. package/dist/source/hooks/useKeyboardBlocker.js +0 -45
  52. package/dist/source/hooks/useNavigation.js +0 -5
  53. package/dist/source/hooks/usePlayer.js +0 -43
  54. package/dist/source/hooks/usePlaylist.js +0 -65
  55. package/dist/source/hooks/useSearch.js +0 -76
  56. package/dist/source/hooks/useSleepTimer.js +0 -48
  57. package/dist/source/hooks/useTerminalSize.js +0 -24
  58. package/dist/source/hooks/useTheme.js +0 -5
  59. package/dist/source/hooks/useYouTubeMusic.js +0 -112
  60. package/dist/source/main.js +0 -127
  61. package/dist/source/services/cache/cache.service.js +0 -67
  62. package/dist/source/services/completions/completions.service.js +0 -313
  63. package/dist/source/services/config/config.service.js +0 -191
  64. package/dist/source/services/discord/discord-rpc.service.js +0 -95
  65. package/dist/source/services/download/download.service.js +0 -350
  66. package/dist/source/services/export/export.service.js +0 -131
  67. package/dist/source/services/history/history.service.js +0 -83
  68. package/dist/source/services/import/import.service.js +0 -272
  69. package/dist/source/services/import/spotify.service.js +0 -171
  70. package/dist/source/services/import/track-matcher.service.js +0 -271
  71. package/dist/source/services/import/youtube-import.service.js +0 -84
  72. package/dist/source/services/logger/logger.service.js +0 -52
  73. package/dist/source/services/lyrics/lyrics.service.js +0 -93
  74. package/dist/source/services/mpris/mpris.service.js +0 -78
  75. package/dist/source/services/notification/notification.service.js +0 -57
  76. package/dist/source/services/player/dependency-check.service.js +0 -140
  77. package/dist/source/services/player/player.service.js +0 -478
  78. package/dist/source/services/player-state/player-state.service.js +0 -122
  79. package/dist/source/services/plugin/plugin-audio-api.js +0 -36
  80. package/dist/source/services/plugin/plugin-context.js +0 -256
  81. package/dist/source/services/plugin/plugin-hooks.service.js +0 -135
  82. package/dist/source/services/plugin/plugin-installer.service.js +0 -248
  83. package/dist/source/services/plugin/plugin-loader.service.js +0 -161
  84. package/dist/source/services/plugin/plugin-permissions.service.js +0 -194
  85. package/dist/source/services/plugin/plugin-registry.service.js +0 -215
  86. package/dist/source/services/plugin/plugin-ui-api.js +0 -46
  87. package/dist/source/services/plugin/plugin-updater.service.js +0 -206
  88. package/dist/source/services/scrobbling/scrobbling.service.js +0 -115
  89. package/dist/source/services/sleep-timer/sleep-timer.service.js +0 -45
  90. package/dist/source/services/version-check/version-check.service.js +0 -121
  91. package/dist/source/services/web/static-file.service.js +0 -185
  92. package/dist/source/services/web/web-server-manager.js +0 -506
  93. package/dist/source/services/web/web-streaming.service.js +0 -290
  94. package/dist/source/services/web/websocket.server.js +0 -267
  95. package/dist/source/services/youtube-music/api.js +0 -649
  96. package/dist/source/services/youtube-music/search.service.js +0 -38
  97. package/dist/source/stores/history.store.js +0 -64
  98. package/dist/source/stores/navigation.store.js +0 -90
  99. package/dist/source/stores/player.store.js +0 -724
  100. package/dist/source/stores/plugins.store.js +0 -177
  101. package/dist/source/types/actions.js +0 -1
  102. package/dist/source/types/cli.types.js +0 -1
  103. package/dist/source/types/config.types.js +0 -1
  104. package/dist/source/types/history.types.js +0 -1
  105. package/dist/source/types/import.types.js +0 -2
  106. package/dist/source/types/keyboard.types.js +0 -1
  107. package/dist/source/types/navigation.types.js +0 -1
  108. package/dist/source/types/player.types.js +0 -1
  109. package/dist/source/types/playlist.types.js +0 -1
  110. package/dist/source/types/plugin.types.js +0 -1
  111. package/dist/source/types/theme.types.js +0 -1
  112. package/dist/source/types/web.types.js +0 -2
  113. package/dist/source/types/youtube-music.types.js +0 -1
  114. package/dist/source/types/youtubei.types.js +0 -3
  115. package/dist/source/utils/constants.js +0 -134
  116. package/dist/source/utils/format.js +0 -24
  117. package/dist/source/utils/icons.js +0 -26
  118. 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,80 +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, } = 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
- // Note: SETTINGS keybinding handled by MainLayout to avoid double-dispatch
66
- const shuffleColor = flashState['shuffle']
67
- ? theme.colors.success
68
- : playerState.shuffle
69
- ? theme.colors.primary
70
- : theme.colors.dim;
71
- const repeatColor = flashState['repeat']
72
- ? theme.colors.success
73
- : playerState.repeat !== 'off'
74
- ? theme.colors.secondary
75
- : theme.colors.dim;
76
- const volumeColor = flashState['volume']
77
- ? theme.colors.success
78
- : theme.colors.primary;
79
- 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: 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 }), ' ', _jsxs(Text, { color: theme.colors.dim, children: [ICONS.VOLUME, " [+/-]"] }), ' ', _jsxs(Text, { color: volumeColor, children: [playerState.volume, "%"] })] })] }));
80
- }