ENTER configurable resolution level when validating. Needs work including setting unresolved variables
This commit is contained in:
parent
530d2f7666
commit
b76182eba1
4 changed files with 132 additions and 8 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
import copy
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ytdl_sub.config.overrides import Overrides
|
from ytdl_sub.config.overrides import Overrides
|
||||||
|
|
@ -7,10 +8,21 @@ from ytdl_sub.config.plugin.preset_plugins import PresetPlugins
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
from ytdl_sub.config.validators.options import OptionsValidator
|
from ytdl_sub.config.validators.options import OptionsValidator
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||||
|
from ytdl_sub.entries.script.variable_types import Variable
|
||||||
|
from ytdl_sub.script.script import Script
|
||||||
from ytdl_sub.validators.string_formatter_validators import validate_formatters
|
from ytdl_sub.validators.string_formatter_validators import validate_formatters
|
||||||
|
|
||||||
|
|
||||||
|
class ResolutionLevel:
|
||||||
|
ORIGINAL = 0
|
||||||
|
FILL = 1
|
||||||
|
RESOLVE = 2
|
||||||
|
INTERNAL = 3
|
||||||
|
|
||||||
|
|
||||||
class VariableValidation:
|
class VariableValidation:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
overrides: Overrides,
|
overrides: Overrides,
|
||||||
|
|
@ -23,7 +35,7 @@ class VariableValidation:
|
||||||
self.output_options = output_options
|
self.output_options = output_options
|
||||||
self.plugins = plugins
|
self.plugins = plugins
|
||||||
|
|
||||||
self.script = self.overrides.script
|
self.script: Script = self.overrides.script
|
||||||
self.unresolved_variables = self.plugins.get_all_variables(
|
self.unresolved_variables = self.plugins.get_all_variables(
|
||||||
additional_options=[self.output_options, self.downloader_options]
|
additional_options=[self.output_options, self.downloader_options]
|
||||||
)
|
)
|
||||||
|
|
@ -39,6 +51,30 @@ class VariableValidation:
|
||||||
|
|
||||||
self.unresolved_variables -= added_variables | modified_variables
|
self.unresolved_variables -= added_variables | modified_variables
|
||||||
|
|
||||||
|
def set_resolution_level(self, resolution_level: int) -> "VariableValidation":
|
||||||
|
if resolution_level == ResolutionLevel.ORIGINAL:
|
||||||
|
# Do not perform any resolution of override variables
|
||||||
|
self.unresolved_variables.update(self.overrides.keys)
|
||||||
|
elif resolution_level == ResolutionLevel.FILL:
|
||||||
|
# If anything is resolvable, 'fill' it with the resolved value.
|
||||||
|
# This will happen automatically when validating
|
||||||
|
pass
|
||||||
|
elif resolution_level == ResolutionLevel.RESOLVE:
|
||||||
|
# Partial resolve everything, but not including internal variables
|
||||||
|
self.script = copy.deepcopy(self.script).resolve_partial(
|
||||||
|
unresolvable=self.unresolved_variables
|
||||||
|
| VARIABLES.variable_names(include_sanitized=True)
|
||||||
|
)
|
||||||
|
elif resolution_level == ResolutionLevel.INTERNAL:
|
||||||
|
# Partial resolve everything including internal variables
|
||||||
|
self.script = copy.deepcopy(self.script).resolve_partial(
|
||||||
|
unresolvable=self.unresolved_variables
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid resolution level")
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def ensure_proper_usage(self) -> Dict:
|
def ensure_proper_usage(self) -> Dict:
|
||||||
"""
|
"""
|
||||||
Validate variables resolve as plugins are executed, and return
|
Validate variables resolve as plugins are executed, and return
|
||||||
|
|
|
||||||
|
|
@ -1135,6 +1135,13 @@ class VariableDefinitions(
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def variable_names(self, include_sanitized: bool):
|
||||||
|
var_names: Set[str] = self.scripts().keys()
|
||||||
|
if include_sanitized:
|
||||||
|
var_names |= {f"{name}_sanitized" for name in var_names}
|
||||||
|
return var_names
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def injected_variables(self) -> Set[MetadataVariable]:
|
def injected_variables(self) -> Set[MetadataVariable]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from ytdl_sub.config.plugin.preset_plugins import PresetPlugins
|
||||||
from ytdl_sub.config.preset import Preset
|
from ytdl_sub.config.preset import Preset
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
from ytdl_sub.config.preset_options import YTDLOptions
|
from ytdl_sub.config.preset_options import YTDLOptions
|
||||||
|
from ytdl_sub.config.validators.variable_validation import ResolutionLevel
|
||||||
from ytdl_sub.config.validators.variable_validation import VariableValidation
|
from ytdl_sub.config.validators.variable_validation import VariableValidation
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
from ytdl_sub.entries.variables.override_variables import SubscriptionVariables
|
from ytdl_sub.entries.variables.override_variables import SubscriptionVariables
|
||||||
|
|
@ -79,7 +80,7 @@ class BaseSubscription(ABC):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validate after adding the subscription name
|
# Validate after adding the subscription name
|
||||||
self._validated_dict = VariableValidation(
|
_ = VariableValidation(
|
||||||
overrides=self.overrides,
|
overrides=self.overrides,
|
||||||
downloader_options=self.downloader_options,
|
downloader_options=self.downloader_options,
|
||||||
output_options=self.output_options,
|
output_options=self.output_options,
|
||||||
|
|
@ -256,10 +257,20 @@ class BaseSubscription(ABC):
|
||||||
"""
|
"""
|
||||||
return self._preset_options.yaml
|
return self._preset_options.yaml
|
||||||
|
|
||||||
def resolved_yaml(self) -> str:
|
def resolved_yaml(self, resolution_level: int = ResolutionLevel.FILL) -> str:
|
||||||
"""
|
"""
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Human-readable, condensed YAML definition of the subscription.
|
Human-readable, condensed YAML definition of the subscription.
|
||||||
"""
|
"""
|
||||||
return dump_yaml(self._validated_dict)
|
out = (
|
||||||
|
VariableValidation(
|
||||||
|
overrides=self.overrides,
|
||||||
|
downloader_options=self.downloader_options,
|
||||||
|
output_options=self.output_options,
|
||||||
|
plugins=self.plugins,
|
||||||
|
)
|
||||||
|
.set_resolution_level(resolution_level)
|
||||||
|
.ensure_proper_usage()
|
||||||
|
)
|
||||||
|
return dump_yaml(out)
|
||||||
|
|
|
||||||
|
|
@ -547,9 +547,73 @@ def test_default_docker_config_and_subscriptions(
|
||||||
|
|
||||||
resolved_yaml_as_json = yaml.safe_load(default_subs[0].resolved_yaml())
|
resolved_yaml_as_json = yaml.safe_load(default_subs[0].resolved_yaml())
|
||||||
|
|
||||||
# Since this creates random values, ignore it for this test
|
assert resolved_yaml_as_json == {
|
||||||
assert "throttle_protection" in resolved_yaml_as_json
|
"chapters": {
|
||||||
del resolved_yaml_as_json["throttle_protection"]
|
"allow_chapters_from_comments": False,
|
||||||
|
"embed_chapters": True,
|
||||||
|
"enable": "True",
|
||||||
|
"force_key_frames": False,
|
||||||
|
},
|
||||||
|
"date_range": {"breaks": "True", "enable": "True", "type": "upload_date"},
|
||||||
|
"download": [
|
||||||
|
{
|
||||||
|
"download_reverse": "True",
|
||||||
|
"include_sibling_metadata": False,
|
||||||
|
"playlist_thumbnails": [
|
||||||
|
{"name": "{avatar_uncropped_thumbnail_file_name}", "uid": "avatar_uncropped"},
|
||||||
|
{"name": "{banner_uncropped_thumbnail_file_name}", "uid": "banner_uncropped"},
|
||||||
|
],
|
||||||
|
"source_thumbnails": [
|
||||||
|
{"name": "{avatar_uncropped_thumbnail_file_name}", "uid": "avatar_uncropped"},
|
||||||
|
{"name": "{banner_uncropped_thumbnail_file_name}", "uid": "banner_uncropped"},
|
||||||
|
],
|
||||||
|
"url": "https://www.youtube.com/@novapbs",
|
||||||
|
"variables": {},
|
||||||
|
"webpage_url": "{modified_webpage_url}",
|
||||||
|
"ytdl_options": {},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"file_convert": {"convert_to": "mp4", "convert_with": "yt-dlp", "enable": "True"},
|
||||||
|
"format": "(bv*[ext=mp4][vcodec~='^((he|a)vc|h26[45])']+ba[ext=m4a]) / (bv[ext=mp4]*+ba[ext=m4a]/b)",
|
||||||
|
"output_options": {
|
||||||
|
"download_archive_name": ".ytdl-sub-NOVA PBS-download-archive.json",
|
||||||
|
"file_name": "{episode_file_path}.{ext}",
|
||||||
|
"info_json_name": "{episode_file_path}.{info_json_ext}",
|
||||||
|
"keep_files_date_eval": "{episode_date_standardized}",
|
||||||
|
"maintain_download_archive": True,
|
||||||
|
"output_directory": f"{output_directory}/NOVA PBS",
|
||||||
|
"preserve_mtime": False,
|
||||||
|
"thumbnail_name": "{thumbnail_file_name}",
|
||||||
|
},
|
||||||
|
"throttle_protection": {
|
||||||
|
"enable": True,
|
||||||
|
"sleep_per_download_s": {"max": "28.4", "min": "13.8"},
|
||||||
|
"sleep_per_request_s": {"max": "0.75", "min": "0.0"},
|
||||||
|
"sleep_per_subscription_s": {"max": "26.1", "min": "16.3"},
|
||||||
|
},
|
||||||
|
"video_tags": {
|
||||||
|
"contentRating": "{episode_content_rating}",
|
||||||
|
"date": "{episode_date_standardized}",
|
||||||
|
"episode_id": "{episode_number}",
|
||||||
|
"genre": "{tv_show_genre}",
|
||||||
|
"show": "{tv_show_name}",
|
||||||
|
"synopsis": "{episode_plot}",
|
||||||
|
"title": "{episode_title}",
|
||||||
|
"year": "{episode_year}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_docker_config_and_subscriptions(
|
||||||
|
docker_default_subscription_path: Path, output_directory: str
|
||||||
|
):
|
||||||
|
default_config = ConfigFile.from_file_path("docker/root/defaults/config.yaml")
|
||||||
|
default_subs = Subscription.from_file_path(
|
||||||
|
config=default_config, subscription_path=docker_default_subscription_path
|
||||||
|
)
|
||||||
|
assert len(default_subs) == 1
|
||||||
|
|
||||||
|
resolved_yaml_as_json = yaml.safe_load(default_subs[0].resolved_yaml(resolution_level=3))
|
||||||
|
|
||||||
assert resolved_yaml_as_json == {
|
assert resolved_yaml_as_json == {
|
||||||
"chapters": {
|
"chapters": {
|
||||||
|
|
@ -589,6 +653,12 @@ def test_default_docker_config_and_subscriptions(
|
||||||
"preserve_mtime": False,
|
"preserve_mtime": False,
|
||||||
"thumbnail_name": "{thumbnail_file_name}",
|
"thumbnail_name": "{thumbnail_file_name}",
|
||||||
},
|
},
|
||||||
|
"throttle_protection": {
|
||||||
|
"enable": True,
|
||||||
|
"sleep_per_download_s": {"max": "28.4", "min": "13.8"},
|
||||||
|
"sleep_per_request_s": {"max": "0.75", "min": "0.0"},
|
||||||
|
"sleep_per_subscription_s": {"max": "26.1", "min": "16.3"},
|
||||||
|
},
|
||||||
"video_tags": {
|
"video_tags": {
|
||||||
"contentRating": "{episode_content_rating}",
|
"contentRating": "{episode_content_rating}",
|
||||||
"date": "{episode_date_standardized}",
|
"date": "{episode_date_standardized}",
|
||||||
|
|
@ -599,4 +669,4 @@ def test_default_docker_config_and_subscriptions(
|
||||||
"title": "{episode_title}",
|
"title": "{episode_title}",
|
||||||
"year": "{episode_year}",
|
"year": "{episode_year}",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue