Merge branch 'master' into j/resolution-level

This commit is contained in:
Jesse Bannon 2026-01-26 10:53:45 -08:00
commit 3693dd6bb6
5 changed files with 98 additions and 6 deletions

View file

@ -62,7 +62,10 @@ class YTDLOptions(UnstructuredOverridesDictFormatterValidator):
Materializes the entire ytdl-options dict from OverrideStringFormatters into
native python.
"""
out = {key: overrides.apply_formatter(val) for key, val in self.dict.items()}
out = {
key: overrides.apply_formatter(val, expected_type=object)
for key, val in self.dict.items()
}
if "cookiefile" in out:
if not FileHandler.is_file_existent(out["cookiefile"]):
raise ValidationException(

View file

@ -212,7 +212,22 @@ class OverridesDictFormatterValidator(DictFormatterValidator):
_key_validator = OverridesStringFormatterValidator
class AnyFormatterValidator(StringFormatterValidator):
"""
Applies no post-processing.
"""
def post_process(self, resolved: Any) -> Any:
return resolved
class AnyOverridesFormatterValidator(AnyFormatterValidator, OverridesStringFormatterValidator):
pass
class UnstructuredDictFormatterValidator(DictFormatterValidator):
_key_validator = AnyFormatterValidator
def __init__(self, name, value):
# Convert the unstructured-ness into a script
if isinstance(value, dict):
@ -221,7 +236,7 @@ class UnstructuredDictFormatterValidator(DictFormatterValidator):
class UnstructuredOverridesDictFormatterValidator(UnstructuredDictFormatterValidator):
_key_validator = OverridesStringFormatterValidator
_key_validator = AnyOverridesFormatterValidator
def _validate_formatter(

View file

@ -117,7 +117,8 @@ def assert_logs(
yield
for call_args in patched_debug.call_args_list:
occurrences += int(expected_message in call_args.args[0])
full_print = call_args.args[0] % call_args.args[1:]
occurrences += int(expected_message in full_print)
if expected_occurrences is not None:
assert (

View file

@ -71,7 +71,7 @@ class TestThrottleProtectionPlugin:
),
assert_logs(
logger=throttle_protection_logger,
expected_message="Sleeping between subscriptions for %0.2f seconds",
expected_message="Sleeping between subscriptions for 0.02 seconds",
log_level="info",
expected_occurrences=1,
),
@ -139,7 +139,7 @@ class TestThrottleProtectionPlugin:
),
assert_logs(
logger=throttle_protection_logger,
expected_message="Reached subscription max downloads of %d",
expected_message="Reached subscription max downloads of 0 for throttle protection",
log_level="info",
expected_occurrences=1,
),

View file

@ -3,11 +3,15 @@ from typing import Any
from typing import Dict
import pytest
from conftest import get_match_filters
import yt_dlp
from conftest import assert_logs
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.downloaders.ytdlp import YTDLP
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.ffmpeg import FFMPEG
from ytdl_sub.utils.file_path import FilePathTruncater
@pytest.fixture
@ -19,6 +23,42 @@ def preset_dict(output_directory) -> Dict[str, Any]:
class TestYtdlOptions:
def test_ytdl_options_are_strings(
self,
default_config: ConfigFile,
preset_dict: Dict[str, Any],
working_directory,
):
expected_ytdl_options = {
"ignoreerrors": True,
"outtmpl": FilePathTruncater.to_native_filepath(
f"{working_directory}/test_ytdl_options/%(id)S.%(ext)s"
),
"writethumbnail": False,
"ffmpeg_location": FFMPEG.ffmpeg_path(),
"match_filter": yt_dlp.utils.match_filter_func(
["!is_live & !is_upcoming & !post_live"], []
),
"skip_download": True,
"writeinfojson": True,
"extract_flat": "discard",
}
with (
assert_logs(
logger=YTDLP.logger,
expected_message=f"ytdl_options: {str(expected_ytdl_options)}",
log_level="debug",
expected_occurrences=1,
),
):
_ = Subscription.from_dict(
config=default_config,
preset_name="test_ytdl_options",
preset_dict=preset_dict,
).download(dry_run=True)
def test_cookiefile_does_not_exist(
self,
default_config: ConfigFile,
@ -36,3 +76,36 @@ class TestYtdlOptions:
preset_name="test_ytdl_options",
preset_dict=preset_dict,
).download(dry_run=False)
def test_ytdl_option_types_preserved(
self,
default_config: ConfigFile,
output_directory: str,
):
preset_dict = {
"download": "https://your.name.here",
"output_options": {"output_directory": output_directory, "file_name": "will_error.mp4"},
"ytdl_options": {
"break_on_existing": True,
"js_runtimes": {"deno": {"path": "/usr/local/bin/{dnope}"}},
"string_path": "verify overrides: {test_string}",
"list_test": ["hmmm"],
},
"overrides": {"test_string": "hi", "dnope": "deno"},
}
sub = Subscription.from_dict(
config=default_config,
preset_name="test_ytdl_options",
preset_dict=preset_dict,
)
out = sub.ytdl_options.to_native_dict(sub.overrides)
expected = {
"break_on_existing": True,
"js_runtimes": {"deno": {"path": "/usr/local/bin/deno"}},
"string_path": "verify overrides: hi",
"list_test": ["hmmm"],
}
assert out == expected