refactor, linting

This commit is contained in:
Jesse Bannon 2023-12-13 15:32:12 -08:00
parent e171fd95c2
commit 57c8cb1ff5
6 changed files with 87 additions and 14 deletions

View file

@ -28,7 +28,7 @@ class Overrides(DictFormatterValidator, Scriptable):
my_example_preset: my_example_preset:
overrides: overrides:
output_directory: "/path/to/media" output_directory: "/path/to/media"
custom_file_name: "{upload_year}.{upload_month_padded}.{upload_day_padded}.{title_sanitized}" custom_file_name: "{upload_date_standardized}.{title_sanitized}"
# Then use the override variables in the output options # Then use the override variables in the output options
output_options: output_options:
@ -55,19 +55,36 @@ class Overrides(DictFormatterValidator, Scriptable):
self.unresolvable.add(VARIABLES.entry_metadata.variable_name) self.unresolvable.add(VARIABLES.entry_metadata.variable_name)
def initial_variables(self, unresolved_variables: Dict[str, str]) -> Dict[str, str]: def initial_variables(
self, unresolved_variables: Optional[Dict[str, str]] = None
) -> Dict[str, str]:
"""
Returns
-------
Variables and format strings for all Override variables + additional variables (Optional)
"""
initial_variables: Dict[str, str] = {} initial_variables: Dict[str, str] = {}
mergedeep.merge( mergedeep.merge(
initial_variables, initial_variables,
self.dict_with_format_strings, self.dict_with_format_strings,
unresolved_variables, unresolved_variables if unresolved_variables else {},
{SUBSCRIPTION_NAME: self.subscription_name}, {SUBSCRIPTION_NAME: self.subscription_name},
) )
return ScriptUtils.add_sanitized_variables(initial_variables) return ScriptUtils.add_sanitized_variables(initial_variables)
def initialize_script(self, unresolved_variables: Dict[str, str]) -> "Overrides": def initialize_script(self, unresolved_variables: Set[str]) -> "Overrides":
self.script.add(self.initial_variables(unresolved_variables=unresolved_variables)) """
self.unresolvable.update(set(unresolved_variables.keys())) Initialize the override script with override variables + any unresolved variables
"""
self.script.add(
self.initial_variables(
unresolved_variables={
var_name: f"{{%throw('Plugin variable {var_name} has not been created yet')}}"
for var_name in unresolved_variables
}
)
)
self.unresolvable.update(unresolved_variables)
self.update_script() self.update_script()
return self return self

View file

@ -108,6 +108,11 @@ class PluginMapping:
def order_options_by( def order_options_by(
cls, zipped: List[Tuple[Type[Plugin], OptionsValidator]], operation: PluginOperation cls, zipped: List[Tuple[Type[Plugin], OptionsValidator]], operation: PluginOperation
) -> List[OptionsValidator]: ) -> List[OptionsValidator]:
"""
Returns
-------
Ordered plugin options with respect to the PluginOperation.
"""
ordered_types: List[Type[Plugin]] = cls._order_by( ordered_types: List[Type[Plugin]] = cls._order_by(
plugin_types=[val[0] for val in zipped], operation=operation plugin_types=[val[0] for val in zipped], operation=operation
) )
@ -131,6 +136,12 @@ class PluginMapping:
def order_plugins_by( def order_plugins_by(
cls, plugins: List[Plugin], operation: PluginOperation, before_split: Optional[bool] = None cls, plugins: List[Plugin], operation: PluginOperation, before_split: Optional[bool] = None
) -> List[Plugin]: ) -> List[Plugin]:
"""
Returns
-------
Ordered plugins with respect to the PluginOperation. Optionally only return plugins
before/after a split plugin.
"""
ordered_types: List[Type[Plugin]] = cls._order_by( ordered_types: List[Type[Plugin]] = cls._order_by(
plugin_types=[type(plugin) for plugin in plugins], operation=operation plugin_types=[type(plugin) for plugin in plugins], operation=operation
) )

View file

@ -50,7 +50,7 @@ def _get_added_and_modified_variables(
def _override_variables(overrides: Overrides) -> Set[str]: def _override_variables(overrides: Overrides) -> Set[str]:
return set(list(overrides.initial_variables(unresolved_variables={}).keys())) return set(list(overrides.initial_variables().keys()))
def _entry_variables() -> Set[str]: def _entry_variables() -> Set[str]:
@ -73,6 +73,9 @@ class VariableValidation:
self.unresolved_variables: Set[str] = set() self.unresolved_variables: Set[str] = set()
def initialize_overrides(self, overrides: Overrides) -> "VariableValidation": def initialize_overrides(self, overrides: Overrides) -> "VariableValidation":
"""
Do some gymnastics to initialize the Overrides script.
"""
entry_variables = _entry_variables() entry_variables = _entry_variables()
override_variables = _override_variables(overrides) override_variables = _override_variables(overrides)
@ -93,12 +96,7 @@ class VariableValidation:
# Initialize overrides with unresolved variables + modified variables to throw an error. # Initialize overrides with unresolved variables + modified variables to throw an error.
# For modified variables, this is to prevent a resolve(update=True) to setting any # For modified variables, this is to prevent a resolve(update=True) to setting any
# dependencies until it has been explicitly added # dependencies until it has been explicitly added
overrides = overrides.initialize_script( overrides = overrides.initialize_script(unresolved_variables=self.unresolved_variables)
unresolved_variables={
var_name: f"{{%throw('Plugin variable {var_name} has not been created yet')}}"
for var_name in self.unresolved_variables
}
)
# copy the script and mock entry variables # copy the script and mock entry variables
self.script = copy.deepcopy(overrides.script).add(_add_dummy_variables(entry_variables)) self.script = copy.deepcopy(overrides.script).add(_add_dummy_variables(entry_variables))

View file

@ -41,7 +41,6 @@ class Entry(BaseEntry, Scriptable):
self.update_script() self.update_script()
def initialize_script(self, other: Optional[Scriptable] = None) -> "Entry": def initialize_script(self, other: Optional[Scriptable] = None) -> "Entry":
# TODO: CLEAN THIS SHIT UP
# Overrides contains added variables that are unresolvable, add them here # Overrides contains added variables that are unresolvable, add them here
if other: if other:
self.script = copy.deepcopy(other.script) self.script = copy.deepcopy(other.script)

View file

@ -16,6 +16,8 @@ from ytdl_sub.entries.script.variable_scripts import ENTRY_REQUIRED_VARIABLES
v: VariableDefinitions = VARIABLES v: VariableDefinitions = VARIABLES
# pylint: disable=protected-access
def _sort_entries(entries: List[TBaseEntry]) -> List[TBaseEntry]: def _sort_entries(entries: List[TBaseEntry]) -> List[TBaseEntry]:
"""Try sorting by playlist_id first, then fall back to uid""" """Try sorting by playlist_id first, then fall back to uid"""
return sorted( return sorted(

View file

@ -23,24 +23,46 @@ _days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
class CustomFunctions: class CustomFunctions:
@staticmethod @staticmethod
def legacy_bracket_safety(value: ReturnableArgument) -> ReturnableArgument: def legacy_bracket_safety(value: ReturnableArgument) -> ReturnableArgument:
"""
ytdl-sub used to replace brackets ('{', '}') with unicode brackets ('', '') to not
interfere with its legacy variable scripting system. This function replicates that
behavior.
"""
if isinstance(value, String): if isinstance(value, String):
value = String(value.value.replace("{", "").replace("}", "")) value = String(value.value.replace("{", "").replace("}", ""))
return value return value
@staticmethod @staticmethod
def to_native_filepath(filepath: String) -> String: def to_native_filepath(filepath: String) -> String:
"""
Convert any unix-based path separators ('/') with the OS's native
separator.
"""
return String(filepath.value.replace(posixpath.sep, os.sep)) return String(filepath.value.replace(posixpath.sep, os.sep))
@staticmethod @staticmethod
def truncate_filepath_if_too_long(filepath: String) -> String: def truncate_filepath_if_too_long(filepath: String) -> String:
"""
If a file-path is too long for the OS, this function will truncate it while preserving
the extension.
"""
return String(FilePathTruncater.maybe_truncate_file_path(filepath.value)) return String(FilePathTruncater.maybe_truncate_file_path(filepath.value))
@staticmethod @staticmethod
def sanitize(value: AnyArgument) -> String: def sanitize(value: AnyArgument) -> String:
"""
Sanitize a string using yt-dlp's ``sanitize_filename`` method to ensure it's safe to use
for file/directory names on any OS.
"""
return String(sanitize_filename(str(value))) return String(sanitize_filename(str(value)))
@staticmethod @staticmethod
def sanitize_plex_episode(string: String) -> String: def sanitize_plex_episode(string: String) -> String:
"""
Sanitize a string using ``sanitize`` and replace numerics with their respective fixed-width
numbers. This is used to have Plex avoid scraping numbers like ``4x4`` as the
season and/or episode.
"""
sanitized_string = CustomFunctions.sanitize(string).value sanitized_string = CustomFunctions.sanitize(string).value
out = "" out = ""
for char in sanitized_string: for char in sanitized_string:
@ -71,6 +93,27 @@ class CustomFunctions:
@staticmethod @staticmethod
def to_date_metadata(yyyymmdd: String) -> Map: def to_date_metadata(yyyymmdd: String) -> Map:
"""
Takes a date in the form of YYYYMMDD and returns a Map containing:
- date (String, YYYYMMDD)
- date_standardized (String, YYYY-MM-DD)
- year (Integer)
- month (Integer)
- day (Integer)
- year_truncated (String, YY from YY[YY])
- month_padded (String)
- day_padded (String)
- year_truncated_reversed (Integer, 100 - year_truncated)
- month_reversed (Integer, 13 - month)
- month_reversed_padded (String)
- day_reversed (Integer, total_days_in_month + 1 - day)
- day_reversed_padded (String)
- day_of_year (Integer)
- day_of_year_padded (String, padded 3)
- day_of_year_reversed (Integer, total_days_in_year + 1 - day_of_year)
- day_of_year_reversed_padded (String, padded 3)
"""
date_str = yyyymmdd.value date_str = yyyymmdd.value
if not (date_str.isnumeric() and len(date_str) == 8): if not (date_str.isnumeric() and len(date_str) == 8):
raise RuntimeException( raise RuntimeException(
@ -123,6 +166,9 @@ class CustomFunctions:
@staticmethod @staticmethod
def register(): def register():
"""
Register Custom functions once and only once
"""
if not Functions.is_built_in("sanitize"): if not Functions.is_built_in("sanitize"):
Functions.register_function(CustomFunctions.legacy_bracket_safety) Functions.register_function(CustomFunctions.legacy_bracket_safety)
Functions.register_function(CustomFunctions.truncate_filepath_if_too_long) Functions.register_function(CustomFunctions.truncate_filepath_if_too_long)