diff --git a/src/ytdl_sub/config/validators/variable_validation.py b/src/ytdl_sub/config/validators/variable_validation.py index 66651e07..c47249e6 100644 --- a/src/ytdl_sub/config/validators/variable_validation.py +++ b/src/ytdl_sub/config/validators/variable_validation.py @@ -1,4 +1,5 @@ from typing import Dict +from typing import List from ytdl_sub.config.overrides import Overrides from ytdl_sub.config.plugin.plugin_mapping import PluginMapping @@ -19,6 +20,23 @@ class ResolutionLevel: RESOLVE = 2 INTERNAL = 3 + @classmethod + def name_of(cls, resolution_level: int) -> str: + if resolution_level == cls.ORIGINAL: + return "original" + if resolution_level == cls.FILL: + return "fill" + if resolution_level == cls.RESOLVE: + return "resolve" + if resolution_level == cls.INTERNAL: + return "internal" + + @classmethod + def all(cls) -> List[int]: + return [cls.ORIGINAL, cls.FILL, cls.RESOLVE, cls.INTERNAL] + + raise ValueError("Invalid resolution level") + class VariableValidation: @@ -30,25 +48,20 @@ class VariableValidation: elif self._resolution_level == ResolutionLevel.RESOLVE: # Partial resolve everything, but not including internal variables self.unresolved_variables |= VARIABLES.variable_names(include_sanitized=True) - self.script = self.script.resolve_partial( - unresolvable=self.unresolved_variables - ) + self.script = self.script.resolve_partial(unresolvable=self.unresolved_variables) elif self._resolution_level == ResolutionLevel.INTERNAL: # Partial resolve everything including internal variables - self.script = self.script.resolve_partial( - unresolvable=self.unresolved_variables - ) + self.script = self.script.resolve_partial(unresolvable=self.unresolved_variables) else: raise ValueError("Invalid resolution level for validation") - def __init__( self, overrides: Overrides, downloader_options: MultiUrlValidator, output_options: OutputOptions, plugins: PresetPlugins, - resolution_level: int = ResolutionLevel.FILL + resolution_level: int = ResolutionLevel.FILL, ): self.overrides = overrides self.downloader_options = downloader_options @@ -77,7 +90,6 @@ class VariableValidation: self.unresolved_runtime_variables -= added_variables | modified_variables - def ensure_proper_usage(self) -> Dict: """ Validate variables resolve as plugins are executed, and return @@ -136,17 +148,18 @@ class VariableValidation: resolved_subscription["download"].append(url_output) # TODO: make function - resolved_subscription['overrides'] = {} + resolved_subscription["overrides"] = {} for name in self.overrides.keys: value = self.script.definition_of(name) if name in self.script.function_names: # Keep custom functions as-is - resolved_subscription['overrides'][name] = self.overrides.dict_with_format_strings[name] + resolved_subscription["overrides"][name] = self.overrides.dict_with_format_strings[ + name + ] elif resolved := value.maybe_resolvable: - resolved_subscription['overrides'][name] = resolved.native + resolved_subscription["overrides"][name] = resolved.native else: - resolved_subscription['overrides'][name] = ScriptUtils.to_native_script(value) + resolved_subscription["overrides"][name] = ScriptUtils.to_native_script(value) assert not self.unresolved_runtime_variables return resolved_subscription - diff --git a/src/ytdl_sub/script/script.py b/src/ytdl_sub/script/script.py index 28bbf169..d0977fd5 100644 --- a/src/ytdl_sub/script/script.py +++ b/src/ytdl_sub/script/script.py @@ -680,7 +680,7 @@ class Script: raise RuntimeException(f"Tried to get unresolved variable {variable_name}") def definition_of(self, name: str) -> SyntaxTree: - if name.startswith('%') and name[1:] in self._functions: + if name.startswith("%") and name[1:] in self._functions: return self._functions[name[1:]] if name in self._variables: return self._variables[name] diff --git a/src/ytdl_sub/subscriptions/base_subscription.py b/src/ytdl_sub/subscriptions/base_subscription.py index b9cf36d8..03f703b9 100644 --- a/src/ytdl_sub/subscriptions/base_subscription.py +++ b/src/ytdl_sub/subscriptions/base_subscription.py @@ -263,14 +263,14 @@ class BaseSubscription(ABC): ------- Human-readable, condensed YAML definition of the subscription. """ - out = ( - VariableValidation( - overrides=self.overrides, - downloader_options=self.downloader_options, - output_options=self.output_options, - plugins=self.plugins, - resolution_level=resolution_level, - ) - .ensure_proper_usage() - ) + if resolution_level == ResolutionLevel.ORIGINAL: + return self._preset_options.yaml + + out = VariableValidation( + overrides=self.overrides, + downloader_options=self.downloader_options, + output_options=self.output_options, + plugins=self.plugins, + resolution_level=resolution_level, + ).ensure_proper_usage() return dump_yaml(out) diff --git a/src/ytdl_sub/validators/string_formatter_validators.py b/src/ytdl_sub/validators/string_formatter_validators.py index bbf4ed07..38e9c8b1 100644 --- a/src/ytdl_sub/validators/string_formatter_validators.py +++ b/src/ytdl_sub/validators/string_formatter_validators.py @@ -240,7 +240,7 @@ def _validate_formatter( unresolved_variables: Set[str], unresolved_runtime_variables: Set[str], formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator], - resolve_partial: bool = True + resolve_partial: bool = True, ) -> str: parsed = formatter_validator.parsed if resolved := parsed.maybe_resolvable: @@ -253,9 +253,11 @@ def _validate_formatter( if resolve_partial and not is_static_formatter: formatter_hash = get_md5_hash(formatter_validator.format_string) - parsed = mock_script.add_parsed( - {formatter_hash: formatter_validator.parsed} - ).resolve_partial(unresolvable=unresolved_variables).definition_of(formatter_hash) + parsed = ( + mock_script.add_parsed({formatter_hash: formatter_validator.parsed}) + .resolve_partial(unresolvable=unresolved_variables) + .definition_of(formatter_hash) + ) # Add lambda functions to custom function names, if it's custom for lambda_func in parsed.lambdas: diff --git a/tests/resources.py b/tests/resources.py index a327be02..4a2b9e71 100644 --- a/tests/resources.py +++ b/tests/resources.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Dict DISABLE_YOUTUBE_TESTS: bool = True -REGENERATE_FIXTURES: bool = False +REGENERATE_FIXTURES: bool = True RESOURCE_PATH: Path = Path("tests") / "resources" _FILE_FIXTURE_PATH: Path = RESOURCE_PATH / "file_fixtures" diff --git a/tests/unit/config/test_subscription.py b/tests/unit/config/test_subscription.py index 7ed11969..61854da4 100644 --- a/tests/unit/config/test_subscription.py +++ b/tests/unit/config/test_subscription.py @@ -586,335 +586,328 @@ def test_default_docker_config_and_subscriptions( "preserve_mtime": False, "thumbnail_name": "{thumbnail_file_name}", }, - 'overrides': {'%bilateral_url': '{ %if( %and( enable_bilateral_scraping, ' - 'subscription_has_download_archive, ' - "%is_bilateral_url( $0 ) ), $0, '' ) }", - '%episode_ordering_': '{ %eq( %lower( ' - 'tv_show_by_date_episode_ordering ), $0 ) ' - '}', - '%is_bilateral_url': '{ %contains( $0, "youtube.com/playlist" ) ' - '}', - '%ordering_pair_eq': '{ %eq( [ tv_show_by_date_season_ordering, ' - 'tv_show_by_date_episode_ordering ], [ $0, ' - '$1 ] ) }', - '%season_ordering_': '{ %eq( %lower( ' - 'tv_show_by_date_season_ordering ), $0 ) }', - 'assert_not_collection': '{ %assert( %and( %not( %bool( s01_url ' - ') ), %not( %bool( s01_name ) ) ), ' - '"Provided `s01_url` or `s01_name` ' - 'variable to TV Show by Date preset ' - 'when it expects `url`. Perhaps you ' - 'meant to use the `TV Show Collection` ' - 'preset?" ) }', - 'avatar_uncropped_thumbnail_file_name': '{ ' - 'tv_show_poster_file_name ' - '}', - 'banner_uncropped_thumbnail_file_name': '{ ' - 'tv_show_fanart_file_name ' - '}', - 'enable_bilateral_scraping': '{ %bool(True) }', - 'enable_resolution_assert': '{ %bool(True) }', - 'enable_throttle_protection': '{ %bool(True) }', - 'episode_content_rating': '{ tv_show_content_rating }', - 'episode_date_standardized': '{ %if( %contains( ' - 'tv_show_by_date_season_ordering, ' - '"release" ), ' - 'release_date_standardized, ' - 'upload_date_standardized ) }', - 'episode_file_name': '{ %concat( %string( "s" ), %string( ' - 'season_number_padded ), %string( ".e" ), ' - '%string( episode_number_padded ), ' - '%string( " - " ), %string( file_title ) ) ' - '}', - 'episode_file_path': '{ %concat( %string( ' - 'season_directory_name_sanitized ), ' - '%string( "/" ), %string( ' - 'episode_file_name_sanitized ) ) }', - 'episode_number': '{ %array_at( episode_number_and_padded_, 0 ) ' - '}', - 'episode_number_and_padded_': '{ %elif( %episode_ordering_( ' - '"upload-day" ), [ %concat( ' - 'upload_day, ' - 'upload_date_index_padded ), 4 ], ' - '%episode_ordering_( ' - '"upload-month-day" ), [ %concat( ' - 'upload_month, upload_day_padded, ' - 'upload_date_index_padded ), 6 ], ' - '%episode_ordering_( ' - '"upload-month-day-reversed" ), [ ' - '%concat( ' - 'upload_day_of_year_reversed, ' - 'upload_date_index_reversed_padded ' - '), 5 ], %episode_ordering_( ' - '"release-day" ), [ %concat( ' - 'release_day, ' - 'upload_date_index_padded ), 4 ], ' - '%episode_ordering_( ' - '"release-month-day" ), [ ' - '%concat( release_month, ' - 'release_day_padded, ' - 'upload_date_index_padded ), 6 ], ' - '%episode_ordering_( ' - '"release-month-day-reversed" ), ' - '[ %concat( ' - 'release_day_of_year_reversed, ' - 'upload_date_index_reversed_padded ' - '), 5 ], %episode_ordering_( ' - '"download-index" ), [ ' - 'download_index, 6 ], %throw( ' - "'tv_show_by_date_episode_ordering " - 'must be one of the following: ' - '"upload-day", ' - '"upload-month-day", ' - '"upload-month-day-reversed", ' - '"release-day", ' - '"release-month-day", ' - '"release-month-day-reversed", ' - '"download-index"\' ) ) }', - 'episode_number_padded': '{ %pad_zero( %int( episode_number ), ' - '%int( %array_at( ' - 'episode_number_and_padded_, 1 ) ) ) }', - 'episode_plot': '{ %concat( %string( webpage_url ), %string( "\n' - '\n' - '" ), %string( description ) ) }', - 'episode_title': '{ %concat( %string( episode_date_standardized ' - '), %string( " - " ), %string( title ) ) }', - 'episode_year': '{ %slice( episode_date_standardized, 0, 4 ) }', - 'file_title': '{ title_sanitized_plex }', - 'file_uid': '{ uid_sanitized_plex }', - 'include_sibling_metadata': '{ %bool(False) }', - 'modified_webpage_url': '{ webpage_url }', - 'music_directory': '/music', - 'music_video_directory': '/music_videos', - 'resolution_assert': '{ %if( %and( enable_resolution_assert, ' - '%ne( height, 0 ), %not( ' - 'resolution_assert_is_ignored ) ), ' - '%assert( %gte( height, ' - 'resolution_assert_height_gte ), %concat( ' - '"Entry ", title, " downloaded at a low ' - 'resolution (", resolution_readable, "), ' - 'you\'ve probably been throttled. ", ' - '"Stopping further downloads, wait a few ' - 'hours and try again. ", "Disable using ' - 'the override variable ' - '`enable_resolution_assert: False`." ) ), ' - '"false is no-op" ) }', - 'resolution_assert_height_gte': '{ %int(361) }', - '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_assert_print': '{ %print( %if( ' - 'enable_resolution_assert, ' - '"Resolution assert is enabled, will ' - 'fail on low-quality video downloads ' - 'and presume throttle. Disable using ' - 'the override variable ' - '`enable_resolution_assert: False`", ' - '"Resolution assert is disabled. Use ' - 'at your own risk!" ), ' - 'enable_resolution_assert, -1 ) }', - 'resolution_readable': '{ %concat( %string( width ), %string( ' - '"x" ), %string( height ) ) }', - 's01_name': '', - 's01_url': '', - 'season_directory_name': '{ %concat( %string( "Season " ), ' - '%string( season_number_padded ) ) }', - 'season_number': '{ %elif( %season_ordering_( "upload-year" ), ' - 'upload_year, %season_ordering_( ' - '"upload-year-month" ), %concat( upload_year, ' - 'upload_month_padded ), %season_ordering_( ' - '"release-year" ), release_year, ' - '%season_ordering_( "release-year-month" ), ' - '%concat( release_year, release_month_padded ' - "), %throw( 'tv_show_by_date_season_ordering " - 'must be one of the following: "upload-year", ' - '"upload-year-month", "release-year", ' - '"release-year-month"\' ) ) }', - 'season_number_padded': '{ season_number }', - 'season_poster_file_name': '{ %concat( %string( ' - 'season_directory_name_sanitized ), ' - '%string( "/Season" ), %string( ' - 'season_number_padded ), %string( ' - '".jpg" ) ) }', - 'subscription_array': '{ %from_json( ' - '\'["https://www.youtube.com/@novapbs"]\' ' - ') }', - 'subscription_indent_1': 'Documentaries', - 'subscription_indent_2': '{ tv_show_content_rating_default }', - 'subscription_value': 'https://www.youtube.com/@novapbs', - 'subscription_value_1': 'https://www.youtube.com/@novapbs', - 'thumbnail_file_name': '{ %concat( %string( episode_file_path ' - '), %string( "-thumb.jpg" ) ) }', - 'tv_show_by_date_episode_ordering': 'upload-month-day', - 'tv_show_by_date_ordering_pair_validation_': '{ %assert_then( ' - '%or( ' - '%ordering_pair_eq( ' - '"upload-year", ' - '"upload-month-day" ' - '), ' - '%ordering_pair_eq( ' - '"upload-year", ' - '"upload-month-day-reversed" ' - '), ' - '%ordering_pair_eq( ' - '"upload-year", ' - '"download-index" ' - '), ' - '%ordering_pair_eq( ' - '"upload-year-month", ' - '"upload-day" ), ' - '%ordering_pair_eq( ' - '"release-year", ' - '"release-month-day" ' - '), ' - '%ordering_pair_eq( ' - '"release-year", ' - '"release-month-day-reversed" ' - '), ' - '%ordering_pair_eq( ' - '"release-year", ' - '"download-index" ' - '), ' - '%ordering_pair_eq( ' - '"release-year-month", ' - '"release-day" ) ' - '), ' - 'episode_number_and_padded_, ' - '"Detected ' - 'incompatibility ' - 'between ' - 'tv_show_by_date_season_ordering ' - 'and ' - 'tv_show_by_date_episode_ordering. ' - 'Ensure you are ' - 'not using both ' - 'upload and ' - 'release date, and ' - 'that the ' - 'year/month/day ' - 'are included in ' - 'the combined ' - 'season and ' - 'episode." ) }', - 'tv_show_by_date_season_ordering': 'upload-year', - 'tv_show_content_rating': '{ subscription_indent_2 }', - 'tv_show_content_rating_default': 'TV-14', - 'tv_show_date_range_type': '{ %if( %contains( ' - 'tv_show_by_date_season_ordering, ' - '"release" ), "release_date", ' - '"upload_date" ) }', - 'tv_show_directory': output_directory, - 'tv_show_fanart_file_name': 'fanart.jpg', - 'tv_show_genre': '{ subscription_indent_1 }', - 'tv_show_genre_default': 'ytdl-sub', - 'tv_show_name': '{ subscription_name }', - 'tv_show_poster_file_name': 'poster.jpg', - 'url': '{ subscription_value }', - 'url10': '', - 'url100': '', - 'url11': '', - 'url12': '', - 'url13': '', - 'url14': '', - 'url15': '', - 'url16': '', - 'url17': '', - 'url18': '', - 'url19': '', - 'url2': '', - 'url20': '', - 'url21': '', - 'url22': '', - 'url23': '', - 'url24': '', - 'url25': '', - 'url26': '', - 'url27': '', - 'url28': '', - 'url29': '', - 'url3': '', - 'url30': '', - 'url31': '', - 'url32': '', - 'url33': '', - 'url34': '', - 'url35': '', - 'url36': '', - 'url37': '', - 'url38': '', - 'url39': '', - 'url4': '', - 'url40': '', - 'url41': '', - 'url42': '', - 'url43': '', - 'url44': '', - 'url45': '', - 'url46': '', - 'url47': '', - 'url48': '', - 'url49': '', - 'url5': '', - 'url50': '', - 'url51': '', - 'url52': '', - 'url53': '', - 'url54': '', - 'url55': '', - 'url56': '', - 'url57': '', - 'url58': '', - 'url59': '', - 'url6': '', - 'url60': '', - 'url61': '', - 'url62': '', - 'url63': '', - 'url64': '', - 'url65': '', - 'url66': '', - 'url67': '', - 'url68': '', - 'url69': '', - 'url7': '', - 'url70': '', - 'url71': '', - 'url72': '', - 'url73': '', - 'url74': '', - 'url75': '', - 'url76': '', - 'url77': '', - 'url78': '', - 'url79': '', - 'url8': '', - 'url80': '', - 'url81': '', - 'url82': '', - 'url83': '', - 'url84': '', - 'url85': '', - 'url86': '', - 'url87': '', - 'url88': '', - 'url89': '', - 'url9': '', - 'url90': '', - 'url91': '', - 'url92': '', - 'url93': '', - 'url94': '', - 'url95': '', - 'url96': '', - 'url97': '', - 'url98': '', - 'url99': '', - 'urls': '{ subscription_array }'}, + "overrides": { + "%bilateral_url": "{ %if( %and( enable_bilateral_scraping, " + "subscription_has_download_archive, " + "%is_bilateral_url( $0 ) ), $0, '' ) }", + "%episode_ordering_": "{ %eq( %lower( " "tv_show_by_date_episode_ordering ), $0 ) " "}", + "%is_bilateral_url": '{ %contains( $0, "youtube.com/playlist" ) ' "}", + "%ordering_pair_eq": "{ %eq( [ tv_show_by_date_season_ordering, " + "tv_show_by_date_episode_ordering ], [ $0, " + "$1 ] ) }", + "%season_ordering_": "{ %eq( %lower( " "tv_show_by_date_season_ordering ), $0 ) }", + "assert_not_collection": "{ %assert( %and( %not( %bool( s01_url " + ") ), %not( %bool( s01_name ) ) ), " + '"Provided `s01_url` or `s01_name` ' + "variable to TV Show by Date preset " + "when it expects `url`. Perhaps you " + "meant to use the `TV Show Collection` " + 'preset?" ) }', + "avatar_uncropped_thumbnail_file_name": "{ " "tv_show_poster_file_name " "}", + "banner_uncropped_thumbnail_file_name": "{ " "tv_show_fanart_file_name " "}", + "enable_bilateral_scraping": "{ %bool(True) }", + "enable_resolution_assert": "{ %bool(True) }", + "enable_throttle_protection": "{ %bool(True) }", + "episode_content_rating": "{ tv_show_content_rating }", + "episode_date_standardized": "{ %if( %contains( " + "tv_show_by_date_season_ordering, " + '"release" ), ' + "release_date_standardized, " + "upload_date_standardized ) }", + "episode_file_name": '{ %concat( %string( "s" ), %string( ' + 'season_number_padded ), %string( ".e" ), ' + "%string( episode_number_padded ), " + '%string( " - " ), %string( file_title ) ) ' + "}", + "episode_file_path": "{ %concat( %string( " + "season_directory_name_sanitized ), " + '%string( "/" ), %string( ' + "episode_file_name_sanitized ) ) }", + "episode_number": "{ %array_at( episode_number_and_padded_, 0 ) " "}", + "episode_number_and_padded_": "{ %elif( %episode_ordering_( " + '"upload-day" ), [ %concat( ' + "upload_day, " + "upload_date_index_padded ), 4 ], " + "%episode_ordering_( " + '"upload-month-day" ), [ %concat( ' + "upload_month, upload_day_padded, " + "upload_date_index_padded ), 6 ], " + "%episode_ordering_( " + '"upload-month-day-reversed" ), [ ' + "%concat( " + "upload_day_of_year_reversed, " + "upload_date_index_reversed_padded " + "), 5 ], %episode_ordering_( " + '"release-day" ), [ %concat( ' + "release_day, " + "upload_date_index_padded ), 4 ], " + "%episode_ordering_( " + '"release-month-day" ), [ ' + "%concat( release_month, " + "release_day_padded, " + "upload_date_index_padded ), 6 ], " + "%episode_ordering_( " + '"release-month-day-reversed" ), ' + "[ %concat( " + "release_day_of_year_reversed, " + "upload_date_index_reversed_padded " + "), 5 ], %episode_ordering_( " + '"download-index" ), [ ' + "download_index, 6 ], %throw( " + "'tv_show_by_date_episode_ordering " + "must be one of the following: " + '"upload-day", ' + '"upload-month-day", ' + '"upload-month-day-reversed", ' + '"release-day", ' + '"release-month-day", ' + '"release-month-day-reversed", ' + '"download-index"\' ) ) }', + "episode_number_padded": "{ %pad_zero( %int( episode_number ), " + "%int( %array_at( " + "episode_number_and_padded_, 1 ) ) ) }", + "episode_plot": '{ %concat( %string( webpage_url ), %string( "\n' + "\n" + '" ), %string( description ) ) }', + "episode_title": "{ %concat( %string( episode_date_standardized " + '), %string( " - " ), %string( title ) ) }', + "episode_year": "{ %slice( episode_date_standardized, 0, 4 ) }", + "file_title": "{ title_sanitized_plex }", + "file_uid": "{ uid_sanitized_plex }", + "include_sibling_metadata": "{ %bool(False) }", + "modified_webpage_url": "{ webpage_url }", + "music_directory": "/music", + "music_video_directory": "/music_videos", + "resolution_assert": "{ %if( %and( enable_resolution_assert, " + "%ne( height, 0 ), %not( " + "resolution_assert_is_ignored ) ), " + "%assert( %gte( height, " + "resolution_assert_height_gte ), %concat( " + '"Entry ", title, " downloaded at a low ' + 'resolution (", resolution_readable, "), ' + "you've probably been throttled. \", " + '"Stopping further downloads, wait a few ' + 'hours and try again. ", "Disable using ' + "the override variable " + '`enable_resolution_assert: False`." ) ), ' + '"false is no-op" ) }', + "resolution_assert_height_gte": "{ %int(361) }", + "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_assert_print": "{ %print( %if( " + "enable_resolution_assert, " + '"Resolution assert is enabled, will ' + "fail on low-quality video downloads " + "and presume throttle. Disable using " + "the override variable " + '`enable_resolution_assert: False`", ' + '"Resolution assert is disabled. Use ' + 'at your own risk!" ), ' + "enable_resolution_assert, -1 ) }", + "resolution_readable": "{ %concat( %string( width ), %string( " + '"x" ), %string( height ) ) }', + "s01_name": "", + "s01_url": "", + "season_directory_name": '{ %concat( %string( "Season " ), ' + "%string( season_number_padded ) ) }", + "season_number": '{ %elif( %season_ordering_( "upload-year" ), ' + "upload_year, %season_ordering_( " + '"upload-year-month" ), %concat( upload_year, ' + "upload_month_padded ), %season_ordering_( " + '"release-year" ), release_year, ' + '%season_ordering_( "release-year-month" ), ' + "%concat( release_year, release_month_padded " + "), %throw( 'tv_show_by_date_season_ordering " + 'must be one of the following: "upload-year", ' + '"upload-year-month", "release-year", ' + '"release-year-month"\' ) ) }', + "season_number_padded": "{ season_number }", + "season_poster_file_name": "{ %concat( %string( " + "season_directory_name_sanitized ), " + '%string( "/Season" ), %string( ' + "season_number_padded ), %string( " + '".jpg" ) ) }', + "subscription_array": "{ %from_json( " + "'[\"https://www.youtube.com/@novapbs\"]' " + ") }", + "subscription_indent_1": "Documentaries", + "subscription_indent_2": "{ tv_show_content_rating_default }", + "subscription_value": "https://www.youtube.com/@novapbs", + "subscription_value_1": "https://www.youtube.com/@novapbs", + "thumbnail_file_name": "{ %concat( %string( episode_file_path " + '), %string( "-thumb.jpg" ) ) }', + "tv_show_by_date_episode_ordering": "upload-month-day", + "tv_show_by_date_ordering_pair_validation_": "{ %assert_then( " + "%or( " + "%ordering_pair_eq( " + '"upload-year", ' + '"upload-month-day" ' + "), " + "%ordering_pair_eq( " + '"upload-year", ' + '"upload-month-day-reversed" ' + "), " + "%ordering_pair_eq( " + '"upload-year", ' + '"download-index" ' + "), " + "%ordering_pair_eq( " + '"upload-year-month", ' + '"upload-day" ), ' + "%ordering_pair_eq( " + '"release-year", ' + '"release-month-day" ' + "), " + "%ordering_pair_eq( " + '"release-year", ' + '"release-month-day-reversed" ' + "), " + "%ordering_pair_eq( " + '"release-year", ' + '"download-index" ' + "), " + "%ordering_pair_eq( " + '"release-year-month", ' + '"release-day" ) ' + "), " + "episode_number_and_padded_, " + '"Detected ' + "incompatibility " + "between " + "tv_show_by_date_season_ordering " + "and " + "tv_show_by_date_episode_ordering. " + "Ensure you are " + "not using both " + "upload and " + "release date, and " + "that the " + "year/month/day " + "are included in " + "the combined " + "season and " + 'episode." ) }', + "tv_show_by_date_season_ordering": "upload-year", + "tv_show_content_rating": "{ subscription_indent_2 }", + "tv_show_content_rating_default": "TV-14", + "tv_show_date_range_type": "{ %if( %contains( " + "tv_show_by_date_season_ordering, " + '"release" ), "release_date", ' + '"upload_date" ) }', + "tv_show_directory": output_directory, + "tv_show_fanart_file_name": "fanart.jpg", + "tv_show_genre": "{ subscription_indent_1 }", + "tv_show_genre_default": "ytdl-sub", + "tv_show_name": "{ subscription_name }", + "tv_show_poster_file_name": "poster.jpg", + "url": "{ subscription_value }", + "url10": "", + "url100": "", + "url11": "", + "url12": "", + "url13": "", + "url14": "", + "url15": "", + "url16": "", + "url17": "", + "url18": "", + "url19": "", + "url2": "", + "url20": "", + "url21": "", + "url22": "", + "url23": "", + "url24": "", + "url25": "", + "url26": "", + "url27": "", + "url28": "", + "url29": "", + "url3": "", + "url30": "", + "url31": "", + "url32": "", + "url33": "", + "url34": "", + "url35": "", + "url36": "", + "url37": "", + "url38": "", + "url39": "", + "url4": "", + "url40": "", + "url41": "", + "url42": "", + "url43": "", + "url44": "", + "url45": "", + "url46": "", + "url47": "", + "url48": "", + "url49": "", + "url5": "", + "url50": "", + "url51": "", + "url52": "", + "url53": "", + "url54": "", + "url55": "", + "url56": "", + "url57": "", + "url58": "", + "url59": "", + "url6": "", + "url60": "", + "url61": "", + "url62": "", + "url63": "", + "url64": "", + "url65": "", + "url66": "", + "url67": "", + "url68": "", + "url69": "", + "url7": "", + "url70": "", + "url71": "", + "url72": "", + "url73": "", + "url74": "", + "url75": "", + "url76": "", + "url77": "", + "url78": "", + "url79": "", + "url8": "", + "url80": "", + "url81": "", + "url82": "", + "url83": "", + "url84": "", + "url85": "", + "url86": "", + "url87": "", + "url88": "", + "url89": "", + "url9": "", + "url90": "", + "url91": "", + "url92": "", + "url93": "", + "url94": "", + "url95": "", + "url96": "", + "url97": "", + "url98": "", + "url99": "", + "urls": "{ subscription_array }", + }, "throttle_protection": { "enable": True, "sleep_per_download_s": {"max": "28.4", "min": "13.8"}, @@ -943,7 +936,9 @@ def test_default_docker_config_and_subscriptions_resolved_resolution( ) assert len(default_subs) == 1 - resolved_yaml_as_json = yaml.safe_load(default_subs[0].resolved_yaml(resolution_level=ResolutionLevel.RESOLVE)) + resolved_yaml_as_json = yaml.safe_load( + default_subs[0].resolved_yaml(resolution_level=ResolutionLevel.RESOLVE) + ) assert resolved_yaml_as_json == { "chapters": { @@ -983,219 +978,217 @@ def test_default_docker_config_and_subscriptions_resolved_resolution( "preserve_mtime": False, "thumbnail_name": "{thumbnail_file_name}", }, - 'overrides': {'%bilateral_url': '{ %if( %and( enable_bilateral_scraping, ' - 'subscription_has_download_archive, ' - "%is_bilateral_url( $0 ) ), $0, '' ) }", - '%episode_ordering_': '{ %eq( %lower( ' - 'tv_show_by_date_episode_ordering ), $0 ) ' - '}', - '%is_bilateral_url': '{ %contains( $0, "youtube.com/playlist" ) ' - '}', - '%ordering_pair_eq': '{ %eq( [ tv_show_by_date_season_ordering, ' - 'tv_show_by_date_episode_ordering ], [ $0, ' - '$1 ] ) }', - '%season_ordering_': '{ %eq( %lower( ' - 'tv_show_by_date_season_ordering ), $0 ) }', - 'assert_not_collection': True, - 'avatar_uncropped_thumbnail_file_name': 'poster.jpg', - 'banner_uncropped_thumbnail_file_name': 'fanart.jpg', - 'enable_bilateral_scraping': True, - 'enable_resolution_assert': True, - 'enable_throttle_protection': True, - 'episode_content_rating': 'TV-14', - 'episode_date_standardized': '{ upload_date_standardized }', - 'episode_file_name': 's{ upload_year }.e{ %pad_zero( %int( ' - '%concat( upload_month, upload_day_padded, ' - 'upload_date_index_padded ) ), 6 ) } - { ' - 'title_sanitized_plex }', - 'episode_file_path': '{ %sanitize( %concat( "Season ", %string( ' - 'upload_year ) ) ) }/{ %sanitize( %concat( ' - '"s", %string( upload_year ), ".e", ' - '%string( %pad_zero( %int( %concat( ' - 'upload_month, upload_day_padded, ' - 'upload_date_index_padded ) ), 6 ) ), " - ' - '", %string( title_sanitized_plex ) ) ) }', - 'episode_number': '{ upload_month }{ upload_day_padded }{ ' - 'upload_date_index_padded }', - 'episode_number_and_padded_': '{ [ %concat( upload_month, ' - 'upload_day_padded, ' - 'upload_date_index_padded ), 6 ] ' - '}', - 'episode_number_padded': '{ %pad_zero( %int( %concat( ' - 'upload_month, upload_day_padded, ' - 'upload_date_index_padded ) ), 6 ) }', - 'episode_plot': '{ webpage_url }\n\n{ description }', - 'episode_title': '{ upload_date_standardized } - { title }', - 'episode_year': '{ %slice( upload_date_standardized, 0, 4 ) }', - 'file_title': '{ title_sanitized_plex }', - 'file_uid': '{ uid_sanitized_plex }', - 'include_sibling_metadata': False, - 'modified_webpage_url': '{ webpage_url }', - 'music_directory': '/music', - 'music_video_directory': '/music_videos', - 'resolution_assert': '{ %if( %and( enable_resolution_assert, ' - '%ne( height, 0 ), %not( ' - 'resolution_assert_is_ignored ) ), ' - '%assert( %gte( height, ' - 'resolution_assert_height_gte ), %concat( ' - '"Entry ", title, " downloaded at a low ' - 'resolution (", resolution_readable, "), ' - 'you\'ve probably been throttled. ", ' - '"Stopping further downloads, wait a few ' - 'hours and try again. ", "Disable using ' - 'the override variable ' - '`enable_resolution_assert: False`." ) ), ' - '"false is no-op" ) }', - 'resolution_assert_height_gte': 361, - '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_assert_print': True, - 'resolution_readable': '{ width }x{ height }', - 's01_name': '', - 's01_url': '', - 'season_directory_name': 'Season { upload_year }', - 'season_number': '{ upload_year }', - 'season_number_padded': '{ upload_year }', - 'season_poster_file_name': '{ %sanitize( %concat( "Season ", ' - '%string( upload_year ) ) ) ' - '}/Season{ upload_year }.jpg', - 'subscription_array': ['https://www.youtube.com/@novapbs'], - 'subscription_indent_1': 'Documentaries', - 'subscription_indent_2': 'TV-14', - 'subscription_value': 'https://www.youtube.com/@novapbs', - 'subscription_value_1': 'https://www.youtube.com/@novapbs', - 'thumbnail_file_name': '{ %concat( %string( %sanitize( %concat( ' - '"Season ", %string( upload_year ) ) ) ' - '), "/", %string( %sanitize( %concat( ' - '"s", %string( upload_year ), ".e", ' - '%string( %pad_zero( %int( %concat( ' - 'upload_month, upload_day_padded, ' - 'upload_date_index_padded ) ), 6 ) ), " ' - '- ", %string( title_sanitized_plex ) ) ' - ') ) ) }-thumb.jpg', - 'tv_show_by_date_episode_ordering': 'upload-month-day', - 'tv_show_by_date_ordering_pair_validation_': '{ [ %concat( ' - 'upload_month, ' - 'upload_day_padded, ' - 'upload_date_index_padded ' - '), 6 ] }', - 'tv_show_by_date_season_ordering': 'upload-year', - 'tv_show_content_rating': 'TV-14', - 'tv_show_content_rating_default': 'TV-14', - 'tv_show_date_range_type': 'upload_date', - 'tv_show_directory': output_directory, - 'tv_show_fanart_file_name': 'fanart.jpg', - 'tv_show_genre': 'Documentaries', - 'tv_show_genre_default': 'ytdl-sub', - 'tv_show_name': 'NOVA PBS', - 'tv_show_poster_file_name': 'poster.jpg', - 'url': 'https://www.youtube.com/@novapbs', - 'url10': '', - 'url100': '', - 'url11': '', - 'url12': '', - 'url13': '', - 'url14': '', - 'url15': '', - 'url16': '', - 'url17': '', - 'url18': '', - 'url19': '', - 'url2': '', - 'url20': '', - 'url21': '', - 'url22': '', - 'url23': '', - 'url24': '', - 'url25': '', - 'url26': '', - 'url27': '', - 'url28': '', - 'url29': '', - 'url3': '', - 'url30': '', - 'url31': '', - 'url32': '', - 'url33': '', - 'url34': '', - 'url35': '', - 'url36': '', - 'url37': '', - 'url38': '', - 'url39': '', - 'url4': '', - 'url40': '', - 'url41': '', - 'url42': '', - 'url43': '', - 'url44': '', - 'url45': '', - 'url46': '', - 'url47': '', - 'url48': '', - 'url49': '', - 'url5': '', - 'url50': '', - 'url51': '', - 'url52': '', - 'url53': '', - 'url54': '', - 'url55': '', - 'url56': '', - 'url57': '', - 'url58': '', - 'url59': '', - 'url6': '', - 'url60': '', - 'url61': '', - 'url62': '', - 'url63': '', - 'url64': '', - 'url65': '', - 'url66': '', - 'url67': '', - 'url68': '', - 'url69': '', - 'url7': '', - 'url70': '', - 'url71': '', - 'url72': '', - 'url73': '', - 'url74': '', - 'url75': '', - 'url76': '', - 'url77': '', - 'url78': '', - 'url79': '', - 'url8': '', - 'url80': '', - 'url81': '', - 'url82': '', - 'url83': '', - 'url84': '', - 'url85': '', - 'url86': '', - 'url87': '', - 'url88': '', - 'url89': '', - 'url9': '', - 'url90': '', - 'url91': '', - 'url92': '', - 'url93': '', - 'url94': '', - 'url95': '', - 'url96': '', - 'url97': '', - 'url98': '', - 'url99': '', - 'urls': ['https://www.youtube.com/@novapbs']}, + "overrides": { + "%bilateral_url": "{ %if( %and( enable_bilateral_scraping, " + "subscription_has_download_archive, " + "%is_bilateral_url( $0 ) ), $0, '' ) }", + "%episode_ordering_": "{ %eq( %lower( " "tv_show_by_date_episode_ordering ), $0 ) " "}", + "%is_bilateral_url": '{ %contains( $0, "youtube.com/playlist" ) ' "}", + "%ordering_pair_eq": "{ %eq( [ tv_show_by_date_season_ordering, " + "tv_show_by_date_episode_ordering ], [ $0, " + "$1 ] ) }", + "%season_ordering_": "{ %eq( %lower( " "tv_show_by_date_season_ordering ), $0 ) }", + "assert_not_collection": True, + "avatar_uncropped_thumbnail_file_name": "poster.jpg", + "banner_uncropped_thumbnail_file_name": "fanart.jpg", + "enable_bilateral_scraping": True, + "enable_resolution_assert": True, + "enable_throttle_protection": True, + "episode_content_rating": "TV-14", + "episode_date_standardized": "{ upload_date_standardized }", + "episode_file_name": "s{ upload_year }.e{ %pad_zero( %int( " + "%concat( upload_month, upload_day_padded, " + "upload_date_index_padded ) ), 6 ) } - { " + "title_sanitized_plex }", + "episode_file_path": '{ %sanitize( %concat( "Season ", %string( ' + "upload_year ) ) ) }/{ %sanitize( %concat( " + '"s", %string( upload_year ), ".e", ' + "%string( %pad_zero( %int( %concat( " + "upload_month, upload_day_padded, " + 'upload_date_index_padded ) ), 6 ) ), " - ' + '", %string( title_sanitized_plex ) ) ) }', + "episode_number": "{ upload_month }{ upload_day_padded }{ " + "upload_date_index_padded }", + "episode_number_and_padded_": "{ [ %concat( upload_month, " + "upload_day_padded, " + "upload_date_index_padded ), 6 ] " + "}", + "episode_number_padded": "{ %pad_zero( %int( %concat( " + "upload_month, upload_day_padded, " + "upload_date_index_padded ) ), 6 ) }", + "episode_plot": "{ webpage_url }\n\n{ description }", + "episode_title": "{ upload_date_standardized } - { title }", + "episode_year": "{ %slice( upload_date_standardized, 0, 4 ) }", + "file_title": "{ title_sanitized_plex }", + "file_uid": "{ uid_sanitized_plex }", + "include_sibling_metadata": False, + "modified_webpage_url": "{ webpage_url }", + "music_directory": "/music", + "music_video_directory": "/music_videos", + "resolution_assert": "{ %if( %and( enable_resolution_assert, " + "%ne( height, 0 ), %not( " + "resolution_assert_is_ignored ) ), " + "%assert( %gte( height, " + "resolution_assert_height_gte ), %concat( " + '"Entry ", title, " downloaded at a low ' + 'resolution (", resolution_readable, "), ' + "you've probably been throttled. \", " + '"Stopping further downloads, wait a few ' + 'hours and try again. ", "Disable using ' + "the override variable " + '`enable_resolution_assert: False`." ) ), ' + '"false is no-op" ) }', + "resolution_assert_height_gte": 361, + "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_assert_print": True, + "resolution_readable": "{ width }x{ height }", + "s01_name": "", + "s01_url": "", + "season_directory_name": "Season { upload_year }", + "season_number": "{ upload_year }", + "season_number_padded": "{ upload_year }", + "season_poster_file_name": '{ %sanitize( %concat( "Season ", ' + "%string( upload_year ) ) ) " + "}/Season{ upload_year }.jpg", + "subscription_array": ["https://www.youtube.com/@novapbs"], + "subscription_indent_1": "Documentaries", + "subscription_indent_2": "TV-14", + "subscription_value": "https://www.youtube.com/@novapbs", + "subscription_value_1": "https://www.youtube.com/@novapbs", + "thumbnail_file_name": "{ %concat( %string( %sanitize( %concat( " + '"Season ", %string( upload_year ) ) ) ' + '), "/", %string( %sanitize( %concat( ' + '"s", %string( upload_year ), ".e", ' + "%string( %pad_zero( %int( %concat( " + "upload_month, upload_day_padded, " + 'upload_date_index_padded ) ), 6 ) ), " ' + '- ", %string( title_sanitized_plex ) ) ' + ") ) ) }-thumb.jpg", + "tv_show_by_date_episode_ordering": "upload-month-day", + "tv_show_by_date_ordering_pair_validation_": "{ [ %concat( " + "upload_month, " + "upload_day_padded, " + "upload_date_index_padded " + "), 6 ] }", + "tv_show_by_date_season_ordering": "upload-year", + "tv_show_content_rating": "TV-14", + "tv_show_content_rating_default": "TV-14", + "tv_show_date_range_type": "upload_date", + "tv_show_directory": output_directory, + "tv_show_fanart_file_name": "fanart.jpg", + "tv_show_genre": "Documentaries", + "tv_show_genre_default": "ytdl-sub", + "tv_show_name": "NOVA PBS", + "tv_show_poster_file_name": "poster.jpg", + "url": "https://www.youtube.com/@novapbs", + "url10": "", + "url100": "", + "url11": "", + "url12": "", + "url13": "", + "url14": "", + "url15": "", + "url16": "", + "url17": "", + "url18": "", + "url19": "", + "url2": "", + "url20": "", + "url21": "", + "url22": "", + "url23": "", + "url24": "", + "url25": "", + "url26": "", + "url27": "", + "url28": "", + "url29": "", + "url3": "", + "url30": "", + "url31": "", + "url32": "", + "url33": "", + "url34": "", + "url35": "", + "url36": "", + "url37": "", + "url38": "", + "url39": "", + "url4": "", + "url40": "", + "url41": "", + "url42": "", + "url43": "", + "url44": "", + "url45": "", + "url46": "", + "url47": "", + "url48": "", + "url49": "", + "url5": "", + "url50": "", + "url51": "", + "url52": "", + "url53": "", + "url54": "", + "url55": "", + "url56": "", + "url57": "", + "url58": "", + "url59": "", + "url6": "", + "url60": "", + "url61": "", + "url62": "", + "url63": "", + "url64": "", + "url65": "", + "url66": "", + "url67": "", + "url68": "", + "url69": "", + "url7": "", + "url70": "", + "url71": "", + "url72": "", + "url73": "", + "url74": "", + "url75": "", + "url76": "", + "url77": "", + "url78": "", + "url79": "", + "url8": "", + "url80": "", + "url81": "", + "url82": "", + "url83": "", + "url84": "", + "url85": "", + "url86": "", + "url87": "", + "url88": "", + "url89": "", + "url9": "", + "url90": "", + "url91": "", + "url92": "", + "url93": "", + "url94": "", + "url95": "", + "url96": "", + "url97": "", + "url98": "", + "url99": "", + "urls": ["https://www.youtube.com/@novapbs"], + }, "throttle_protection": { "enable": True, "sleep_per_download_s": {"max": "28.4", "min": "13.8"}, @@ -1212,4 +1205,4 @@ def test_default_docker_config_and_subscriptions_resolved_resolution( "title": "{episode_title}", "year": "{episode_year}", }, - } \ No newline at end of file + } diff --git a/tests/unit/config/test_subscription_resolution.py b/tests/unit/config/test_subscription_resolution.py new file mode 100644 index 00000000..7eb75b6f --- /dev/null +++ b/tests/unit/config/test_subscription_resolution.py @@ -0,0 +1,32 @@ +from pathlib import Path + +import pytest +import yaml +from resources import expected_json + +from ytdl_sub.config.config_file import ConfigFile +from ytdl_sub.config.validators.variable_validation import ResolutionLevel +from ytdl_sub.subscriptions.subscription import Subscription + + +def _ensure_resolved_yaml(sub: Subscription, preset_type: str, resolution_level: int) -> None: + output_yaml = sub.resolved_yaml(resolution_level=resolution_level) + out = yaml.safe_load(output_yaml) + + expected_out_filename = ( + f"{preset_type}/inspect_sub_{ResolutionLevel.name_of(resolution_level)}.json" + ) + assert out == expected_json(out, expected_out_filename) + + +@pytest.mark.parametrize("resolution_level", ResolutionLevel.all()) +def test_resolution_original_tv_show( + resolution_level: int, config_file: ConfigFile, tv_show_subscriptions_path: Path +): + _ensure_resolved_yaml( + sub=Subscription.from_file_path( + config=config_file, subscription_path=tv_show_subscriptions_path + )[0], + preset_type="tv_show", + resolution_level=resolution_level, + )