ttyplayer 0.5.0__tar.gz → 0.7.0__tar.gz

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 (28) hide show
  1. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/PKG-INFO +65 -5
  2. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/README.md +64 -4
  3. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/pyproject.toml +1 -1
  4. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/pyproject.toml.orig +1 -1
  5. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/cli.py +200 -35
  6. ttyplayer-0.7.0/src/ttyplayer/models.py +25 -0
  7. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/player.py +249 -15
  8. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/remote.py +11 -3
  9. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/server.py +52 -11
  10. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/settings.py +9 -0
  11. ttyplayer-0.7.0/src/ttyplayer/spotify.py +338 -0
  12. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/static/index.html +8 -0
  13. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/static/remote.css +2 -1
  14. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/static/remote.js +58 -6
  15. ttyplayer-0.7.0/src/ttyplayer/stream.py +233 -0
  16. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/tui.py +83 -18
  17. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/tui.tcss +19 -1
  18. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/utils.py +17 -3
  19. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/youtube.py +24 -2
  20. ttyplayer-0.5.0/src/ttyplayer/models.py +0 -13
  21. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/LICENSE +0 -0
  22. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/__init__.py +0 -0
  23. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/control.py +0 -0
  24. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/favorites.py +0 -0
  25. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/history.py +0 -0
  26. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/playlists.py +0 -0
  27. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/static/icon.svg +0 -0
  28. {ttyplayer-0.5.0 → ttyplayer-0.7.0}/src/ttyplayer/static/manifest.webmanifest +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ttyplayer
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: ttyplayer: a modern YouTube player for the terminal — search, queue, and play audio or video via mpv
5
5
  Keywords: youtube,music,player,mpv,cli,terminal,tui,yt-dlp
6
6
  License-Expression: MIT
@@ -35,8 +35,10 @@ ttyplayer play <playlist url> queue every entry of a playlist
35
35
  ttyplayer play <words...> search, pick one or more results, play them in order
36
36
  ttyplayer play ... --video open a video window as well
37
37
  ttyplayer play ... --limit 10 show more search results
38
+ ttyplayer play ... --source soundcloud search SoundCloud instead of YouTube
38
39
 
39
40
  ttyplayer search <words...> list results with durations
41
+ ttyplayer search --source soundcloud <words...> the same, on SoundCloud
40
42
  ttyplayer history recently played, newest first
41
43
  ttyplayer history --play pick from history and play again
42
44
  ttyplayer history --clear forget the history
@@ -46,15 +48,19 @@ ttyplayer favorites --play pick from favorites and play them
46
48
  ttyplayer favorites --remove 2 drop the second favorite as listed
47
49
  ttyplayer favorites --clear forget all favorites
48
50
  ttyplayer playlist ... your own named playlists (see Playlists below)
51
+ ttyplayer spotify ... bring Spotify playlists over, found on YouTube (see Spotify below)
49
52
  ttyplayer tui [--video] full-screen: search box, results list, now-playing bar
50
53
  ttyplayer serve [--host] [--port] play headless, driven over HTTP from any device (see Server below)
54
+ ttyplayer serve --stream the same, but the sound goes to the web remote, not the speakers
51
55
  ttyplayer config list the settings (see Settings below)
52
- ttyplayer doctor check Python, yt-dlp, mpv and ttyplayer's folders
56
+ ttyplayer doctor check Python, yt-dlp, mpv, ffmpeg (optional) and ttyplayer's folders
53
57
  ttyplayer version
54
58
  ```
55
59
 
56
60
  Channels and playlists in search results are skipped; a track that cannot be played is reported and skipped.
57
61
 
62
+ Search looks on YouTube or SoundCloud. `--source soundcloud` (on `play`, `search` and `playlist add`) picks SoundCloud for one search; `ttyplayer config set search_source soundcloud` makes it the default everywhere. SoundCloud rows carry an `SC` tag in every list, and history, favorites and playlists keep their SoundCloud link. Links from any site yt-dlp knows play as they are.
63
+
58
64
  While ttyplayer plays in one terminal, any other terminal can drive it:
59
65
 
60
66
  ```
@@ -62,9 +68,14 @@ ttyplayer pause pause / resume
62
68
  ttyplayer next next in the queue
63
69
  ttyplayer prev previous in the queue
64
70
  ttyplayer stop quit the player
71
+ ttyplayer sleep 30m Sleeping in 30:00 (also 1h, 1h30m, 90 seconds)
72
+ ttyplayer sleep end stop when the current track ends
73
+ ttyplayer sleep off cancel it (no argument: show it)
65
74
  ttyplayer status 1:23 / 4:56 Playing <title>
66
75
  ```
67
76
 
77
+ When the sleep timer runs out, the volume fades to nothing over 5 seconds and the player stops as if `q` was pressed; the volume is put back first, so the next play starts at the old level. Paused or idle, it stops at once. `ttyplayer status`, the TUI and the web remote show `zz 27:13` (or `zz end`) while a timer is armed.
78
+
68
79
  ## Playlists
69
80
 
70
81
  Playlists are named lists you keep, in the order you choose, separate from history and favorites. A name is 1 to 40 letters, digits, spaces, `_` or `-`.
@@ -102,9 +113,32 @@ Picks accept several numbers at once: `1 3 5` queues those three in that order.
102
113
 
103
114
  `TTYPLAYER_TIMING=1 ttyplayer play <words>` prints how long the YouTube lookup took and adds `started in 2.4s` (from `loadfile` to the first sound) to the status line.
104
115
 
116
+ ## Spotify
117
+
118
+ ttyplayer can bring a Spotify playlist over as one of its own playlists. It reads only the playlist's *track list* from Spotify, never its audio (Spotify's audio is DRM-protected): each track is searched on YouTube as `<first artist> <title>`, and the first hit is added. A track YouTube has no hit for is reported and skipped; local files and podcast episodes in the playlist are skipped too. The result is a normal playlist: `ttyplayer playlist play <name>`, or the Playlists tab in the TUI.
119
+
120
+ ```
121
+ ttyplayer spotify login log in to Spotify in the browser, once
122
+ ttyplayer spotify playlists your Spotify playlists: name, tracks, id
123
+ ttyplayer spotify import <link | id> [--as <name>] [--limit N] find each track on YouTube and save it
124
+ ```
125
+
126
+ `import` takes a playlist link (`https://open.spotify.com/playlist/<id>`), a `spotify:playlist:<id>` URI, or the bare id. It names the playlist after the Spotify one (unless `--as`), creating it, or appending to it if one by that name exists; it prints `[3/40] ✓ Artist – Title → <YouTube title>` (or `✗ … not found`) per track, and `Saved N of M tracks to <name>` at the end. Each YouTube search takes a second or two, so a long playlist takes a while; `--limit N` imports only the first N tracks. If the network fails part way, what was saved stays saved.
127
+
128
+ Logging in needs a Spotify app of your own (free, once):
129
+
130
+ 1. Open the [Spotify developer dashboard](https://developer.spotify.com/dashboard), log in, and press **Create app**.
131
+ 2. Give it any name and description, set the **Redirect URI** to exactly `http://127.0.0.1:8765/callback`, tick **Web API**, and save.
132
+ 3. Open the app's **Settings**, copy its **Client ID**, and run `ttyplayer config set spotify_client_id <client id>`.
133
+ 4. Run `ttyplayer spotify login`: the browser opens Spotify's consent page; approve it, and the terminal says `Logged in as <your name>`.
134
+
135
+ ttyplayer asks only for read access to your playlists (`playlist-read-private playlist-read-collaborative`) and uses no client secret (the login is OAuth with PKCE). The login is kept in `spotify.json` next to `settings.toml`, readable only by you; it holds a refresh token, so treat it like a password. To revoke ttyplayer's access, remove the app under **Manage apps** on your Spotify account page (spotify.com/account/apps) and delete `spotify.json`.
136
+
105
137
  ## TUI
106
138
 
107
- `ttyplayer tui` opens a full-screen player: a search box, Search / Queue / History / Favorites / Playlists tabs, and a now-playing panel. Type a search or paste a link and press Enter. Ctrl-P opens the command palette (search, playlists, save queue as playlist, next theme, settings, help, quit, pause, next, previous, mute, and Textual's own theme picker); `?` lists every key and command.
139
+ `ttyplayer tui` opens a full-screen player: a search box, Search / Queue / History / Favorites / Playlists tabs, and a now-playing panel. Type a search or paste a link and press Enter; start the search with `sc:` (`sc: boards of canada`) to search SoundCloud, or `yt:` for YouTube, whatever `search_source` says. The Search tab's heading then reads `SoundCloud results` or `YouTube results`. Ctrl-P opens the command palette (search, playlists, save queue as playlist, next theme, settings, help, quit, pause, next, previous, mute, sleep, and Textual's own theme picker); **Sleep…** asks for a sleep timer (`30m` filled in; `end`, `off`, `1h30m`, … work as on the command line; Esc leaves the timer alone); `?` lists every key and command.
140
+
141
+ Under the volume, the panel's level meter shows two bars, `L` and `R`, that follow the sound's peaks about ten times a second (empty at −60 dBFS and below, full at 0 dBFS; empty while paused). `ttyplayer config set show_levels false`, or Enter on `show_levels` in the Settings screen, hides them and takes mpv's measuring filter out.
108
142
 
109
143
  | Where | Key | Action |
110
144
  |---|---|---|
@@ -157,6 +191,10 @@ ttyplayer keeps its preferences in `~/.config/ttyplayer/settings.toml` (`$XDG_CO
157
191
  | `server_port` | `7700` | the port `ttyplayer serve` listens on (1–65535) |
158
192
  | `server_token` | *generated* | the token every API request needs; `ttyplayer serve` makes one on first use |
159
193
  | `remote_url` | *none* | the server `ttyplayer tui` drives instead of playing itself, e.g. `http://host:7700` |
194
+ | `stream_enabled` | `false` | `ttyplayer serve` streams the sound to the web remote instead of playing it, as `--stream` does |
195
+ | `spotify_client_id` | *none* | the Client ID of your own Spotify app, which `ttyplayer spotify login` needs (see Spotify above) |
196
+ | `show_levels` | `true` | the TUI's level meter: mpv measures the sound's peaks and the now-playing panel shows them |
197
+ | `search_source` | `youtube` | where a search looks: `youtube` or `soundcloud`; `--source` and the TUI's `sc:` / `yt:` prefix override it once |
160
198
 
161
199
  ```
162
200
  ttyplayer config every setting, (default) when unchanged
@@ -165,7 +203,7 @@ ttyplayer config set <key> <value> change it: ttyplayer config set show_clock
165
203
  ttyplayer config path where the file is
166
204
  ```
167
205
 
168
- In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true / false one flips it and saves it (the clock shows or hides at once); the others are set with `ttyplayer config set`.
206
+ In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true / false one flips it and saves it (the clock and the level meter show or hide at once); the others are set with `ttyplayer config set`.
169
207
 
170
208
  ## Server
171
209
 
@@ -175,6 +213,7 @@ In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true /
175
213
  ttyplayer serve listen on 127.0.0.1:7700 (this machine only)
176
214
  ttyplayer serve --host 0.0.0.0 listen on every address, so a phone on the LAN can reach it
177
215
  ttyplayer serve --port 8000 another port
216
+ ttyplayer serve --stream no sound here: Listen here on the web remote plays it (see below)
178
217
  ```
179
218
 
180
219
  It prints the address to open, with the token, and a QR code of it for a phone:
@@ -198,7 +237,7 @@ curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http
198
237
  | GET | `/api/status` | the player's status, plus `queue` (the videos) and `index` (1-based) |
199
238
  | POST | `/api/play` | `{"url": "…"}` or `{"query": "…"}`: the link's videos, or the first search result, replace the queue and play |
200
239
  | POST | `/api/queue` | `{"url": "…"}` or `{"query": "…"}`: appended to the queue (played at once when nothing plays) |
201
- | POST | `/api/command` | `{"name": "pause"\|"next"\|"prev"\|"stop"\|"mute"\|"seek"\|"volume"\|"jump"\|"remove"\|"move"\|"clear_others", "value"?}`; `seek` and `volume` take a number of seconds / steps, `jump` and `remove` a 0-based queue row, `move` two (`[from, to]`; the current track stays current); `clear_others` keeps only the current track → the new status |
240
+ | POST | `/api/command` | `{"name": "pause"\|"next"\|"prev"\|"stop"\|"mute"\|"sleep"\|"seek"\|"volume"\|"jump"\|"remove"\|"move"\|"clear_others", "value"?}`; `sleep` takes the text `ttyplayer sleep` does (`"30m"`, `"end"`, `"off"`), `seek` and `volume` a number of seconds / steps, `jump` and `remove` a 0-based queue row, `move` two (`[from, to]`; the current track stays current); `clear_others` keeps only the current track → the new status |
202
241
  | GET | `/api/commands` | the command names `/api/command` takes |
203
242
  | GET | `/api/search?q=…` | the search results (`search_limit` of them) |
204
243
  | GET | `/api/favorites` | the favorites, newest first |
@@ -207,8 +246,29 @@ curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http
207
246
  | POST | `/api/playlists/<name>/play` | that playlist becomes the queue |
208
247
  | GET, PATCH | `/api/settings` | every setting but the token; PATCH `{"key": value}` changes and saves them, checked like `config set` |
209
248
  | WS | `/ws?token=…` | sends the status on connect and on every change; takes the same `{"name", "value"?}` commands as `/api/command` and answers each with the status |
249
+ | GET | `/stream` | with `serve --stream`: the sound as an `audio/ogg` (Opus) stream, for as long as the client listens; 404 otherwise |
210
250
  | GET | `/` | the web remote (needs no token itself; it reads the token from its address); its files are under `/static/`, plus `/manifest.webmanifest` |
211
251
 
252
+ ### Listen on another device
253
+
254
+ `ttyplayer serve --stream` is the "music box on a server" mode, for a machine with no speakers of its own (a Raspberry Pi, a VPS, a closet PC): nothing plays on the server; the web remote gets a **Listen here** button that plays what the server plays, in that browser, on any device. Press it again (**Stop listening**) to stop. Several devices can listen at once; one on a bad connection skips, the others do not. Pausing and changing tracks keep the stream open (you hear silence in between). What you hear runs a little behind the remote's controls: the server adds about half a second, the browser its own buffer. `ttyplayer config set stream_enabled true` makes it the default for `serve`.
255
+
256
+ ```
257
+ ttyplayer serve --stream --host 0.0.0.0
258
+ ```
259
+
260
+ It needs ffmpeg, which nothing else in ttyplayer does (`ttyplayer doctor` shows whether it is there), and runs on macOS and Linux, not Windows:
261
+
262
+ | System | Install ffmpeg |
263
+ |---|---|
264
+ | macOS | `brew install ffmpeg` |
265
+ | Debian, Ubuntu, Raspberry Pi OS | `sudo apt-get install -y ffmpeg` |
266
+ | Fedora | `sudo dnf install -y ffmpeg-free` |
267
+ | Arch | `sudo pacman -S --noconfirm ffmpeg` |
268
+ | Alpine | `sudo apk add ffmpeg` |
269
+
270
+ The stream is gated by the same token as the API and is plain HTTP; the security notes below apply to it too.
271
+
212
272
  ### The TUI as a remote
213
273
 
214
274
  `ttyplayer tui --remote http://host:7700` opens the same screen, with the same keys and tabs, on another machine (a laptop, say) while the server plays: Enter on a result makes the *server's* speakers play, and the now-playing panel and the Queue tab follow the server over its socket (the header says `remote: host:7700`). The token is the `server_token` setting, so copy it into the laptop's settings with `ttyplayer config set server_token <token>`; `--token <token>` works too, but leaves it in your shell history. `ttyplayer config set remote_url http://host:7700` makes the remote the default, so a plain `ttyplayer tui` drives it.
@@ -10,8 +10,10 @@ ttyplayer play <playlist url> queue every entry of a playlist
10
10
  ttyplayer play <words...> search, pick one or more results, play them in order
11
11
  ttyplayer play ... --video open a video window as well
12
12
  ttyplayer play ... --limit 10 show more search results
13
+ ttyplayer play ... --source soundcloud search SoundCloud instead of YouTube
13
14
 
14
15
  ttyplayer search <words...> list results with durations
16
+ ttyplayer search --source soundcloud <words...> the same, on SoundCloud
15
17
  ttyplayer history recently played, newest first
16
18
  ttyplayer history --play pick from history and play again
17
19
  ttyplayer history --clear forget the history
@@ -21,15 +23,19 @@ ttyplayer favorites --play pick from favorites and play them
21
23
  ttyplayer favorites --remove 2 drop the second favorite as listed
22
24
  ttyplayer favorites --clear forget all favorites
23
25
  ttyplayer playlist ... your own named playlists (see Playlists below)
26
+ ttyplayer spotify ... bring Spotify playlists over, found on YouTube (see Spotify below)
24
27
  ttyplayer tui [--video] full-screen: search box, results list, now-playing bar
25
28
  ttyplayer serve [--host] [--port] play headless, driven over HTTP from any device (see Server below)
29
+ ttyplayer serve --stream the same, but the sound goes to the web remote, not the speakers
26
30
  ttyplayer config list the settings (see Settings below)
27
- ttyplayer doctor check Python, yt-dlp, mpv and ttyplayer's folders
31
+ ttyplayer doctor check Python, yt-dlp, mpv, ffmpeg (optional) and ttyplayer's folders
28
32
  ttyplayer version
29
33
  ```
30
34
 
31
35
  Channels and playlists in search results are skipped; a track that cannot be played is reported and skipped.
32
36
 
37
+ Search looks on YouTube or SoundCloud. `--source soundcloud` (on `play`, `search` and `playlist add`) picks SoundCloud for one search; `ttyplayer config set search_source soundcloud` makes it the default everywhere. SoundCloud rows carry an `SC` tag in every list, and history, favorites and playlists keep their SoundCloud link. Links from any site yt-dlp knows play as they are.
38
+
33
39
  While ttyplayer plays in one terminal, any other terminal can drive it:
34
40
 
35
41
  ```
@@ -37,9 +43,14 @@ ttyplayer pause pause / resume
37
43
  ttyplayer next next in the queue
38
44
  ttyplayer prev previous in the queue
39
45
  ttyplayer stop quit the player
46
+ ttyplayer sleep 30m Sleeping in 30:00 (also 1h, 1h30m, 90 seconds)
47
+ ttyplayer sleep end stop when the current track ends
48
+ ttyplayer sleep off cancel it (no argument: show it)
40
49
  ttyplayer status 1:23 / 4:56 Playing <title>
41
50
  ```
42
51
 
52
+ When the sleep timer runs out, the volume fades to nothing over 5 seconds and the player stops as if `q` was pressed; the volume is put back first, so the next play starts at the old level. Paused or idle, it stops at once. `ttyplayer status`, the TUI and the web remote show `zz 27:13` (or `zz end`) while a timer is armed.
53
+
43
54
  ## Playlists
44
55
 
45
56
  Playlists are named lists you keep, in the order you choose, separate from history and favorites. A name is 1 to 40 letters, digits, spaces, `_` or `-`.
@@ -77,9 +88,32 @@ Picks accept several numbers at once: `1 3 5` queues those three in that order.
77
88
 
78
89
  `TTYPLAYER_TIMING=1 ttyplayer play <words>` prints how long the YouTube lookup took and adds `started in 2.4s` (from `loadfile` to the first sound) to the status line.
79
90
 
91
+ ## Spotify
92
+
93
+ ttyplayer can bring a Spotify playlist over as one of its own playlists. It reads only the playlist's *track list* from Spotify, never its audio (Spotify's audio is DRM-protected): each track is searched on YouTube as `<first artist> <title>`, and the first hit is added. A track YouTube has no hit for is reported and skipped; local files and podcast episodes in the playlist are skipped too. The result is a normal playlist: `ttyplayer playlist play <name>`, or the Playlists tab in the TUI.
94
+
95
+ ```
96
+ ttyplayer spotify login log in to Spotify in the browser, once
97
+ ttyplayer spotify playlists your Spotify playlists: name, tracks, id
98
+ ttyplayer spotify import <link | id> [--as <name>] [--limit N] find each track on YouTube and save it
99
+ ```
100
+
101
+ `import` takes a playlist link (`https://open.spotify.com/playlist/<id>`), a `spotify:playlist:<id>` URI, or the bare id. It names the playlist after the Spotify one (unless `--as`), creating it, or appending to it if one by that name exists; it prints `[3/40] ✓ Artist – Title → <YouTube title>` (or `✗ … not found`) per track, and `Saved N of M tracks to <name>` at the end. Each YouTube search takes a second or two, so a long playlist takes a while; `--limit N` imports only the first N tracks. If the network fails part way, what was saved stays saved.
102
+
103
+ Logging in needs a Spotify app of your own (free, once):
104
+
105
+ 1. Open the [Spotify developer dashboard](https://developer.spotify.com/dashboard), log in, and press **Create app**.
106
+ 2. Give it any name and description, set the **Redirect URI** to exactly `http://127.0.0.1:8765/callback`, tick **Web API**, and save.
107
+ 3. Open the app's **Settings**, copy its **Client ID**, and run `ttyplayer config set spotify_client_id <client id>`.
108
+ 4. Run `ttyplayer spotify login`: the browser opens Spotify's consent page; approve it, and the terminal says `Logged in as <your name>`.
109
+
110
+ ttyplayer asks only for read access to your playlists (`playlist-read-private playlist-read-collaborative`) and uses no client secret (the login is OAuth with PKCE). The login is kept in `spotify.json` next to `settings.toml`, readable only by you; it holds a refresh token, so treat it like a password. To revoke ttyplayer's access, remove the app under **Manage apps** on your Spotify account page (spotify.com/account/apps) and delete `spotify.json`.
111
+
80
112
  ## TUI
81
113
 
82
- `ttyplayer tui` opens a full-screen player: a search box, Search / Queue / History / Favorites / Playlists tabs, and a now-playing panel. Type a search or paste a link and press Enter. Ctrl-P opens the command palette (search, playlists, save queue as playlist, next theme, settings, help, quit, pause, next, previous, mute, and Textual's own theme picker); `?` lists every key and command.
114
+ `ttyplayer tui` opens a full-screen player: a search box, Search / Queue / History / Favorites / Playlists tabs, and a now-playing panel. Type a search or paste a link and press Enter; start the search with `sc:` (`sc: boards of canada`) to search SoundCloud, or `yt:` for YouTube, whatever `search_source` says. The Search tab's heading then reads `SoundCloud results` or `YouTube results`. Ctrl-P opens the command palette (search, playlists, save queue as playlist, next theme, settings, help, quit, pause, next, previous, mute, sleep, and Textual's own theme picker); **Sleep…** asks for a sleep timer (`30m` filled in; `end`, `off`, `1h30m`, … work as on the command line; Esc leaves the timer alone); `?` lists every key and command.
115
+
116
+ Under the volume, the panel's level meter shows two bars, `L` and `R`, that follow the sound's peaks about ten times a second (empty at −60 dBFS and below, full at 0 dBFS; empty while paused). `ttyplayer config set show_levels false`, or Enter on `show_levels` in the Settings screen, hides them and takes mpv's measuring filter out.
83
117
 
84
118
  | Where | Key | Action |
85
119
  |---|---|---|
@@ -132,6 +166,10 @@ ttyplayer keeps its preferences in `~/.config/ttyplayer/settings.toml` (`$XDG_CO
132
166
  | `server_port` | `7700` | the port `ttyplayer serve` listens on (1–65535) |
133
167
  | `server_token` | *generated* | the token every API request needs; `ttyplayer serve` makes one on first use |
134
168
  | `remote_url` | *none* | the server `ttyplayer tui` drives instead of playing itself, e.g. `http://host:7700` |
169
+ | `stream_enabled` | `false` | `ttyplayer serve` streams the sound to the web remote instead of playing it, as `--stream` does |
170
+ | `spotify_client_id` | *none* | the Client ID of your own Spotify app, which `ttyplayer spotify login` needs (see Spotify above) |
171
+ | `show_levels` | `true` | the TUI's level meter: mpv measures the sound's peaks and the now-playing panel shows them |
172
+ | `search_source` | `youtube` | where a search looks: `youtube` or `soundcloud`; `--source` and the TUI's `sc:` / `yt:` prefix override it once |
135
173
 
136
174
  ```
137
175
  ttyplayer config every setting, (default) when unchanged
@@ -140,7 +178,7 @@ ttyplayer config set <key> <value> change it: ttyplayer config set show_clock
140
178
  ttyplayer config path where the file is
141
179
  ```
142
180
 
143
- In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true / false one flips it and saves it (the clock shows or hides at once); the others are set with `ttyplayer config set`.
181
+ In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true / false one flips it and saves it (the clock and the level meter show or hide at once); the others are set with `ttyplayer config set`.
144
182
 
145
183
  ## Server
146
184
 
@@ -150,6 +188,7 @@ In the TUI, `S` (or Settings… in Ctrl-P) lists the settings: Enter on a true /
150
188
  ttyplayer serve listen on 127.0.0.1:7700 (this machine only)
151
189
  ttyplayer serve --host 0.0.0.0 listen on every address, so a phone on the LAN can reach it
152
190
  ttyplayer serve --port 8000 another port
191
+ ttyplayer serve --stream no sound here: Listen here on the web remote plays it (see below)
153
192
  ```
154
193
 
155
194
  It prints the address to open, with the token, and a QR code of it for a phone:
@@ -173,7 +212,7 @@ curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http
173
212
  | GET | `/api/status` | the player's status, plus `queue` (the videos) and `index` (1-based) |
174
213
  | POST | `/api/play` | `{"url": "…"}` or `{"query": "…"}`: the link's videos, or the first search result, replace the queue and play |
175
214
  | POST | `/api/queue` | `{"url": "…"}` or `{"query": "…"}`: appended to the queue (played at once when nothing plays) |
176
- | POST | `/api/command` | `{"name": "pause"\|"next"\|"prev"\|"stop"\|"mute"\|"seek"\|"volume"\|"jump"\|"remove"\|"move"\|"clear_others", "value"?}`; `seek` and `volume` take a number of seconds / steps, `jump` and `remove` a 0-based queue row, `move` two (`[from, to]`; the current track stays current); `clear_others` keeps only the current track → the new status |
215
+ | POST | `/api/command` | `{"name": "pause"\|"next"\|"prev"\|"stop"\|"mute"\|"sleep"\|"seek"\|"volume"\|"jump"\|"remove"\|"move"\|"clear_others", "value"?}`; `sleep` takes the text `ttyplayer sleep` does (`"30m"`, `"end"`, `"off"`), `seek` and `volume` a number of seconds / steps, `jump` and `remove` a 0-based queue row, `move` two (`[from, to]`; the current track stays current); `clear_others` keeps only the current track → the new status |
177
216
  | GET | `/api/commands` | the command names `/api/command` takes |
178
217
  | GET | `/api/search?q=…` | the search results (`search_limit` of them) |
179
218
  | GET | `/api/favorites` | the favorites, newest first |
@@ -182,8 +221,29 @@ curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http
182
221
  | POST | `/api/playlists/<name>/play` | that playlist becomes the queue |
183
222
  | GET, PATCH | `/api/settings` | every setting but the token; PATCH `{"key": value}` changes and saves them, checked like `config set` |
184
223
  | WS | `/ws?token=…` | sends the status on connect and on every change; takes the same `{"name", "value"?}` commands as `/api/command` and answers each with the status |
224
+ | GET | `/stream` | with `serve --stream`: the sound as an `audio/ogg` (Opus) stream, for as long as the client listens; 404 otherwise |
185
225
  | GET | `/` | the web remote (needs no token itself; it reads the token from its address); its files are under `/static/`, plus `/manifest.webmanifest` |
186
226
 
227
+ ### Listen on another device
228
+
229
+ `ttyplayer serve --stream` is the "music box on a server" mode, for a machine with no speakers of its own (a Raspberry Pi, a VPS, a closet PC): nothing plays on the server; the web remote gets a **Listen here** button that plays what the server plays, in that browser, on any device. Press it again (**Stop listening**) to stop. Several devices can listen at once; one on a bad connection skips, the others do not. Pausing and changing tracks keep the stream open (you hear silence in between). What you hear runs a little behind the remote's controls: the server adds about half a second, the browser its own buffer. `ttyplayer config set stream_enabled true` makes it the default for `serve`.
230
+
231
+ ```
232
+ ttyplayer serve --stream --host 0.0.0.0
233
+ ```
234
+
235
+ It needs ffmpeg, which nothing else in ttyplayer does (`ttyplayer doctor` shows whether it is there), and runs on macOS and Linux, not Windows:
236
+
237
+ | System | Install ffmpeg |
238
+ |---|---|
239
+ | macOS | `brew install ffmpeg` |
240
+ | Debian, Ubuntu, Raspberry Pi OS | `sudo apt-get install -y ffmpeg` |
241
+ | Fedora | `sudo dnf install -y ffmpeg-free` |
242
+ | Arch | `sudo pacman -S --noconfirm ffmpeg` |
243
+ | Alpine | `sudo apk add ffmpeg` |
244
+
245
+ The stream is gated by the same token as the API and is plain HTTP; the security notes below apply to it too.
246
+
187
247
  ### The TUI as a remote
188
248
 
189
249
  `ttyplayer tui --remote http://host:7700` opens the same screen, with the same keys and tabs, on another machine (a laptop, say) while the server plays: Enter on a result makes the *server's* speakers play, and the now-playing panel and the Queue tab follow the server over its socket (the header says `remote: host:7700`). The token is the `server_token` setting, so copy it into the laptop's settings with `ttyplayer config set server_token <token>`; `--token <token>` works too, but leaves it in your shell history. `ttyplayer config set remote_url http://host:7700` makes the remote the default, so a plain `ttyplayer tui` drives it.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ttyplayer"
3
- version = "0.5.0"
3
+ version = "0.7.0"
4
4
  description = "ttyplayer: a modern YouTube player for the terminal — search, queue, and play audio or video via mpv"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ttyplayer"
3
- version = "0.5.0"
3
+ version = "0.7.0"
4
4
  description = "ttyplayer: a modern YouTube player for the terminal — search, queue, and play audio or video via mpv"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"