override format string, thumbnail hotfix
This commit is contained in:
parent
e3239153ec
commit
fb7993cf70
7 changed files with 111 additions and 7 deletions
|
|
@ -7,7 +7,6 @@ from typing import Generic
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import overload
|
|
||||||
|
|
||||||
import dicttoxml
|
import dicttoxml
|
||||||
import music_tag
|
import music_tag
|
||||||
|
|
@ -169,6 +168,19 @@ class Subscription(Generic[S], ABC):
|
||||||
if self.output_options.thumbnail_name:
|
if self.output_options.thumbnail_name:
|
||||||
source_thumbnail_path = entry.thumbnail_path(relative_directory=self.working_directory)
|
source_thumbnail_path = entry.thumbnail_path(relative_directory=self.working_directory)
|
||||||
|
|
||||||
|
# Bug that mismatches webp and jpg extensions. Try to hotfix here
|
||||||
|
if not os.path.isfile(source_thumbnail_path):
|
||||||
|
actual_thumbnail_ext = ".webp"
|
||||||
|
if entry.thumbnail_ext == "webp":
|
||||||
|
actual_thumbnail_ext = ".jpg"
|
||||||
|
|
||||||
|
source_thumbnail_path = source_thumbnail_path.replace(
|
||||||
|
f".{entry.thumbnail_ext}", actual_thumbnail_ext
|
||||||
|
)
|
||||||
|
if not os.path.isfile(source_thumbnail_path):
|
||||||
|
# TODO: make more formal
|
||||||
|
raise ValueError("Youtube thumbnails are a lie")
|
||||||
|
|
||||||
output_thumbnail_name = self._apply_formatter(
|
output_thumbnail_name = self._apply_formatter(
|
||||||
formatter=self.output_options.thumbnail_name, entry=entry
|
formatter=self.output_options.thumbnail_name, entry=entry
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from typing import final
|
||||||
from ytdl_subscribe.validators.base.validators import LiteralDictValidator
|
from ytdl_subscribe.validators.base.validators import LiteralDictValidator
|
||||||
from ytdl_subscribe.validators.base.validators import Validator
|
from ytdl_subscribe.validators.base.validators import Validator
|
||||||
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
||||||
|
from ytdl_subscribe.validators.exceptions import StringFormattingVariableNotFoundException
|
||||||
|
|
||||||
|
|
||||||
class StringFormatterValidator(Validator):
|
class StringFormatterValidator(Validator):
|
||||||
|
|
@ -89,7 +90,7 @@ class StringFormatterValidator(Validator):
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(
|
||||||
f"Format variable '{variable_name}' does not exist. "
|
f"Format variable '{variable_name}' does not exist. "
|
||||||
f"Available variables: {available_fields}",
|
f"Available variables: {available_fields}",
|
||||||
exception_class=StringFormattingException,
|
exception_class=StringFormattingVariableNotFoundException,
|
||||||
)
|
)
|
||||||
|
|
||||||
return StringFormatterValidator(
|
return StringFormatterValidator(
|
||||||
|
|
@ -119,6 +120,12 @@ class StringFormatterValidator(Validator):
|
||||||
return formatter.format_string
|
return formatter.format_string
|
||||||
|
|
||||||
|
|
||||||
|
class OverridesStringFormatterValidator(StringFormatterValidator):
|
||||||
|
"""
|
||||||
|
A string formatter that should strictly use overrides that resolve without any entry variables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class DictFormatterValidator(LiteralDictValidator):
|
class DictFormatterValidator(LiteralDictValidator):
|
||||||
"""
|
"""
|
||||||
Validates a dictionary made up of key: string_formatters
|
Validates a dictionary made up of key: string_formatters
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,10 @@ class DictValidator(Validator):
|
||||||
_expected_value_type = dict
|
_expected_value_type = dict
|
||||||
_expected_value_type_name = "object"
|
_expected_value_type_name = "object"
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self.__validator_dict: Dict[str, Validator] = {}
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@property
|
@property
|
||||||
def _dict(self) -> dict:
|
def _dict(self) -> dict:
|
||||||
|
|
@ -113,6 +117,15 @@ class DictValidator(Validator):
|
||||||
"""
|
"""
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
|
@final
|
||||||
|
@property
|
||||||
|
def _validator_dict(self) -> Dict[str, Validator]:
|
||||||
|
"""
|
||||||
|
Returns dict containing names and validators of any keys that were validated.
|
||||||
|
This allows top-level validators to recursively search a dict validator.
|
||||||
|
"""
|
||||||
|
return self.__validator_dict
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@property
|
@property
|
||||||
def _keys(self) -> List[str]:
|
def _keys(self) -> List[str]:
|
||||||
|
|
@ -147,11 +160,15 @@ class DictValidator(Validator):
|
||||||
if key not in self._dict and default is None:
|
if key not in self._dict and default is None:
|
||||||
raise self._validation_exception(f"{key} is missing when it should be present.")
|
raise self._validation_exception(f"{key} is missing when it should be present.")
|
||||||
|
|
||||||
return validator(
|
validator_name = f"{self._name}.{key}"
|
||||||
name=f"{self._name}.{key}",
|
validator_instance = validator(
|
||||||
|
name=validator_name,
|
||||||
value=self._dict.get(key, default),
|
value=self._dict.get(key, default),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.__validator_dict[validator_name] = validator_instance
|
||||||
|
return validator_instance
|
||||||
|
|
||||||
@final
|
@final
|
||||||
def _validate_key_if_present(
|
def _validate_key_if_present(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
||||||
|
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
||||||
|
OverridesStringFormatterValidator,
|
||||||
|
)
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,3 +15,11 @@ class NFOValidator(StrictDictValidator):
|
||||||
self.nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
|
self.nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
|
||||||
self.nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
|
self.nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
|
||||||
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
|
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
|
||||||
|
|
||||||
|
|
||||||
|
class OutputDirectoryNFOValidator(NFOValidator):
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self.nfo_name = self._validate_key(
|
||||||
|
key="nfo_name", validator=OverridesStringFormatterValidator
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
|
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
||||||
|
OverridesStringFormatterValidator,
|
||||||
|
)
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
||||||
|
|
||||||
|
|
@ -18,13 +21,16 @@ class OutputOptionsValidator(StrictDictValidator):
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
|
|
||||||
self.output_directory: StringFormatterValidator = self._validate_key(
|
# Output directory should resolve without any entry variables.
|
||||||
key="output_directory", validator=StringFormatterValidator
|
# This is to check the directory for any download-archives before any downloads begin
|
||||||
|
self.output_directory: OverridesStringFormatterValidator = self._validate_key(
|
||||||
|
key="output_directory", validator=OverridesStringFormatterValidator
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# file name and thumbnails however can use entry variables
|
||||||
self.file_name: StringFormatterValidator = self._validate_key(
|
self.file_name: StringFormatterValidator = self._validate_key(
|
||||||
key="file_name", validator=StringFormatterValidator
|
key="file_name", validator=StringFormatterValidator
|
||||||
)
|
)
|
||||||
|
|
||||||
self.convert_thumbnail = self._validate_key_if_present(
|
self.convert_thumbnail = self._validate_key_if_present(
|
||||||
key="convert_thumbnail", validator=ConvertThumbnailValidator
|
key="convert_thumbnail", validator=ConvertThumbnailValidator
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@ from typing import Optional
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
|
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
||||||
|
OverridesStringFormatterValidator,
|
||||||
|
)
|
||||||
|
from ytdl_subscribe.validators.base.validators import DictValidator
|
||||||
|
from ytdl_subscribe.validators.base.validators import Validator
|
||||||
from ytdl_subscribe.validators.config.metadata_options.metadata_options_validator import (
|
from ytdl_subscribe.validators.config.metadata_options.metadata_options_validator import (
|
||||||
MetadataOptionsValidator,
|
MetadataOptionsValidator,
|
||||||
)
|
)
|
||||||
|
|
@ -25,6 +30,7 @@ from ytdl_subscribe.validators.config.source_options.source_validators import So
|
||||||
from ytdl_subscribe.validators.config.ytdl_options.ytdl_options_validator import (
|
from ytdl_subscribe.validators.config.ytdl_options.ytdl_options_validator import (
|
||||||
YTDLOptionsValidator,
|
YTDLOptionsValidator,
|
||||||
)
|
)
|
||||||
|
from ytdl_subscribe.validators.exceptions import StringFormattingVariableNotFoundException
|
||||||
from ytdl_subscribe.validators.exceptions import ValidationException
|
from ytdl_subscribe.validators.exceptions import ValidationException
|
||||||
|
|
||||||
PRESET_SOURCE_VALIDATOR_MAPPING: Dict[str, Type[DownloadStrategyValidator]] = {
|
PRESET_SOURCE_VALIDATOR_MAPPING: Dict[str, Type[DownloadStrategyValidator]] = {
|
||||||
|
|
@ -74,6 +80,43 @@ class PresetValidator(StrictDictValidator):
|
||||||
|
|
||||||
return download_strategy_validator.source_validator
|
return download_strategy_validator.source_validator
|
||||||
|
|
||||||
|
def __validate_override_string_formatter_validator(
|
||||||
|
self, formatter_validator: OverridesStringFormatterValidator
|
||||||
|
):
|
||||||
|
# Gather all resolvable override variables
|
||||||
|
resolvable_override_variables: List[str] = []
|
||||||
|
for name, override_variable in self.overrides.dict.items():
|
||||||
|
try:
|
||||||
|
_ = override_variable.apply_formatter(self.overrides.dict_with_format_strings)
|
||||||
|
except StringFormattingVariableNotFoundException:
|
||||||
|
continue
|
||||||
|
resolvable_override_variables.append(name)
|
||||||
|
|
||||||
|
for variable_name in formatter_validator.format_variables:
|
||||||
|
if variable_name not in resolvable_override_variables:
|
||||||
|
raise StringFormattingVariableNotFoundException(
|
||||||
|
f"This variable can only use override variables that resolve without needing "
|
||||||
|
f"variables from a downloaded file. The only override variables defined that "
|
||||||
|
f"meet this condition are: {', '.join(sorted(resolvable_override_variables))}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __recursive_preset_validate(
|
||||||
|
self, validator_dict: Optional[Dict[str, Validator]] = None
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Ensure all OverridesStringFormatterValidator's only contain variables from the overrides
|
||||||
|
and resolve.
|
||||||
|
"""
|
||||||
|
if validator_dict is None:
|
||||||
|
validator_dict = self._validator_dict
|
||||||
|
|
||||||
|
for name, validator in validator_dict.items():
|
||||||
|
if isinstance(validator, DictValidator):
|
||||||
|
self.__recursive_preset_validate(validator._validator_dict)
|
||||||
|
|
||||||
|
if isinstance(validator, OverridesStringFormatterValidator):
|
||||||
|
self.__validate_override_string_formatter_validator(validator)
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
super().__init__(name=name, value=value)
|
super().__init__(name=name, value=value)
|
||||||
|
|
||||||
|
|
@ -94,3 +137,7 @@ class PresetValidator(StrictDictValidator):
|
||||||
self.overrides = self._validate_key(
|
self.overrides = self._validate_key(
|
||||||
key="overrides", validator=OverridesValidator, default={}
|
key="overrides", validator=OverridesValidator, default={}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# After all options are initialized, perform a recursive post-validate that requires
|
||||||
|
# values from multiple validators
|
||||||
|
self.__recursive_preset_validate()
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,7 @@ class ValidationException(ValueError):
|
||||||
|
|
||||||
class StringFormattingException(ValidationException):
|
class StringFormattingException(ValidationException):
|
||||||
"""Tried to format a string but failed due to user misconfigured variables"""
|
"""Tried to format a string but failed due to user misconfigured variables"""
|
||||||
|
|
||||||
|
|
||||||
|
class StringFormattingVariableNotFoundException(StringFormattingException):
|
||||||
|
"""Tried to format a string but the variable was not found"""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue