From 1b2c44933148d45eeb790bb0e3cb6ebe88dc37a9 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 19 Dec 2023 23:56:45 -0800 Subject: [PATCH] override gen --- src/ytdl_sub/config/overrides.py | 8 +- .../entries/script/variable_definitions.py | 715 +++++++++--------- .../entries/variables/override_variables.py | 18 +- tools/docgen/entry_variables.py | 29 +- tools/docgen/functions.py | 3 +- tools/docgen/override_variables.py | 20 + tools/docgen/utils.py | 13 +- 7 files changed, 445 insertions(+), 361 deletions(-) create mode 100644 tools/docgen/override_variables.py diff --git a/src/ytdl_sub/config/overrides.py b/src/ytdl_sub/config/overrides.py index 8f6903d2..fc633a52 100644 --- a/src/ytdl_sub/config/overrides.py +++ b/src/ytdl_sub/config/overrides.py @@ -8,7 +8,7 @@ import mergedeep from ytdl_sub.entries.entry import Entry from ytdl_sub.entries.script.variable_definitions import VARIABLES from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME -from ytdl_sub.entries.variables.override_variables import OverrideVariables +from ytdl_sub.entries.variables.override_variables import OverrideHelpers from ytdl_sub.script.parser import parse from ytdl_sub.script.script import Script from ytdl_sub.script.utils.exceptions import ScriptVariableNotResolved @@ -89,7 +89,7 @@ class Overrides(DictFormatterValidator, Scriptable): """ Ensures the variable name does not collide with any entry variables or built-in functions. """ - if not OverrideVariables.is_valid_name(name): + if not OverrideHelpers.is_valid_name(name): override_type = "function" if name.startswith("%") else "variable" raise self._validation_exception( f"Override {override_type} with name {name} is invalid. Names must be" @@ -97,14 +97,14 @@ class Overrides(DictFormatterValidator, Scriptable): exception_class=InvalidVariableNameException, ) - if OverrideVariables.is_entry_variable_name(name): + if OverrideHelpers.is_entry_variable_name(name): raise self._validation_exception( f"Override variable with name {name} cannot be used since it is a" " built-in ytdl-sub entry variable name.", exception_class=InvalidVariableNameException, ) - if OverrideVariables.is_function_name(name): + if OverrideHelpers.is_function_name(name): raise self._validation_exception( f"Override function definition with name {name} cannot be used since it is" " a built-in ytdl-sub function name.", diff --git a/src/ytdl_sub/entries/script/variable_definitions.py b/src/ytdl_sub/entries/script/variable_definitions.py index 3432a012..e711a214 100644 --- a/src/ytdl_sub/entries/script/variable_definitions.py +++ b/src/ytdl_sub/entries/script/variable_definitions.py @@ -1,3 +1,4 @@ +from abc import ABC from dataclasses import dataclass # This file contains mixins to a BaseEntry subclass. Ignore pylint's "no kwargs member" suggestion @@ -35,225 +36,53 @@ class SiblingMetadata(MetadataVariable): pass -class VariableDefinitions: +class MetadataVariableDefinitions(ABC): @property - def entry_metadata(self) -> Metadata: + def entry_metadata(self: "VariableDefinitions") -> Metadata: """ The entry's info.json """ return Metadata("entry_metadata") @property - def playlist_metadata(self) -> RelativeMetadata: + def playlist_metadata(self: "VariableDefinitions") -> RelativeMetadata: """ Metadata from the playlist (i.e. the parent metadata, like playlist -> entry) """ return RelativeMetadata("playlist_metadata", metadata_key="playlist_metadata") @property - def source_metadata(self) -> RelativeMetadata: + def source_metadata(self: "VariableDefinitions") -> RelativeMetadata: """ Metadata from the source (i.e. the grandparent metadata, like channel -> playlist -> entry) """ return RelativeMetadata("source_metadata", metadata_key="source_metadata") @property - def sibling_metadata(self) -> SiblingMetadata: + def sibling_metadata(self: "VariableDefinitions") -> SiblingMetadata: """ Metadata from any sibling entries that reside in the same playlist as this entry. """ return SiblingMetadata("sibling_metadata", metadata_key="sibling_metadata") - @property - def uid(self) -> MetadataVariable: - """ - The entry's unique ID - """ - return MetadataVariable(metadata_key="id", variable_name="uid") +class PlaylistVariableDefinitions(ABC): @property - def duration(self) -> MetadataVariable: - """ - The duration of the entry in seconds - """ - return MetadataVariable("duration", metadata_key="duration") - - @property - def uid_sanitized_plex(self) -> Variable: - """ - 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 Variable("uid_sanitized_plex") - - @property - def ie_key(self) -> MetadataVariable: - """ - The ie_key, used in legacy yt-dlp things as the 'info-extractor key' - """ - return MetadataVariable(metadata_key="ie_key", variable_name="ie_key") - - @property - def extractor_key(self) -> MetadataVariable: - """ - The yt-dlp extractor key - """ - return MetadataVariable(metadata_key="extractor_key", variable_name="extractor_key") - - @property - def extractor(self) -> MetadataVariable: - """ - The yt-dlp extractor name - """ - return MetadataVariable(variable_name="extractor", metadata_key="extractor") - - @property - def epoch(self) -> MetadataVariable: - """ - The unix epoch of when the metadata was scraped by yt-dlp. - """ - return MetadataVariable(metadata_key="epoch", variable_name="epoch") - - @property - def epoch_date(self) -> Variable: - """ - The epoch's date, in YYYYMMDD format. - """ - return Variable("epoch_date") - - @property - def epoch_hour(self) -> Variable: - """ - The epoch's hour - """ - return Variable("epoch_hour") - - @property - def title(self) -> MetadataVariable: - """ - The title of the entry. If a title does not exist, returns its unique ID. - """ - return MetadataVariable(variable_name="title", metadata_key="title") - - @property - def title_sanitized_plex(self) -> Variable: - """ - 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 Variable("title_sanitized_plex") - - @property - def webpage_url(self) -> MetadataVariable: - """ - The url to the webpage. - """ - return MetadataVariable(metadata_key="webpage_url", variable_name="webpage_url") - - @property - def info_json_ext(self) -> Variable: - """ - The "info.json" extension - """ - return Variable("info_json_ext") - - @property - def description(self) -> MetadataVariable: - """ - The description if it exists. Otherwise, returns an emtpy string. - """ - return MetadataVariable(variable_name="description", metadata_key="description") - - @property - def uploader_id(self) -> MetadataVariable: - """ - The uploader id if it exists, otherwise return the unique ID. - """ - return MetadataVariable(variable_name="uploader_id", metadata_key="uploader_id") - - @property - def uploader(self) -> MetadataVariable: - """ - The uploader if it exists, otherwise return the uploader ID. - """ - return MetadataVariable(variable_name="uploader", metadata_key="uploader") - - @property - def uploader_url(self) -> MetadataVariable: - """ - The uploader url if it exists, otherwise returns the webpage_url. - """ - return MetadataVariable("uploader_url", metadata_key="uploader_url") - - @property - def source_title(self) -> MetadataVariable: - """ - Name of the source (i.e. channel with multiple playlists) if it exists, otherwise - returns its playlist_title. - """ - return MetadataVariable("source_title", metadata_key=self.title.metadata_key) - - @property - def source_uid(self) -> MetadataVariable: - """ - The source unique id if it exists, otherwise returns the playlist unique ID. - """ - return MetadataVariable("source_uid", metadata_key=self.uid.metadata_key) - - @property - def source_index(self) -> MetadataVariable: - """ - 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 MetadataVariable("source_index", metadata_key=self.playlist_index.metadata_key) - - @property - def source_index_padded(self) -> Variable: - """ - The source index, padded. - """ - return Variable("source_index_padded") - - @property - def source_count(self) -> MetadataVariable: - """ - The source count if it exists, otherwise returns the playlist count. - """ - return MetadataVariable("source_count", metadata_key=self.playlist_count.metadata_key) - - @property - def source_webpage_url(self) -> MetadataVariable: - """ - The source webpage url if it exists, otherwise returns the playlist webpage url. - """ - return MetadataVariable("source_webpage_url", metadata_key=self.webpage_url.metadata_key) - - @property - def source_description(self) -> MetadataVariable: - """ - The source description if it exists, otherwise returns the playlist description. - """ - return MetadataVariable("source_description", metadata_key=self.description.metadata_key) - - @property - def playlist_uid(self) -> MetadataVariable: + def playlist_uid(self: "VariableDefinitions") -> MetadataVariable: """ The playlist unique ID if it exists, otherwise return the entry unique ID. """ return MetadataVariable(variable_name="playlist_uid", metadata_key="playlist_id") @property - def playlist_title(self) -> MetadataVariable: + def playlist_title(self: "VariableDefinitions") -> MetadataVariable: """ Name of its parent playlist/channel if it exists, otherwise returns its title. """ return MetadataVariable(variable_name="playlist_title", metadata_key="playlist_title") @property - def playlist_index(self) -> MetadataVariable: + def playlist_index(self: "VariableDefinitions") -> MetadataVariable: """ Playlist index if it exists, otherwise returns ``1``. @@ -263,42 +92,42 @@ class VariableDefinitions: return MetadataVariable(metadata_key="playlist_index", variable_name="playlist_index") @property - def playlist_index_reversed(self) -> Variable: + def playlist_index_reversed(self: "VariableDefinitions") -> Variable: """ Playlist index reversed via ``playlist_count - playlist_index + 1`` """ return Variable("playlist_index_reversed") @property - def playlist_index_padded(self) -> Variable: + def playlist_index_padded(self: "VariableDefinitions") -> Variable: """ playlist_index padded two digits """ return Variable("playlist_index_padded") @property - def playlist_index_reversed_padded(self) -> Variable: + def playlist_index_reversed_padded(self: "VariableDefinitions") -> Variable: """ playlist_index_reversed padded two digits """ return Variable("playlist_index_reversed_padded") @property - def playlist_index_padded6(self) -> Variable: + def playlist_index_padded6(self: "VariableDefinitions") -> Variable: """ playlist_index padded six digits. """ return Variable("playlist_index_padded6") @property - def playlist_index_reversed_padded6(self) -> Variable: + def playlist_index_reversed_padded6(self: "VariableDefinitions") -> Variable: """ playlist_index_reversed padded six digits. """ return Variable("playlist_index_reversed_padded6") @property - def playlist_count(self) -> MetadataVariable: + def playlist_count(self: "VariableDefinitions") -> MetadataVariable: """ Playlist count if it exists, otherwise returns ``1``. @@ -308,7 +137,7 @@ class VariableDefinitions: return MetadataVariable(variable_name="playlist_count", metadata_key="playlist_count") @property - def playlist_description(self) -> MetadataVariable: + def playlist_description(self: "VariableDefinitions") -> MetadataVariable: """ The playlist description if it exists, otherwise returns the entry's description. """ @@ -317,7 +146,7 @@ class VariableDefinitions: ) @property - def playlist_webpage_url(self) -> MetadataVariable: + def playlist_webpage_url(self: "VariableDefinitions") -> MetadataVariable: """ The playlist webpage url if it exists. Otherwise, returns the entry webpage url. """ @@ -326,7 +155,7 @@ class VariableDefinitions: ) @property - def playlist_max_upload_date(self) -> Variable: + def playlist_max_upload_date(self: "VariableDefinitions") -> Variable: """ Max upload_date for all entries in this entry's playlist if it exists, otherwise returns ``upload_date`` @@ -334,7 +163,7 @@ class VariableDefinitions: return Variable("playlist_max_upload_date") @property - def playlist_max_upload_year(self) -> Variable: + def playlist_max_upload_year(self: "VariableDefinitions") -> Variable: """ Max upload_year for all entries in this entry's playlist if it exists, otherwise returns ``upload_year`` @@ -343,7 +172,7 @@ class VariableDefinitions: return Variable("playlist_max_upload_year") @property - def playlist_max_upload_year_truncated(self) -> Variable: + def playlist_max_upload_year_truncated(self: "VariableDefinitions") -> Variable: """ The max playlist truncated upload year for all entries in this entry's playlist if it exists, otherwise returns ``upload_year_truncated``. @@ -351,21 +180,21 @@ class VariableDefinitions: return Variable("playlist_max_upload_year_truncated") @property - def playlist_uploader_id(self) -> MetadataVariable: + def playlist_uploader_id(self: "VariableDefinitions") -> MetadataVariable: """ The playlist uploader id if it exists, otherwise returns the entry uploader ID. """ return MetadataVariable("playlist_uploader_id", metadata_key="playlist_uploader_id") @property - def playlist_uploader(self) -> MetadataVariable: + def playlist_uploader(self: "VariableDefinitions") -> MetadataVariable: """ The playlist uploader if it exists, otherwise return the entry uploader. """ return MetadataVariable("playlist_uploader", metadata_key=self.uploader.metadata_key) @property - def playlist_uploader_url(self) -> MetadataVariable: + def playlist_uploader_url(self: "VariableDefinitions") -> MetadataVariable: """ The playlist uploader url if it exists, otherwise returns the playlist webpage_url. """ @@ -373,164 +202,107 @@ class VariableDefinitions: "playlist_uploader_url", metadata_key=self.uploader_url.metadata_key ) + +class SourceVariableDefinitions(ABC): @property - def source_uploader_id(self) -> MetadataVariable: + def source_title(self: "VariableDefinitions") -> MetadataVariable: + """ + Name of the source (i.e. channel with multiple playlists) if it exists, otherwise + returns its playlist_title. + """ + return MetadataVariable("source_title", metadata_key=self.title.metadata_key) + + @property + def source_uid(self: "VariableDefinitions") -> MetadataVariable: + """ + The source unique id if it exists, otherwise returns the playlist unique ID. + """ + return MetadataVariable("source_uid", metadata_key=self.uid.metadata_key) + + @property + def source_index(self: "VariableDefinitions") -> MetadataVariable: + """ + 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 MetadataVariable("source_index", metadata_key=self.playlist_index.metadata_key) + + @property + def source_index_padded(self: "VariableDefinitions") -> Variable: + """ + The source index, padded. + """ + return Variable("source_index_padded") + + @property + def source_count(self: "VariableDefinitions") -> MetadataVariable: + """ + The source count if it exists, otherwise returns the playlist count. + """ + return MetadataVariable("source_count", metadata_key=self.playlist_count.metadata_key) + + @property + def source_webpage_url(self: "VariableDefinitions") -> MetadataVariable: + """ + The source webpage url if it exists, otherwise returns the playlist webpage url. + """ + return MetadataVariable("source_webpage_url", metadata_key=self.webpage_url.metadata_key) + + @property + def source_description(self: "VariableDefinitions") -> MetadataVariable: + """ + The source description if it exists, otherwise returns the playlist description. + """ + return MetadataVariable("source_description", metadata_key=self.description.metadata_key) + + @property + def source_uploader_id(self: "VariableDefinitions") -> MetadataVariable: """ The source uploader id if it exists, otherwise returns the playlist_uploader_id """ return MetadataVariable("source_uploader_id", metadata_key=self.uploader_id.metadata_key) @property - def source_uploader(self) -> MetadataVariable: + def source_uploader(self: "VariableDefinitions") -> MetadataVariable: """ The source uploader if it exists, otherwise return the playlist_uploader """ return MetadataVariable("source_uploader", metadata_key=self.uploader.metadata_key) @property - def source_uploader_url(self) -> MetadataVariable: + def source_uploader_url(self: "VariableDefinitions") -> MetadataVariable: """ The source uploader url if it exists, otherwise returns the source webpage_url. """ return MetadataVariable("source_uploader_url", metadata_key=self.uploader_url.metadata_key) - @property - def creator(self) -> MetadataVariable: - """ - The creator name if it exists, otherwise returns the channel. - """ - return MetadataVariable(variable_name="creator", metadata_key="creator") +class UploadDateVariableDefinitions(ABC): @property - def channel(self) -> MetadataVariable: - """ - The channel name if it exists, otherwise returns the uploader. - """ - return MetadataVariable(variable_name="channel", metadata_key="channel") - - @property - def channel_id(self) -> MetadataVariable: - """ - The channel id if it exists, otherwise returns the entry uploader ID. - """ - return MetadataVariable(variable_name="channel_id", metadata_key="channel_id") - - @property - def ext(self) -> MetadataVariable: - """ - The downloaded entry's file extension - """ - return MetadataVariable(variable_name="ext", metadata_key="ext") - - @property - def thumbnail_ext(self) -> Variable: - """ - 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 Variable("thumbnail_ext") - - @property - def comments(self) -> MetadataVariable: - """ - Comments if they are requested - """ - return MetadataVariable("comments", "comments") - - @property - def chapters(self) -> MetadataVariable: - """ - Chapters if they exist - """ - return MetadataVariable("chapters", "chapters") - - @property - def sponsorblock_chapters(self) -> MetadataVariable: - """ - Sponsorblock Chapters if they are requested and exist - """ - return MetadataVariable("sponsorblock_chapters", "sponsorblock_chapters") - - @property - def requested_subtitles(self) -> MetadataVariable: - """ - Subtitles if they are requested and exist - """ - return MetadataVariable("requested_subtitles", "requested_subtitles") - - @property - def ytdl_sub_input_url(self) -> Variable: - """ - The input URL used in ytdl-sub to create this entry. - """ - return Variable("ytdl_sub_input_url") - - @property - def download_index(self) -> Variable: - """ - The i'th entry downloaded. NOTE that this is fetched dynamically from the download - archive. - """ - return Variable(variable_name="download_index") - - @property - def download_index_padded6(self) -> Variable: - """ - The download_index padded six digits - """ - return Variable("download_index_padded6") - - @property - def upload_date_index(self) -> Variable: - """ - The i'th entry downloaded with this upload date. - """ - return Variable(variable_name="upload_date_index") - - @property - def upload_date_index_padded(self) -> Variable: - """ - The upload_date_index padded two digits - """ - return Variable("upload_date_index_padded") - - @property - def upload_date_index_reversed(self) -> Variable: - """ - 100 - upload_date_index - """ - return Variable("upload_date_index_reversed") - - @property - def upload_date_index_reversed_padded(self) -> Variable: - """ - The upload_date_index padded two digits - """ - return Variable("upload_date_index_reversed_padded") - - @property - def upload_date(self) -> MetadataVariable: + def upload_date(self: "VariableDefinitions") -> MetadataVariable: """ The entry’s uploaded date, in YYYYMMDD format. If not present, return today’s date. """ return MetadataVariable(variable_name="upload_date", metadata_key="upload_date") @property - def upload_year(self) -> Variable: + def upload_year(self: "VariableDefinitions") -> Variable: """ The entry's upload year """ return Variable("upload_year") @property - def upload_year_truncated(self) -> Variable: + def upload_year_truncated(self: "VariableDefinitions") -> Variable: """ The last two digits of the upload year, i.e. 22 in 2022 """ return Variable("upload_year_truncated") @property - def upload_year_truncated_reversed(self) -> Variable: + def upload_year_truncated_reversed(self: "VariableDefinitions") -> Variable: """ The upload year truncated, but reversed using ``100 - {upload_year_truncated}``, i.e. 2022 returns ``100 - 22`` = ``78`` @@ -538,49 +310,49 @@ class VariableDefinitions: return Variable("upload_year_truncated_reversed") @property - def upload_month_reversed(self) -> Variable: + def upload_month_reversed(self: "VariableDefinitions") -> Variable: """ The upload month, but reversed using ``13 - {upload_month}``, i.e. March returns ``10`` """ return Variable("upload_month_reversed") @property - def upload_month_reversed_padded(self) -> Variable: + def upload_month_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed upload month, but padded. i.e. November returns "02" """ return Variable("upload_month_reversed_padded") @property - def upload_month_padded(self) -> Variable: + def upload_month_padded(self: "VariableDefinitions") -> Variable: """ The entry's upload month padded to two digits, i.e. March returns "03" """ return Variable("upload_month_padded") @property - def upload_day_padded(self) -> Variable: + def upload_day_padded(self: "VariableDefinitions") -> Variable: """ The entry's upload day padded to two digits, i.e. the fifth returns "05" """ return Variable("upload_day_padded") @property - def upload_month(self) -> Variable: + def upload_month(self: "VariableDefinitions") -> Variable: """ The upload month as an integer (no padding). """ return Variable("upload_month") @property - def upload_day(self) -> Variable: + def upload_day(self: "VariableDefinitions") -> Variable: """ The upload day as an integer (no padding). """ return Variable("upload_day") @property - def upload_day_reversed(self) -> Variable: + def upload_day_reversed(self: "VariableDefinitions") -> Variable: """ 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`` @@ -588,28 +360,28 @@ class VariableDefinitions: return Variable("upload_day_reversed") @property - def upload_day_reversed_padded(self) -> Variable: + def upload_day_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed upload day, but padded. i.e. August 30th returns "02". """ return Variable("upload_day_reversed_padded") @property - def upload_day_of_year(self) -> Variable: + def upload_day_of_year(self: "VariableDefinitions") -> Variable: """ The day of the year, i.e. February 1st returns ``32`` """ return Variable("upload_day_of_year") @property - def upload_day_of_year_padded(self) -> Variable: + def upload_day_of_year_padded(self: "VariableDefinitions") -> Variable: """ The upload day of year, but padded i.e. February 1st returns "032" """ return Variable("upload_day_of_year_padded") @property - def upload_day_of_year_reversed(self) -> Variable: + def upload_day_of_year_reversed(self: "VariableDefinitions") -> Variable: """ 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`` @@ -617,42 +389,44 @@ class VariableDefinitions: return Variable("upload_day_of_year_reversed") @property - def upload_day_of_year_reversed_padded(self) -> Variable: + def upload_day_of_year_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed upload day of year, but padded i.e. December 31st returns "001" """ return Variable("upload_day_of_year_reversed_padded") @property - def upload_date_standardized(self) -> Variable: + def upload_date_standardized(self: "VariableDefinitions") -> Variable: """ The uploaded date formatted as YYYY-MM-DD """ return Variable("upload_date_standardized") + +class ReleaseDateVariableDefinitions(ABC): @property - def release_date(self) -> MetadataVariable: + def release_date(self: "VariableDefinitions") -> MetadataVariable: """ The entry’s release date, in YYYYMMDD format. If not present, return the upload date. """ return MetadataVariable(variable_name="release_date", metadata_key="release_date") @property - def release_year(self) -> Variable: + def release_year(self: "VariableDefinitions") -> Variable: """ The entry's release year """ return Variable("release_year") @property - def release_year_truncated(self) -> Variable: + def release_year_truncated(self: "VariableDefinitions") -> Variable: """ The last two digits of the release year, i.e. 22 in 2022 """ return Variable("release_year_truncated") @property - def release_year_truncated_reversed(self) -> Variable: + def release_year_truncated_reversed(self: "VariableDefinitions") -> Variable: """ The release year truncated, but reversed using ``100 - {release_year_truncated}``, i.e. 2022 returns ``100 - 22`` = ``78`` @@ -660,7 +434,7 @@ class VariableDefinitions: return Variable("release_year_truncated_reversed") @property - def release_month_reversed(self) -> Variable: + def release_month_reversed(self: "VariableDefinitions") -> Variable: """ The release month, but reversed using ``13 - {release_month}``, i.e. March returns ``10`` @@ -668,42 +442,42 @@ class VariableDefinitions: return Variable("release_month_reversed") @property - def release_month_reversed_padded(self) -> Variable: + def release_month_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed release month, but padded. i.e. November returns "02" """ return Variable("release_month_reversed_padded") @property - def release_month_padded(self) -> Variable: + def release_month_padded(self: "VariableDefinitions") -> Variable: """ The entry's release month padded to two digits, i.e. March returns "03" """ return Variable("release_month_padded") @property - def release_day_padded(self) -> Variable: + def release_day_padded(self: "VariableDefinitions") -> Variable: """ The entry's release day padded to two digits, i.e. the fifth returns "05" """ return Variable("release_day_padded") @property - def release_month(self) -> Variable: + def release_month(self: "VariableDefinitions") -> Variable: """ The release month as an integer (no padding). """ return Variable("release_month") @property - def release_day(self) -> Variable: + def release_day(self: "VariableDefinitions") -> Variable: """ The release day as an integer (no padding). """ return Variable("release_day") @property - def release_day_reversed(self) -> Variable: + def release_day_reversed(self: "VariableDefinitions") -> Variable: """ The release 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`` @@ -711,28 +485,28 @@ class VariableDefinitions: return Variable("release_day_reversed") @property - def release_day_reversed_padded(self) -> Variable: + def release_day_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed release day, but padded. i.e. August 30th returns "02". """ return Variable("release_day_reversed_padded") @property - def release_day_of_year(self) -> Variable: + def release_day_of_year(self: "VariableDefinitions") -> Variable: """ The day of the year, i.e. February 1st returns ``32`` """ return Variable("release_day_of_year") @property - def release_day_of_year_padded(self) -> Variable: + def release_day_of_year_padded(self: "VariableDefinitions") -> Variable: """ The release day of year, but padded i.e. February 1st returns "032" """ return Variable("release_day_of_year_padded") @property - def release_day_of_year_reversed(self) -> Variable: + def release_day_of_year_reversed(self: "VariableDefinitions") -> Variable: """ The release 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`` @@ -740,19 +514,270 @@ class VariableDefinitions: return Variable("release_day_of_year_reversed") @property - def release_day_of_year_reversed_padded(self) -> Variable: + def release_day_of_year_reversed_padded(self: "VariableDefinitions") -> Variable: """ The reversed release day of year, but padded i.e. December 31st returns "001" """ return Variable("release_day_of_year_reversed_padded") @property - def release_date_standardized(self) -> Variable: + def release_date_standardized(self: "VariableDefinitions") -> Variable: """ The release date formatted as YYYY-MM-DD """ return Variable("release_date_standardized") +class YtdlSubVariableDefinitions(ABC): + @property + def ytdl_sub_input_url(self: "VariableDefinitions") -> Variable: + """ + The input URL used in ytdl-sub to create this entry. + """ + return Variable("ytdl_sub_input_url") + + @property + def download_index(self: "VariableDefinitions") -> Variable: + """ + The i'th entry downloaded. NOTE that this is fetched dynamically from the download + archive. + """ + return Variable(variable_name="download_index") + + @property + def download_index_padded6(self: "VariableDefinitions") -> Variable: + """ + The download_index padded six digits + """ + return Variable("download_index_padded6") + + @property + def upload_date_index(self: "VariableDefinitions") -> Variable: + """ + The i'th entry downloaded with this upload date. + """ + return Variable(variable_name="upload_date_index") + + @property + def upload_date_index_padded(self: "VariableDefinitions") -> Variable: + """ + The upload_date_index padded two digits + """ + return Variable("upload_date_index_padded") + + @property + def upload_date_index_reversed(self: "VariableDefinitions") -> Variable: + """ + 100 - upload_date_index + """ + return Variable("upload_date_index_reversed") + + @property + def upload_date_index_reversed_padded(self: "VariableDefinitions") -> Variable: + """ + The upload_date_index padded two digits + """ + return Variable("upload_date_index_reversed_padded") + + +class EntryVariableDefinitions(ABC): + @property + def uid(self: "VariableDefinitions") -> MetadataVariable: + """ + The entry's unique ID + """ + return MetadataVariable(metadata_key="id", variable_name="uid") + + @property + def duration(self: "VariableDefinitions") -> MetadataVariable: + """ + The duration of the entry in seconds + """ + return MetadataVariable("duration", metadata_key="duration") + + @property + def uid_sanitized_plex(self: "VariableDefinitions") -> Variable: + """ + 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 Variable("uid_sanitized_plex") + + @property + def ie_key(self: "VariableDefinitions") -> MetadataVariable: + """ + The ie_key, used in legacy yt-dlp things as the 'info-extractor key' + """ + return MetadataVariable(metadata_key="ie_key", variable_name="ie_key") + + @property + def extractor_key(self: "VariableDefinitions") -> MetadataVariable: + """ + The yt-dlp extractor key + """ + return MetadataVariable(metadata_key="extractor_key", variable_name="extractor_key") + + @property + def extractor(self: "VariableDefinitions") -> MetadataVariable: + """ + The yt-dlp extractor name + """ + return MetadataVariable(variable_name="extractor", metadata_key="extractor") + + @property + def epoch(self: "VariableDefinitions") -> MetadataVariable: + """ + The unix epoch of when the metadata was scraped by yt-dlp. + """ + return MetadataVariable(metadata_key="epoch", variable_name="epoch") + + @property + def epoch_date(self: "VariableDefinitions") -> Variable: + """ + The epoch's date, in YYYYMMDD format. + """ + return Variable("epoch_date") + + @property + def epoch_hour(self: "VariableDefinitions") -> Variable: + """ + The epoch's hour + """ + return Variable("epoch_hour") + + @property + def title(self: "VariableDefinitions") -> MetadataVariable: + """ + The title of the entry. If a title does not exist, returns its unique ID. + """ + return MetadataVariable(variable_name="title", metadata_key="title") + + @property + def title_sanitized_plex(self: "VariableDefinitions") -> Variable: + """ + 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 Variable("title_sanitized_plex") + + @property + def webpage_url(self: "VariableDefinitions") -> MetadataVariable: + """ + The url to the webpage. + """ + return MetadataVariable(metadata_key="webpage_url", variable_name="webpage_url") + + @property + def info_json_ext(self: "VariableDefinitions") -> Variable: + """ + The "info.json" extension + """ + return Variable("info_json_ext") + + @property + def description(self: "VariableDefinitions") -> MetadataVariable: + """ + The description if it exists. Otherwise, returns an emtpy string. + """ + return MetadataVariable(variable_name="description", metadata_key="description") + + @property + def uploader_id(self: "VariableDefinitions") -> MetadataVariable: + """ + The uploader id if it exists, otherwise return the unique ID. + """ + return MetadataVariable(variable_name="uploader_id", metadata_key="uploader_id") + + @property + def uploader(self: "VariableDefinitions") -> MetadataVariable: + """ + The uploader if it exists, otherwise return the uploader ID. + """ + return MetadataVariable(variable_name="uploader", metadata_key="uploader") + + @property + def uploader_url(self: "VariableDefinitions") -> MetadataVariable: + """ + The uploader url if it exists, otherwise returns the webpage_url. + """ + return MetadataVariable("uploader_url", metadata_key="uploader_url") + + @property + def creator(self: "VariableDefinitions") -> MetadataVariable: + """ + The creator name if it exists, otherwise returns the channel. + """ + return MetadataVariable(variable_name="creator", metadata_key="creator") + + @property + def channel(self: "VariableDefinitions") -> MetadataVariable: + """ + The channel name if it exists, otherwise returns the uploader. + """ + return MetadataVariable(variable_name="channel", metadata_key="channel") + + @property + def channel_id(self: "VariableDefinitions") -> MetadataVariable: + """ + The channel id if it exists, otherwise returns the entry uploader ID. + """ + return MetadataVariable(variable_name="channel_id", metadata_key="channel_id") + + @property + def ext(self: "VariableDefinitions") -> MetadataVariable: + """ + The downloaded entry's file extension + """ + return MetadataVariable(variable_name="ext", metadata_key="ext") + + @property + def thumbnail_ext(self: "VariableDefinitions") -> Variable: + """ + 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 Variable("thumbnail_ext") + + @property + def comments(self: "VariableDefinitions") -> MetadataVariable: + """ + Comments if they are requested + """ + return MetadataVariable("comments", "comments") + + @property + def chapters(self: "VariableDefinitions") -> MetadataVariable: + """ + Chapters if they exist + """ + return MetadataVariable("chapters", "chapters") + + @property + def sponsorblock_chapters(self: "VariableDefinitions") -> MetadataVariable: + """ + Sponsorblock Chapters if they are requested and exist + """ + return MetadataVariable("sponsorblock_chapters", "sponsorblock_chapters") + + @property + def requested_subtitles(self: "VariableDefinitions") -> MetadataVariable: + """ + Subtitles if they are requested and exist + """ + return MetadataVariable("requested_subtitles", "requested_subtitles") + + +class VariableDefinitions( + EntryVariableDefinitions, + MetadataVariableDefinitions, + PlaylistVariableDefinitions, + SourceVariableDefinitions, + UploadDateVariableDefinitions, + ReleaseDateVariableDefinitions, + YtdlSubVariableDefinitions, +): + pass + + # Singleton to use externally VARIABLES: VariableDefinitions = VariableDefinitions() diff --git a/src/ytdl_sub/entries/variables/override_variables.py b/src/ytdl_sub/entries/variables/override_variables.py index a798dd18..04af57b0 100644 --- a/src/ytdl_sub/entries/variables/override_variables.py +++ b/src/ytdl_sub/entries/variables/override_variables.py @@ -10,15 +10,15 @@ SUBSCRIPTION_ARRAY = "subscription_array" class OverrideVariables: - @classmethod - def subscription_name(cls) -> str: + @staticmethod + def subscription_name() -> str: """ Name of the subscription """ return SUBSCRIPTION_NAME - @classmethod - def subscription_value(cls) -> str: + @staticmethod + def subscription_value() -> str: """ For subscriptions in the form of @@ -30,8 +30,8 @@ class OverrideVariables: """ return SUBSCRIPTION_VALUE - @classmethod - def subscription_indent_i(cls, index: int) -> str: + @staticmethod + def subscription_indent_i(index: int) -> str: """ For subscriptions in the form of @@ -46,8 +46,8 @@ class OverrideVariables: """ return f"subscription_indent_{index + 1}" - @classmethod - def subscription_value_i(cls, index: int) -> str: + @staticmethod + def subscription_value_i(index: int) -> str: """ For subscriptions in the form of @@ -63,6 +63,8 @@ class OverrideVariables: """ return f"subscription_value_{index + 1}" + +class OverrideHelpers: @classmethod def is_entry_variable_name(cls, name: str) -> bool: """ diff --git a/tools/docgen/entry_variables.py b/tools/docgen/entry_variables.py index 8b8012cf..315be0fc 100644 --- a/tools/docgen/entry_variables.py +++ b/tools/docgen/entry_variables.py @@ -1,14 +1,39 @@ +from typing import Any +from typing import Dict +from typing import Type + +from tools.docgen.utils import camel_case_to_human from tools.docgen.utils import get_function_docs from tools.docgen.utils import properties from tools.docgen.utils import section from ytdl_sub.entries.script.variable_definitions import VariableDefinitions +def variable_class_to_name(obj: Type[Any]) -> str: + assert "VariableDefinitions" in obj.__name__, f"{obj.__name__} doesnt have VariableDefinitions" + return ( + camel_case_to_human(obj.__name__) + .replace("Variable Definitions", "Variables") + .replace("Ytdl Sub", "Ytdl-Sub") + ) + + def generate_variable_docs() -> str: docs = section("Entry Variables", level=0) - for variable_name in properties(VariableDefinitions): - docs += get_function_docs(function_name=variable_name, obj=VariableDefinitions, level=1) + parent_objs: Dict[str, Type[Any]] = { + variable_class_to_name(obj): obj for obj in VariableDefinitions.__bases__ + } + + for name in sorted(parent_objs.keys()): + docs += section(name, level=1) + + for variable_function_name in properties(parent_objs[name]): + docs += get_function_docs( + function_name=variable_function_name, + obj=parent_objs[name], + level=2, + ) return docs diff --git a/tools/docgen/functions.py b/tools/docgen/functions.py index 8e5828c2..00383a17 100644 --- a/tools/docgen/functions.py +++ b/tools/docgen/functions.py @@ -3,6 +3,7 @@ from typing import Dict from typing import Optional from typing import Type +from tools.docgen.utils import camel_case_to_human from tools.docgen.utils import get_function_docs from tools.docgen.utils import section from tools.docgen.utils import static_methods @@ -21,7 +22,7 @@ def maybe_get_function_name(function_name: str) -> Optional[str]: def function_class_to_name(obj: Type[Any]) -> str: assert "Functions" in obj.__name__ - return obj.__name__.replace("Functions", " Functions") + return camel_case_to_human(obj.__name__) def generate_function_docs() -> str: diff --git a/tools/docgen/override_variables.py b/tools/docgen/override_variables.py new file mode 100644 index 00000000..f5b35dd5 --- /dev/null +++ b/tools/docgen/override_variables.py @@ -0,0 +1,20 @@ +from tools.docgen.utils import get_function_docs +from tools.docgen.utils import section +from tools.docgen.utils import static_methods +from ytdl_sub.entries.variables.override_variables import OverrideVariables + + +def generate_override_docs() -> str: + docs = section("Override Variables", level=0) + + for name in static_methods(OverrideVariables): + docs += get_function_docs( + function_name=name, + obj=OverrideVariables, + level=1, + ) + + return docs + + +print(generate_override_docs()) diff --git a/tools/docgen/utils.py b/tools/docgen/utils.py index 24e6e06f..d236bb5f 100644 --- a/tools/docgen/utils.py +++ b/tools/docgen/utils.py @@ -13,7 +13,7 @@ def section(name: str, level: int) -> str: def properties(obj: Type[Any]) -> List[str]: - return [prop for prop in dir(obj) if isinstance(getattr(obj, prop), property)] + return sorted(prop for prop in dir(obj) if isinstance(getattr(obj, prop), property)) def static_methods(obj: Type[Any]) -> List[str]: @@ -22,6 +22,17 @@ def static_methods(obj: Type[Any]) -> List[str]: ) +def camel_case_to_human(string: str) -> str: + output_str = string[0] + for char in string[1:]: + if char.islower(): + output_str += char + else: + output_str += f" {char}" + + return output_str + + def get_function_docs( function_name: str, obj: Any, level: int, display_function_name: Optional[str] = None ) -> str: