From ced547b14a3e1c9f999fac8186d07e140a24e288 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Mon, 2 Feb 2026 10:23:08 -0800 Subject: [PATCH] [FEATURE] Arg to shuffle subscriptions (#1263) Closes https://github.com/jmbannon/ytdl-sub/issues/1234 Adds `--shuffle` support which shuffles the order of subscriptions when downloading. --- src/ytdl_sub/cli/entrypoint.py | 9 ++++++ src/ytdl_sub/cli/parsers/main.py | 11 +++++++ tests/integration/cli/test_entrypoint.py | 39 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/ytdl_sub/cli/entrypoint.py b/src/ytdl_sub/cli/entrypoint.py index b825d45e..29f4f482 100644 --- a/src/ytdl_sub/cli/entrypoint.py +++ b/src/ytdl_sub/cli/entrypoint.py @@ -1,5 +1,6 @@ import gc import os +import random import sys from datetime import datetime from pathlib import Path @@ -75,6 +76,7 @@ def _download_subscriptions_from_yaml_files( subscription_override_dict: Dict, update_with_info_json: bool, dry_run: bool, + shuffle: bool, ) -> List[Subscription]: """ Downloads all subscriptions from one or many subscription yaml files. @@ -91,6 +93,8 @@ def _download_subscriptions_from_yaml_files( Whether to actually download or update using existing info json dry_run Whether to dry run or not + shuffle + Whether to shuffle the subscription download order Returns ------- @@ -112,6 +116,10 @@ def _download_subscriptions_from_yaml_files( subscription_override_dict=subscription_override_dict, ) + if shuffle: + logger.info("Shuffling subscriptions") + random.shuffle(subscriptions) + for subscription in subscriptions: with subscription.exception_handling(): logger.info( @@ -253,6 +261,7 @@ def main() -> List[Subscription]: subscription_override_dict=subscription_override_dict, update_with_info_json=args.update_with_info_json, dry_run=args.dry_run, + shuffle=args.shuffle, ) # One-off download diff --git a/src/ytdl_sub/cli/parsers/main.py b/src/ytdl_sub/cli/parsers/main.py index aa26b591..eb9b49ca 100644 --- a/src/ytdl_sub/cli/parsers/main.py +++ b/src/ytdl_sub/cli/parsers/main.py @@ -172,6 +172,10 @@ class SubArguments: short="-o", long="--dl-override", ) + SHUFFLE = CLIArgument( + short="-sh", + long="--shuffle", + ) subscription_parser = subparsers.add_parser("sub") @@ -197,6 +201,13 @@ subscription_parser.add_argument( help="override all subscription config values using `dl` syntax, " "i.e. --dl-override='--ytdl_options.max_downloads 3'", ) +subscription_parser.add_argument( + SubArguments.SHUFFLE.short, + SubArguments.SHUFFLE.long, + action="store_true", + help="shuffle subscription order when downloading", + default=False, +) ################################################################################################### # DOWNLOAD PARSER diff --git a/tests/integration/cli/test_entrypoint.py b/tests/integration/cli/test_entrypoint.py index 084df414..90ea3579 100644 --- a/tests/integration/cli/test_entrypoint.py +++ b/tests/integration/cli/test_entrypoint.py @@ -9,6 +9,7 @@ import pytest from ytdl_sub.cli.entrypoint import _download_subscriptions_from_yaml_files from ytdl_sub.cli.entrypoint import main +from ytdl_sub.config.config_file import ConfigFile from ytdl_sub.subscriptions.subscription import Subscription from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled @@ -59,6 +60,7 @@ def test_subscription_logs_write_to_file( subscription_override_dict={}, update_with_info_json=False, dry_run=dry_run, + shuffle=False, ) except ValueError: assert not mock_success_output @@ -120,3 +122,40 @@ def test_update_with_info_json_requires_experimental_flag( pytest.raises(ExperimentalFeatureNotEnabled), ): _ = main() + + +def test_subscription_shuffle( + default_config: ConfigFile, + mock_subscription_download_factory: Callable, + music_video_subscription_path: Path, +): + + subscription_paths = [str(music_video_subscription_path)] + + with ( + patch.object( + Subscription, + "download", + new=mock_subscription_download_factory(mock_success_output=True), + ), + ): + out1 = _download_subscriptions_from_yaml_files( + config=default_config, + subscription_paths=subscription_paths, + subscription_matches=[], + subscription_override_dict={}, + update_with_info_json=False, + dry_run=True, + shuffle=True, + ) + out2 = _download_subscriptions_from_yaml_files( + config=default_config, + subscription_paths=subscription_paths, + subscription_matches=[], + subscription_override_dict={}, + update_with_info_json=False, + dry_run=True, + shuffle=True, + ) + + assert [sub.name for sub in out1] != [sub.name for sub in out2]