@involvex/youtube-music-cli 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +352 -0
- package/dist/eslint.config.d.ts +2 -0
- package/dist/eslint.config.js +55 -0
- package/dist/source/app.d.ts +4 -0
- package/dist/source/app.js +17 -0
- package/dist/source/cli.d.ts +2 -0
- package/dist/source/cli.js +241 -0
- package/dist/source/components/common/ErrorBoundary.d.ts +15 -0
- package/dist/source/components/common/ErrorBoundary.js +22 -0
- package/dist/source/components/common/Help.d.ts +1 -0
- package/dist/source/components/common/Help.js +10 -0
- package/dist/source/components/common/ShortcutsBar.d.ts +1 -0
- package/dist/source/components/common/ShortcutsBar.js +33 -0
- package/dist/source/components/config/ConfigLayout.d.ts +1 -0
- package/dist/source/components/config/ConfigLayout.js +84 -0
- package/dist/source/components/layouts/MainLayout.d.ts +4 -0
- package/dist/source/components/layouts/MainLayout.js +83 -0
- package/dist/source/components/layouts/PlayerLayout.d.ts +1 -0
- package/dist/source/components/layouts/PlayerLayout.js +10 -0
- package/dist/source/components/layouts/PluginsLayout.d.ts +1 -0
- package/dist/source/components/layouts/PluginsLayout.js +77 -0
- package/dist/source/components/layouts/SearchLayout.d.ts +4 -0
- package/dist/source/components/layouts/SearchLayout.js +81 -0
- package/dist/source/components/player/NowPlaying.d.ts +1 -0
- package/dist/source/components/player/NowPlaying.js +21 -0
- package/dist/source/components/player/PlayerControls.d.ts +1 -0
- package/dist/source/components/player/PlayerControls.js +41 -0
- package/dist/source/components/player/ProgressBar.d.ts +1 -0
- package/dist/source/components/player/ProgressBar.js +18 -0
- package/dist/source/components/player/QueueList.d.ts +4 -0
- package/dist/source/components/player/QueueList.js +30 -0
- package/dist/source/components/player/Suggestions.d.ts +1 -0
- package/dist/source/components/player/Suggestions.js +47 -0
- package/dist/source/components/playlist/PlaylistList.d.ts +1 -0
- package/dist/source/components/playlist/PlaylistList.js +11 -0
- package/dist/source/components/plugins/PluginInstallDialog.d.ts +5 -0
- package/dist/source/components/plugins/PluginInstallDialog.js +41 -0
- package/dist/source/components/plugins/PluginsAvailable.d.ts +5 -0
- package/dist/source/components/plugins/PluginsAvailable.js +55 -0
- package/dist/source/components/plugins/PluginsList.d.ts +8 -0
- package/dist/source/components/plugins/PluginsList.js +18 -0
- package/dist/source/components/search/SearchBar.d.ts +8 -0
- package/dist/source/components/search/SearchBar.js +50 -0
- package/dist/source/components/search/SearchResults.d.ts +10 -0
- package/dist/source/components/search/SearchResults.js +111 -0
- package/dist/source/components/settings/Settings.d.ts +1 -0
- package/dist/source/components/settings/Settings.js +42 -0
- package/dist/source/components/theme/ThemeSwitcher.d.ts +1 -0
- package/dist/source/components/theme/ThemeSwitcher.js +11 -0
- package/dist/source/config/themes.config.d.ts +3 -0
- package/dist/source/config/themes.config.js +63 -0
- package/dist/source/contexts/theme.context.d.ts +13 -0
- package/dist/source/contexts/theme.context.js +29 -0
- package/dist/source/hooks/useKeyboard.d.ts +10 -0
- package/dist/source/hooks/useKeyboard.js +104 -0
- package/dist/source/hooks/useNavigation.d.ts +1 -0
- package/dist/source/hooks/useNavigation.js +5 -0
- package/dist/source/hooks/usePlayer.d.ts +23 -0
- package/dist/source/hooks/usePlayer.js +35 -0
- package/dist/source/hooks/usePlaylist.d.ts +8 -0
- package/dist/source/hooks/usePlaylist.js +50 -0
- package/dist/source/hooks/useSearch.d.ts +8 -0
- package/dist/source/hooks/useSearch.js +76 -0
- package/dist/source/hooks/useTerminalSize.d.ts +4 -0
- package/dist/source/hooks/useTerminalSize.js +24 -0
- package/dist/source/hooks/useTheme.d.ts +6 -0
- package/dist/source/hooks/useTheme.js +5 -0
- package/dist/source/hooks/useYouTubeMusic.d.ts +11 -0
- package/dist/source/hooks/useYouTubeMusic.js +112 -0
- package/dist/source/main.d.ts +4 -0
- package/dist/source/main.js +69 -0
- package/dist/source/services/config/config.service.d.ts +26 -0
- package/dist/source/services/config/config.service.js +125 -0
- package/dist/source/services/logger/logger.service.d.ts +10 -0
- package/dist/source/services/logger/logger.service.js +52 -0
- package/dist/source/services/player/player.service.d.ts +58 -0
- package/dist/source/services/player/player.service.js +349 -0
- package/dist/source/services/player-state/player-state.service.d.ts +24 -0
- package/dist/source/services/player-state/player-state.service.js +122 -0
- package/dist/source/services/plugin/plugin-audio-api.d.ts +17 -0
- package/dist/source/services/plugin/plugin-audio-api.js +36 -0
- package/dist/source/services/plugin/plugin-context.d.ts +5 -0
- package/dist/source/services/plugin/plugin-context.js +256 -0
- package/dist/source/services/plugin/plugin-hooks.service.d.ts +62 -0
- package/dist/source/services/plugin/plugin-hooks.service.js +135 -0
- package/dist/source/services/plugin/plugin-installer.service.d.ts +27 -0
- package/dist/source/services/plugin/plugin-installer.service.js +247 -0
- package/dist/source/services/plugin/plugin-loader.service.d.ts +33 -0
- package/dist/source/services/plugin/plugin-loader.service.js +161 -0
- package/dist/source/services/plugin/plugin-permissions.service.d.ts +72 -0
- package/dist/source/services/plugin/plugin-permissions.service.js +194 -0
- package/dist/source/services/plugin/plugin-registry.service.d.ts +76 -0
- package/dist/source/services/plugin/plugin-registry.service.js +215 -0
- package/dist/source/services/plugin/plugin-ui-api.d.ts +25 -0
- package/dist/source/services/plugin/plugin-ui-api.js +46 -0
- package/dist/source/services/plugin/plugin-updater.service.d.ts +23 -0
- package/dist/source/services/plugin/plugin-updater.service.js +206 -0
- package/dist/source/services/youtube-music/api.d.ts +13 -0
- package/dist/source/services/youtube-music/api.js +371 -0
- package/dist/source/services/youtube-music/search.service.d.ts +11 -0
- package/dist/source/services/youtube-music/search.service.js +38 -0
- package/dist/source/stores/navigation.store.d.ts +10 -0
- package/dist/source/stores/navigation.store.js +67 -0
- package/dist/source/stores/player.store.d.ts +28 -0
- package/dist/source/stores/player.store.js +458 -0
- package/dist/source/stores/plugins.store.d.ts +46 -0
- package/dist/source/stores/plugins.store.js +177 -0
- package/dist/source/types/actions.d.ts +119 -0
- package/dist/source/types/actions.js +1 -0
- package/dist/source/types/cli.types.d.ts +14 -0
- package/dist/source/types/cli.types.js +1 -0
- package/dist/source/types/config.types.d.ts +19 -0
- package/dist/source/types/config.types.js +1 -0
- package/dist/source/types/keyboard.types.d.ts +5 -0
- package/dist/source/types/keyboard.types.js +1 -0
- package/dist/source/types/navigation.types.d.ts +14 -0
- package/dist/source/types/navigation.types.js +1 -0
- package/dist/source/types/player.types.d.ts +16 -0
- package/dist/source/types/player.types.js +1 -0
- package/dist/source/types/playlist.types.d.ts +12 -0
- package/dist/source/types/playlist.types.js +1 -0
- package/dist/source/types/plugin.types.d.ts +239 -0
- package/dist/source/types/plugin.types.js +1 -0
- package/dist/source/types/theme.types.d.ts +18 -0
- package/dist/source/types/theme.types.js +1 -0
- package/dist/source/types/youtube-music.types.d.ts +35 -0
- package/dist/source/types/youtube-music.types.js +1 -0
- package/dist/source/types/youtubei.types.d.ts +60 -0
- package/dist/source/types/youtubei.types.js +3 -0
- package/dist/source/utils/constants.d.ts +65 -0
- package/dist/source/utils/constants.js +82 -0
- package/dist/source/utils/format.d.ts +3 -0
- package/dist/source/utils/format.js +24 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +13 -0
- package/package.json +100 -0
package/README.md
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🎵 youtube-music-cli
|
|
4
|
+
|
|
5
|
+
A powerful Terminal User Interface (TUI) music player for YouTube Music
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@involvex/youtube-music-cli)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
[Features](#features) • [Installation](#installation) • [Usage](#usage) • [Plugins](#plugins) • [Documentation](https://involvex.github.io/youtube-music-cli)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 🎨 **Beautiful TUI** - Rich terminal interface built with React and Ink
|
|
19
|
+
- 🔍 **Search** - Find songs, albums, artists, and playlists
|
|
20
|
+
- 📋 **Queue Management** - Build and manage your playback queue
|
|
21
|
+
- 🔀 **Shuffle & Repeat** - Multiple playback modes
|
|
22
|
+
- 🎚️ **Volume Control** - Fine-grained volume adjustment
|
|
23
|
+
- 💡 **Smart Suggestions** - Discover related tracks
|
|
24
|
+
- 🎨 **Themes** - Dark, Light, Midnight, Matrix themes
|
|
25
|
+
- 🔌 **Plugin System** - Extend functionality with plugins
|
|
26
|
+
- ⌨️ **Keyboard-Driven** - Efficient vim-style navigation
|
|
27
|
+
- 🖥️ **Headless Mode** - Run without TUI for scripting
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
**Required:**
|
|
32
|
+
|
|
33
|
+
- [mpv](https://mpv.io/) - Media player for audio playback
|
|
34
|
+
- [yt-dlp](https://github.com/yt-dlp/yt-dlp) - YouTube audio extraction
|
|
35
|
+
|
|
36
|
+
### Installing Prerequisites
|
|
37
|
+
|
|
38
|
+
<details>
|
|
39
|
+
<summary><b>Windows</b></summary>
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# With Scoop
|
|
43
|
+
scoop install mpv yt-dlp
|
|
44
|
+
|
|
45
|
+
# With Chocolatey
|
|
46
|
+
choco install mpv yt-dlp
|
|
47
|
+
|
|
48
|
+
# With winget
|
|
49
|
+
winget install mpv yt-dlp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
</details>
|
|
53
|
+
|
|
54
|
+
<details>
|
|
55
|
+
<summary><b>macOS</b></summary>
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
brew install mpv yt-dlp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
</details>
|
|
62
|
+
|
|
63
|
+
<details>
|
|
64
|
+
<summary><b>Linux</b></summary>
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Ubuntu/Debian
|
|
68
|
+
sudo apt install mpv
|
|
69
|
+
pip install yt-dlp
|
|
70
|
+
|
|
71
|
+
# Arch Linux
|
|
72
|
+
sudo pacman -S mpv yt-dlp
|
|
73
|
+
|
|
74
|
+
# Fedora
|
|
75
|
+
sudo dnf install mpv yt-dlp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
### npm (Recommended)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install -g @involvex/youtube-music-cli
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Bun
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bun install -g @involvex/youtube-music-cli
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### From Source
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
git clone https://github.com/involvex/youtube-music-cli.git
|
|
98
|
+
cd youtube-music-cli
|
|
99
|
+
bun install
|
|
100
|
+
bun run build
|
|
101
|
+
bun link
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Usage
|
|
105
|
+
|
|
106
|
+
### Interactive Mode
|
|
107
|
+
|
|
108
|
+
Launch the TUI:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
youtube-music-cli
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### CLI Commands
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Play a specific track
|
|
118
|
+
youtube-music-cli play <video-id>
|
|
119
|
+
|
|
120
|
+
# Search for music
|
|
121
|
+
youtube-music-cli search "artist or song name"
|
|
122
|
+
|
|
123
|
+
# Play a playlist
|
|
124
|
+
youtube-music-cli playlist <playlist-id>
|
|
125
|
+
|
|
126
|
+
# Get suggestions based on current track
|
|
127
|
+
youtube-music-cli suggestions
|
|
128
|
+
|
|
129
|
+
# Playback control
|
|
130
|
+
youtube-music-cli pause
|
|
131
|
+
youtube-music-cli resume
|
|
132
|
+
youtube-music-cli skip
|
|
133
|
+
youtube-music-cli back
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Options
|
|
137
|
+
|
|
138
|
+
| Flag | Short | Description |
|
|
139
|
+
| ------------ | ----- | -------------------------------------------- |
|
|
140
|
+
| `--theme` | `-t` | Theme: `dark`, `light`, `midnight`, `matrix` |
|
|
141
|
+
| `--volume` | `-v` | Initial volume (0-100) |
|
|
142
|
+
| `--shuffle` | `-s` | Enable shuffle mode |
|
|
143
|
+
| `--repeat` | `-r` | Repeat mode: `off`, `all`, `one` |
|
|
144
|
+
| `--headless` | | Run without TUI |
|
|
145
|
+
| `--help` | `-h` | Show help |
|
|
146
|
+
|
|
147
|
+
### Examples
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Launch with matrix theme at 80% volume
|
|
151
|
+
youtube-music-cli --theme=matrix --volume=80
|
|
152
|
+
|
|
153
|
+
# Search and play in headless mode
|
|
154
|
+
youtube-music-cli search "lofi beats" --headless
|
|
155
|
+
|
|
156
|
+
# Play with shuffle enabled
|
|
157
|
+
youtube-music-cli play dQw4w9WgXcQ --shuffle
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Keyboard Shortcuts
|
|
161
|
+
|
|
162
|
+
### Global
|
|
163
|
+
|
|
164
|
+
| Key | Action |
|
|
165
|
+
| ----------- | --------------- |
|
|
166
|
+
| `?` | Show help |
|
|
167
|
+
| `/` | Search |
|
|
168
|
+
| `p` | Plugins manager |
|
|
169
|
+
| `g` | Suggestions |
|
|
170
|
+
| `,` | Settings |
|
|
171
|
+
| `q` / `Esc` | Quit / Go back |
|
|
172
|
+
| `Ctrl+L` | Refresh screen |
|
|
173
|
+
|
|
174
|
+
### Playback
|
|
175
|
+
|
|
176
|
+
| Key | Action |
|
|
177
|
+
| --------- | ----------------- |
|
|
178
|
+
| `Space` | Play / Pause |
|
|
179
|
+
| `n` / `→` | Next track |
|
|
180
|
+
| `b` / `←` | Previous track |
|
|
181
|
+
| `Shift+→` | Seek forward 10s |
|
|
182
|
+
| `Shift+←` | Seek backward 10s |
|
|
183
|
+
| `=` | Volume up |
|
|
184
|
+
| `-` | Volume down |
|
|
185
|
+
| `s` | Toggle shuffle |
|
|
186
|
+
| `r` | Cycle repeat mode |
|
|
187
|
+
|
|
188
|
+
### Navigation
|
|
189
|
+
|
|
190
|
+
| Key | Action |
|
|
191
|
+
| --------- | --------- |
|
|
192
|
+
| `↑` / `k` | Move up |
|
|
193
|
+
| `↓` / `j` | Move down |
|
|
194
|
+
| `Enter` | Select |
|
|
195
|
+
| `Esc` | Back |
|
|
196
|
+
|
|
197
|
+
## Plugins
|
|
198
|
+
|
|
199
|
+
Extend youtube-music-cli with plugins!
|
|
200
|
+
|
|
201
|
+
### Managing Plugins
|
|
202
|
+
|
|
203
|
+
**TUI Mode:** Press `p` to open the plugins manager.
|
|
204
|
+
|
|
205
|
+
**CLI Mode:**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# List installed plugins
|
|
209
|
+
youtube-music-cli plugins list
|
|
210
|
+
|
|
211
|
+
# Install from default repository
|
|
212
|
+
youtube-music-cli plugins install adblock
|
|
213
|
+
|
|
214
|
+
# Install from GitHub URL
|
|
215
|
+
youtube-music-cli plugins install https://github.com/user/my-plugin
|
|
216
|
+
|
|
217
|
+
# Enable/disable
|
|
218
|
+
youtube-music-cli plugins enable my-plugin
|
|
219
|
+
youtube-music-cli plugins disable my-plugin
|
|
220
|
+
|
|
221
|
+
# Update
|
|
222
|
+
youtube-music-cli plugins update my-plugin
|
|
223
|
+
|
|
224
|
+
# Remove
|
|
225
|
+
youtube-music-cli plugins remove my-plugin
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Available Plugins
|
|
229
|
+
|
|
230
|
+
| Plugin | Description |
|
|
231
|
+
| --------------- | --------------------------------------- |
|
|
232
|
+
| `adblock` | Block ads and sponsored content |
|
|
233
|
+
| `lyrics` | Display synchronized lyrics |
|
|
234
|
+
| `scrobbler` | Scrobble to Last.fm |
|
|
235
|
+
| `discord-rpc` | Discord Rich Presence integration |
|
|
236
|
+
| `notifications` | Desktop notifications for track changes |
|
|
237
|
+
|
|
238
|
+
### Developing Plugins
|
|
239
|
+
|
|
240
|
+
See [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md) and [Plugin API Reference](docs/PLUGIN_API.md).
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Start from a template
|
|
244
|
+
cp -r templates/plugin-basic my-plugin
|
|
245
|
+
cd my-plugin
|
|
246
|
+
|
|
247
|
+
# Edit plugin.json and index.ts
|
|
248
|
+
# Install for testing
|
|
249
|
+
youtube-music-cli plugins install /path/to/my-plugin
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Configuration
|
|
253
|
+
|
|
254
|
+
Config is stored in `~/.youtube-music-cli/config.json`:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"theme": "dark",
|
|
259
|
+
"volume": 70,
|
|
260
|
+
"shuffle": false,
|
|
261
|
+
"repeat": "off",
|
|
262
|
+
"streamQuality": "high"
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Stream Quality
|
|
267
|
+
|
|
268
|
+
| Quality | Description |
|
|
269
|
+
| -------- | ----------------------- |
|
|
270
|
+
| `low` | 64kbps - Save bandwidth |
|
|
271
|
+
| `medium` | 128kbps - Balanced |
|
|
272
|
+
| `high` | 256kbps+ - Best quality |
|
|
273
|
+
|
|
274
|
+
## Troubleshooting
|
|
275
|
+
|
|
276
|
+
### mpv not found
|
|
277
|
+
|
|
278
|
+
Ensure mpv is installed and in your PATH:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
mpv --version
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### No audio
|
|
285
|
+
|
|
286
|
+
1. Check volume isn't muted (`=` to increase)
|
|
287
|
+
2. Verify yt-dlp is working: `yt-dlp --version`
|
|
288
|
+
3. Try a different track
|
|
289
|
+
|
|
290
|
+
### TUI rendering issues
|
|
291
|
+
|
|
292
|
+
Press `Ctrl+L` to refresh the screen, or try a different terminal emulator.
|
|
293
|
+
|
|
294
|
+
### Plugin not loading
|
|
295
|
+
|
|
296
|
+
1. Check `plugin.json` syntax is valid
|
|
297
|
+
2. Verify the plugin is enabled: `youtube-music-cli plugins list`
|
|
298
|
+
3. Check logs for errors
|
|
299
|
+
|
|
300
|
+
## Contributing
|
|
301
|
+
|
|
302
|
+
Contributions are welcome!
|
|
303
|
+
|
|
304
|
+
1. Fork the repository
|
|
305
|
+
2. Create a feature branch: `git checkout -b feature/my-feature`
|
|
306
|
+
3. Make your changes
|
|
307
|
+
4. Run tests: `bun run test`
|
|
308
|
+
5. Commit: `git commit -m 'feat: add my feature'`
|
|
309
|
+
6. Push: `git push origin feature/my-feature`
|
|
310
|
+
7. Open a Pull Request
|
|
311
|
+
|
|
312
|
+
### Development
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Install dependencies
|
|
316
|
+
bun install
|
|
317
|
+
|
|
318
|
+
# Run in development mode
|
|
319
|
+
bun run dev
|
|
320
|
+
|
|
321
|
+
# Build
|
|
322
|
+
bun run build
|
|
323
|
+
|
|
324
|
+
# Lint and format
|
|
325
|
+
bun run lint:fix
|
|
326
|
+
bun run format
|
|
327
|
+
|
|
328
|
+
# Type check
|
|
329
|
+
bun run typecheck
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Tech Stack
|
|
333
|
+
|
|
334
|
+
- **Runtime:** [Bun](https://bun.sh/) / Node.js
|
|
335
|
+
- **UI Framework:** [Ink](https://github.com/vadimdemedes/ink) (React for CLI)
|
|
336
|
+
- **Language:** TypeScript
|
|
337
|
+
- **Audio:** mpv + yt-dlp
|
|
338
|
+
- **API:** YouTube Music Innertube API
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
MIT © [Involvex](https://github.com/involvex)
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
<div align="center">
|
|
347
|
+
|
|
348
|
+
**[Documentation](https://involvex.github.io/youtube-music-cli)** • **[Report Bug](https://github.com/involvex/youtube-music-cli/issues)** • **[Request Feature](https://github.com/involvex/youtube-music-cli/issues)**
|
|
349
|
+
|
|
350
|
+
Made with ❤️ for music lovers
|
|
351
|
+
|
|
352
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import pluginReact from 'eslint-plugin-react';
|
|
2
|
+
import hooksPlugin from 'eslint-plugin-react-hooks';
|
|
3
|
+
import { defineConfig } from 'eslint/config';
|
|
4
|
+
import tseslint from 'typescript-eslint';
|
|
5
|
+
import globals from 'globals';
|
|
6
|
+
import js from '@eslint/js';
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
{
|
|
9
|
+
ignores: ['templates/**/*', 'plugins/**/*'],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
files: ['**/*.{js,mjs,cjs}'],
|
|
13
|
+
plugins: { js },
|
|
14
|
+
extends: ['js/recommended'],
|
|
15
|
+
languageOptions: { globals: { ...globals.node } },
|
|
16
|
+
},
|
|
17
|
+
...tseslint.configs.recommended,
|
|
18
|
+
{
|
|
19
|
+
files: ['**/*.{ts,mts,cts,jsx,tsx}'],
|
|
20
|
+
languageOptions: {
|
|
21
|
+
globals: { ...globals.node },
|
|
22
|
+
ecmaVersion: 'latest',
|
|
23
|
+
sourceType: 'module',
|
|
24
|
+
parserOptions: {
|
|
25
|
+
projectService: true,
|
|
26
|
+
tsconfigRootDir: import.meta.dirname,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
files: ['**/*.jsx', '**/*.tsx'],
|
|
32
|
+
...pluginReact.configs.flat['recommended'],
|
|
33
|
+
plugins: {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
'react-hooks': hooksPlugin,
|
|
36
|
+
},
|
|
37
|
+
settings: {
|
|
38
|
+
react: { version: 'detect' },
|
|
39
|
+
jsxRuntime: 'automatic',
|
|
40
|
+
},
|
|
41
|
+
rules: {
|
|
42
|
+
...hooksPlugin.configs.recommended.rules,
|
|
43
|
+
'react/react-in-jsx-scope': 'off',
|
|
44
|
+
'react/jsx-uses-react': 'off',
|
|
45
|
+
'@typescript-eslint/no-unused-vars': [
|
|
46
|
+
'error',
|
|
47
|
+
{
|
|
48
|
+
argsIgnorePattern: '^_',
|
|
49
|
+
varsIgnorePattern: '^_',
|
|
50
|
+
caughtErrorsIgnorePattern: '^_',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Root application component
|
|
3
|
+
import { useApp } from 'ink';
|
|
4
|
+
import Main from "./main.js";
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
// Handle unmounting
|
|
7
|
+
let unmount = null;
|
|
8
|
+
export default function App({ flags }) {
|
|
9
|
+
const { exit } = useApp();
|
|
10
|
+
// Store unmount function globally
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!unmount) {
|
|
13
|
+
unmount = exit;
|
|
14
|
+
}
|
|
15
|
+
}, [exit]);
|
|
16
|
+
return _jsx(Main, { flags: flags });
|
|
17
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
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
|
+
const cli = meow(`
|
|
10
|
+
Usage
|
|
11
|
+
$ youtube-music-cli
|
|
12
|
+
$ youtube-music-cli play <track-id>
|
|
13
|
+
$ youtube-music-cli search <query>
|
|
14
|
+
$ youtube-music-cli playlist <playlist-id>
|
|
15
|
+
$ youtube-music-cli suggestions
|
|
16
|
+
$ youtube-music-cli pause
|
|
17
|
+
$ youtube-music-cli resume
|
|
18
|
+
$ youtube-music-cli skip
|
|
19
|
+
$ youtube-music-cli back
|
|
20
|
+
|
|
21
|
+
Plugin Commands
|
|
22
|
+
$ youtube-music-cli plugins list
|
|
23
|
+
$ youtube-music-cli plugins install <name|url>
|
|
24
|
+
$ youtube-music-cli plugins remove <name>
|
|
25
|
+
$ youtube-music-cli plugins update <name>
|
|
26
|
+
$ youtube-music-cli plugins enable <name>
|
|
27
|
+
$ youtube-music-cli plugins disable <name>
|
|
28
|
+
|
|
29
|
+
Options
|
|
30
|
+
--theme, -t Theme to use (dark, light, midnight, matrix)
|
|
31
|
+
--volume, -v Initial volume (0-100)
|
|
32
|
+
--shuffle, -s Enable shuffle mode
|
|
33
|
+
--repeat, -r Repeat mode (off, all, one)
|
|
34
|
+
--headless Run without TUI (just play)
|
|
35
|
+
--help, -h Show this help
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
$ youtube-music-cli
|
|
39
|
+
$ youtube-music-cli play dQw4w9WgXcQ
|
|
40
|
+
$ youtube-music-cli search "Rick Astley"
|
|
41
|
+
$ youtube-music-cli play dQw4w9WgXcQ --headless
|
|
42
|
+
$ youtube-music-cli plugins install adblock
|
|
43
|
+
`, {
|
|
44
|
+
importMeta: import.meta,
|
|
45
|
+
flags: {
|
|
46
|
+
theme: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
shortFlag: 't',
|
|
49
|
+
},
|
|
50
|
+
volume: {
|
|
51
|
+
type: 'number',
|
|
52
|
+
shortFlag: 'v',
|
|
53
|
+
},
|
|
54
|
+
shuffle: {
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
shortFlag: 's',
|
|
57
|
+
default: false,
|
|
58
|
+
},
|
|
59
|
+
repeat: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
shortFlag: 'r',
|
|
62
|
+
},
|
|
63
|
+
headless: {
|
|
64
|
+
type: 'boolean',
|
|
65
|
+
default: false,
|
|
66
|
+
},
|
|
67
|
+
help: {
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
shortFlag: 'h',
|
|
70
|
+
default: false,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
autoVersion: true,
|
|
74
|
+
autoHelp: false,
|
|
75
|
+
});
|
|
76
|
+
if (cli.flags.help) {
|
|
77
|
+
cli.showHelp(0);
|
|
78
|
+
}
|
|
79
|
+
// Handle plugin commands
|
|
80
|
+
const command = cli.input[0];
|
|
81
|
+
const args = cli.input.slice(1);
|
|
82
|
+
if (command === 'plugins') {
|
|
83
|
+
const subCommand = args[0];
|
|
84
|
+
const pluginArg = args[1];
|
|
85
|
+
void (async () => {
|
|
86
|
+
const installer = getPluginInstallerService();
|
|
87
|
+
const updater = getPluginUpdaterService();
|
|
88
|
+
const registry = getPluginRegistryService();
|
|
89
|
+
// Load existing plugins
|
|
90
|
+
await registry.loadAllPlugins();
|
|
91
|
+
switch (subCommand) {
|
|
92
|
+
case 'list': {
|
|
93
|
+
const plugins = registry.getAllPlugins();
|
|
94
|
+
if (plugins.length === 0) {
|
|
95
|
+
console.log('No plugins installed.');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log('Installed plugins:');
|
|
99
|
+
for (const plugin of plugins) {
|
|
100
|
+
const status = plugin.enabled ? '●' : '○';
|
|
101
|
+
console.log(` ${status} ${plugin.manifest.name} v${plugin.manifest.version}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
process.exit(0);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case 'install': {
|
|
108
|
+
if (!pluginArg) {
|
|
109
|
+
console.error('Usage: youtube-music-cli plugins install <name|url>');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
console.log(`Installing ${pluginArg}...`);
|
|
113
|
+
let result;
|
|
114
|
+
if (pluginArg.startsWith('http')) {
|
|
115
|
+
result = await installer.installFromGitHub(pluginArg);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
result = await installer.installFromDefaultRepo(pluginArg);
|
|
119
|
+
}
|
|
120
|
+
if (result.success) {
|
|
121
|
+
console.log(`✓ Successfully installed ${result.pluginId}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.error(`✗ Failed: ${result.error}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
process.exit(0);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'remove':
|
|
131
|
+
case 'uninstall': {
|
|
132
|
+
if (!pluginArg) {
|
|
133
|
+
console.error('Usage: youtube-music-cli plugins remove <name>');
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
console.log(`Removing ${pluginArg}...`);
|
|
137
|
+
try {
|
|
138
|
+
await registry.unloadPlugin(pluginArg);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Plugin may not be loaded
|
|
142
|
+
}
|
|
143
|
+
const result = await installer.uninstall(pluginArg);
|
|
144
|
+
if (result.success) {
|
|
145
|
+
console.log(`✓ Successfully removed ${pluginArg}`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.error(`✗ Failed: ${result.error}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
process.exit(0);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'update': {
|
|
155
|
+
if (!pluginArg) {
|
|
156
|
+
console.error('Usage: youtube-music-cli plugins update <name>');
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.log(`Updating ${pluginArg}...`);
|
|
160
|
+
const result = await updater.updatePlugin(pluginArg);
|
|
161
|
+
if (result.success) {
|
|
162
|
+
console.log(`✓ Updated ${pluginArg} from ${result.oldVersion} to ${result.newVersion}`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.error(`✗ Failed: ${result.error}`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
process.exit(0);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'enable': {
|
|
172
|
+
if (!pluginArg) {
|
|
173
|
+
console.error('Usage: youtube-music-cli plugins enable <name>');
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
await registry.enablePlugin(pluginArg);
|
|
178
|
+
console.log(`✓ Enabled ${pluginArg}`);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error(`✗ Failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
process.exit(0);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case 'disable': {
|
|
188
|
+
if (!pluginArg) {
|
|
189
|
+
console.error('Usage: youtube-music-cli plugins disable <name>');
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
await registry.disablePlugin(pluginArg);
|
|
194
|
+
console.log(`✓ Disabled ${pluginArg}`);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
console.error(`✗ Failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
process.exit(0);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
default:
|
|
204
|
+
console.error('Usage: youtube-music-cli plugins <list|install|remove|update|enable|disable>');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
})();
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// Handle other direct commands
|
|
211
|
+
if (command === 'play' && args[0]) {
|
|
212
|
+
// Play specific track
|
|
213
|
+
cli.flags.playTrack = args[0];
|
|
214
|
+
}
|
|
215
|
+
else if (command === 'search' && args[0]) {
|
|
216
|
+
// Search for query
|
|
217
|
+
cli.flags.searchQuery = args.join(' ');
|
|
218
|
+
}
|
|
219
|
+
else if (command === 'playlist' && args[0]) {
|
|
220
|
+
// Play specific playlist
|
|
221
|
+
cli.flags.playPlaylist = args[0];
|
|
222
|
+
}
|
|
223
|
+
else if (command === 'suggestions') {
|
|
224
|
+
// Show suggestions
|
|
225
|
+
cli.flags.showSuggestions = true;
|
|
226
|
+
}
|
|
227
|
+
else if (command === 'pause') {
|
|
228
|
+
cli.flags.action = 'pause';
|
|
229
|
+
}
|
|
230
|
+
else if (command === 'resume') {
|
|
231
|
+
cli.flags.action = 'resume';
|
|
232
|
+
}
|
|
233
|
+
else if (command === 'skip') {
|
|
234
|
+
cli.flags.action = 'next';
|
|
235
|
+
}
|
|
236
|
+
else if (command === 'back') {
|
|
237
|
+
cli.flags.action = 'previous';
|
|
238
|
+
}
|
|
239
|
+
// Render the app
|
|
240
|
+
render(_jsx(App, { flags: cli.flags }));
|
|
241
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
interface State {
|
|
6
|
+
hasError: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
}
|
|
9
|
+
export declare class ErrorBoundary extends Component<Props, State> {
|
|
10
|
+
state: State;
|
|
11
|
+
static getDerivedStateFromError(error: Error): State;
|
|
12
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
13
|
+
render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
14
|
+
}
|
|
15
|
+
export {};
|