Every metadata call site previously built its own command list. The new
ytsage/core/ytsage_client.py centralizes binary resolution (managed,
verified binary only), cookie/proxy args from ConfigManager (with
session-state overrides), utf-8 output handling, process-group-safe
timeouts, and a short-TTL cache for flat/search results.
Provides fetch_video_info, fetch_flat_info, fetch_flat_entries (with
-I range pagination), search (ytsearchN:), and fetch_account_feed
(youtube.com/feed/subscriptions with cookies) plus a generic YtdlpWorker
QThread. AnalysisThread now delegates its subprocess execution to the
shared runner; its signal surface is unchanged.
Groundwork for the SageTube watch/search/browse/feed features.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- get_yt_dlp_path() no longer implicitly executes a bare "yt-dlp" from
PATH (on Windows that lookup includes the CWD, so a planted binary
in a writable directory could be run). A system yt-dlp is used only
behind the explicit advanced.allow_system_ytdlp config opt-in, and
then always as a which()-resolved absolute path. Analysis and
download refuse to exec the not-installed sentinel.
- Analysis subprocesses now run in their own session and the whole
process group is killed on timeout, so deno grandchildren no longer
leak; partial stderr is preserved and logged, and output decoding is
pinned to utf-8 with replacement (Windows locale codecs crashed on
non-UTF8 titles).
- Flat-playlist entries are filtered for None (private/deleted first
video no longer breaks analysis).
- update_yt_dlp() normalizes the sentinel to Path, unbreaking the pip
fallback path that crashed on str.exists().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Downloader: add detection for "Auto-generated" subtitle selection and only append --write-auto-subs when an auto-generated subtitle is explicitly chosen (prevents always enabling the flag when any subtitles are requested). GUI: update subtitle label to display the number of selected subtitles when >0, falling back to the zero-selected message otherwise. Small UX and behavior fixes in DownloadThread and AnalysisMixin.
Update AnalysisThread error handling to detect yt-dlp stderr containing "Private video" or "Sign in". For those cases, emit a localized errors.private_video message and log a specific private-video error; otherwise preserve the existing generic ytdlp_failed behavior. Playlist visibility signals and early return remain unchanged.
Load user's default_subtitle_language from ConfigManager and preselect matching manual or auto-generated subtitles on analysis load. Handles comma-separated strings, checks against available_subtitles and available_automatic_subtitles, and updates the selected subtitles list. Attempts to update the UI label and button state (with style refresh) inside a try/except to avoid errors during initialization.
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.
Stop storing result_data["all_formats"] on the mixin and instead call update_format_table(result_data["all_formats"]). Replaces the previous filter_formats() call so the format table is populated directly from the fresh analysis result, avoiding reliance on a separate self.all_formats attribute.
Emit progress updates during URL analysis and simulate gradual progress for long-running yt-dlp operations. Added QTimer import and a progress_update Signal on AnalysisThread, with emits at multiple analysis milestones (e.g. 15, 30, 60, 70, 85, 90, 92, 95, 100). In AnalysisMixin introduced _analysis_timer and _fake_progress, connected thread progress to _handle_analysis_progress, and implemented a _update_fake_progress timer that slowly advances the progress bar between real updates. Timers are stopped on real progress, completion or error and the UI progress is reset appropriately to improve UX during long extraction steps.
Introduce a shake animation to visually indicate invalid inputs and wire it into validation/error flows. Changes: import QPoint; add animate_widget_shake(QWidget) implementation that uses QPropertyAnimation on widget.pos with keyframes; call the animation when URL/path/format validations fail and replace some direct status_label updates with set_status_message_animated. Updated files: ytsage/gui/ytsage_gui_main.py and ytsage/gui/ytsage_gui_analysis.py.
Moved all files from src/gui/ to ytsage/gui/ and updated import statements to use relative imports within the new package structure. This improves modularity and prepares the codebase for distribution as a proper Python package.