[FEATURE] Add --dl-override to pass dl arg overrides to all subscriptions (#882)

With both `--match` and `--dl-override`, it is now possible to more easily experiment with subscriptions without needing to create a separate file.

For example, suppose you are changing some values in your subscription "Rick A" and want to test them out. You can now run:
```
ytdl-sub sub --dry-run --match Rick --dl-override '--ytdl-options.max_downloads 3'
```
This will
1. Dry-run
2. Only run for the subscription "Rick A"
3. Apply setting max_downloads to 3
This commit is contained in:
Jesse Bannon 2024-01-08 15:19:01 -08:00 committed by GitHub
parent 9382be5c42
commit 78cdcce464
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 2 deletions

View file

@ -3,6 +3,7 @@ import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict
from typing import List
from typing import Optional
@ -70,6 +71,7 @@ def _download_subscriptions_from_yaml_files(
config: ConfigFile,
subscription_paths: List[str],
subscription_matches: List[str],
subscription_override_dict: Dict,
update_with_info_json: bool,
dry_run: bool,
) -> List[Subscription]:
@ -102,7 +104,11 @@ def _download_subscriptions_from_yaml_files(
# Load all the subscriptions first to perform all validation before downloading
for path in subscription_paths:
subscriptions += Subscription.from_file_path(config=config, subscription_path=path)
subscriptions += Subscription.from_file_path(
config=config,
subscription_path=path,
subscription_override_dict=subscription_override_dict,
)
if subscriptions and subscription_matches:
logger.info("Filtering subscriptions by name based on --match arguments")
@ -233,11 +239,18 @@ def main() -> List[Subscription]:
"full backup before usage. You have been warned!",
)
subscription_override_dict = {}
if args.dl_override:
subscription_override_dict = DownloadArgsParser.from_dl_override(
override=args.dl_override, config=config
).to_subscription_dict()
logger.info("Validating subscriptions...")
subscriptions = _download_subscriptions_from_yaml_files(
config=config,
subscription_paths=args.subscription_paths,
subscription_matches=args.match,
subscription_override_dict=subscription_override_dict,
update_with_info_json=args.update_with_info_json,
dry_run=args.dry_run,
)

View file

@ -9,6 +9,7 @@ from typing import Tuple
from mergedeep import mergedeep
from ytdl_sub.cli.parsers.main import MainArguments
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.config.config_validator import ConfigOptions
from ytdl_sub.utils.exceptions import InvalidDlArguments
@ -247,3 +248,12 @@ class DownloadArgsParser:
"""
hash_string = str(sorted(self._unknown_arguments))
return hashlib.sha256(hash_string.encode()).hexdigest()[-8:]
@classmethod
def from_dl_override(cls, override: str, config: ConfigFile) -> "DownloadArgsParser":
"""
Create a DownloadArgsParser from a sub --override argument value
"""
return DownloadArgsParser(
extra_arguments=override.split(), config_options=config.config_options
)

View file

@ -157,6 +157,10 @@ class SubArguments:
short="-u",
long="--update-with-info-json",
)
OVERRIDE = CLIArgument(
short="-o",
long="--dl-override",
)
subscription_parser = subparsers.add_parser("sub")
@ -175,6 +179,13 @@ subscription_parser.add_argument(
help="update all subscriptions with the current config using info.json files",
default=False,
)
subscription_parser.add_argument(
SubArguments.OVERRIDE.short,
SubArguments.OVERRIDE.long,
type=str,
help="override all subscription config values using `dl` syntax, "
"i.e. --dl-override='--ytdl_options.max_downloads 3'",
)
###################################################################################################
# DOWNLOAD PARSER

View file

@ -3,6 +3,9 @@ from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from mergedeep import mergedeep
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.config.preset import Preset
@ -69,7 +72,10 @@ class Subscription(SubscriptionDownload):
@classmethod
def from_file_path(
cls, config: ConfigFile, subscription_path: str | Path
cls,
config: ConfigFile,
subscription_path: str | Path,
subscription_override_dict: Optional[Dict] = None,
) -> List["Subscription"]:
"""
Loads subscriptions from a file.
@ -80,6 +86,8 @@ class Subscription(SubscriptionDownload):
Validated instance of the config
subscription_path:
File path to the subscription yaml file
subscription_override_dict:
Optional dict containing overrides to every subscription
Returns
-------
@ -122,6 +130,13 @@ class Subscription(SubscriptionDownload):
)
for subscription_key, subscription_object in subscriptions_dicts.items():
# Hard-override subscriptions here
mergedeep.merge(
subscription_object,
subscription_override_dict or {},
strategy=mergedeep.Strategy.ADDITIVE,
)
subscriptions.append(
cls.from_dict(
config=config,

View file

@ -199,3 +199,26 @@ class TestPlaylist:
dry_run=dry_run,
expected_download_summary_file_name="youtube/test_playlist.json",
)
def test_playlist_download_from_cli_sub_with_override_arg(
self,
preset_dict_to_subscription_yaml_generator,
playlist_preset_dict,
output_directory,
):
# TODO: Fix CLI parsing on windows when dealing with spaces
if IS_WINDOWS:
return
# No config needed when using only prebuilt presets
with preset_dict_to_subscription_yaml_generator(
subscription_name="music_video_playlist_test", preset_dict=playlist_preset_dict
) as subscription_path:
args = (
f"--dry-run sub '{subscription_path}' --dl-override '--date_range.after 20240101'"
)
subscriptions = mock_run_from_cli(args=args)
assert len(subscriptions) == 1
assert subscriptions[0].transaction_log.is_empty

View file

@ -53,6 +53,7 @@ def test_subscription_logs_write_to_file(
config=config,
subscription_paths=subscription_paths,
subscription_matches=match,
subscription_override_dict={},
update_with_info_json=False,
dry_run=dry_run,
)