9c4749e49b
Also stops one benign libmpv message being logged as an error: `after creating texture: OpenGL error INVALID_ENUM` comes from the GL driver, arrives several times per playback start and per fullscreen toggle, and playback continues regardless. Hundreds of lines a session buried the errors that matter -- it is a debug line now. Verified end to end on the real display with an actual video playing: ten tab switches and four fullscreen toggles while frames were rendering, position advancing throughout, the render context intact, no crash, and an empty error log afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
189 lines
12 KiB
Markdown
189 lines
12 KiB
Markdown
# Changelog
|
||
|
||
Kept as the work happens, not assembled at release time. Format is
|
||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
||
[SemVer](https://semver.org/spec/v2.0.0.html).
|
||
|
||
**The `vX.Y.Z` tags already on this repository are upstream YTSage's**, inherited
|
||
with its history. SageTube's own versions start below and are the ones this file
|
||
records.
|
||
|
||
## 5.5.0 — 2026-08-09
|
||
|
||
### Added
|
||
|
||
- **The YouTube account is findable.** Signing in with browser cookies was
|
||
reachable only via Downloads → *Custom Options* → *Login with Cookies* — the
|
||
last tab — while the features that need it (the Feed's account mode, Search,
|
||
Browse) are other tabs with no route to it, and the method that opens that
|
||
dialog on the right tab had no caller at all. There is now an account button
|
||
in the corner of the tab bar, visible from everywhere, showing whether
|
||
cookies are in use and which browser they came from. The Feed offers the same
|
||
thing beside its account mode instead of just greying it out, and now says
|
||
why it is unavailable.
|
||
- **Cookie changes take effect immediately.** The player read them once, when
|
||
it was built, so signing in only reached playback after restarting the app.
|
||
It now reloads at the current position.
|
||
- **The player responds to the keyboard and the mouse.** It had a key handler
|
||
for Space, F and Escape that could never fire — nothing in the player set a
|
||
focus policy, so focus always landed on a child button or slider, which
|
||
swallowed the keys. Now: Space/K play-pause, ←/→ ∓5s, J/L ∓10s, Shift+←/→
|
||
∓1s, `,`/`.` frame step, ↑/↓ volume, M mute, `[`/`]` speed with Backspace to
|
||
reset, C subtitles, F fullscreen, Escape to leave, 0–9 to jump by tenths,
|
||
Home/End, N/P for next and previous. Click pauses, double-click goes
|
||
fullscreen, the wheel changes volume and Ctrl+wheel seeks, and right-click
|
||
opens a menu built from the same table as the shortcuts. The bindings are
|
||
scoped to the player, so typing in the Search box or the URL field is
|
||
unaffected.
|
||
- **Previous, next and mute buttons**, and a buffering indicator — a stalled
|
||
stream previously showed a frozen frame for 25 seconds with nothing on
|
||
screen before the automatic retry.
|
||
- In fullscreen the controls and pointer fade out after a few idle seconds and
|
||
return on any movement. In a window they never hide, so the layout does not
|
||
jump.
|
||
- **Real icons.** The app had no icon system: transport buttons used the
|
||
platform style's dark monochrome glyphs painted on saturated red, and
|
||
everything else called an icon was an emoji baked into the English strings.
|
||
A drawn SVG set is now rendered through QtSvg and recoloured at load — Qt
|
||
stylesheets cannot recolour a `QIcon`, which is exactly why the old ones were
|
||
unreadable. Buttons across the Downloads tab gained icons too.
|
||
- **Tooltips and accessible names on every button.** Three widgets in the whole
|
||
application had a tooltip. An application-level polish filter now fills them
|
||
in as each button is first shown, and swaps placeholder emoji for icons —
|
||
which covers dialogs owned by upstream without editing them.
|
||
- **Skip this version** in the update dialog. The skipped version is never
|
||
offered again; later ones still are.
|
||
|
||
### Changed
|
||
|
||
- **The tab order is Feed → Search → Browse → Downloads → Watch**, each with an
|
||
icon, and the app opens on the Feed. Watch was first, which is where the
|
||
other tabs send you rather than somewhere you start.
|
||
- **Opening the Feed refreshes it**, but only channels not seen for 30 minutes
|
||
(`feed.auto_refresh_on_open_minutes`, 0 to switch off), at most eight per
|
||
visit, once per interval per session, and after a short delay. A refresh is
|
||
one yt-dlp subprocess per subscribed channel, so an unthrottled one would be
|
||
a lot of them. The cached feed still appears instantly; manual Refresh always
|
||
does everything.
|
||
- mpv is given an explicit `hwdec` list per platform (`player.hwdec`, default
|
||
`auto`), each ending in software decoding, so a machine with broken GPU
|
||
interop plays rather than showing a black frame. This does **not** silence
|
||
`Cannot load libcuda.so.1` — that comes from the driver stack below mpv and
|
||
appears with `hwdec=no` too.
|
||
- **The update check now looks at SageTube's own releases.** It queried PyPI's
|
||
`ytsage` package for the version and `oop7/YTSage` for the changelog, then
|
||
linked to upstream's downloads — a different program's release stream. It now
|
||
reads `git.houmeres.sk/Houmeres/SageTube` releases, anonymously, and the
|
||
"Download update" button opens this repository's release page.
|
||
- The beta channel follows SageTube pre-releases. Upstream's inherited `b` tags
|
||
(`v5.3.0b` and earlier) read as pre-releases, so a stable instance cannot be
|
||
offered one.
|
||
- The check now runs at most once a day rather than on every start.
|
||
- The About dialog and the update settings say SageTube rather than YTSage. The
|
||
"Based on YTSage by oop7" attribution link stays — it is the MIT credit.
|
||
- Configs now carry `config_version`. A file written before 5.4.0 — including one
|
||
inherited from an upstream YTSage install — has its stored `check_app_updates`
|
||
cleared once, so SageTube's own default applies rather than a setting that
|
||
pointed at another project's releases.
|
||
|
||
### Fixed
|
||
|
||
- The error log filled with hundreds of `after creating texture: OpenGL error
|
||
INVALID_ENUM` lines per session. They come from the GL driver, playback
|
||
continues regardless, and they buried the errors that do matter, so they are
|
||
logged at debug level now.
|
||
- The player's cookie and proxy options were joined with commas and never
|
||
escaped, so a cookie path or a proxy URL containing one silently truncated
|
||
the option and produced a bogus one. The geo-bypass proxy was honoured by the
|
||
downloader but never passed to the player at all.
|
||
- **The rest of the interface is themed.** `StyleSheet.MAIN` styled the window,
|
||
inputs, buttons and tables and nothing else, so the main tab bar, combo
|
||
boxes, sliders, lists, menus, splitters, tooltips and the horizontal
|
||
scrollbar were drawn by the platform — the tab bar with white text forced
|
||
onto system-coloured tabs, tooltips in the system's light style on a black
|
||
app. All of them now match. `QFrame#tabContent` finally has the rule the tab
|
||
widget's own comment promised.
|
||
- Checkboxes were round, which reads as a radio button — "one of these" rather
|
||
than "on or off". They are square with a tick.
|
||
- Clicking any button shifted its label two pixels down and right, and clipped
|
||
the artwork on fixed-width icon buttons: the pressed style changed the
|
||
padding. The background change alone reads as pressed.
|
||
- The window icon shipped only at 48px and was upscaled everywhere; the 256px
|
||
master now ships beside it. Its fallback was the platform's download arrow
|
||
standing in for the application's identity.
|
||
- **The feed grid no longer flickers or loses your scroll position.** It was
|
||
cleared and rebuilt from scratch after every channel finished, re-reading
|
||
every thumbnail from disk each time; finished channels are now merged in and
|
||
existing cards left alone. The docstring's claim that it "fills
|
||
incrementally" is finally true.
|
||
- Card layout items accumulated: each `Load more` re-added cards the layout
|
||
already held, and the resize check compared against `columnCount()`, which
|
||
never shrinks — so it relaid out on every single resize event.
|
||
- Feed failures were invisible. The error was written into the same label the
|
||
next successful channel immediately overwrote; the count is now reported once
|
||
when the refresh ends.
|
||
- Switching the feed to account mode left the local videos on screen, so one
|
||
mode's contents appeared to be the other's.
|
||
- Leaving the Feed tab or closing the window cancels a running refresh.
|
||
`cancel()` existed and had no callers, so a sweep kept running and the window
|
||
could be closed while its thread was live.
|
||
- Browse's Subscribe button never said "Unsubscribe", although pressing it
|
||
toggles; its only feedback was a status line on a different tab.
|
||
- "Play all" quietly queued just the loaded page and claimed to be everything.
|
||
- The feed sorted by publish time and fetch time in the same expression, so the
|
||
most recently refreshed channel floated to the top regardless of how old its
|
||
videos were.
|
||
- **The player no longer crashes the app.** Qt destroys and recreates a
|
||
widget's OpenGL context whenever it moves to another top-level window, and
|
||
calls `initializeGL()` again. libmpv allows one render context per handle, so
|
||
the second creation failed — and the failure path dropped the last reference
|
||
to the *first* context while libmpv still held a function pointer into it.
|
||
Python then collected the callback trampoline under libmpv's feet and the
|
||
next frame notification jumped into freed memory. `initializeGL()` now tears
|
||
down first so recreation is clean, teardown clears the callback and frees
|
||
with the GL context current, and it is wired to
|
||
`QOpenGLContext.aboutToBeDestroyed` so it runs before the context goes away
|
||
rather than never. Verified over ten forced context destroy/recreate cycles.
|
||
- **Fullscreen no longer reparents the video.** It took the player out of its
|
||
layout and into a new top-level window, destroying the OpenGL context twice
|
||
per toggle — the most direct route to the crash above — and on the way back
|
||
re-added the panel at the end of the splitter, so the video reappeared beside
|
||
the queue with the pane sizes lost. The main window now goes fullscreen and
|
||
the chrome around the video is hidden instead. Nothing is reparented.
|
||
- **Switching tabs no longer risks the player.** The cross-fade grabbed a
|
||
screenshot of the outgoing page; on an OpenGL surface that forces a
|
||
framebuffer readback and returns black, so the "fade" was a black slab and
|
||
the readback endangered the context. Pages containing the video now switch
|
||
without the fade.
|
||
- Dialogs dimmed instead of blurred while the player exists, for the same
|
||
reason — every dialog in the app screenshotted the whole window.
|
||
- The saved volume never reached mpv: the slider set its restored value before
|
||
its change signal was connected, so playback always started at 100.
|
||
- **The app no longer offers upstream YTSage's releases as its own updates.**
|
||
`__version__` and `pyproject.toml` had been left at the scaffolded `0.1.0`
|
||
while the released tag was `v5.4.0`, so every comparison against upstream's
|
||
published version reported an update. Both now read `5.4.0`, the single
|
||
source of truth for the window title, the About dialog and the update check.
|
||
- `ConfigManager` merged a stored config over the defaults **shallowly**, so a
|
||
file written by an older build replaced whole nested objects: a stored
|
||
`"player"` or `"feed"` containing only some keys silently discarded every
|
||
default added since. The merge is now recursive. Keys present only in the
|
||
stored file are preserved, so downgrading cannot destroy settings.
|
||
- Opening *Custom Options* and clicking OK re-enabled the update checker, because
|
||
the settings tab read a missing `check_app_updates` as enabled and then
|
||
persisted that reading unconditionally.
|
||
|
||
## 5.4.0 — 2026-08-08
|
||
|
||
### Changed
|
||
|
||
- The documentation left the repository: `docs/BUILDING.md`, `docs/UPSTREAM.md`
|
||
and `docs/UPSTREAM_README.md` are now the
|
||
[wiki](https://git.houmeres.sk/Houmeres/SageTube/wiki).
|
||
- **The history was deliberately not rewritten.** Every other repository in this
|
||
org had its documentation removed from every commit on 2026-08-08. SageTube is
|
||
a fork that still merges from `oop7/YTSage`, and a rewrite changes every commit
|
||
id — which would end that. The files leave in an ordinary commit instead.
|
||
- `.github/` and `readme-translations/` are upstream's and were left alone, so
|
||
the next merge does not conflict over them.
|