from abc import ABC from functools import cache from functools import cached_property from typing import Dict from typing import Set from ytdl_sub.entries.script.custom_functions import CustomFunctions from ytdl_sub.entries.script.variable_types import ArrayMetadataVariable from ytdl_sub.entries.script.variable_types import IntegerMetadataVariable from ytdl_sub.entries.script.variable_types import IntegerVariable from ytdl_sub.entries.script.variable_types import MapMetadataVariable from ytdl_sub.entries.script.variable_types import MapVariable from ytdl_sub.entries.script.variable_types import MetadataVariable from ytdl_sub.entries.script.variable_types import StringDateMetadataVariable from ytdl_sub.entries.script.variable_types import StringDateVariable from ytdl_sub.entries.script.variable_types import StringMetadataVariable from ytdl_sub.entries.script.variable_types import StringVariable from ytdl_sub.entries.script.variable_types import Variable # 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 # pylint: disable=too-many-lines # pylint: disable=method-cache-max-size-none class MetadataVariableDefinitions(ABC): @cached_property def entry_metadata(self: "VariableDefinitions") -> MapVariable: """ :description: The entry's info.json """ return MapVariable(variable_name="entry_metadata", definition="{ {} }") @cached_property def playlist_metadata(self: "VariableDefinitions") -> MapMetadataVariable: """ :description: Metadata from the playlist (i.e. the parent metadata, like playlist -> entry) """ return MapMetadataVariable.from_entry( metadata_key="playlist_metadata", default={}, ) @cached_property def source_metadata(self: "VariableDefinitions") -> MapMetadataVariable: """ :description: Metadata from the source (i.e. the grandparent metadata, like channel -> playlist -> entry) """ return MapMetadataVariable.from_entry( metadata_key="source_metadata", default={}, ) @cached_property def sibling_metadata(self: "VariableDefinitions") -> ArrayMetadataVariable: """ :description: Metadata from any sibling entries that reside in the same playlist as this entry. """ return ArrayMetadataVariable.from_entry( metadata_key="sibling_metadata", default=[], ) class PlaylistVariableDefinitions(ABC): @cached_property def playlist_uid(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist unique ID if it exists, otherwise return the entry unique ID. """ return StringMetadataVariable.from_playlist( metadata_key="playlist_id", variable_name="playlist_uid", default=self.uid ) @cached_property def playlist_title(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: Name of its parent playlist/channel if it exists, otherwise returns its title. """ return StringMetadataVariable.from_entry(metadata_key="playlist_title", default=self.title) @cached_property def playlist_index(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: Playlist index if it exists, otherwise returns ``1``. Note that for channels/playlists, any change (i.e. adding or removing a video) will make this value change. Use with caution. """ return IntegerMetadataVariable.from_entry(metadata_key="playlist_index", default=1) @cached_property def playlist_index_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: Playlist index reversed via ``playlist_count - playlist_index + 1`` """ return IntegerVariable( variable_name="playlist_index_reversed", definition=f"""{{ %sub( {self.playlist_count.variable_name}, {self.playlist_index.variable_name}, -1 ) }}""", ) @cached_property def playlist_index_padded(self: "VariableDefinitions") -> StringVariable: """ :description: playlist_index padded two digits """ return self.playlist_index.to_padded_int(variable_name="playlist_index_padded", pad=2) @cached_property def playlist_index_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: playlist_index_reversed padded two digits """ return self.playlist_index_reversed.to_padded_int( variable_name="playlist_index_reversed_padded", pad=2, ) @cached_property def playlist_index_padded6(self: "VariableDefinitions") -> StringVariable: """ :description: playlist_index padded six digits. """ return self.playlist_index.to_padded_int(variable_name="playlist_index_padded6", pad=6) @cached_property def playlist_index_reversed_padded6(self: "VariableDefinitions") -> StringVariable: """ :description: playlist_index_reversed padded six digits. """ return self.playlist_index_reversed.to_padded_int( variable_name="playlist_index_reversed_padded6", pad=6 ) @cached_property def playlist_count(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: Playlist count if it exists, otherwise returns ``1``. Note that for channels/playlists, any change (i.e. adding or removing a video) will make this value change. Use with caution. """ return IntegerMetadataVariable.from_entry(metadata_key="playlist_count", default=1) @cached_property def playlist_description(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist description if it exists, otherwise returns the entry's description. """ return StringMetadataVariable.from_playlist( metadata_key=self.description.metadata_key, variable_name="playlist_description", default=self.description, ) @cached_property def playlist_webpage_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist webpage url if it exists. Otherwise, returns the entry webpage url. """ return StringMetadataVariable.from_playlist( metadata_key=self.webpage_url.metadata_key, variable_name="playlist_webpage_url", default=self.webpage_url, ) @cached_property def playlist_max_upload_date(self: "VariableDefinitions") -> StringDateVariable: """ :description: Max upload_date for all entries in this entry's playlist if it exists, otherwise returns ``upload_date`` """ return StringVariable( variable_name="playlist_max_upload_date", definition=f"""{{ %array_reduce( %if_passthrough( %extract_field_from_siblings('{self.upload_date.variable_name}'), [{self.upload_date.variable_name}] ), %max ) }}""", ).as_date_variable() @cached_property def playlist_max_upload_year(self: "VariableDefinitions") -> IntegerVariable: """ :description: Max upload_year for all entries in this entry's playlist if it exists, otherwise returns ``upload_year`` """ return self.playlist_max_upload_date.get_integer_date_metadata( date_metadata_key="year", variable_name="playlist_max_upload_year", ) @cached_property def playlist_max_upload_year_truncated(self: "VariableDefinitions") -> IntegerVariable: """ :description: The max playlist truncated upload year for all entries in this entry's playlist if it exists, otherwise returns ``upload_year_truncated``. """ return self.playlist_max_upload_date.get_integer_date_metadata( date_metadata_key="year_truncated", variable_name="playlist_max_upload_year_truncated" ) @cached_property def playlist_uploader_id(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist uploader id if it exists, otherwise returns the entry uploader ID. """ return StringMetadataVariable.from_entry( metadata_key="playlist_uploader_id", default=self.uploader_id, ) @cached_property def playlist_uploader(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist uploader if it exists, otherwise return the entry uploader. """ return StringMetadataVariable.from_playlist( metadata_key=self.uploader.metadata_key, variable_name="playlist_uploader", default=self.uploader, ) @cached_property def playlist_uploader_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The playlist uploader url if it exists, otherwise returns the playlist webpage_url. """ return StringMetadataVariable.from_playlist( metadata_key=self.uploader_url.metadata_key, variable_name="playlist_uploader_url", default=self.webpage_url, ) class SourceVariableDefinitions(ABC): @cached_property def source_title(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: Name of the source (i.e. channel with multiple playlists) if it exists, otherwise returns its playlist_title. """ return StringMetadataVariable.from_source( metadata_key=self.title.metadata_key, variable_name="source_title", default=self.playlist_title, ) @cached_property def source_uid(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source unique id if it exists, otherwise returns the playlist unique ID. """ return StringMetadataVariable.from_source( metadata_key=self.uid.metadata_key, variable_name="source_uid", default=self.playlist_uid, ) @cached_property def source_index(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: Source index if it exists, otherwise returns ``1``. It is recommended to not use this unless you know the source will never add new content (it is easy for this value to change). """ return IntegerMetadataVariable.from_playlist( metadata_key=self.playlist_index.metadata_key, variable_name="source_index", default=1, ) @cached_property def source_index_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The source index, padded two digits. """ return self.source_index.to_padded_int(variable_name="source_index_padded", pad=2) @cached_property def source_count(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: The source count if it exists, otherwise returns ``1``. """ return IntegerMetadataVariable.from_playlist( metadata_key=self.playlist_count.metadata_key, variable_name="source_count", default=1, ) @cached_property def source_webpage_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source webpage url if it exists, otherwise returns the playlist webpage url. """ return StringMetadataVariable.from_source( metadata_key=self.webpage_url.metadata_key, variable_name="source_webpage_url", default=self.playlist_webpage_url, ) @cached_property def source_description(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source description if it exists, otherwise returns the playlist description. """ return StringMetadataVariable.from_source( metadata_key=self.description.metadata_key, variable_name="source_description", default=self.playlist_description, ) @cached_property def source_uploader_id(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source uploader id if it exists, otherwise returns the playlist_uploader_id """ return StringMetadataVariable.from_source( metadata_key=self.uploader_id.metadata_key, variable_name="source_uploader_id", default=self.playlist_uploader_id, ) @cached_property def source_uploader(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source uploader if it exists, otherwise return the playlist_uploader """ return StringMetadataVariable.from_source( metadata_key=self.uploader.metadata_key, variable_name="source_uploader", default=self.playlist_uploader, ) @cached_property def source_uploader_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The source uploader url if it exists, otherwise returns the source webpage_url. """ return StringMetadataVariable.from_source( metadata_key=self.uploader_url.metadata_key, variable_name="source_uploader_url", default=self.source_webpage_url, ) class UploadDateVariableDefinitions(ABC): @cached_property def upload_date(self: "VariableDefinitions") -> StringDateMetadataVariable: """ :description: The entry’s uploaded date, in YYYYMMDD format. If not present, return today’s date. """ return StringDateMetadataVariable.from_entry( metadata_key="upload_date", default=self.epoch_date ).as_date_variable() @cached_property def upload_year(self: "VariableDefinitions") -> IntegerVariable: """ :description: The entry's upload year """ return self.upload_date.get_integer_date_metadata( date_metadata_key="year", variable_name="upload_year" ) @cached_property def upload_year_truncated(self: "VariableDefinitions") -> IntegerVariable: """ :description: The last two digits of the upload year, i.e. 22 in 2022 """ return self.upload_date.get_integer_date_metadata( date_metadata_key="year_truncated", variable_name="upload_year_truncated" ) @cached_property def upload_year_truncated_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload year truncated, but reversed using ``100 - {upload_year_truncated}``, i.e. 2022 returns ``100 - 22`` = ``78`` """ return self.upload_date.get_integer_date_metadata( date_metadata_key="year_truncated_reversed", variable_name="upload_year_truncated_reversed", ) @cached_property def upload_month_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload month, but reversed using ``13 - {upload_month}``, i.e. March returns ``10`` """ return self.upload_date.get_integer_date_metadata( date_metadata_key="month_reversed", variable_name="upload_month_reversed" ) @cached_property def upload_month_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload month, but padded. i.e. November returns "02" """ return self.upload_date.get_string_date_metadata( date_metadata_key="month_reversed_padded", variable_name="upload_month_reversed_padded" ) @cached_property def upload_month_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The entry's upload month padded to two digits, i.e. March returns "03" """ return self.upload_date.get_string_date_metadata( date_metadata_key="month_padded", variable_name="upload_month_padded" ) @cached_property def upload_day_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The entry's upload day padded to two digits, i.e. the fifth returns "05" """ return self.upload_date.get_string_date_metadata( date_metadata_key="day_padded", variable_name="upload_day_padded" ) @cached_property def upload_month(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload month as an integer (no padding). """ return self.upload_date.get_integer_date_metadata( date_metadata_key="month", variable_name="upload_month" ) @cached_property def upload_day(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day as an integer (no padding). """ return self.upload_date.get_integer_date_metadata( date_metadata_key="day", variable_name="upload_day" ) @cached_property def upload_day_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day, but reversed using ``{total_days_in_month} + 1 - {upload_day}``, i.e. August 8th would have upload_day_reversed of ``31 + 1 - 8`` = ``24`` """ return self.upload_date.get_integer_date_metadata( date_metadata_key="day_reversed", variable_name="upload_day_reversed" ) @cached_property def upload_day_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload day, but padded. i.e. August 30th returns "02". """ return self.upload_date.get_string_date_metadata( date_metadata_key="day_reversed_padded", variable_name="upload_day_reversed_padded" ) @cached_property def upload_day_of_year(self: "VariableDefinitions") -> IntegerVariable: """ :description: The day of the year, i.e. February 1st returns ``32`` """ return self.upload_date.get_integer_date_metadata( date_metadata_key="day_of_year", variable_name="upload_day_of_year" ) @cached_property def upload_day_of_year_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The upload day of year, but padded i.e. February 1st returns "032" """ return self.upload_date.get_string_date_metadata( date_metadata_key="day_of_year_padded", variable_name="upload_day_of_year_padded" ) @cached_property def upload_day_of_year_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day, but reversed using ``{total_days_in_year} + 1 - {upload_day}``, i.e. February 2nd would have upload_day_of_year_reversed of ``365 + 1 - 32`` = ``334`` """ return self.upload_date.get_integer_date_metadata( date_metadata_key="day_of_year_reversed", variable_name="upload_day_of_year_reversed" ) @cached_property def upload_day_of_year_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload day of year, but padded i.e. December 31st returns "001" """ return self.upload_date.get_string_date_metadata( date_metadata_key="day_of_year_reversed_padded", variable_name="upload_day_of_year_reversed_padded", ) @cached_property def upload_date_standardized(self: "VariableDefinitions") -> StringVariable: """ :description: The uploaded date formatted as YYYY-MM-DD """ return self.upload_date.get_string_date_metadata( date_metadata_key="date_standardized", variable_name="upload_date_standardized" ) class ReleaseDateVariableDefinitions(ABC): @cached_property def release_date(self: "VariableDefinitions") -> StringDateMetadataVariable: """ :description: The entry’s release date, in YYYYMMDD format. If not present, return the upload date. """ return StringMetadataVariable.from_entry( metadata_key="release_date", default=self.upload_date ).as_date_variable() @cached_property def release_year(self: "VariableDefinitions") -> IntegerVariable: """ :description: The entry's upload year """ return self.release_date.get_integer_date_metadata( date_metadata_key="year", variable_name="release_year" ) @cached_property def release_year_truncated(self: "VariableDefinitions") -> IntegerVariable: """ :description: The last two digits of the upload year, i.e. 22 in 2022 """ return self.release_date.get_integer_date_metadata( date_metadata_key="year_truncated", variable_name="release_year_truncated" ) @cached_property def release_year_truncated_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload year truncated, but reversed using ``100 - {release_year_truncated}``, i.e. 2022 returns ``100 - 22`` = ``78`` """ return self.release_date.get_integer_date_metadata( date_metadata_key="year_truncated_reversed", variable_name="release_year_truncated_reversed", ) @cached_property def release_month_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload month, but reversed using ``13 - {release_month}``, i.e. March returns ``10`` """ return self.release_date.get_integer_date_metadata( date_metadata_key="month_reversed", variable_name="release_month_reversed" ) @cached_property def release_month_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload month, but padded. i.e. November returns "02" """ return self.release_date.get_string_date_metadata( date_metadata_key="month_reversed_padded", variable_name="release_month_reversed_padded" ) @cached_property def release_month_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The entry's upload month padded to two digits, i.e. March returns "03" """ return self.release_date.get_string_date_metadata( date_metadata_key="month_padded", variable_name="release_month_padded" ) @cached_property def release_day_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The entry's upload day padded to two digits, i.e. the fifth returns "05" """ return self.release_date.get_string_date_metadata( date_metadata_key="day_padded", variable_name="release_day_padded" ) @cached_property def release_month(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload month as an integer (no padding). """ return self.release_date.get_integer_date_metadata( date_metadata_key="month", variable_name="release_month" ) @cached_property def release_day(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day as an integer (no padding). """ return self.release_date.get_integer_date_metadata( date_metadata_key="day", variable_name="release_day" ) @cached_property def release_day_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day, but reversed using ``{total_days_in_month} + 1 - {release_day}``, i.e. August 8th would have release_day_reversed of ``31 + 1 - 8`` = ``24`` """ return self.release_date.get_integer_date_metadata( date_metadata_key="day_reversed", variable_name="release_day_reversed" ) @cached_property def release_day_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload day, but padded. i.e. August 30th returns "02". """ return self.release_date.get_string_date_metadata( date_metadata_key="day_reversed_padded", variable_name="release_day_reversed_padded" ) @cached_property def release_day_of_year(self: "VariableDefinitions") -> IntegerVariable: """ :description: The day of the year, i.e. February 1st returns ``32`` """ return self.release_date.get_integer_date_metadata( date_metadata_key="day_of_year", variable_name="release_day_of_year" ) @cached_property def release_day_of_year_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The upload day of year, but padded i.e. February 1st returns "032" """ return self.release_date.get_string_date_metadata( date_metadata_key="day_of_year_padded", variable_name="release_day_of_year_padded" ) @cached_property def release_day_of_year_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: The upload day, but reversed using ``{total_days_in_year} + 1 - {release_day}``, i.e. February 2nd would have release_day_of_year_reversed of ``365 + 1 - 32`` = ``334`` """ return self.release_date.get_integer_date_metadata( date_metadata_key="day_of_year_reversed", variable_name="release_day_of_year_reversed" ) @cached_property def release_day_of_year_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The reversed upload day of year, but padded i.e. December 31st returns "001" """ return self.release_date.get_string_date_metadata( date_metadata_key="day_of_year_reversed_padded", variable_name="release_day_of_year_reversed_padded", ) @cached_property def release_date_standardized(self: "VariableDefinitions") -> StringVariable: """ :description: The uploaded date formatted as YYYY-MM-DD """ return self.release_date.get_string_date_metadata( date_metadata_key="date_standardized", variable_name="release_date_standardized" ) class YtdlSubVariableDefinitions(ABC): @cached_property def ytdl_sub_input_url(self: "VariableDefinitions") -> StringVariable: """ :description: The input URL used in ytdl-sub to create this entry. """ return StringVariable(variable_name="ytdl_sub_input_url", definition="{ %string('') }") @cached_property def ytdl_sub_input_url_index(self: "VariableDefinitions") -> IntegerVariable: """ :description: The index of the input URL as defined in the subscription, top-most being the 0th index. """ # init as -1 so if prior downloaded entries are known when they do not have this value # in their .info.json return IntegerVariable(variable_name="ytdl_sub_input_url_index", definition="{ %int(-1) }") @cached_property def ytdl_sub_input_url_count(self: "VariableDefinitions") -> IntegerVariable: """ :description: The total number of input URLs as defined in the subscription. """ return IntegerVariable(variable_name="ytdl_sub_input_url_count", definition="{ %int(0) }") @cached_property def download_index(self: "VariableDefinitions") -> IntegerVariable: """ :description: The i'th entry downloaded. NOTE that this is fetched dynamically from the download archive. """ return IntegerVariable(variable_name="download_index", definition="{ %int(1) }") @cached_property def download_index_padded6(self: "VariableDefinitions") -> StringVariable: """ :description: The download_index padded six digits """ return self.download_index.to_padded_int(variable_name="download_index_padded6", pad=6) @cached_property def upload_date_index(self: "VariableDefinitions") -> IntegerVariable: """ :description: The i'th entry downloaded with this upload date. """ return IntegerVariable(variable_name="upload_date_index", definition="{ %int(1) }") @cached_property def upload_date_index_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The upload_date_index padded two digits """ return self.upload_date_index.to_padded_int(variable_name="upload_date_index_padded", pad=2) @cached_property def upload_date_index_reversed(self: "VariableDefinitions") -> IntegerVariable: """ :description: 100 - upload_date_index """ return IntegerVariable( variable_name="upload_date_index_reversed", definition=f"{{%sub(100, {self.upload_date_index.variable_name})}}", ) @cached_property def upload_date_index_reversed_padded(self: "VariableDefinitions") -> StringVariable: """ :description: The upload_date_index padded two digits """ return self.upload_date_index_reversed.to_padded_int( variable_name="upload_date_index_reversed_padded", pad=2 ) class EntryVariableDefinitions(ABC): @cached_property def uid(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The entry's unique ID """ return StringMetadataVariable.from_entry(metadata_key="id", variable_name="uid") @cached_property def duration(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: The duration of the entry in seconds if it exists. Defaults to zero otherwise. """ return IntegerMetadataVariable.from_entry(metadata_key="duration", default=0) @cached_property def uid_sanitized_plex(self: "VariableDefinitions") -> StringVariable: """ :description: The sanitized uid with additional sanitizing for Plex. Replaces numbers with fixed-width numbers so Plex does not recognize them as season or episode numbers. """ return self.uid.to_sanitized_plex(variable_name="uid_sanitized_plex") @cached_property def ie_key(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The ie_key, used in legacy yt-dlp things as the 'info-extractor key'. If it does not exist, return ``extractor_key`` """ return StringMetadataVariable.from_entry(metadata_key="ie_key", default=self.extractor_key) @cached_property def extractor_key(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The yt-dlp extractor key """ return StringMetadataVariable.from_entry(metadata_key="extractor_key") @cached_property def extractor(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The yt-dlp extractor name """ return StringMetadataVariable.from_entry(metadata_key="extractor") @cached_property def epoch(self: "VariableDefinitions") -> IntegerMetadataVariable: """ :description: The unix epoch of when the metadata was scraped by yt-dlp. """ return IntegerMetadataVariable.from_entry(metadata_key="epoch") @cached_property def epoch_date(self: "VariableDefinitions") -> StringVariable: """ :description: The epoch's date, in YYYYMMDD format. """ return StringVariable( variable_name="epoch_date", definition=f"{{%datetime_strftime({self.epoch.variable_name}, '%Y%m%d')}}", ) @cached_property def epoch_hour(self: "VariableDefinitions") -> StringVariable: """ :description: The epoch's hour """ return StringVariable( variable_name="epoch_hour", definition=f"{{%datetime_strftime({self.epoch.variable_name}, '%H')}}", ) @cached_property def title(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The title of the entry. If a title does not exist, returns its unique ID. """ return StringMetadataVariable.from_entry(metadata_key="title", default=self.uid) @cached_property def title_sanitized_plex(self: "VariableDefinitions") -> StringVariable: """ :description: The sanitized title with additional sanitizing for Plex. It replaces numbers with fixed-width numbers so Plex does not recognize them as season or episode numbers. """ return self.title.to_sanitized_plex(variable_name="title_sanitized_plex") @cached_property def webpage_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The url to the webpage. """ return StringMetadataVariable.from_entry(metadata_key="webpage_url") @cached_property def info_json_ext(self: "VariableDefinitions") -> StringVariable: """ :description: The "info.json" extension """ return StringVariable( variable_name="info_json_ext", definition="info.json", ) @cached_property def description(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The description if it exists. Otherwise, returns an emtpy string. """ return StringMetadataVariable.from_entry( metadata_key="description", default="", ) @cached_property def uploader_id(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The uploader id if it exists, otherwise return the unique ID. """ return StringMetadataVariable.from_entry( metadata_key="uploader_id", default=self.uid, ) @cached_property def uploader(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The uploader if it exists, otherwise return the uploader ID. """ return StringMetadataVariable.from_entry( metadata_key="uploader", default=self.uploader_id, ) @cached_property def uploader_url(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The uploader url if it exists, otherwise returns the webpage_url. """ return StringMetadataVariable.from_entry( metadata_key="uploader_url", default=self.webpage_url, ) @cached_property def creator(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The creator name if it exists, otherwise returns the channel. """ return StringMetadataVariable.from_entry( metadata_key="creator", default=self.channel, ) @cached_property def channel(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The channel name if it exists, otherwise returns the uploader. """ return StringMetadataVariable.from_entry( metadata_key="channel", default=self.uploader, ) @cached_property def channel_id(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The channel id if it exists, otherwise returns the entry uploader ID. """ return StringMetadataVariable.from_entry( metadata_key="channel_id", default=self.uploader_id, ) @cached_property def ext(self: "VariableDefinitions") -> StringMetadataVariable: """ :description: The downloaded entry's file extension """ return StringMetadataVariable.from_entry(metadata_key="ext") @cached_property def thumbnail_ext(self: "VariableDefinitions") -> StringVariable: """ :description: The download entry's thumbnail extension. Will always return 'jpg'. Until there is a need to support other image types, we always convert to jpg. """ return StringVariable( variable_name="thumbnail_ext", definition="jpg", ) @cached_property def comments(self: "VariableDefinitions") -> ArrayMetadataVariable: """ :description: Comments if they are requested """ return ArrayMetadataVariable( metadata_key="comments", variable_name="comments", definition="{ [] }" ) @cached_property def chapters(self: "VariableDefinitions") -> ArrayMetadataVariable: """ :description: Chapters if they exist """ return ArrayMetadataVariable( metadata_key="chapters", variable_name="chapters", definition="{ [] }" ) @cached_property def sponsorblock_chapters(self: "VariableDefinitions") -> ArrayMetadataVariable: """ :description: Sponsorblock Chapters if they are requested and exist """ return ArrayMetadataVariable( metadata_key="sponsorblock_chapters", variable_name="sponsorblock_chapters", definition="{ [] }", ) @cached_property def requested_subtitles(self: "VariableDefinitions") -> MapMetadataVariable: """ :description: Subtitles if they are requested and exist """ return MapMetadataVariable( metadata_key="requested_subtitles", variable_name="requested_subtitles", definition="{ {} }", ) class VariableDefinitions( EntryVariableDefinitions, MetadataVariableDefinitions, PlaylistVariableDefinitions, SourceVariableDefinitions, UploadDateVariableDefinitions, ReleaseDateVariableDefinitions, YtdlSubVariableDefinitions, ): @cache def scripts(self) -> Dict[str, str]: """ Returns all variables and their scripts in dict form """ return { var.variable_name: var.definition for var in [ getattr(self, attr) for attr in dir(self) if isinstance(getattr(self, attr), Variable) ] } @cache def injected_variables(self) -> Set[MetadataVariable]: """ Returns variables that get injected in the download-stage """ return { self.download_index, self.upload_date_index, self.comments, self.requested_subtitles, self.chapters, self.sponsorblock_chapters, self.ytdl_sub_input_url, self.ytdl_sub_input_url_index, self.ytdl_sub_input_url_count, } @cache def required_entry_variables(self) -> Set[MetadataVariable]: """ Returns variables that the entry requires to exist """ return { self.uid, self.extractor_key, self.epoch, self.webpage_url, self.ext, } @cache def default_entry_variables(self) -> Set[MetadataVariable]: """ Returns variables that reside on the entry that may or may not exist, but have defaults """ return { self.title, self.extractor, self.description, self.ie_key, self.uploader_id, self.uploader, self.uploader_url, self.upload_date, self.release_date, self.channel, self.creator, self.channel_id, self.duration, self.playlist_index, self.playlist_count, self.playlist_uid, self.playlist_title, self.playlist_uploader_id, } @cache def unresolvable_static_variables(self) -> Set[Variable]: """ Returns variables that are not static (i.e. depend on runtime) """ return { VARIABLES.entry_metadata, } | self.injected_variables() # Singletons to use externally VARIABLES: VariableDefinitions = VariableDefinitions() VARIABLE_SCRIPTS: Dict[str, str] = VARIABLES.scripts() UNRESOLVED_VARIABLES: Set[str] = { var.variable_name for var in VARIABLES.unresolvable_static_variables() } CustomFunctions.register()