ttyplayer 0.3.1__tar.gz → 0.5.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.
- ttyplayer-0.5.0/PKG-INFO +332 -0
- ttyplayer-0.5.0/README.md +307 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/pyproject.toml +3 -1
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/pyproject.toml.orig +3 -1
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/cli.py +244 -9
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/favorites.py +5 -22
- ttyplayer-0.5.0/src/ttyplayer/history.py +38 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/player.py +25 -1
- ttyplayer-0.5.0/src/ttyplayer/playlists.py +99 -0
- ttyplayer-0.5.0/src/ttyplayer/remote.py +299 -0
- ttyplayer-0.5.0/src/ttyplayer/server.py +456 -0
- ttyplayer-0.5.0/src/ttyplayer/settings.py +145 -0
- ttyplayer-0.5.0/src/ttyplayer/static/icon.svg +6 -0
- ttyplayer-0.5.0/src/ttyplayer/static/index.html +74 -0
- ttyplayer-0.5.0/src/ttyplayer/static/manifest.webmanifest +14 -0
- ttyplayer-0.5.0/src/ttyplayer/static/remote.css +202 -0
- ttyplayer-0.5.0/src/ttyplayer/static/remote.js +398 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/tui.py +390 -25
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/tui.tcss +49 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/utils.py +51 -2
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/youtube.py +7 -2
- ttyplayer-0.3.1/PKG-INFO +0 -223
- ttyplayer-0.3.1/README.md +0 -200
- ttyplayer-0.3.1/src/ttyplayer/history.py +0 -64
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/LICENSE +0 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/__init__.py +0 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/control.py +0 -0
- {ttyplayer-0.3.1 → ttyplayer-0.5.0}/src/ttyplayer/models.py +0 -0
ttyplayer-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ttyplayer
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: ttyplayer: a modern YouTube player for the terminal — search, queue, and play audio or video via mpv
|
|
5
|
+
Keywords: youtube,music,player,mpv,cli,terminal,tui,yt-dlp
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Players
|
|
16
|
+
Requires-Dist: aiohttp>=3.10
|
|
17
|
+
Requires-Dist: qrcode>=8
|
|
18
|
+
Requires-Dist: textual>=8.2
|
|
19
|
+
Requires-Dist: typer>=0.27.2
|
|
20
|
+
Requires-Dist: yt-dlp>=2026.8.19
|
|
21
|
+
Requires-Python: >=3.13
|
|
22
|
+
Project-URL: Homepage, https://github.com/webliftro/ttyplayer
|
|
23
|
+
Project-URL: Repository, https://github.com/webliftro/ttyplayer
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# ttyplayer
|
|
27
|
+
|
|
28
|
+
A modern YouTube player for the terminal. Search YouTube, queue videos and playlists, and play audio, or video too, through mpv: from the command line, or from a full-screen TUI. Keep a history and favorites, and drive the player from any other terminal.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
ttyplayer play <url> play a video link, audio only
|
|
34
|
+
ttyplayer play <playlist url> queue every entry of a playlist
|
|
35
|
+
ttyplayer play <words...> search, pick one or more results, play them in order
|
|
36
|
+
ttyplayer play ... --video open a video window as well
|
|
37
|
+
ttyplayer play ... --limit 10 show more search results
|
|
38
|
+
|
|
39
|
+
ttyplayer search <words...> list results with durations
|
|
40
|
+
ttyplayer history recently played, newest first
|
|
41
|
+
ttyplayer history --play pick from history and play again
|
|
42
|
+
ttyplayer history --clear forget the history
|
|
43
|
+
ttyplayer favorite <url | words...> add a link, a playlist, or search picks to favorites
|
|
44
|
+
ttyplayer favorites favorites, newest first
|
|
45
|
+
ttyplayer favorites --play pick from favorites and play them
|
|
46
|
+
ttyplayer favorites --remove 2 drop the second favorite as listed
|
|
47
|
+
ttyplayer favorites --clear forget all favorites
|
|
48
|
+
ttyplayer playlist ... your own named playlists (see Playlists below)
|
|
49
|
+
ttyplayer tui [--video] full-screen: search box, results list, now-playing bar
|
|
50
|
+
ttyplayer serve [--host] [--port] play headless, driven over HTTP from any device (see Server below)
|
|
51
|
+
ttyplayer config list the settings (see Settings below)
|
|
52
|
+
ttyplayer doctor check Python, yt-dlp, mpv and ttyplayer's folders
|
|
53
|
+
ttyplayer version
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Channels and playlists in search results are skipped; a track that cannot be played is reported and skipped.
|
|
57
|
+
|
|
58
|
+
While ttyplayer plays in one terminal, any other terminal can drive it:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
ttyplayer pause pause / resume
|
|
62
|
+
ttyplayer next next in the queue
|
|
63
|
+
ttyplayer prev previous in the queue
|
|
64
|
+
ttyplayer stop quit the player
|
|
65
|
+
ttyplayer status 1:23 / 4:56 Playing <title>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Playlists
|
|
69
|
+
|
|
70
|
+
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 `-`.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
ttyplayer playlist list every playlist and how many videos it holds
|
|
74
|
+
ttyplayer playlist show <name> its videos, numbered
|
|
75
|
+
ttyplayer playlist create <name> a new, empty playlist
|
|
76
|
+
ttyplayer playlist add <name> <url | words...> append a link, every entry of a playlist link, or search picks
|
|
77
|
+
ttyplayer playlist remove <name> 3 drop the third video
|
|
78
|
+
ttyplayer playlist move <name> 3 1 move the third video to the top
|
|
79
|
+
ttyplayer playlist delete <name> [--yes] delete it (asks first unless --yes)
|
|
80
|
+
ttyplayer playlist play <name> [--video] [--shuffle] queue the whole playlist, in order or shuffled
|
|
81
|
+
ttyplayer playlist import <playlist url> [--as <name>] save a YouTube playlist, named after its title
|
|
82
|
+
ttyplayer playlist save-queue <name> save what the playing ttyplayer has queued, replacing <name>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`save-queue` works from another terminal while ttyplayer plays, like `pause` and `status`; it creates the playlist if there is none by that name. `import` refuses a name that is already taken; give another with `--as`.
|
|
86
|
+
|
|
87
|
+
Keys in `ttyplayer play` (the terminal player; `ttyplayer tui` has its own table below):
|
|
88
|
+
|
|
89
|
+
| Key | Action |
|
|
90
|
+
|---|---|
|
|
91
|
+
| space | pause / resume |
|
|
92
|
+
| left / right, `,` / `.` | seek 5 seconds |
|
|
93
|
+
| up / down, `-` / `+` | volume |
|
|
94
|
+
| `n` / `p` | next / previous in the queue |
|
|
95
|
+
| `q` or Ctrl-C | quit, restores the terminal and stops mpv |
|
|
96
|
+
|
|
97
|
+
On Windows (Windows Terminal, PowerShell) the keys, arrows included, map the same way.
|
|
98
|
+
|
|
99
|
+
The volume meter, and the volume keys here and in the TUI, follow the Mac's system volume on macOS (the Mac's own volume keys move the meter within 2 seconds) and mpv's device (per-app) volume on Linux and Windows.
|
|
100
|
+
|
|
101
|
+
Picks accept several numbers at once: `1 3 5` queues those three in that order. After a search, `m` lists the next batch of results.
|
|
102
|
+
|
|
103
|
+
`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
|
+
|
|
105
|
+
## TUI
|
|
106
|
+
|
|
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.
|
|
108
|
+
|
|
109
|
+
| Where | Key | Action |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| table | `↑` / `↓` | move through the list (volume is `-` / `+`) |
|
|
112
|
+
| anywhere | `/` | focus the search box (`esc` returns to the table) |
|
|
113
|
+
| anywhere | `?` | help: every key and command (`esc` closes) |
|
|
114
|
+
| anywhere | `1` `2` `3` `4` `5` | Search / Queue / History / Favorites / Playlists tab |
|
|
115
|
+
| anywhere | Ctrl-C | quit and stop mpv |
|
|
116
|
+
| anywhere | Ctrl-P | command palette |
|
|
117
|
+
| anywhere | `t` | next theme (remembered for next time) |
|
|
118
|
+
| table | `P` | save the queue as a playlist (asks for a name; an existing playlist of that name is replaced) |
|
|
119
|
+
| table | `S` | settings: Enter flips a true / false one, `esc` closes |
|
|
120
|
+
| table | `q` | quit and stop mpv (in the search box it is just a letter) |
|
|
121
|
+
| table | `space` | pause / resume |
|
|
122
|
+
| table | `n` / `p` | next / previous |
|
|
123
|
+
| table | `,` / `.` | seek −5 s / +5 s |
|
|
124
|
+
| table | `<` / `>` | seek −30 s / +30 s |
|
|
125
|
+
| table | `-` / `+` | volume −5 / +5 |
|
|
126
|
+
| table | `M` | mute / unmute (🔇 in the panel) |
|
|
127
|
+
| table | `f` | favorite / unfavorite this row (the track playing when there is no row) |
|
|
128
|
+
| Search · History · Favorites row | Enter | play this one, then the rows after it |
|
|
129
|
+
| Search · History · Favorites row | `a` | add to the queue |
|
|
130
|
+
| Search · History · Favorites · Queue row | `A` | add to a playlist (pick one, or New playlist…) |
|
|
131
|
+
| Search | `m` | more results |
|
|
132
|
+
| Queue row | Enter | jump to this item |
|
|
133
|
+
| Queue row | `d` | remove from the queue |
|
|
134
|
+
| Queue row | `K` / `J` (or `shift+↑` / `shift+↓`) | move up / down |
|
|
135
|
+
| Queue | `c` | clear the queue (keeps the current track playing) |
|
|
136
|
+
| Favorites row | `d` | remove from favorites |
|
|
137
|
+
| Playlists | Enter | open the playlist (`esc` or Backspace goes back to the list) |
|
|
138
|
+
| Playlists | `d` | delete the playlist (asks first: `y` or Enter deletes, `esc` keeps it) |
|
|
139
|
+
| playlist track | Enter | play the whole playlist from this track |
|
|
140
|
+
| playlist track | `a` | add to the queue |
|
|
141
|
+
| playlist track | `d` | remove from the playlist |
|
|
142
|
+
| playlist track | `K` / `J` (or `shift+↑` / `shift+↓`) | move up / down in the playlist |
|
|
143
|
+
| open playlist | `s` | shuffle-play the playlist |
|
|
144
|
+
|
|
145
|
+
In the search box, letters, digits, `/` and `?` are typed as text; `esc` leaves it for the table.
|
|
146
|
+
|
|
147
|
+
## Settings
|
|
148
|
+
|
|
149
|
+
ttyplayer keeps its preferences in `~/.config/ttyplayer/settings.toml` (`$XDG_CONFIG_HOME/ttyplayer/` when that is set, `%APPDATA%\ttyplayer\` on Windows). Every key is optional; a missing file means the defaults.
|
|
150
|
+
|
|
151
|
+
| Key | Default | What it does |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `show_clock` | `true` | the clock in the TUI's header |
|
|
154
|
+
| `theme` | `textual-dark` | the TUI's color theme; `t` in the TUI picks the next one and saves it |
|
|
155
|
+
| `search_limit` | `10` | how many results a TUI search fetches, and `m` adds (1–50) |
|
|
156
|
+
| `server_host` | `127.0.0.1` | the address `ttyplayer serve` listens on; `0.0.0.0` opens it to the network |
|
|
157
|
+
| `server_port` | `7700` | the port `ttyplayer serve` listens on (1–65535) |
|
|
158
|
+
| `server_token` | *generated* | the token every API request needs; `ttyplayer serve` makes one on first use |
|
|
159
|
+
| `remote_url` | *none* | the server `ttyplayer tui` drives instead of playing itself, e.g. `http://host:7700` |
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
ttyplayer config every setting, (default) when unchanged
|
|
163
|
+
ttyplayer config get <key> one setting's value
|
|
164
|
+
ttyplayer config set <key> <value> change it: ttyplayer config set show_clock false
|
|
165
|
+
ttyplayer config path where the file is
|
|
166
|
+
```
|
|
167
|
+
|
|
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`.
|
|
169
|
+
|
|
170
|
+
## Server
|
|
171
|
+
|
|
172
|
+
`ttyplayer serve` runs the player headless on the machine with the speakers (no keyboard, no TUI) and lets any device on the network drive it through a small HTTP + WebSocket API. The other terminals' `ttyplayer pause`, `next`, `status` and `stop` keep working too.
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
ttyplayer serve listen on 127.0.0.1:7700 (this machine only)
|
|
176
|
+
ttyplayer serve --host 0.0.0.0 listen on every address, so a phone on the LAN can reach it
|
|
177
|
+
ttyplayer serve --port 8000 another port
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
It prints the address to open, with the token, and a QR code of it for a phone:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
Serving ttyplayer at http://192.168.1.20:7700/?token=…
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Open that address on a phone and it is a remote: what plays (with a moving progress bar), previous / play-pause / next, a volume slider and mute, a search box (a pasted link plays at once; each result has **Play** and **Queue**), the queue (tap a row to jump there, **×** removes it, **Clear** keeps only what plays), your favorites (the heart on any row adds or drops one) and your playlists. It follows the phone's dark or light mode, reconnects by itself when the server restarts, and "Add to Home Screen" makes it an app icon. The page keeps the token only for that browser tab; opened without one (from the home screen, say) it asks you to paste the token `serve` printed. Ctrl-C (or `ttyplayer stop`, or a `stop` command) stops the server and the player.
|
|
187
|
+
|
|
188
|
+
Every `/api/…` request and the socket need the token, as `Authorization: Bearer <token>` or `?token=<token>`; without it the reply is `401 {"error": "unauthorized"}`. Replies are JSON; errors are `{"error": "…"}`.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
curl -H "Authorization: Bearer $TOKEN" http://host:7700/api/status
|
|
192
|
+
curl -H "Authorization: Bearer $TOKEN" -d '{"query": "lofi beats"}' http://host:7700/api/play
|
|
193
|
+
curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http://host:7700/api/command
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| Method | Path | Body / reply |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| GET | `/api/status` | the player's status, plus `queue` (the videos) and `index` (1-based) |
|
|
199
|
+
| POST | `/api/play` | `{"url": "…"}` or `{"query": "…"}`: the link's videos, or the first search result, replace the queue and play |
|
|
200
|
+
| 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 |
|
|
202
|
+
| GET | `/api/commands` | the command names `/api/command` takes |
|
|
203
|
+
| GET | `/api/search?q=…` | the search results (`search_limit` of them) |
|
|
204
|
+
| GET | `/api/favorites` | the favorites, newest first |
|
|
205
|
+
| POST | `/api/favorites/<id>` | unfavorites that video, or favorites it (the body is the video: `{"title", "uploader", "duration"}`) → the favorites |
|
|
206
|
+
| GET | `/api/playlists` | `[{"name": …, "count": …}]` |
|
|
207
|
+
| POST | `/api/playlists/<name>/play` | that playlist becomes the queue |
|
|
208
|
+
| GET, PATCH | `/api/settings` | every setting but the token; PATCH `{"key": value}` changes and saves them, checked like `config set` |
|
|
209
|
+
| 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 |
|
|
210
|
+
| GET | `/` | the web remote (needs no token itself; it reads the token from its address); its files are under `/static/`, plus `/manifest.webmanifest` |
|
|
211
|
+
|
|
212
|
+
### The TUI as a remote
|
|
213
|
+
|
|
214
|
+
`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.
|
|
215
|
+
|
|
216
|
+
Searching still happens on the laptop; the server looks each picked video up again before it plays or queues it, so a long queue fills in over a few seconds. History stays the server's: the laptop records nothing. When the server cannot be reached the TUI says so once and keeps trying; a refused token says `Server rejected the token`.
|
|
217
|
+
|
|
218
|
+
Security: the token is the only gate, and plain HTTP carries it in clear text. That is fine on a home network you trust; it is why `serve` listens on 127.0.0.1 unless told otherwise. To reach it beyond your LAN (a VPS, say), put it behind a reverse proxy with HTTPS. Anyone with the token can control the player, including stopping it; change the token with `ttyplayer config set server_token <new>` and restart.
|
|
219
|
+
|
|
220
|
+
## Install
|
|
221
|
+
|
|
222
|
+
One command installs everything ttyplayer needs.
|
|
223
|
+
|
|
224
|
+
macOS and Linux:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
curl -LsSf https://raw.githubusercontent.com/webliftro/ttyplayer/main/install.sh | sh
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Windows (PowerShell):
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
irm https://raw.githubusercontent.com/webliftro/ttyplayer/main/install.ps1 | iex
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Or clone the repository and run `./install.sh` (macOS, Linux) or `powershell -ExecutionPolicy ByPass -File .\install.ps1` (Windows) inside it; that installs the checkout you cloned.
|
|
237
|
+
|
|
238
|
+
The script:
|
|
239
|
+
|
|
240
|
+
1. installs [uv](https://docs.astral.sh/uv/) with its official installer if `uv` is missing (uv brings its own Python 3.13+; it runs as you, without sudo),
|
|
241
|
+
2. installs mpv if it is missing: Homebrew on macOS, your package manager on Linux (`apt-get`, `dnf`, `pacman`, `zypper` or `apk`, with `sudo`), winget on Windows,
|
|
242
|
+
3. installs ttyplayer with `uv tool install`,
|
|
243
|
+
4. runs `ttyplayer doctor`: every line should start with ✓.
|
|
244
|
+
|
|
245
|
+
It prints every command before running it. `TTYPLAYER_INSTALL_DRY_RUN=1 ./install.sh` and `install.ps1 -DryRun` only print them. If Homebrew (macOS) or winget (Windows) is missing, the script tells you how to install it and stops. If `ttyplayer` is not found in a new terminal, run `uv tool update-shell`.
|
|
246
|
+
|
|
247
|
+
<details>
|
|
248
|
+
<summary>By hand</summary>
|
|
249
|
+
|
|
250
|
+
Three steps on every system: install uv, install mpv, install ttyplayer. Then run `ttyplayer doctor`.
|
|
251
|
+
|
|
252
|
+
macOS:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
256
|
+
brew install mpv
|
|
257
|
+
uv tool install ttyplayer
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Linux:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
264
|
+
sudo apt-get install -y mpv
|
|
265
|
+
uv tool install ttyplayer
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
On Fedora use `sudo dnf install -y mpv`, on Arch `sudo pacman -S --noconfirm mpv`, on openSUSE `sudo zypper install -y mpv`, on Alpine `sudo apk add mpv`.
|
|
269
|
+
|
|
270
|
+
Windows:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
274
|
+
winget install -e --id shinchiro.mpv
|
|
275
|
+
uv tool install ttyplayer
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Instead of winget, `scoop install mpv` (from the `extras` bucket) or `choco install mpv` work too.
|
|
279
|
+
|
|
280
|
+
`uv tool install ttyplayer` installs from PyPI once ttyplayer is published there. Until then install straight from GitHub:
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
uv tool install git+https://github.com/webliftro/ttyplayer
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
</details>
|
|
287
|
+
|
|
288
|
+
Upgrade with `uv tool upgrade ttyplayer`. Uninstall with `uv tool uninstall ttyplayer`.
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
Install from a checkout, with mpv installed as above:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
uv tool install --editable .
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The `--editable` flag means edits to the source are live without reinstalling.
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
uv run pytest # unit tests, no network and no mpv needed
|
|
302
|
+
uv run ttyplayer ... # run from the checkout
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The live tests talk to the real YouTube through `ttyplayer.youtube`, so a yt-dlp or YouTube change shows up as a failing test. They are skipped unless `TTYPLAYER_LIVE=1` is set:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
uv run pytest -q # default suite, live tests skipped
|
|
309
|
+
TTYPLAYER_LIVE=1 uv run pytest -q tests/test_live.py # live tests, needs network
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
History lives in `$XDG_DATA_HOME/ttyplayer/history.jsonl`, by default `~/.local/share/ttyplayer/history.jsonl` (`%LOCALAPPDATA%\ttyplayer\history.jsonl` on Windows). Favorites live next to it in `favorites.jsonl`, and each playlist in `playlists/<name>.jsonl`. History and favorites kept under the player's earlier name are moved here on the first run.
|
|
313
|
+
|
|
314
|
+
### Releasing
|
|
315
|
+
|
|
316
|
+
CI (`.github/workflows/tests.yml`) runs the suite on Linux and macOS on every push. Pushing a `v*` tag runs `.github/workflows/release.yml`, which builds with `uv build` and publishes to PyPI through trusted publishing, so there is no token to store.
|
|
317
|
+
|
|
318
|
+
One-time setup on PyPI: create an account, then under "Publishing" add a pending GitHub publisher for the project `ttyplayer` with owner `webliftro`, repository `ttyplayer` and workflow `release.yml`. The first tagged release creates the project.
|
|
319
|
+
|
|
320
|
+
To release, bump the version (`uv version --bump minor`, or edit `version` in `pyproject.toml`), commit, then:
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
git tag v0.3.0 && git push --tags
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## How it works
|
|
327
|
+
|
|
328
|
+
yt-dlp resolves what to play: a search or a playlist becomes a list of videos, a link becomes one. mpv produces the sound, started in the background with a private IPC socket. ttyplayer talks to it in JSON over that socket, owns the keyboard, and redraws a one-line status. See `docs/architecture.md`.
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# ttyplayer
|
|
2
|
+
|
|
3
|
+
A modern YouTube player for the terminal. Search YouTube, queue videos and playlists, and play audio, or video too, through mpv: from the command line, or from a full-screen TUI. Keep a history and favorites, and drive the player from any other terminal.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ttyplayer play <url> play a video link, audio only
|
|
9
|
+
ttyplayer play <playlist url> queue every entry of a playlist
|
|
10
|
+
ttyplayer play <words...> search, pick one or more results, play them in order
|
|
11
|
+
ttyplayer play ... --video open a video window as well
|
|
12
|
+
ttyplayer play ... --limit 10 show more search results
|
|
13
|
+
|
|
14
|
+
ttyplayer search <words...> list results with durations
|
|
15
|
+
ttyplayer history recently played, newest first
|
|
16
|
+
ttyplayer history --play pick from history and play again
|
|
17
|
+
ttyplayer history --clear forget the history
|
|
18
|
+
ttyplayer favorite <url | words...> add a link, a playlist, or search picks to favorites
|
|
19
|
+
ttyplayer favorites favorites, newest first
|
|
20
|
+
ttyplayer favorites --play pick from favorites and play them
|
|
21
|
+
ttyplayer favorites --remove 2 drop the second favorite as listed
|
|
22
|
+
ttyplayer favorites --clear forget all favorites
|
|
23
|
+
ttyplayer playlist ... your own named playlists (see Playlists below)
|
|
24
|
+
ttyplayer tui [--video] full-screen: search box, results list, now-playing bar
|
|
25
|
+
ttyplayer serve [--host] [--port] play headless, driven over HTTP from any device (see Server below)
|
|
26
|
+
ttyplayer config list the settings (see Settings below)
|
|
27
|
+
ttyplayer doctor check Python, yt-dlp, mpv and ttyplayer's folders
|
|
28
|
+
ttyplayer version
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Channels and playlists in search results are skipped; a track that cannot be played is reported and skipped.
|
|
32
|
+
|
|
33
|
+
While ttyplayer plays in one terminal, any other terminal can drive it:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
ttyplayer pause pause / resume
|
|
37
|
+
ttyplayer next next in the queue
|
|
38
|
+
ttyplayer prev previous in the queue
|
|
39
|
+
ttyplayer stop quit the player
|
|
40
|
+
ttyplayer status 1:23 / 4:56 Playing <title>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Playlists
|
|
44
|
+
|
|
45
|
+
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 `-`.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
ttyplayer playlist list every playlist and how many videos it holds
|
|
49
|
+
ttyplayer playlist show <name> its videos, numbered
|
|
50
|
+
ttyplayer playlist create <name> a new, empty playlist
|
|
51
|
+
ttyplayer playlist add <name> <url | words...> append a link, every entry of a playlist link, or search picks
|
|
52
|
+
ttyplayer playlist remove <name> 3 drop the third video
|
|
53
|
+
ttyplayer playlist move <name> 3 1 move the third video to the top
|
|
54
|
+
ttyplayer playlist delete <name> [--yes] delete it (asks first unless --yes)
|
|
55
|
+
ttyplayer playlist play <name> [--video] [--shuffle] queue the whole playlist, in order or shuffled
|
|
56
|
+
ttyplayer playlist import <playlist url> [--as <name>] save a YouTube playlist, named after its title
|
|
57
|
+
ttyplayer playlist save-queue <name> save what the playing ttyplayer has queued, replacing <name>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`save-queue` works from another terminal while ttyplayer plays, like `pause` and `status`; it creates the playlist if there is none by that name. `import` refuses a name that is already taken; give another with `--as`.
|
|
61
|
+
|
|
62
|
+
Keys in `ttyplayer play` (the terminal player; `ttyplayer tui` has its own table below):
|
|
63
|
+
|
|
64
|
+
| Key | Action |
|
|
65
|
+
|---|---|
|
|
66
|
+
| space | pause / resume |
|
|
67
|
+
| left / right, `,` / `.` | seek 5 seconds |
|
|
68
|
+
| up / down, `-` / `+` | volume |
|
|
69
|
+
| `n` / `p` | next / previous in the queue |
|
|
70
|
+
| `q` or Ctrl-C | quit, restores the terminal and stops mpv |
|
|
71
|
+
|
|
72
|
+
On Windows (Windows Terminal, PowerShell) the keys, arrows included, map the same way.
|
|
73
|
+
|
|
74
|
+
The volume meter, and the volume keys here and in the TUI, follow the Mac's system volume on macOS (the Mac's own volume keys move the meter within 2 seconds) and mpv's device (per-app) volume on Linux and Windows.
|
|
75
|
+
|
|
76
|
+
Picks accept several numbers at once: `1 3 5` queues those three in that order. After a search, `m` lists the next batch of results.
|
|
77
|
+
|
|
78
|
+
`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
|
+
|
|
80
|
+
## TUI
|
|
81
|
+
|
|
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.
|
|
83
|
+
|
|
84
|
+
| Where | Key | Action |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| table | `↑` / `↓` | move through the list (volume is `-` / `+`) |
|
|
87
|
+
| anywhere | `/` | focus the search box (`esc` returns to the table) |
|
|
88
|
+
| anywhere | `?` | help: every key and command (`esc` closes) |
|
|
89
|
+
| anywhere | `1` `2` `3` `4` `5` | Search / Queue / History / Favorites / Playlists tab |
|
|
90
|
+
| anywhere | Ctrl-C | quit and stop mpv |
|
|
91
|
+
| anywhere | Ctrl-P | command palette |
|
|
92
|
+
| anywhere | `t` | next theme (remembered for next time) |
|
|
93
|
+
| table | `P` | save the queue as a playlist (asks for a name; an existing playlist of that name is replaced) |
|
|
94
|
+
| table | `S` | settings: Enter flips a true / false one, `esc` closes |
|
|
95
|
+
| table | `q` | quit and stop mpv (in the search box it is just a letter) |
|
|
96
|
+
| table | `space` | pause / resume |
|
|
97
|
+
| table | `n` / `p` | next / previous |
|
|
98
|
+
| table | `,` / `.` | seek −5 s / +5 s |
|
|
99
|
+
| table | `<` / `>` | seek −30 s / +30 s |
|
|
100
|
+
| table | `-` / `+` | volume −5 / +5 |
|
|
101
|
+
| table | `M` | mute / unmute (🔇 in the panel) |
|
|
102
|
+
| table | `f` | favorite / unfavorite this row (the track playing when there is no row) |
|
|
103
|
+
| Search · History · Favorites row | Enter | play this one, then the rows after it |
|
|
104
|
+
| Search · History · Favorites row | `a` | add to the queue |
|
|
105
|
+
| Search · History · Favorites · Queue row | `A` | add to a playlist (pick one, or New playlist…) |
|
|
106
|
+
| Search | `m` | more results |
|
|
107
|
+
| Queue row | Enter | jump to this item |
|
|
108
|
+
| Queue row | `d` | remove from the queue |
|
|
109
|
+
| Queue row | `K` / `J` (or `shift+↑` / `shift+↓`) | move up / down |
|
|
110
|
+
| Queue | `c` | clear the queue (keeps the current track playing) |
|
|
111
|
+
| Favorites row | `d` | remove from favorites |
|
|
112
|
+
| Playlists | Enter | open the playlist (`esc` or Backspace goes back to the list) |
|
|
113
|
+
| Playlists | `d` | delete the playlist (asks first: `y` or Enter deletes, `esc` keeps it) |
|
|
114
|
+
| playlist track | Enter | play the whole playlist from this track |
|
|
115
|
+
| playlist track | `a` | add to the queue |
|
|
116
|
+
| playlist track | `d` | remove from the playlist |
|
|
117
|
+
| playlist track | `K` / `J` (or `shift+↑` / `shift+↓`) | move up / down in the playlist |
|
|
118
|
+
| open playlist | `s` | shuffle-play the playlist |
|
|
119
|
+
|
|
120
|
+
In the search box, letters, digits, `/` and `?` are typed as text; `esc` leaves it for the table.
|
|
121
|
+
|
|
122
|
+
## Settings
|
|
123
|
+
|
|
124
|
+
ttyplayer keeps its preferences in `~/.config/ttyplayer/settings.toml` (`$XDG_CONFIG_HOME/ttyplayer/` when that is set, `%APPDATA%\ttyplayer\` on Windows). Every key is optional; a missing file means the defaults.
|
|
125
|
+
|
|
126
|
+
| Key | Default | What it does |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| `show_clock` | `true` | the clock in the TUI's header |
|
|
129
|
+
| `theme` | `textual-dark` | the TUI's color theme; `t` in the TUI picks the next one and saves it |
|
|
130
|
+
| `search_limit` | `10` | how many results a TUI search fetches, and `m` adds (1–50) |
|
|
131
|
+
| `server_host` | `127.0.0.1` | the address `ttyplayer serve` listens on; `0.0.0.0` opens it to the network |
|
|
132
|
+
| `server_port` | `7700` | the port `ttyplayer serve` listens on (1–65535) |
|
|
133
|
+
| `server_token` | *generated* | the token every API request needs; `ttyplayer serve` makes one on first use |
|
|
134
|
+
| `remote_url` | *none* | the server `ttyplayer tui` drives instead of playing itself, e.g. `http://host:7700` |
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
ttyplayer config every setting, (default) when unchanged
|
|
138
|
+
ttyplayer config get <key> one setting's value
|
|
139
|
+
ttyplayer config set <key> <value> change it: ttyplayer config set show_clock false
|
|
140
|
+
ttyplayer config path where the file is
|
|
141
|
+
```
|
|
142
|
+
|
|
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`.
|
|
144
|
+
|
|
145
|
+
## Server
|
|
146
|
+
|
|
147
|
+
`ttyplayer serve` runs the player headless on the machine with the speakers (no keyboard, no TUI) and lets any device on the network drive it through a small HTTP + WebSocket API. The other terminals' `ttyplayer pause`, `next`, `status` and `stop` keep working too.
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
ttyplayer serve listen on 127.0.0.1:7700 (this machine only)
|
|
151
|
+
ttyplayer serve --host 0.0.0.0 listen on every address, so a phone on the LAN can reach it
|
|
152
|
+
ttyplayer serve --port 8000 another port
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
It prints the address to open, with the token, and a QR code of it for a phone:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Serving ttyplayer at http://192.168.1.20:7700/?token=…
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Open that address on a phone and it is a remote: what plays (with a moving progress bar), previous / play-pause / next, a volume slider and mute, a search box (a pasted link plays at once; each result has **Play** and **Queue**), the queue (tap a row to jump there, **×** removes it, **Clear** keeps only what plays), your favorites (the heart on any row adds or drops one) and your playlists. It follows the phone's dark or light mode, reconnects by itself when the server restarts, and "Add to Home Screen" makes it an app icon. The page keeps the token only for that browser tab; opened without one (from the home screen, say) it asks you to paste the token `serve` printed. Ctrl-C (or `ttyplayer stop`, or a `stop` command) stops the server and the player.
|
|
162
|
+
|
|
163
|
+
Every `/api/…` request and the socket need the token, as `Authorization: Bearer <token>` or `?token=<token>`; without it the reply is `401 {"error": "unauthorized"}`. Replies are JSON; errors are `{"error": "…"}`.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
curl -H "Authorization: Bearer $TOKEN" http://host:7700/api/status
|
|
167
|
+
curl -H "Authorization: Bearer $TOKEN" -d '{"query": "lofi beats"}' http://host:7700/api/play
|
|
168
|
+
curl -H "Authorization: Bearer $TOKEN" -d '{"name": "volume", "value": -5}' http://host:7700/api/command
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
| Method | Path | Body / reply |
|
|
172
|
+
|---|---|---|
|
|
173
|
+
| GET | `/api/status` | the player's status, plus `queue` (the videos) and `index` (1-based) |
|
|
174
|
+
| POST | `/api/play` | `{"url": "…"}` or `{"query": "…"}`: the link's videos, or the first search result, replace the queue and play |
|
|
175
|
+
| 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 |
|
|
177
|
+
| GET | `/api/commands` | the command names `/api/command` takes |
|
|
178
|
+
| GET | `/api/search?q=…` | the search results (`search_limit` of them) |
|
|
179
|
+
| GET | `/api/favorites` | the favorites, newest first |
|
|
180
|
+
| POST | `/api/favorites/<id>` | unfavorites that video, or favorites it (the body is the video: `{"title", "uploader", "duration"}`) → the favorites |
|
|
181
|
+
| GET | `/api/playlists` | `[{"name": …, "count": …}]` |
|
|
182
|
+
| POST | `/api/playlists/<name>/play` | that playlist becomes the queue |
|
|
183
|
+
| GET, PATCH | `/api/settings` | every setting but the token; PATCH `{"key": value}` changes and saves them, checked like `config set` |
|
|
184
|
+
| 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 |
|
|
185
|
+
| GET | `/` | the web remote (needs no token itself; it reads the token from its address); its files are under `/static/`, plus `/manifest.webmanifest` |
|
|
186
|
+
|
|
187
|
+
### The TUI as a remote
|
|
188
|
+
|
|
189
|
+
`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.
|
|
190
|
+
|
|
191
|
+
Searching still happens on the laptop; the server looks each picked video up again before it plays or queues it, so a long queue fills in over a few seconds. History stays the server's: the laptop records nothing. When the server cannot be reached the TUI says so once and keeps trying; a refused token says `Server rejected the token`.
|
|
192
|
+
|
|
193
|
+
Security: the token is the only gate, and plain HTTP carries it in clear text. That is fine on a home network you trust; it is why `serve` listens on 127.0.0.1 unless told otherwise. To reach it beyond your LAN (a VPS, say), put it behind a reverse proxy with HTTPS. Anyone with the token can control the player, including stopping it; change the token with `ttyplayer config set server_token <new>` and restart.
|
|
194
|
+
|
|
195
|
+
## Install
|
|
196
|
+
|
|
197
|
+
One command installs everything ttyplayer needs.
|
|
198
|
+
|
|
199
|
+
macOS and Linux:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
curl -LsSf https://raw.githubusercontent.com/webliftro/ttyplayer/main/install.sh | sh
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Windows (PowerShell):
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
irm https://raw.githubusercontent.com/webliftro/ttyplayer/main/install.ps1 | iex
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Or clone the repository and run `./install.sh` (macOS, Linux) or `powershell -ExecutionPolicy ByPass -File .\install.ps1` (Windows) inside it; that installs the checkout you cloned.
|
|
212
|
+
|
|
213
|
+
The script:
|
|
214
|
+
|
|
215
|
+
1. installs [uv](https://docs.astral.sh/uv/) with its official installer if `uv` is missing (uv brings its own Python 3.13+; it runs as you, without sudo),
|
|
216
|
+
2. installs mpv if it is missing: Homebrew on macOS, your package manager on Linux (`apt-get`, `dnf`, `pacman`, `zypper` or `apk`, with `sudo`), winget on Windows,
|
|
217
|
+
3. installs ttyplayer with `uv tool install`,
|
|
218
|
+
4. runs `ttyplayer doctor`: every line should start with ✓.
|
|
219
|
+
|
|
220
|
+
It prints every command before running it. `TTYPLAYER_INSTALL_DRY_RUN=1 ./install.sh` and `install.ps1 -DryRun` only print them. If Homebrew (macOS) or winget (Windows) is missing, the script tells you how to install it and stops. If `ttyplayer` is not found in a new terminal, run `uv tool update-shell`.
|
|
221
|
+
|
|
222
|
+
<details>
|
|
223
|
+
<summary>By hand</summary>
|
|
224
|
+
|
|
225
|
+
Three steps on every system: install uv, install mpv, install ttyplayer. Then run `ttyplayer doctor`.
|
|
226
|
+
|
|
227
|
+
macOS:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
231
|
+
brew install mpv
|
|
232
|
+
uv tool install ttyplayer
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Linux:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
239
|
+
sudo apt-get install -y mpv
|
|
240
|
+
uv tool install ttyplayer
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
On Fedora use `sudo dnf install -y mpv`, on Arch `sudo pacman -S --noconfirm mpv`, on openSUSE `sudo zypper install -y mpv`, on Alpine `sudo apk add mpv`.
|
|
244
|
+
|
|
245
|
+
Windows:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
249
|
+
winget install -e --id shinchiro.mpv
|
|
250
|
+
uv tool install ttyplayer
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Instead of winget, `scoop install mpv` (from the `extras` bucket) or `choco install mpv` work too.
|
|
254
|
+
|
|
255
|
+
`uv tool install ttyplayer` installs from PyPI once ttyplayer is published there. Until then install straight from GitHub:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
uv tool install git+https://github.com/webliftro/ttyplayer
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
</details>
|
|
262
|
+
|
|
263
|
+
Upgrade with `uv tool upgrade ttyplayer`. Uninstall with `uv tool uninstall ttyplayer`.
|
|
264
|
+
|
|
265
|
+
## Development
|
|
266
|
+
|
|
267
|
+
Install from a checkout, with mpv installed as above:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
uv tool install --editable .
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The `--editable` flag means edits to the source are live without reinstalling.
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
uv run pytest # unit tests, no network and no mpv needed
|
|
277
|
+
uv run ttyplayer ... # run from the checkout
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The live tests talk to the real YouTube through `ttyplayer.youtube`, so a yt-dlp or YouTube change shows up as a failing test. They are skipped unless `TTYPLAYER_LIVE=1` is set:
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
uv run pytest -q # default suite, live tests skipped
|
|
284
|
+
TTYPLAYER_LIVE=1 uv run pytest -q tests/test_live.py # live tests, needs network
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
History lives in `$XDG_DATA_HOME/ttyplayer/history.jsonl`, by default `~/.local/share/ttyplayer/history.jsonl` (`%LOCALAPPDATA%\ttyplayer\history.jsonl` on Windows). Favorites live next to it in `favorites.jsonl`, and each playlist in `playlists/<name>.jsonl`. History and favorites kept under the player's earlier name are moved here on the first run.
|
|
288
|
+
|
|
289
|
+
### Releasing
|
|
290
|
+
|
|
291
|
+
CI (`.github/workflows/tests.yml`) runs the suite on Linux and macOS on every push. Pushing a `v*` tag runs `.github/workflows/release.yml`, which builds with `uv build` and publishes to PyPI through trusted publishing, so there is no token to store.
|
|
292
|
+
|
|
293
|
+
One-time setup on PyPI: create an account, then under "Publishing" add a pending GitHub publisher for the project `ttyplayer` with owner `webliftro`, repository `ttyplayer` and workflow `release.yml`. The first tagged release creates the project.
|
|
294
|
+
|
|
295
|
+
To release, bump the version (`uv version --bump minor`, or edit `version` in `pyproject.toml`), commit, then:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
git tag v0.3.0 && git push --tags
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## How it works
|
|
302
|
+
|
|
303
|
+
yt-dlp resolves what to play: a search or a playlist becomes a list of videos, a link becomes one. mpv produces the sound, started in the background with a private IPC socket. ttyplayer talks to it in JSON over that socket, owns the keyboard, and redraws a one-line status. See `docs/architecture.md`.
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT. See `LICENSE`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ttyplayer"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.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"
|
|
@@ -27,6 +27,8 @@ classifiers = [
|
|
|
27
27
|
"Topic :: Multimedia :: Sound/Audio :: Players",
|
|
28
28
|
]
|
|
29
29
|
dependencies = [
|
|
30
|
+
"aiohttp>=3.10",
|
|
31
|
+
"qrcode>=8",
|
|
30
32
|
"textual>=8.2",
|
|
31
33
|
"typer>=0.27.2",
|
|
32
34
|
"yt-dlp>=2026.8.19",
|