diff --git a/Building.md b/Building.md index 13421af..3654a77 100644 --- a/Building.md +++ b/Building.md @@ -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 diff --git a/Home.md b/Home.md index bbd273f..de3dfb1 100644 --- a/Home.md +++ b/Home.md @@ -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. | diff --git a/Using-it.md b/Using-it.md new file mode 100644 index 0000000..fdbd7b4 --- /dev/null +++ b/Using-it.md @@ -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 0–90 % | `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.