Commit Graph

37 Commits

Author SHA1 Message Date
Homer 996d581a07 Show the account feed after it loads
Refresh fetched the account feed correctly -- the status line said "60 videos
in feed" -- and the page stayed empty with "No subscriptions yet" on it.

The empty-state hint added in 5.5.0 hides the grid so it can sit centred
rather than pinned under a large blank area. Every path that changes the grid
re-runs that check afterwards and shows it again; the account path was the one
that did not, so the grid stayed hidden from startup no matter what was loaded
into it. The local feed re-runs it and was unaffected, which is why this only
showed up for someone signed in.

Two related things while here. The empty-state message was written for local
subscriptions and is wrong advice in account mode, which does not use them --
it now says to press Refresh rather than to go and subscribe to something. And
the Subscriptions panel, which lists local subscriptions, is hidden in account
mode instead of sitting empty beside a full feed and implying something failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 01:38:07 +02:00
Homer fd744a1a85 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>
2026-08-09 01:15:21 +02:00
Homer 79a6f26c2f Open on the Feed, and refresh it without hammering yt-dlp
The tab order was Watch, Search, Feed, Browse, Downloads, and the app opened
on Watch -- which is where the other tabs send you, not somewhere you start.
It is now Feed, Search, Browse, Downloads, Watch, with icons. Routing is
unaffected: _tab_index_of resolves by widget identity, not position.

Refresh-on-open needed care rather than a call in showEvent. A local refresh
is one yt-dlp subprocess per subscribed channel, serially, so it is braked
four ways: only channels not seen for feed.auto_refresh_on_open_minutes (30,
0 to disable), at most eight per visit, once per interval per session, and
after a delay so it does not race the tab transition or first-run setup. The
cached feed is already on screen throughout. That is a new config key rather
than the dead auto_refresh_minutes, because existing configs store that as 0
and would have read as opting out of a feature that did not exist yet.

SmoothTabWidget gained the currentChanged signal, icons and a corner slot it
never had. Its set_current_index needed a re-entrancy flag, not just an index
check: setting the tab bar's index emits its currentChanged straight back into
the same method, and at that point the stack has not moved, so every switch
fired the activation hook twice.

The grid was cleared and rebuilt after every channel finished -- flicker, lost
scroll position and every thumbnail re-read from disk each time. merge_entries
keeps existing cards. While there, _relayout was re-adding cards the layout
already owned, so layout items accumulated on every Load more, and the resize
check compared against columnCount(), which never shrinks, so it relaid out on
every resize event.

Smaller things this exposed: feed errors were written into the label the next
success overwrote, so failures were invisible; switching to account mode left
the local videos on screen; cancel() had no callers, so a refresh outlived the
tab and the window; Browse's Subscribe never said Unsubscribe though it
toggles; "Play all" queued one page while claiming otherwise; and the feed
sorted publish times against wall-clock fetch times in one COALESCE, so the
last-refreshed channel floated to the top.

Feed is the first thing seen now, so an empty one says what to do about it
instead of showing a bare grid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 01:11:40 +02:00
Homer 27933dd495 Make the player answer the keyboard and the mouse
PlayerPanel.keyPressEvent handled Space, F and Escape and was dead code:
nothing in the player called setFocusPolicy, so focus went to whichever child
button, slider or combo box was first in the tab order, and that child ate the
keys. Fixing focus alone would not have been enough -- clicking play parks
focus on the play button, which then swallows Space -- so the control bar is
explicitly NoFocus and the bindings are QShortcuts with
WidgetWithChildrenShortcut context on the panel. That fires whichever
descendant holds focus, and does not reach the Search box or the URL field on
other tabs the way an application shortcut would.

mpv's own input stays disabled. With vo=libmpv there is no mpv-owned window,
so its input layer receives nothing; enabling default bindings would mean
hand-forwarding events through a Qt-to-mpv key-name table, handing mpv the
OSD and OSC that are switched off here, and letting `q` quit the core out from
under the Qt UI. One declarative action table now drives the shortcuts, the
context menu and the buttons.

There was no mouse handling at all. Click pauses, double-click goes
fullscreen -- via a doubleClickInterval timer, so a double-click does not also
pause on the way -- the wheel changes volume and Ctrl+wheel seeks, with
sub-notch deltas accumulated so a trackpad is not inert.

The control bar gained previous, next and mute. Mute and volume are driven by
observed mpv properties rather than assumed, so the icon follows a change made
by key, menu or mpv itself. Buffering was completely invisible: a stalled
stream showed a frozen frame for 25 seconds before the retry with nothing on
screen, so paused-for-cache now surfaces a label over the video.

WatchPage owns next/previous because it owns the queue; the panel only asks.
Previous restarts the current item when it is more than five seconds in, as
every other player does, and pushes the interrupted item back onto the head of
the queue rather than dropping it.

Verified: all 38 bindings install, every action is a no-op rather than an
exception with nothing loaded, focus policies are as intended, and a
fullscreen round trip drives the player state correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 01:04:16 +02:00
Homer 3aca43b372 Give the app icons, tooltips and a finished theme
There was no icon system at all. Transport buttons called
QStyle.standardIcon(SP_MediaPlay), which returns the platform theme's dark
monochrome glyph -- painted onto the app's saturated red buttons at a fixed
36px with no text, that is the black square. Everything else called an icon
was an emoji baked into en.json, which is tofu wherever the emoji font is
missing. Qt stylesheets cannot recolour a QIcon, so the colour is an argument
to the new helper; that is the whole fix.

The SVGs are drawn here rather than vendored, which keeps a third-party
licence out of the tree, and they live in the module rather than as asset
files, which keeps them out of package-data and safe in a frozen build.

Tooltips were the other half: three widgets in the entire application had one
and nothing set an accessible name. Filling that in at the call sites would
have meant editing well over a hundred of them, most in upstream-owned files.
Instead one application-level event filter handles QEvent.Polish, which Qt
sends to every widget once before it is shown -- so it also reaches dialogs
built by upstream code, and survives the next merge. It maps placeholder
emoji to icons, fills empty tooltips from the button text, and logs icon-only
buttons that still have none so the gaps are findable.

StyleSheet.MAIN styles the window, inputs, buttons and tables and nothing
else, so the main tab bar, combos, sliders, lists, menus, splitters, tooltips
and the horizontal scrollbar fell through to the platform style. EXTRA_QSS
covers them, in a fork-owned module appended at the one application site.
SmoothTabWidget names its frame "tabContent" with the comment "We draw border
on content instead" -- that rule existed only inside two dialogs, and now
exists for the main window too.

Two corrections to rules that were already there: the pressed style changed
the padding, shifting every label two pixels and clipping fixed-width icon
buttons, and checkboxes were fully rounded, which reads as a radio button
rather than an on/off toggle.

Verified by screenshot on the real display: tab bar, buttons, combo carets
and checkboxes all render as intended, and all 35 icons rasterise non-empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 00:58:38 +02:00
Homer 2420091a4e Check for SageTube updates, not YTSage's
The update check asked PyPI for the `ytsage` package's version, asked GitHub
for oop7/YTSage's release notes, and pointed the download button at upstream's
releases. None of that describes this program. Being reminded to install
YTSage was the visible symptom; the cause was that the check had never been
repointed when the fork was made.

It now reads SageTube's own releases from git.houmeres.sk. Gitea's release API
is shaped like GitHub's, so the dialog and the caller are unchanged -- the
class keeps its name and signal signature, and only its body moved out to
core/ytsage_app_update.py, which is fork-owned and will not conflict on the
next merge from upstream.

Upstream's inherited tags end in `b` (v5.3.0b and earlier). packaging reads
that as a beta marker, so they sort below v5.4.0 and a stable instance cannot
be handed one. Tag parsing is defensive anyway: one unparseable tag must not
take the whole check down with it.

Also: the check is rate-limited to once a day rather than every start, the
dialog gained a "Skip this version" that survives a restart, the thread is now
joined on close, and the About dialog says SageTube. The three binary updaters
-- yt-dlp, Deno, ffmpeg -- legitimately track their own upstreams and are
deliberately untouched; ytsage_app_update's docstring says so, because "update"
is an overloaded word in this codebase.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 00:36:14 +02:00
Jaroslav Beneš 8a05d5ff8f Rebrand user-visible surfaces to SageTube
The internal ytsage package name is kept deliberately - renaming it
would touch every file and destroy the ability to merge upstream
YTSage changes.

- pyproject: distribution name sagetube v0.1.0, sagetube entrypoint
  (ytsage alias retained), URLs point at the Gitea repo with an
  Upstream link to YTSage.
- Data dirs move to SageTube/ (fresh fork, fresh state) and the config
  file becomes sagetube_config.json; QApplication name and window
  title read SageTube.
- About dialog credits Houmeres and links "Based on YTSage by oop7".
- App self-update check against the upstream PyPI package is disabled
  by default; yt-dlp/deno/ffmpeg update flows are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:45:58 +02:00
Jaroslav Beneš a8ebe89cc1 Wire watch history, resume positions and persistent queue
WatchPage now records every played video in watch_history, saves the
playback position every 5 seconds and on stop/end (completed at >=95%),
and seeks back on replay when player.resume is "auto" - resume kicks in
between 30 seconds and 95% of the duration. The play queue persists
across restarts and reorders/removals write through immediately; the
main window's closeEvent flushes position and queue and releases libmpv.

PlayerPanel gains an automatic stall-retry: YouTube's CDN intermittently
serves stalled streams to non-browser clients (reproduced ~1/3 of
attempts headless with identical code), and a reload re-resolves onto a
healthy node - two retries after 25s of no playback, then a user-facing
error.

ytsage_constants now prepends the managed-binaries dir to PATH so
yt-dlp subprocesses (including mpv's ytdl_hook) can find the managed
Deno runtime - previously nothing exported APP_BIN_DIR, so the deno
integration silently never worked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:42:38 +02:00
Jaroslav Beneš 3afe96a8cd Add local subscriptions and the Feed tab
New ytsage/utils/ytsage_library_manager.py: sagetube_library.db
(separate from the upstream download-history DB) with WAL from day one,
holding subscriptions, cached feed_items, watch_history with resume
positions, and the persisted play_queue. Watch history and queue tables
are wired up by the next commit.

FeedPage:
- Local mode: FeedRefreshWorker refreshes each subscribed channel
  sequentially (feed.per_channel_items, default 15) and the grid fills
  incrementally per channel; results are cached so the feed is
  populated instantly on startup.
- Account mode: fetches youtube.com/feed/subscriptions with the user's
  browser cookies - the real logged-in feed; the option is enabled only
  while cookies are active and errors surface as a status banner.
- Sidebar lists subscriptions (double-click opens the channel in
  Browse; context menu unsubscribes). Browse's Subscribe button now
  toggles subscription state through the Feed page.

Verified live: subscribe -> refresh -> 15 videos cached in SQLite and
rendered as cards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:56:12 +02:00
Jaroslav Beneš 8e935c6116 Implement channel and playlist browsing
BrowsePage accepts pasted or routed channel/playlist URLs:
- Channels get Videos / Shorts / Live sub-tabs mapped to the channel's
  /videos, /shorts and /streams listings, lazily fetched 24 at a time
  with -I range pagination; channel title and id resolve from a cheap
  -I 1:1 metadata fetch. Subscribe emits subscribeRequested for the
  Feed page to wire up.
- Playlists get a single grid with Play all (bulk-enqueues into the
  Watch queue) and a Download button that deep-links the playlist into
  the Downloads tab.

Workers are parented to their pages and fetch errors surface as status
text instead of crashing (verified against a channel with no videos
tab). fetch_flat_info gains an items="1:1" limiter so metadata probes
no longer enumerate whole channels.

Verified live: kurzgesagt channel (24 cards, title+id resolved) and a
17-video playlist with Play-all queueing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:53:44 +02:00
Jaroslav Beneš 16be4b4afa Restructure main window into a watch-first tab shell
The single-page downloader layout becomes the Downloads tab of a
SmoothTabWidget with Watch / Search / Feed / Browse / Downloads pages.
The init_ui edit is deliberately small (the old central widget is now
self.download_page); all new behavior lives in new modules:

- ytsage_gui_router.py: AppRouter signal hub (playVideo, queueVideo,
  downloadVideo, openChannel, openPlaylist). A card's Download button
  deep-links into the Downloads tab with the URL prefilled and analysis
  started automatically.
- ytsage_gui_cards.py: VideoCard (thumbnail with disk cache under
  APP_THUMBNAILS_DIR, title/channel/duration, Play/Queue/Download
  actions, double-click to play) and VideoCardGrid (responsive grid,
  Load more pagination).
- ytsage_gui_watch.py: WatchPage hosting the mpv PlayerPanel and a
  drag-reorderable play queue with auto-advance on end of file.
- ytsage_gui_search.py: SearchPage running YtdlpClient.search off the
  GUI thread with Load more pagination.
- Browse and Feed pages are placeholders, implemented next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:48:39 +02:00
Jaroslav Beneš f886125857 Add embedded mpv player (render API + QOpenGLWidget)
New ytsage/gui/ytsage_gui_player.py:
- MpvRenderWidget hosts libmpv through MpvRenderContext in a
  QOpenGLWidget - works on native Wayland where wid-embedding cannot -
  with all mpv-thread callbacks marshalled to the GUI thread via queued
  signals only.
- PlayerPanel adds transport controls: play/pause, seek slider, time
  display, quality selector (caps ytdl-format height and reloads in
  place), speed, volume (persisted), subtitle toggle, fullscreen
  (reparent to top-level window), and Space/F/Escape keys.
- Playback resolves watch URLs through mpv's ytdl_hook pointed at the
  app-managed SHA256-verified yt-dlp binary (script-opts
  ytdl_hook-ytdl_path), inheriting cookies and proxy settings via
  ytdl-raw-options - stream freshness, DASH muxing and nsig handling
  stay in yt-dlp's hands.

New ytsage/core/ytsage_mpv.py probes libmpv availability; without it
the Watch UI shows a per-OS install hint and everything else works.
python-mpv added to dependencies (libmpv itself is a system package).
Config gains player.* and feed.* defaults.

Verified on Wayland: real YouTube video streams with position/duration
signals flowing and no thread-safety errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:44:25 +02:00
oop7 e78b634bee Add remember_settings and private_video keys
Add two new localization keys across multiple language files: "remember_settings" (cookie dialog) and "private_video" (download/error message) to inform users about remembering cookie settings and private videos. Also fix minor formatting in en.json (split combined line for open_folder/save_playlist/reset). Updated locale files: ar, de, en, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh.
2026-04-28 13:31:07 +03:00
oop7 bb461898e4 Update translations across multiple locales
Update and improve translations for multiple language files (ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh). Key changes include localized strings for playlist saving ("Save Playlist As" -> localized), save/result/error messages, concurrent connections and help text, default selection/settings (video quality and subtitle language), and various wording/typo fixes. Also add/translate FFmpeg installation and yt-dlp setup messages in Turkish and Chinese. No functional code changes.
2026-04-25 17:42:36 +03:00
oop7 a2e11eec06 Update French translations
Translate several UI strings in ytsage/languages/fr.json from English to French: update `save_playlist`, `filter_playlist_placeholder`, and a group of settings strings (`concurrent_fragments`, `concurrent_fragments_help`, `defaults_settings`, `default_video_quality`, `default_subtitle_language`, `defaults_help`) to provide correct French localization and improve consistency.
2026-04-25 17:40:49 +03:00
oop7 9856dbf301 Update Spanish and Hindi translations
Improve localization by updating translations in ytsage/languages/es.json and ytsage/languages/hi.json. Spanish fixes provide full translations for interface help text, restart notice, playlist/save labels, dialog placeholders, and playlist save messages. Hindi adds translated strings for concurrent connections, related help text, and default selection settings for video quality and subtitles.
2026-04-25 17:40:24 +03:00
oop7 3d06c4dba6 Update German and Hindi translations
Translate several UI strings in ytsage/languages for German (de.json) and Hindi (hi.json). Changes include localized labels for saving playlists and filtering videos, dialog placeholders, and various settings text (e.g. concurrent connections and default selection help in German). Hindi updates add translated playlist save dialog messages and related UI strings.
2026-04-25 17:39:52 +03:00
oop7 d8c3ce8fa1 Add new i18n keys to language files
Add new localization keys across multiple ytsage language JSONs. Common addition: "filter_playlist_placeholder" added to most languages. Spanish, Turkish and Chinese receive several new strings for custom commands, proxy settings, FFmpeg installation messages, history loading, time/placeholders and validation messages. Also ensure files end with newline. Files modified: ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh.
2026-04-25 10:58:43 +03:00
oop7 57f5450725 Add save_playlist key to language files
Add a new "save_playlist" entry (placeholder "Save Playlist As") to multiple locale JSON files (ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh) to provide a translation key for saving playlists. Also adds the required trailing commas when inserting the new entries.
2026-04-25 10:39:00 +03:00
oop7 405ae07cac Add i18n entries for defaults and playlist save
Add new localization keys across multiple languages to support default selection settings and playlist saving UI. New keys include defaults_settings, default_video_quality, default_subtitle_language, defaults_help, and playlist save messages (save_as, save_success_title, saved_successfully, save_error_title, no_videos_to_save, save_error_msg). Files updated: ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh.
2026-04-25 10:37:40 +03:00
oop7 3b6ac7ce81 Add playlist save and default selection strings
Update English locale with new UI strings for playlist saving and default selections. Adds 'save_playlist' and related save messages (save_as, save_success_title, saved_successfully, save_error_title, no_videos_to_save, save_error_msg), adds default selection settings keys (defaults_settings, default_video_quality, default_subtitle_language, defaults_help), and documents the %(playlist_index)s variable in filename_format_help. Also minor formatting cleanup in the file.
2026-04-25 10:33:10 +03:00
oop7 e86536d585 Add concurrent connections i18n keys
Add new i18n keys "concurrent_fragments" and "concurrent_fragments_help" to multiple language JSON files (ar, de, en, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh). These expose a UI option for the number of concurrent connections per download with a help string (default: 1, warns about throttling/temporary blocks). Strings were added in English across locales and may need translation.
2026-04-09 18:27:50 +02:00
oop7 437344695d Add UI tab labels to translation files
Add "tab_general", "tab_format", and "tab_file" entries to multiple ytsage/languages/*.json files to provide localized labels for the UI tabs (General, Format, File). Also include a minor formatting/newline fix in en.json.
2026-03-21 16:26:41 +02:00
oop7 62b8ede273 Add audio normalization docs and translations
Document Audio Normalization (EBU R128) in README and translated READMEs, and add corresponding localization keys and help text in ytsage language files. Updates include README.md, readme-translations (ar,de,es,fr,hi,id,it,ja,pl,pt,ru,tr,zh) and ytsage/languages JSON files to explain the feature and note that normalization requires re-encoding and forcing a specific audio format (e.g. MP3 or M4A).
2026-03-21 15:21:40 +02:00
oop7 b048f8ce09 Add audio normalization option and support
Introduce an "audio_normalization" option across the app and implement normalization for audio-only downloads. Updates include: add config default (ConfigManager), new language strings, settings UI checkbox + help text and logic to auto-enable force-audio-format when normalization is enabled, and persist the setting. Pass the setting from main app to DownloadThread, and in the downloader force re-encoding to mp3 when necessary and append --postprocessor-args ExtractAudio:-af loudnorm=... to apply EBU R128 normalization. This ensures normalization works reliably (avoids ffmpeg stream-copy errors) and keeps the behavior scoped to ExtractAudio postprocessor.
2026-03-21 15:08:48 +02:00
oop7 fff674a0b8 Add check_app_updates key to locales
Add a new "check_app_updates" translation string to multiple language files so the UI can display the option to check for YTSage updates at startup. Updated languages: ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh.
2026-03-19 14:49:37 +02:00
oop7 4a16aad2ac Add check_app_updates setting and UI
Introduce a new "check_app_updates" configuration (default: true) to allow users to enable/disable automatic YTSage update checks. Adds a styled checkbox to the Updater tab with getter, loads the setting (defaults to enabled for older configs), and saves it from the custom options dialog. The main update check now respects this setting and will skip checking when disabled. Also include English localization entries for the new UI strings.
2026-03-19 14:45:56 +02:00
oop7 9afca46c5d Add generic-mode URL validation and UI
Introduce a "generic_mode" option to allow validating/downloading from non-YouTube sites and wire it through the UI, config, and validation logic. Key changes:

- Add generic_mode default to ConfigManager and persist setting from DownloadSettingsDialog (checkbox + help text).
- Extend validate_video_url to accept a generic_mode flag and allow any http/https URL with a domain when enabled; pass this flag from Analysis and Download flows.
- Update YTSageApp to load/save generic_mode, update URL placeholder and settings tooltip behavior, and refresh tooltip when settings change.
- Improve robustness in FormatTableMixin: handle None/incorrect types for format_note, abr, resolution, ext, and codec values to avoid type errors and ensure consistent display.
- Add localization entries for generic mode, placeholder, and related help text across multiple language files and update the in-app default localization strings.

These changes enable broader site support via yt-dlp while hardening UI format handling and keeping user settings persistent.
2026-03-12 11:10:10 +02:00
oop7 79f2613cf7 Add open_logs strings to localizations
Add open_logs and logs_tooltip localization entries across multiple language JSON files (ar, de, en, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh) so the About/System Info UI can expose an action to open the application logs folder. Also add an "about" section to ytsage/utils/ytsage_localization.py with default entries (open_logs, logs_tooltip, refresh). This wires the logs-folder UI text into the localization system.
2026-02-10 13:50:22 +02:00
oop7 04e043c689 Add app update strings to translations
Add 'app_updates_title' and 'check_beta_updates' translation keys to language files (ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh). These localized strings support the app updates settings UI (including an option to receive beta updates) and keep translations consistent with the existing auto-update-related entries.
2026-02-08 17:43:24 +02:00
oop7 d250c1b05f Add opt-in beta update UI and checks
Introduce an opt-in beta updates feature: add a styled "Receive Beta Updates" checkbox in the Updater UI with i18n strings, load/save logic in the custom options dialog, and a default config key (check_beta_updates=false).

Add _fetch_github_beta_version to the update thread to query GitHub releases (selecting the highest tag) and emit update_available for newer beta builds. When beta checking is enabled, the thread will perform the GitHub beta check and return early to avoid conflicting with the PyPI check. This enables users to opt into preview releases safely.
2026-02-08 17:41:13 +02:00
oop7 351504ade9 Add reset and filename_format translations
Add a "reset" button label and new "filename_format" + "filename_format_help" entries to multiple locale JSON files (ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, zh). The help text documents supported yt-dlp output template variables (%(title)s, %(uploader)s, %(upload_date)s, %(resolution)s, %(id)s, %(ext)s) so the UI can accept a custom output filename format.
2026-02-07 15:55:10 +02:00
oop7 1fbc3a43c2 Add Reset button for filename format
Add a Reset button next to the filename format input so users can restore the default pattern. The filename input and reset button are grouped in a QHBoxLayout; the button is fixed width (70) and sets the field back to the default %(title)s_%(resolution)s.%(ext)s. Also add the corresponding i18n key (buttons.reset -> "Reset") to en.json.
2026-02-07 15:37:26 +02:00
oop7 ed1469898c Add configurable output filename format
Introduce a new filename_format setting and UI to control yt-dlp output templates. ConfigManager now includes a default filename_format (%(title)s_%(resolution)s.%(ext)s). The DownloadSettingsDialog exposes a text input and help text for the format and saves the value to ConfigManager. DownloadThread now accepts a filename_format argument and uses it when building output templates for single videos and playlists. YTSageApp reads the config and passes the filename format into the download thread. Added corresponding English language strings.
2026-02-07 15:28:06 +02:00
oop7 cd9e410ea3 Simplify analysis status messages in locales
Standardize and simplify analysis/status strings across multiple language files by removing percent-based prefixes and verbose phrasing, adding concise messages with trailing ellipses. This updates localization for ar, de, es, fr, hi, id, it, ja, pl, pt, ru, tr, and zh (ytsage/languages/*.json) to make progress messages cleaner and consistent for both regular and ytdlp-related steps.
2026-02-03 19:20:41 +02:00
oop7 e86704c5cd Simplify analyzing status messages
Update English UI strings in ytsage/languages/en.json to remove progress percentages and the repeated "Analyzing" prefix, replacing them with concise action messages (e.g., "Extracting basic info...", "Loading thumbnail..."). This makes status messages shorter and more consistent across regular and ytdlp-related entries without changing functionality.
2026-02-03 19:17:44 +02:00
oop7 acda70150f Move language files to ytsage/languages directory
Renamed and relocated all language JSON files from the root 'languages' directory to 'ytsage/languages' for improved project organization.
2026-01-25 14:03:01 +02:00