From 45c155c039a103e3a0230fb621a19ab485cd9f25 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Sat, 13 Sep 2025 11:32:54 -0700 Subject: [PATCH] [FEATURE] resolution_assert_ignore_titles to ignore specific low-resolution videos (#1336) Closes https://github.com/jmbannon/ytdl-sub/issues/1313 Adds `resolution_assert_ignore_titles` variable to be able to ignore specific videos in a subscription where 360p is suspected to be the only option. Ideally, it would be nice if ytdl-sub could determine whether 360p is actually the only resolution or not automatically, and download if it's true. We need to do more research to determine if this is possible. In the meantime, the above variable should help manually get around this. Usage: ``` # use tilda mode to set override variables to the subscription "~My Subscription": url: "https://youtube.com/@channel" resolution_assert_ignore_titles: - "This 360p Video Title" ``` --- docs/source/faq/index.rst | 5 ++ docs/source/prebuilt_presets/helpers.rst | 18 +++++++ .../helpers/throttle_protection.yaml | 13 ++++- .../plugins/test_throttle_protection.py | 48 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/source/faq/index.rst b/docs/source/faq/index.rst index da43c1cf..bb5f4fe0 100644 --- a/docs/source/faq/index.rst +++ b/docs/source/faq/index.rst @@ -287,3 +287,8 @@ tell yt-dlp to explicitly download English metadata using. :alt: The Plex Agents settings page has Local Media Assets enabled for Personal Media Shows and Movies tabs. + +...ytdl-sub errors when downloading a 360p video with resolution assert +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:ref:`See how to either ignore this specific video or disable resolution assertion entirely here. ` diff --git a/docs/source/prebuilt_presets/helpers.rst b/docs/source/prebuilt_presets/helpers.rst index 83505fff..86752ed4 100644 --- a/docs/source/prebuilt_presets/helpers.rst +++ b/docs/source/prebuilt_presets/helpers.rst @@ -176,3 +176,21 @@ following overrides: enable_resolution_assert: false # Change the resolution below which to assume downloading is throttled: resolution_assert_height_gte: 720 + +.. _resolution assert handling: + +Handling Low Quality Videos +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A side effect from throttle protection's resolution assert is, if the only resolution available is 360p or lower, it will +error. You can either disable resolution assert entirely (see above), or ignore specific titles in the subscription +using the ``resolution_assert_ignore_titles`` variable. Add a subset of the title (case-sensitive) as a list entry +to your subscription, like so: + +.. code-block:: yaml + + # use tilda mode to set override variables to the subscription + "~My Subscription": + url: "https://youtube.com/@channel" + resolution_assert_ignore_titles: + - "This 360p Video Title" diff --git a/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml b/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml index 2f3afee3..1194e004 100644 --- a/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml +++ b/src/ytdl_sub/prebuilt_presets/helpers/throttle_protection.yaml @@ -47,6 +47,16 @@ presets: -1 ) } + # list variable that contains partial titles to ignore + resolution_assert_ignore_titles: "{ [] }" + resolution_assert_is_ignored: >- + { + %print_if_true( + %concat(title, " has a match in resolution_assert_ignore_titles, skipping resolution assert."), + %contains_any(title, resolution_assert_ignore_titles) + ) + } + resolution_readable: "{width}x{height}" # height is not a guaranteed populated variable, do not assert if it's missing (which defaults to 0) resolution_assert: >- @@ -54,7 +64,8 @@ presets: %if( %and( enable_resolution_assert, - %ne( height, 0 ) + %ne( height, 0 ), + %not(resolution_assert_is_ignored) ), %assert( %gte( height, resolution_assert_height_gte ), diff --git a/tests/integration/plugins/test_throttle_protection.py b/tests/integration/plugins/test_throttle_protection.py index 391ec299..6154f70a 100644 --- a/tests/integration/plugins/test_throttle_protection.py +++ b/tests/integration/plugins/test_throttle_protection.py @@ -290,6 +290,54 @@ class TestResolutionAssert: ): _ = subscription.download(dry_run=True) + def test_ignore_title_low_resolution( + self, + config, + subscription_name, + throttle_subscription_dict, + output_directory, + mock_download_collection_entries, + ): + throttle_subscription_dict["throttle_protection"]["max_downloads_per_subscription"] = { + "max": 1, + "min": 1, + } + throttle_subscription_dict["overrides"]["resolution_assert_ignore_titles"] = [ + "Entry 20-3", + ] + subscription = Subscription.from_dict( + config=config, + preset_name=subscription_name, + preset_dict=throttle_subscription_dict, + ) + + # expected_message = ( + # "Entry Mock Entry 20-3 downloaded at a low resolution (640x360), " + # "you've probably been throttled. " + # "Stopping further downloads, wait a few hours and try again. " + # "Disable using the override variable `enable_resolution_assert: False`" + # ) + + with ( + mock_download_collection_entries( + is_youtube_channel=False, + num_urls=1, + is_extracted_audio=False, + is_dry_run=True, + mock_entry_kwargs={"height": 360, "width": 640}, + ), + assert_logs( + logger=script_print_logger, + expected_message=( + "Mock Entry 20-3 has a match in resolution_assert_ignore_titles, " + "skipping resolution assert." + ), + log_level="info", + expected_occurrences=1, + ), + ): + _ = subscription.download(dry_run=True) + def test_sleep_per_download_supports_entry_variables( self, config,