[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 import sys
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -70,6 +71,7 @@ def _download_subscriptions_from_yaml_files(
config: ConfigFile, config: ConfigFile,
subscription_paths: List[str], subscription_paths: List[str],
subscription_matches: List[str], subscription_matches: List[str],
subscription_override_dict: Dict,
update_with_info_json: bool, update_with_info_json: bool,
dry_run: bool, dry_run: bool,
) -> List[Subscription]: ) -> List[Subscription]:
@ -102,7 +104,11 @@ def _download_subscriptions_from_yaml_files(
# Load all the subscriptions first to perform all validation before downloading # Load all the subscriptions first to perform all validation before downloading
for path in subscription_paths: 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: if subscriptions and subscription_matches:
logger.info("Filtering subscriptions by name based on --match arguments") 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!", "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...") logger.info("Validating subscriptions...")
subscriptions = _download_subscriptions_from_yaml_files( subscriptions = _download_subscriptions_from_yaml_files(
config=config, config=config,
subscription_paths=args.subscription_paths, subscription_paths=args.subscription_paths,
subscription_matches=args.match, subscription_matches=args.match,
subscription_override_dict=subscription_override_dict,
update_with_info_json=args.update_with_info_json, update_with_info_json=args.update_with_info_json,
dry_run=args.dry_run, dry_run=args.dry_run,
) )

View file

@ -9,6 +9,7 @@ from typing import Tuple
from mergedeep import mergedeep from mergedeep import mergedeep
from ytdl_sub.cli.parsers.main import MainArguments 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.config.config_validator import ConfigOptions
from ytdl_sub.utils.exceptions import InvalidDlArguments from ytdl_sub.utils.exceptions import InvalidDlArguments
@ -247,3 +248,12 @@ class DownloadArgsParser:
""" """
hash_string = str(sorted(self._unknown_arguments)) hash_string = str(sorted(self._unknown_arguments))
return hashlib.sha256(hash_string.encode()).hexdigest()[-8:] 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", short="-u",
long="--update-with-info-json", long="--update-with-info-json",
) )
OVERRIDE = CLIArgument(
short="-o",
long="--dl-override",
)
subscription_parser = subparsers.add_parser("sub") 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", help="update all subscriptions with the current config using info.json files",
default=False, 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 # DOWNLOAD PARSER

View file

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

View file

@ -199,3 +199,26 @@ class TestPlaylist:
dry_run=dry_run, dry_run=dry_run,
expected_download_summary_file_name="youtube/test_playlist.json", 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, config=config,
subscription_paths=subscription_paths, subscription_paths=subscription_paths,
subscription_matches=match, subscription_matches=match,
subscription_override_dict={},
update_with_info_json=False, update_with_info_json=False,
dry_run=dry_run, dry_run=dry_run,
) )