Document the 5.5 interface: tabs, cookies, keys

A new "Using it" page covering what a user needs and could not previously
find written down anywhere: the tab order (Feed first, Watch last, and why),
the Feed's two modes and the brakes on its refresh-on-open, where the account
button is and what signing in with cookies buys, and the full keyboard and
mouse table for the player -- including that F11 and Escape work from anywhere
in the window, which matters because fullscreen hides the tab bar.

Building gains the parts that only bite when working on the code: the render
API embedding and its two hard rules (the render context must not outlive its
GL context; nothing may reparent, screenshot or apply a graphics effect to the
video widget), that pip install is a copy rather than a link, and how to test
playback without depending on YouTube's throttling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 01:33:07 +02:00
parent 1e1708ac63
commit 05c41cb5c1
3 changed files with 154 additions and 4 deletions
+49 -4
@@ -14,7 +14,10 @@
- ffmpeg (for download merging; the app can auto-install it)
Without libmpv the app still runs — the Watch tab shows an install hint and
search/browse/download work normally.
feed/search/browse/download work normally.
QtSvg is used for the interface icons. It ships with PySide6-Essentials, so
there is nothing extra to install.
## Run from source
@@ -31,13 +34,55 @@ the Deno runtime (yt-dlp's JS challenge solver) into the SageTube data dir:
- macOS: `~/Library/Application Support/SageTube/bin/`
- Windows: `%LOCALAPPDATA%\SageTube\bin\`
The settings referenced in [Using it](Using-it) live in `sagetube_config.json`
beside those binaries, in `data/` rather than `bin/`. The subscriptions, feed
cache, watch history and play queue are in `sagetube_library.db` in the same
place.
**Installing from source is a copy, not a link.** `pip install .` snapshots the
package into site-packages, so editing the checkout changes nothing until you
reinstall. Use `pip install -e .` while developing, or reinstall each time.
## Notes on YouTube playback reliability
Playback resolves streams through mpv's `ytdl_hook` using the managed yt-dlp.
YouTube's CDN intermittently serves stalled streams to non-browser clients;
the player retries automatically (twice, after 25 s of no playback). Keeping
yt-dlp updated (built-in updater, stable or nightly channel) is the long-term
fix channel for extractor breakage.
the player retries automatically (twice, after 25 s of no playback) and shows a
buffering indicator meanwhile. Keeping yt-dlp updated (built-in updater, stable
or nightly channel) is the long-term fix channel for extractor breakage.
## Notes on the embedded player
The player renders libmpv into a `QOpenGLWidget` through mpv's **render API**.
The `wid=winId()` embedding path is not used: it does not work on native
Wayland.
Two consequences worth knowing before changing anything in
`gui/ytsage_gui_player.py`:
- **The mpv handle outlives the OpenGL context, and the render context must
not.** Qt destroys and recreates a widget's `QOpenGLContext` whenever the
widget moves to another top-level window, and libmpv allows one render
context per handle. `initializeGL()` therefore tears down before it creates,
teardown clears the update callback and frees with the GL context current,
and it is wired to `QOpenGLContext.aboutToBeDestroyed`. Getting this wrong
segfaults rather than failing visibly.
- **Nothing may reparent the video widget, screenshot it, or put a
`QGraphicsEffect` on an ancestor of it.** Fullscreen fullscreens the *main
window* for this reason; the tab cross-fade and the dialog blur both skip
their `grab()` when an OpenGL surface is present.
Testing playback against YouTube is unreliable — it throttles non-browser
clients unpredictably. Generate a clip instead:
```bash
ffmpeg -f lavfi -i testsrc=size=640x360:rate=25 -f lavfi -i sine=frequency=440 \
-t 30 -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest testclip.mp4
```
`QT_QPA_PLATFORM=offscreen` works for GUI tests generally, but offscreen has no
real GL, so the render context never succeeds there — use a real display for
anything touching the player.
## Release workflows
+1
@@ -10,6 +10,7 @@ what it is and how to install it is in the
| Page | What it is |
|---|---|
| [Using it](Using-it) | The tabs, the Feed's two modes, signing in with cookies, and the player's keys and mouse. |
| [Building](Building) | Requirements, building and running from source — including the mpv piece, without which the app is a downloader rather than a player. |
| [Tracking-upstream](Tracking-upstream) | How the fork relates to YTSage, and how a merge from upstream is done. |
| [Upstream-README](Upstream-README) | YTSage's own README, kept verbatim for reference. |
+104
@@ -0,0 +1,104 @@
# Using SageTube
## The tabs
Five, in the order you move through them:
| Tab | What it is |
|---|---|
| **Feed** | Your subscriptions and their recent uploads. Opens here. |
| **Search** | YouTube search, as a grid of cards. |
| **Browse** | A channel or playlist by URL — Videos / Shorts / Live for a channel. |
| **Downloads** | The full yt-dlp downloader inherited from YTSage. |
| **Watch** | The player and the play queue. |
Watch is **last** on purpose: it is where the other tabs send you, not
somewhere you start. Every card has *Play*, *Queue* and a *Download* button
that opens the Downloads tab with the video ready to analyse.
## The Feed
Two modes, chosen with the selector at the top left:
- **Local subscriptions** — subscribe to channels from Browse, and SageTube
aggregates their recent uploads itself. No account, no cookies.
- **YouTube account** — your real signed-in subscription feed. Needs cookies,
and is disabled until you have them.
**Opening the tab refreshes it**, but conservatively: a refresh is one yt-dlp
run *per subscribed channel*, so only channels not seen for
`feed.auto_refresh_on_open_minutes` (default 30) are fetched, at most eight per
visit, and at most once per interval per session. The cached feed is on screen
immediately either way. The **Refresh** button always does the lot.
Set `feed.auto_refresh_on_open_minutes` to `0` to switch automatic refreshing
off entirely.
## Signing in with cookies
The **account button in the top-right corner of the tab bar** shows whether
SageTube is using an account, and opens the cookie settings. The Feed offers
the same thing beside its mode selector when you have not signed in.
Cookies are read from a browser profile (`--cookies-from-browser`) or from a
`cookies.txt` file. They enable the account feed, age-restricted and
members-only videos, and premium formats. **Changes take effect immediately**
anything playing reloads at its current position.
## Player keys and mouse
The player takes focus when you click the video. Bindings work whenever the
player or one of its controls has focus, so they never interfere with typing in
the Search box or the Downloads URL field.
| Key | Action | Key | Action |
|---|---|---|---|
| `Space`, `K` | Play / pause | `↑` / `↓` | Volume ±5 |
| `←` / `→` | Seek ∓5 s | `M` | Mute |
| `J` / `L` | Seek ∓10 s | `C` | Subtitles on / off |
| `Shift`+`←` / `→` | Seek ∓1 s | `[` / `]` | Speed ∓0.25× |
| `,` / `.` | Frame back / forward | `Backspace` | Normal speed |
| `0``9` | Jump to 090 % | `N` / `P` | Next / previous in queue |
| `Home` / `End` | Start / end | `Ctrl`+`→` / `←` | Next / previous |
| `F` | Fullscreen | `S` | Screenshot |
**`F11` toggles fullscreen and `Escape` leaves it from anywhere in the
window**, not just the player — useful because fullscreen hides the tab bar.
| Mouse | Action |
|---|---|
| Click | Play / pause |
| Double-click | Fullscreen |
| Wheel | Volume |
| `Ctrl` + wheel | Seek ±5 s |
| Right-click | Menu — speed, quality, subtitles, copy link, open in browser |
In fullscreen the controls and the pointer hide after a few idle seconds and
come back on any movement. In a window they never hide, so nothing shifts under
the cursor.
## Settings worth knowing
Everything below lives in the config file — see
[Building](Building) for where that is on each platform.
| Key | Default | What it does |
|---|---|---|
| `player.hwdec` | `auto` | `auto` picks a per-platform decoder list ending in software. Set `no` to force software decoding when diagnosing playback. |
| `player.default_quality` | `1080` | Maximum height the player requests. Also the quality selector in the control bar. |
| `player.resume` | `auto` | Resume where you left off. `off` always starts at the beginning. |
| `feed.per_channel_items` | `15` | Videos fetched per channel on a refresh. |
| `feed.auto_refresh_on_open_minutes` | `30` | Staleness before opening the Feed refreshes a channel. `0` disables. |
| `check_app_updates` | `false` | Check this repository's releases on startup. |
| `check_beta_updates` | `false` | Include pre-releases in that check. |
**Updates are checked against this repository**, not upstream YTSage — see
[Tracking-upstream](Tracking-upstream) for why that distinction matters. The
yt-dlp, Deno and ffmpeg updaters are separate and track their own projects.
## Language
The interface is translated into fourteen languages, but the strings SageTube
added — the tab names, feed, cards, player and account text — exist only in
English so far. A non-English interface shows English for those and its own
language for everything inherited from YTSage.