Make the YouTube account reachable, and applied without a restart

The cookie login was not missing, it was buried: Downloads tab -> Custom
Options -> the "Login with Cookies" tab, where Downloads is one tab of five.
The three features that depend on it -- the Feed's account mode, Search and
Browse -- are other tabs, with no route to it. show_cookie_login_dialog(),
which opens that dialog on the right tab, had no caller anywhere in the
codebase; it does now.

An account button sits in the corner of the tab bar, visible from every tab,
saying whether cookies are in use and where they came from. The Feed offers
the same thing next to its account mode rather than greying the option out and
leaving it at that, and the disabled entry now says why it is disabled.

Applying cookies used to require a restart before playback saw them:
_build_ytdl_raw_options was read once, when the player widget was built. A
cookiesChanged signal now reaches the account button, the Feed and the live
mpv handle, and the player reloads at its current position -- ytdl_hook
consults the option when it resolves a URL, so anything already playing keeps
the streams it resolved with. YtdlpClient needed no change: it re-reads config
per call.

Two bugs in the same function: the options are joined with commas and were
never escaped, so a cookie path or a proxy URL containing one silently ended
the option and started a bogus one; and geo_proxy_url was honoured by the
downloader but never passed to the player, so a geo-restricted video would
download and then refuse to play.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-09 01:15:21 +02:00
parent 79a6f26c2f
commit fd744a1a85
7 changed files with 204 additions and 5 deletions
+24
View File
@@ -567,6 +567,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
from .ytsage_gui_router import AppRouter
from .ytsage_gui_search import SearchPage
from .ytsage_gui_watch import WatchPage
from .ytsage_gui_account import AccountStatusButton
from .ytsage_player_fullscreen import FullscreenController
self.router = AppRouter(self)
@@ -607,6 +608,19 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
# A new subscription changes what the Feed's empty state should say.
self.feed_page.subscriptionsChanged.connect(self.browse_page.refresh_subscribe_state)
# The cookie login had exactly one entry point -- Downloads tab ->
# Custom Options -> Login with Cookies -- while the features needing
# it live on other tabs. A corner button makes it reachable from all
# of them, and the Feed offers it where account mode is disabled.
self.account_button = AccountStatusButton(self.main_tabs)
self.account_button.loginRequested.connect(self.show_cookie_login_dialog)
self.main_tabs.setCornerWidget(self.account_button)
self.router.cookieSetupRequested.connect(self.show_cookie_login_dialog)
self.router.cookiesChanged.connect(self.account_button.refresh)
self.router.cookiesChanged.connect(self.feed_page.on_cookies_changed)
if isinstance(self.watch_page.player, PlayerPanel):
self.router.cookiesChanged.connect(self.watch_page.player.reload_auth)
def _on_main_tab_changed(self, index: int) -> None:
"""
Tell a page it has become visible.
@@ -1333,6 +1347,11 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
_("proxy.cleared_message"),
)
# This dialog's Cookies tab persists on OK too, and proxy settings
# reach the player by the same route, so announce both from here.
if hasattr(self, "router"):
self.router.cookiesChanged.emit()
def show_about_dialog(self) -> None: # ADDED METHOD HERE
dialog = AboutDialog(self)
self.run_dialog_with_blur(dialog)
@@ -1711,6 +1730,11 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
self.cookie_file_path = None # Clear path if dialog accepted but no file selected
self.browser_cookies_option = None # Clear browser cookies too
# The dialog has already written the config; tell the rest of the
# app so signing in takes effect now rather than after a restart.
if hasattr(self, "router"):
self.router.cookiesChanged.emit()
def cancel_download(self) -> None:
if self.current_download:
self.current_download.cancelled = True