From 98bd2df4478aaef7b40c45e4e81d86e6636389ea Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 5 Dec 2023 00:16:00 -0800 Subject: [PATCH] ugh --- .../entries/script/function_scripts.py | 99 +++++++++++++++++++ src/ytdl_sub/entries/script/kwargs.py | 82 --------------- .../{variables.py => variable_definitions.py} | 13 ++- .../entries/script/variable_scripts.py | 91 +++++++++-------- .../script/functions/string_functions.py | 8 -- .../script/functions/test_string_functions.py | 12 --- 6 files changed, 160 insertions(+), 145 deletions(-) delete mode 100644 src/ytdl_sub/entries/script/kwargs.py rename src/ytdl_sub/entries/script/{variables.py => variable_definitions.py} (98%) diff --git a/src/ytdl_sub/entries/script/function_scripts.py b/src/ytdl_sub/entries/script/function_scripts.py index 8b137891..de0990bc 100644 --- a/src/ytdl_sub/entries/script/function_scripts.py +++ b/src/ytdl_sub/entries/script/function_scripts.py @@ -1 +1,100 @@ +from yt_dlp.utils import sanitize_filename +from ytdl_sub.script.types.map import Map +from ytdl_sub.script.types.resolvable import Integer +from ytdl_sub.script.types.resolvable import String +from ytdl_sub.script.utils.exceptions import RuntimeException + + +def _pad(num: int, width: int): + return str(num).zfill(width) + + +_days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + +class CustomFunctions: + @staticmethod + def sanitize(string: String) -> String: + return String(sanitize_filename(string.value)) + + @staticmethod + def sanitize_plex_episode(string: String) -> String: + out = CustomFunctions.sanitize(string).value + for char in out: + match char: + case "0": + out += "0" + case "1": + out += "1" + case "2": + out += "2" + case "3": + out += "3" + case "4": + out += "4" + case "5": + out += "5" + case "6": + out += "6" + case "7": + out += "7" + case "8": + out += "8" + case "9": + out += "9" + case _: + out += char + return String(out) + + @staticmethod + def date_metadata(yyyymmdd: String) -> Map: + date_str = yyyymmdd.value + if not (date_str.isnumeric() and len(date_str) == 6): + raise RuntimeException( + f"Expected input of date_metadata to be YYYYMMDD, but received {date_str}" + ) + + year: int = int(date_str[:4]) + month_padded: str = date_str[4:6] + day_padded: str = date_str[6:8] + + month: int = int(month_padded) + day: int = int(day_padded) + year_truncated: int = int(str(year)[-2:]) + + day_of_year: int = sum(_days_in_month[:month]) + day + total_days_in_month: int = _days_in_month[month] + total_days_in_year: int = 365 + if year % 4 == 0: + total_days_in_year += 1 + if month == 2: + total_days_in_month += 1 + if month > 2: + day_of_year += 1 + + day_of_year_reversed: int = total_days_in_year + 1 - day_of_year + month_reversed: int = 13 - month + day_reversed: int = total_days_in_month + 1 - day + + return Map( + { + String("date"): yyyymmdd, + String("date_standardized"): String(f"{year}-{month_padded}-{day_padded}"), + String("year"): Integer(year), + String("month"): Integer(month), + String("day"): Integer(day), + String("year_truncated"): String(year_truncated), + String("month_padded"): String(month_padded), + String("day_padded"): String(day_padded), + String("year_truncated_reversed"): Integer(100 - year_truncated), + String("month_reversed"): Integer(month_reversed), + String("month_reversed_padded"): String(_pad(month_reversed, width=2)), + String("day_reversed"): Integer(day_reversed), + String("day_reversed_padded"): String(_pad(day_reversed, width=2)), + String("day_of_year"): Integer(day_of_year), + String("day_of_year_padded"): String(_pad(day_of_year, width=3)), + String("day_of_year_reversed"): Integer(day_of_year_reversed), + String("day_of_year_reversed_padded"): String(_pad(day_of_year_reversed, width=3)), + } + ) diff --git a/src/ytdl_sub/entries/script/kwargs.py b/src/ytdl_sub/entries/script/kwargs.py deleted file mode 100644 index fe48804d..00000000 --- a/src/ytdl_sub/entries/script/kwargs.py +++ /dev/null @@ -1,82 +0,0 @@ -from dataclasses import dataclass -from typing import List -from typing import Optional - - -class KwargKeys: - keys: List[str] = [] - backend_keys: List[str] = [] - - -@dataclass(frozen=True) -class KwargKey: - entry_key: str - variable_name: str - - @classmethod - def init(cls, entry_key: str, variable_name: Optional[str] = None) -> "KwargKey": - return cls(entry_key=entry_key, variable_name=variable_name if variable_name else entry_key) - - -def _(entry_key: str, variable_name: Optional[str] = None, backend: bool = False) -> KwargKey: - if backend: - assert entry_key not in KwargKeys.backend_keys - KwargKeys.backend_keys.append(entry_key) - else: - assert entry_key not in KwargKeys.keys - KwargKeys.keys.append(entry_key) - return KwargKey( - entry_key=entry_key, variable_name=variable_name if variable_name is not None else entry_key - ) - - -SOURCE_ENTRY = _("source_entry", backend=True) -SOURCE_INDEX = _("source_index") -SOURCE_COUNT = _("source_count") -SOURCE_TITLE = _("source_title") -SOURCE_UID = _("source_uid") -SOURCE_DESCRIPTION = _("source_description") -SOURCE_WEBPAGE_URL = _("source_webpage_url") -SOURCE_UPLOADER = _("source_uploader") -SOURCE_UPLOADER_ID = _("source_uploader_id") -SOURCE_UPLOADER_URL = _("source_uploader_url") - -PLAYLIST_ENTRY = _("playlist_entry", backend=True) -PLAYLIST_WEBPAGE_URL = _("playlist_webpage_url") -PLAYLIST_INDEX = _("playlist_index") -PLAYLIST_COUNT = _("playlist_count") -PLAYLIST_MAX_UPLOAD_YEAR = _("playlist_max_upload_year") -PLAYLIST_MAX_UPLOAD_YEAR_TRUNCATED = _("playlist_max_upload_year_truncated") -PLAYLIST_TITLE = _("playlist_title") -PLAYLIST_DESCRIPTION = _("playlist_description") -PLAYLIST_UID = _("playlist_uid") -PLAYLIST_UPLOADER = _("playlist_uploader") -PLAYLIST_UPLOADER_ID = _("playlist_uploader_id") -PLAYLIST_UPLOADER_URL = _("playlist_uploader_url") - -COLLECTION_URL = _("collection_url", backend=True) -DOWNLOAD_INDEX = _("download_index", backend=True) -UPLOAD_DATE_INDEX = _("upload_date_index", backend=True) -REQUESTED_SUBTITLES = _("requested_subtitles", backend=True) -CHAPTERS = _("chapters", backend=True) -YTDL_SUB_CUSTOM_CHAPTERS = _("ytdl_sub_custom_chapters", backend=True) -YTDL_SUB_REGEX_SOURCE_VARS = _("ytdl_sub_regex_source_vars", backend=True) -SPONSORBLOCK_CHAPTERS = _("sponsorblock_chapters", backend=True) -SPLIT_BY_CHAPTERS_PARENT_ENTRY = _("split_by_chapters_parent_entry", backend=True) -COMMENTS = _("comments", backend=True) -UID = _(entry_key="id", variable_name="uid") -EXTRACTOR = _("extractor") -IE_KEY = _("ie_key") -EPOCH = _("epoch") -CHANNEL = _("channel") -CHANNEL_ID = _("channel_id") -CREATOR = _("creator") -EXT = _("ext") -TITLE = _("title") -DESCRIPTION = _("description") -WEBPAGE_URL = _("webpage_url") -RELEASE_DATE = _("release_date") -UPLOAD_DATE = _("upload_date") -UPLOADER = _("uploader") -UPLOADER_ID = _("uploader_id") -UPLOADER_URL = _("uploader_url") diff --git a/src/ytdl_sub/entries/script/variables.py b/src/ytdl_sub/entries/script/variable_definitions.py similarity index 98% rename from src/ytdl_sub/entries/script/variables.py rename to src/ytdl_sub/entries/script/variable_definitions.py index c3b5151a..cd70b322 100644 --- a/src/ytdl_sub/entries/script/variables.py +++ b/src/ytdl_sub/entries/script/variable_definitions.py @@ -1,10 +1,21 @@ -from ytdl_sub.entries.script.kwargs import KwargKey +from dataclasses import dataclass +from typing import Optional # This file contains mixins to a BaseEntry subclass. Ignore pylint's "no kwargs member" suggestion # pylint: disable=no-member # pylint: disable=too-many-public-methods +@dataclass(frozen=True) +class KwargKey: + entry_key: str + variable_name: str + + @classmethod + def init(cls, entry_key: str, variable_name: Optional[str] = None) -> "KwargKey": + return cls(entry_key=entry_key, variable_name=variable_name if variable_name else entry_key) + + class Variables: @property def uid(self) -> KwargKey: diff --git a/src/ytdl_sub/entries/script/variable_scripts.py b/src/ytdl_sub/entries/script/variable_scripts.py index 36372562..45290419 100644 --- a/src/ytdl_sub/entries/script/variable_scripts.py +++ b/src/ytdl_sub/entries/script/variable_scripts.py @@ -1,8 +1,8 @@ from typing import Dict from typing import Optional -from ytdl_sub.entries.script.kwargs import KwargKey -from ytdl_sub.entries.script.variables import Variables +from ytdl_sub.entries.script.variable_definitions import KwargKey +from ytdl_sub.entries.script.variable_definitions import Variables ENTRY_MAP_VARIABLE = "entry" @@ -32,8 +32,11 @@ def sanitized(key: KwargKey) -> str: def sanitized_plex(key: KwargKey) -> str: - return f"""{{ - }}""" + return f"{{%sanitize_plex_episode({key.variable_name})}}" + + +def date_metadata(date_key: KwargKey, metadata_key: str) -> str: + return f"{{%map_get(%date_metadata({date_key.variable_name}), '{metadata_key}')}}" # singleton to produce variable keys @@ -62,10 +65,12 @@ ENTRY_DEFAULT_VARIABLES: Dict[KwargKey, str] = { } ENTRY_DERIVED_VARIABLES: Dict[KwargKey, str] = { - # UID_SANITIZED - # UID_SANITIZED_PLEX - # title_sanitized - # title_sanitized_plex + v.uid_sanitized: sanitized(v.uid), + v.uid_sanitized_plex: sanitized_plex(v.uid_sanitized_plex), + v.title_sanitized: sanitized(v.title), + v.title_sanitized_plex: sanitized_plex(v.title), + v.upload_date: entry_get(v.upload_date, "TODO CURRENT TIME"), + v.release_date: entry_get(v.release_date, "TODO CURRENT TIME"), # epoch_date # epoch_hour # creator @@ -86,43 +91,45 @@ ENTRY_ARCHIVE_VARIABLES: Dict[KwargKey, str] = { } ENTRY_UPLOAD_DATE_VARIABLES: Dict[KwargKey, str] = { - # upload_date - # upload_year - # upload_year_truncated - # upload_year_truncated_reversed - # upload_month_reversed - # upload_month_reversed_padded - # upload_month_padded - # upload_day_padded - # upload_month - # upload_day - # upload_day_reversed - # upload_day_reversed_padded - # upload_day_of_year - # upload_day_of_year_padded - # upload_day_of_year_reversed - # upload_day_of_year_reversed_padded - # upload_date_standardized + v.upload_year: date_metadata(v.upload_date, "year"), + v.upload_year_truncated: date_metadata(v.upload_date, "year_truncated"), + v.upload_year_truncated_reversed: date_metadata(v.upload_date, "year_truncated_reversed"), + v.upload_month_reversed: date_metadata(v.upload_date, "month_reversed"), + v.upload_month_reversed_padded: date_metadata(v.upload_date, "month_reversed_padded"), + v.upload_month_padded: date_metadata(v.upload_date, "month_padded"), + v.upload_day_padded: date_metadata(v.upload_date, "day_padded"), + v.upload_month: date_metadata(v.upload_date, "month"), + v.upload_day: date_metadata(v.upload_date, "day"), + v.upload_day_reversed: date_metadata(v.upload_date, "day_reversed"), + v.upload_day_reversed_padded: date_metadata(v.upload_date, "day_reversed_padded"), + v.upload_day_of_year: date_metadata(v.upload_date, "day_of_year"), + v.upload_day_of_year_padded: date_metadata(v.upload_date, "day_of_year_padded"), + v.upload_day_of_year_reversed: date_metadata(v.upload_date, "day_of_year_reversed"), + v.upload_day_of_year_reversed_padded: date_metadata( + v.upload_date, "day_of_year_reversed_padded" + ), + v.upload_date_standardized: date_metadata(v.upload_date, "date_standardized"), } ENTRY_RELEASE_DATE_VARIABLES: Dict[KwargKey, str] = { - # release_date - # release_year - # release_year_truncated - # release_year_truncated_reversed - # release_month_reversed - # release_month_reversed_padded - # release_month_padded - # release_day_padded - # release_month - # release_day - # release_day_reversed - # release_day_reversed_padded - # release_day_of_year - # release_day_of_year_padded - # release_day_of_year_reversed - # release_day_of_year_reversed_padded - # release_date_standardized + v.release_year: date_metadata(v.release_date, "year"), + v.release_year_truncated: date_metadata(v.release_date, "year_truncated"), + v.release_year_truncated_reversed: date_metadata(v.release_date, "year_truncated_reversed"), + v.release_month_reversed: date_metadata(v.release_date, "month_reversed"), + v.release_month_reversed_padded: date_metadata(v.release_date, "month_reversed_padded"), + v.release_month_padded: date_metadata(v.release_date, "month_padded"), + v.release_day_padded: date_metadata(v.release_date, "day_padded"), + v.release_month: date_metadata(v.release_date, "month"), + v.release_day: date_metadata(v.release_date, "day"), + v.release_day_reversed: date_metadata(v.release_date, "day_reversed"), + v.release_day_reversed_padded: date_metadata(v.release_date, "day_reversed_padded"), + v.release_day_of_year: date_metadata(v.release_date, "day_of_year"), + v.release_day_of_year_padded: date_metadata(v.release_date, "day_of_year_padded"), + v.release_day_of_year_reversed: date_metadata(v.release_date, "day_of_year_reversed"), + v.release_day_of_year_reversed_padded: date_metadata( + v.release_date, "day_of_year_reversed_padded" + ), + v.release_date_standardized: date_metadata(v.release_date, "date_standardized"), } ENTRY_SOURCE_VARIABLES: Dict[KwargKey, str] = { diff --git a/src/ytdl_sub/script/functions/string_functions.py b/src/ytdl_sub/script/functions/string_functions.py index 104db663..8e731674 100644 --- a/src/ytdl_sub/script/functions/string_functions.py +++ b/src/ytdl_sub/script/functions/string_functions.py @@ -85,11 +85,3 @@ class StringFunctions: length=length, char=String("0"), ) - - @staticmethod - def sanitize(string: String) -> String: - """ - Returns a sanitized string that is safe to use in file paths (Windows, OSX, Unix). - """ - # TODO: Move this out of script before making it a separate library - return String(sanitize_filename(string.value)) diff --git a/tests/unit/script/functions/test_string_functions.py b/tests/unit/script/functions/test_string_functions.py index 82adf75a..5f0c226e 100644 --- a/tests/unit/script/functions/test_string_functions.py +++ b/tests/unit/script/functions/test_string_functions.py @@ -98,15 +98,3 @@ class TestNumericFunctions: def test_pad_zero(self, values: str, expected_output: str): output = single_variable_output(f"{{%pad_zero({values})}}") assert output == expected_output - - @pytest.mark.parametrize( - "values, expected_output", - [ - ("'2012'", "2012"), - ("'$?ahh'", "$?ahh"), - ], - ) - def test_sanitize(self, values: str, expected_output: str): - # TODO: Move this out once it is its own library - output = single_variable_output(f"{{%sanitize({values})}}") - assert output == expected_output