in main
This commit is contained in:
parent
189b54964b
commit
ff3707adb9
7 changed files with 107 additions and 5 deletions
|
|
@ -14,6 +14,7 @@ from ytdl_sub.cli.download_args_parser import DownloadArgsParser
|
||||||
from ytdl_sub.cli.main_args_parser import parser
|
from ytdl_sub.cli.main_args_parser import parser
|
||||||
from ytdl_sub.config.config_file import ConfigFile
|
from ytdl_sub.config.config_file import ConfigFile
|
||||||
from ytdl_sub.subscriptions.subscription import Subscription
|
from ytdl_sub.subscriptions.subscription import Subscription
|
||||||
|
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
from ytdl_sub.utils.exceptions import ValidationException
|
||||||
from ytdl_sub.utils.file_handler import FileHandler
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
|
|
@ -62,7 +63,7 @@ def _maybe_write_subscription_log_file(
|
||||||
|
|
||||||
|
|
||||||
def _download_subscriptions_from_yaml_files(
|
def _download_subscriptions_from_yaml_files(
|
||||||
config: ConfigFile, subscription_paths: List[str], dry_run: bool
|
config: ConfigFile, subscription_paths: List[str], update_with_info_json: bool, dry_run: bool
|
||||||
) -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
|
) -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
|
||||||
"""
|
"""
|
||||||
Downloads all subscriptions from one or many subscription yaml files.
|
Downloads all subscriptions from one or many subscription yaml files.
|
||||||
|
|
@ -73,6 +74,8 @@ def _download_subscriptions_from_yaml_files(
|
||||||
Configuration file
|
Configuration file
|
||||||
subscription_paths
|
subscription_paths
|
||||||
Path to subscription files to download
|
Path to subscription files to download
|
||||||
|
update_with_info_json
|
||||||
|
Whether to actually download or update using existing info json
|
||||||
dry_run
|
dry_run
|
||||||
Whether to dry run or not
|
Whether to dry run or not
|
||||||
|
|
||||||
|
|
@ -101,7 +104,10 @@ def _download_subscriptions_from_yaml_files(
|
||||||
logger.debug("Subscription full yaml:\n%s", subscription.as_yaml())
|
logger.debug("Subscription full yaml:\n%s", subscription.as_yaml())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
transaction_log = subscription.download(dry_run=dry_run)
|
if update_with_info_json:
|
||||||
|
transaction_log = subscription.update_with_info_json(dry_run=dry_run)
|
||||||
|
else:
|
||||||
|
transaction_log = subscription.download(dry_run=dry_run)
|
||||||
except Exception as exc: # pylint: disable=broad-except
|
except Exception as exc: # pylint: disable=broad-except
|
||||||
_maybe_write_subscription_log_file(
|
_maybe_write_subscription_log_file(
|
||||||
config=config, subscription=subscription, dry_run=dry_run, exception=exc
|
config=config, subscription=subscription, dry_run=dry_run, exception=exc
|
||||||
|
|
@ -305,9 +311,21 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
|
||||||
|
|
||||||
with working_directory_lock(config=config):
|
with working_directory_lock(config=config):
|
||||||
if args.subparser == "sub":
|
if args.subparser == "sub":
|
||||||
|
if (
|
||||||
|
args.update_with_info_json
|
||||||
|
and not config.config_options.experimental.enable_update_with_info_json
|
||||||
|
):
|
||||||
|
raise ExperimentalFeatureNotEnabled(
|
||||||
|
"--update-with-info-json requires setting "
|
||||||
|
"configuration.experimental.update_with_info_json to True. This feature is ",
|
||||||
|
"still being tested and has the ability to destroy files. Ensure you have a ",
|
||||||
|
"full backup before usage. You have been warned!",
|
||||||
|
)
|
||||||
|
|
||||||
transaction_logs = _download_subscriptions_from_yaml_files(
|
transaction_logs = _download_subscriptions_from_yaml_files(
|
||||||
config=config,
|
config=config,
|
||||||
subscription_paths=args.subscription_paths,
|
subscription_paths=args.subscription_paths,
|
||||||
|
update_with_info_json=args.update_with_info_json,
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,13 @@ _add_shared_arguments(parser, suppress_defaults=False)
|
||||||
subparsers = parser.add_subparsers(dest="subparser")
|
subparsers = parser.add_subparsers(dest="subparser")
|
||||||
###################################################################################################
|
###################################################################################################
|
||||||
# SUBSCRIPTION PARSER
|
# SUBSCRIPTION PARSER
|
||||||
|
class SubArguments:
|
||||||
|
UPDATE_WITH_INFO_JSON = CLIArgument(
|
||||||
|
short="-u",
|
||||||
|
long="--update-with-info-json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
subscription_parser = subparsers.add_parser("sub")
|
subscription_parser = subparsers.add_parser("sub")
|
||||||
_add_shared_arguments(subscription_parser, suppress_defaults=True)
|
_add_shared_arguments(subscription_parser, suppress_defaults=True)
|
||||||
subscription_parser.add_argument(
|
subscription_parser.add_argument(
|
||||||
|
|
@ -144,6 +151,14 @@ subscription_parser.add_argument(
|
||||||
help="path to subscription files, uses subscriptions.yaml if not provided",
|
help="path to subscription files, uses subscriptions.yaml if not provided",
|
||||||
default=["subscriptions.yaml"],
|
default=["subscriptions.yaml"],
|
||||||
)
|
)
|
||||||
|
subscription_parser.add_argument(
|
||||||
|
SubArguments.UPDATE_WITH_INFO_JSON.short,
|
||||||
|
SubArguments.UPDATE_WITH_INFO_JSON.long,
|
||||||
|
action="store_true",
|
||||||
|
help="update all subscriptions with the current config using info.json files",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
###################################################################################################
|
###################################################################################################
|
||||||
# DOWNLOAD PARSER
|
# DOWNLOAD PARSER
|
||||||
download_parser = subparsers.add_parser("dl")
|
download_parser = subparsers.add_parser("dl")
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,27 @@ else:
|
||||||
_DEFAULT_FFPROBE_PATH = "/usr/bin/ffprobe"
|
_DEFAULT_FFPROBE_PATH = "/usr/bin/ffprobe"
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentalValidator(StrictDictValidator):
|
||||||
|
_optional_keys = {"enable_update_with_info_json"}
|
||||||
|
_allow_extra_keys = True
|
||||||
|
|
||||||
|
def __init__(self, name: str, value: Any):
|
||||||
|
super().__init__(name, value)
|
||||||
|
|
||||||
|
self._enable_update_with_info_json = self._validate_key(
|
||||||
|
key="enable_update_with_info_json", validator=BoolValidator, default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enable_update_with_info_json(self) -> bool:
|
||||||
|
"""
|
||||||
|
Enables modifying subscription files using info.json files using the argument
|
||||||
|
``--update-with-info-json``. This feature is still being tested and has the ability to
|
||||||
|
destroy files. Ensure you have a full backup before usage. You have been warned!
|
||||||
|
"""
|
||||||
|
return self._enable_update_with_info_json.value
|
||||||
|
|
||||||
|
|
||||||
class PersistLogsValidator(StrictDictValidator):
|
class PersistLogsValidator(StrictDictValidator):
|
||||||
_required_keys = {"logs_directory"}
|
_required_keys = {"logs_directory"}
|
||||||
_optional_keys = {"keep_logs_after", "keep_successful_logs"}
|
_optional_keys = {"keep_logs_after", "keep_successful_logs"}
|
||||||
|
|
@ -79,6 +100,7 @@ class PersistLogsValidator(StrictDictValidator):
|
||||||
return self._keep_successful_logs.value
|
return self._keep_successful_logs.value
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
class ConfigOptions(StrictDictValidator):
|
class ConfigOptions(StrictDictValidator):
|
||||||
_required_keys = {"working_directory"}
|
_required_keys = {"working_directory"}
|
||||||
_optional_keys = {
|
_optional_keys = {
|
||||||
|
|
@ -88,6 +110,7 @@ class ConfigOptions(StrictDictValidator):
|
||||||
"lock_directory",
|
"lock_directory",
|
||||||
"ffmpeg_path",
|
"ffmpeg_path",
|
||||||
"ffprobe_path",
|
"ffprobe_path",
|
||||||
|
"experimental",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
|
|
@ -114,6 +137,9 @@ class ConfigOptions(StrictDictValidator):
|
||||||
self._ffprobe_path = self._validate_key(
|
self._ffprobe_path = self._validate_key(
|
||||||
key="ffprobe_path", validator=FFprobeFileValidator, default=_DEFAULT_FFPROBE_PATH
|
key="ffprobe_path", validator=FFprobeFileValidator, default=_DEFAULT_FFPROBE_PATH
|
||||||
)
|
)
|
||||||
|
self._experimental = self._validate_key(
|
||||||
|
key="experimental", validator=ExperimentalValidator, default={}
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def working_directory(self) -> str:
|
def working_directory(self) -> str:
|
||||||
|
|
@ -167,6 +193,13 @@ class ConfigOptions(StrictDictValidator):
|
||||||
"""
|
"""
|
||||||
return self._persist_logs
|
return self._persist_logs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def experimental(self) -> ExperimentalValidator:
|
||||||
|
"""
|
||||||
|
Experimental validator. readthedocs in the validator itself!
|
||||||
|
"""
|
||||||
|
return self._experimental
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lock_directory(self) -> str:
|
def lock_directory(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class InfoJsonDownloader(BaseDownloader[InfoJsonDownloaderOptions]):
|
||||||
file_names_mtime: Dict[str, Dict[str, float]] = defaultdict(dict)
|
file_names_mtime: Dict[str, Dict[str, float]] = defaultdict(dict)
|
||||||
entries: List[Entry] = []
|
entries: List[Entry] = []
|
||||||
|
|
||||||
for download_mapping in self._enhanced_download_archive.mapping._entry_mappings.values():
|
for download_mapping in self._enhanced_download_archive.mapping.entry_mappings.values():
|
||||||
entry = self._get_entry_from_download_mapping(download_mapping)
|
entry = self._get_entry_from_download_mapping(download_mapping)
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ class InfoJsonDownloader(BaseDownloader[InfoJsonDownloaderOptions]):
|
||||||
the working directory
|
the working directory
|
||||||
"""
|
"""
|
||||||
# Use original mapping since the live mapping gets wiped
|
# Use original mapping since the live mapping gets wiped
|
||||||
entry_file_names = self._original_mapping._entry_mappings[entry.uid].file_names
|
entry_file_names = self._original_mapping.entry_mappings[entry.uid].file_names
|
||||||
|
|
||||||
for file_name in entry_file_names:
|
for file_name in entry_file_names:
|
||||||
ext = get_file_extension(file_name)
|
ext = get_file_extension(file_name)
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,7 @@ class InvalidDlArguments(ValidationException):
|
||||||
|
|
||||||
class FileNotDownloadedException(ValueError):
|
class FileNotDownloadedException(ValueError):
|
||||||
"""ytdlp failed to download something"""
|
"""ytdlp failed to download something"""
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentalFeatureNotEnabled(ValidationException):
|
||||||
|
"""Feature is not enabled for usage"""
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,15 @@ class DownloadMappings:
|
||||||
download_mappings._entry_mappings = entry_mappings_json
|
download_mappings._entry_mappings = entry_mappings_json
|
||||||
return download_mappings
|
return download_mappings
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entry_mappings(self) -> Dict[str, DownloadMapping]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Mapping of entries to files
|
||||||
|
"""
|
||||||
|
return self._entry_mappings
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entry_ids(self) -> List[str]:
|
def entry_ids(self) -> List[str]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ from ytdl_sub.cli.main import logger as main_logger
|
||||||
from ytdl_sub.cli.main import main
|
from ytdl_sub.cli.main import main
|
||||||
from ytdl_sub.config.config_file import ConfigFile
|
from ytdl_sub.config.config_file import ConfigFile
|
||||||
from ytdl_sub.subscriptions.subscription import Subscription
|
from ytdl_sub.subscriptions.subscription import Subscription
|
||||||
|
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
|
||||||
from ytdl_sub.utils.file_handler import FileHandler
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
from ytdl_sub.utils.file_handler import FileMetadata
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
|
|
@ -127,7 +128,10 @@ def test_subscription_logs_write_to_file(
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
_download_subscriptions_from_yaml_files(
|
_download_subscriptions_from_yaml_files(
|
||||||
config=config, subscription_paths=subscription_paths, dry_run=dry_run
|
config=config,
|
||||||
|
subscription_paths=subscription_paths,
|
||||||
|
update_with_info_json=False,
|
||||||
|
dry_run=dry_run,
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
assert not mock_success_output
|
assert not mock_success_output
|
||||||
|
|
@ -275,3 +279,22 @@ def test_output_summary():
|
||||||
|
|
||||||
_ = _output_summary(transaction_logs=mock_subscriptions)
|
_ = _output_summary(transaction_logs=mock_subscriptions)
|
||||||
assert True # Test used for manual inspection - too hard to test ansi color codes
|
assert True # Test used for manual inspection - too hard to test ansi color codes
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_with_info_json_requires_experimental_flag(
|
||||||
|
music_video_config_path: Path,
|
||||||
|
music_video_subscription_path: Path,
|
||||||
|
) -> None:
|
||||||
|
with patch.object(
|
||||||
|
sys,
|
||||||
|
"argv",
|
||||||
|
[
|
||||||
|
"ytdl-sub",
|
||||||
|
"--config",
|
||||||
|
str(music_video_config_path),
|
||||||
|
"sub",
|
||||||
|
str(music_video_subscription_path),
|
||||||
|
"--update-with-info-json",
|
||||||
|
],
|
||||||
|
), pytest.raises(ExperimentalFeatureNotEnabled):
|
||||||
|
_ = main()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue