This commit is contained in:
Jesse Bannon 2023-10-19 23:24:51 -07:00
parent 82c9beb00a
commit e464235ac0
5 changed files with 86 additions and 52 deletions

View file

@ -5,7 +5,6 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Tuple
from yt_dlp.utils import sanitize_filename from yt_dlp.utils import sanitize_filename
@ -20,7 +19,6 @@ from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled 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_lock import working_directory_lock from ytdl_sub.utils.file_lock import working_directory_lock
from ytdl_sub.utils.logger import Logger from ytdl_sub.utils.logger import Logger
@ -67,7 +65,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], update_with_info_json: bool, dry_run: bool config: ConfigFile, subscription_paths: List[str], update_with_info_json: bool, dry_run: bool
) -> List[Tuple[Subscription, FileHandlerTransactionLog]]: ) -> List[Subscription]:
""" """
Downloads all subscriptions from one or many subscription yaml files. Downloads all subscriptions from one or many subscription yaml files.
@ -84,7 +82,7 @@ def _download_subscriptions_from_yaml_files(
Returns Returns
------- -------
List of (subscription, transaction_log) List of subscriptions processed
Raises Raises
------ ------
@ -92,7 +90,7 @@ def _download_subscriptions_from_yaml_files(
Any exception during download Any exception during download
""" """
subscriptions: List[Subscription] = [] subscriptions: List[Subscription] = []
output: List[Tuple[Subscription, FileHandlerTransactionLog]] = [] has_exception = False
# 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:
@ -106,31 +104,31 @@ 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: with subscription.exception_handling():
if update_with_info_json: if update_with_info_json:
transaction_log = subscription.update_with_info_json(dry_run=dry_run) subscription.update_with_info_json(dry_run=dry_run)
else: else:
transaction_log = subscription.download(dry_run=dry_run) subscription.download(dry_run=dry_run)
except Exception as exc: # pylint: disable=broad-except
_maybe_write_subscription_log_file(
config=config, subscription=subscription, dry_run=dry_run, exception=exc
)
raise
else:
output.append((subscription, transaction_log))
_maybe_write_subscription_log_file(
config=config, subscription=subscription, dry_run=dry_run
)
Logger.cleanup() # Cleanup logger after each successful subscription download
finally:
gc.collect() # Garbage collect after each subscription download
return output has_exception = has_exception or subscription.exception is not None
_maybe_write_subscription_log_file(
config=config,
subscription=subscription,
dry_run=dry_run,
exception=subscription.exception,
)
if not has_exception:
Logger.cleanup() # Cleanup logger if no exceptions occurred
gc.collect() # Garbage collect after each subscription download
return subscriptions
def _download_subscription_from_cli( def _download_subscription_from_cli(
config: ConfigFile, dry_run: bool, extra_args: List[str] config: ConfigFile, dry_run: bool, extra_args: List[str]
) -> Tuple[Subscription, FileHandlerTransactionLog]: ) -> Subscription:
""" """
Downloads a one-off subscription using the CLI Downloads a one-off subscription using the CLI
@ -158,12 +156,12 @@ def _download_subscription_from_cli(
) )
logger.info("Beginning CLI %s", ("dry run" if dry_run else "download")) logger.info("Beginning CLI %s", ("dry run" if dry_run else "download"))
return subscription, subscription.download(dry_run=dry_run) subscription.download(dry_run=dry_run)
return subscription
def _view_url_from_cli( def _view_url_from_cli(config: ConfigFile, url: str, split_chapters: bool) -> Subscription:
config: ConfigFile, url: str, split_chapters: bool
) -> Tuple[Subscription, FileHandlerTransactionLog]:
""" """
`ytdl-sub view` dry-runs a URL to print its source variables. Use the pre-built `_view` preset, `ytdl-sub view` dry-runs a URL to print its source variables. Use the pre-built `_view` preset,
inject the URL argument, and dry-run. inject the URL argument, and dry-run.
@ -180,10 +178,12 @@ def _view_url_from_cli(
url, url,
" with split chapters" if split_chapters else "", " with split chapters" if split_chapters else "",
) )
return subscription, subscription.download(dry_run=True) subscription.download(dry_run=True)
return subscription
def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]: def main() -> List[Subscription]:
""" """
Entrypoint for ytdl-sub, without the error handling Entrypoint for ytdl-sub, without the error handling
""" """
@ -203,7 +203,7 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
logger.info("No config specified, using defaults.") logger.info("No config specified, using defaults.")
config = ConfigFile(name="default_config", value={}) config = ConfigFile(name="default_config", value={})
transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]] = [] subscriptions: List[Subscription] = []
# If transaction log file is specified, make sure we can open it # If transaction log file is specified, make sure we can open it
_maybe_validate_transaction_log_file(transaction_log_file_path=args.transaction_log) _maybe_validate_transaction_log_file(transaction_log_file_path=args.transaction_log)
@ -222,7 +222,7 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
"full backup before usage. You have been warned!", "full backup before usage. You have been warned!",
) )
transaction_logs = _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,
update_with_info_json=args.update_with_info_json, update_with_info_json=args.update_with_info_json,
@ -231,13 +231,13 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
# One-off download # One-off download
elif args.subparser == "dl": elif args.subparser == "dl":
transaction_logs.append( subscriptions.append(
_download_subscription_from_cli( _download_subscription_from_cli(
config=config, dry_run=args.dry_run, extra_args=extra_args config=config, dry_run=args.dry_run, extra_args=extra_args
) )
) )
elif args.subparser == "view": elif args.subparser == "view":
transaction_logs.append( subscriptions.append(
_view_url_from_cli(config=config, url=args.url, split_chapters=args.split_chapters) _view_url_from_cli(config=config, url=args.url, split_chapters=args.split_chapters)
) )
else: else:
@ -245,10 +245,10 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
if not args.suppress_transaction_log: if not args.suppress_transaction_log:
output_transaction_log( output_transaction_log(
transaction_logs=transaction_logs, subscriptions=subscriptions,
transaction_log_file_path=args.transaction_log, transaction_log_file_path=args.transaction_log,
) )
output_summary(transaction_logs) output_summary(subscriptions)
return transaction_logs return subscriptions

View file

@ -1,10 +1,8 @@
from typing import List from typing import List
from typing import Tuple
from colorama import Fore from colorama import Fore
from ytdl_sub.subscriptions.subscription import Subscription from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.logger import Logger from ytdl_sub.utils.logger import Logger
logger = Logger.get() logger = Logger.get()
@ -37,12 +35,12 @@ def _color_int(value: int) -> str:
return _no_color(str_int) return _no_color(str_int)
def output_summary(transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]]) -> str: def output_summary(subscriptions: List[Subscription]) -> str:
""" """
Parameters Parameters
---------- ----------
transaction_logs subscriptions
Transaction logs from downloaded subscriptions Processed subscriptions
Returns Returns
------- -------
@ -58,7 +56,7 @@ def output_summary(transaction_logs: List[Tuple[Subscription, FileHandlerTransac
width_num_entries: int = 0 width_num_entries: int = 0
# Calculate min width needed # Calculate min width needed
for subscription, _ in transaction_logs: for subscription in subscriptions:
width_sub_name = max(width_sub_name, len(subscription.name)) width_sub_name = max(width_sub_name, len(subscription.name))
width_num_entries_added = max( width_num_entries_added = max(
width_num_entries_added, len(_color_int(subscription.num_entries_added)) width_num_entries_added, len(_color_int(subscription.num_entries_added))
@ -76,12 +74,12 @@ def output_summary(transaction_logs: List[Tuple[Subscription, FileHandlerTransac
width_num_entries += 4 width_num_entries += 4
# Build the summary # Build the summary
for subscription, _ in transaction_logs: for subscription in subscriptions:
num_entries_added = _color_int(subscription.num_entries_added) num_entries_added = _color_int(subscription.num_entries_added)
num_entries_modified = _color_int(subscription.num_entries_modified) num_entries_modified = _color_int(subscription.num_entries_modified)
num_entries_removed = _color_int(subscription.num_entries_removed * -1) num_entries_removed = _color_int(subscription.num_entries_removed * -1)
num_entries = str(subscription.num_entries) num_entries = str(subscription.num_entries)
status = _green("success") status = _red("error") if subscription.exception else _green("success")
summary.append( summary.append(
f"{subscription.name:<{width_sub_name}} " f"{subscription.name:<{width_sub_name}} "

View file

@ -1,10 +1,8 @@
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Tuple
from ytdl_sub.subscriptions.subscription import Subscription from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ValidationException from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.logger import Logger from ytdl_sub.utils.logger import Logger
logger = Logger.get() logger = Logger.get()
@ -23,7 +21,7 @@ def _maybe_validate_transaction_log_file(transaction_log_file_path: Optional[str
def output_transaction_log( def output_transaction_log(
transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]], subscriptions: List[Subscription],
transaction_log_file_path: Optional[str], transaction_log_file_path: Optional[str],
) -> None: ) -> None:
""" """
@ -31,19 +29,19 @@ def output_transaction_log(
Parameters Parameters
---------- ----------
transaction_logs subscriptions
The transaction logs from downloaded subscriptions Processed subscriptions
transaction_log_file_path transaction_log_file_path
Optional file path to write to Optional file path to write to
""" """
transaction_log_file_contents = "" transaction_log_file_contents = ""
for subscription, transaction_log in transaction_logs: for subscription in subscriptions:
if transaction_log.is_empty: if subscription.transaction_log.is_empty:
transaction_log_contents = f"\nNo files changed for {subscription.name}" transaction_log_contents = f"\nNo files changed for {subscription.name}"
else: else:
transaction_log_contents = ( transaction_log_contents = (
f"Transaction log for {subscription.name}:\n" f"Transaction log for {subscription.name}:\n"
f"{transaction_log.to_output_message(subscription.output_directory)}" f"{subscription.transaction_log.to_output_message(subscription.output_directory)}"
) )
if transaction_log_file_path: if transaction_log_file_path:

View file

@ -9,6 +9,7 @@ from ytdl_sub.config.preset_options import OutputOptions
from ytdl_sub.config.preset_options import Overrides from ytdl_sub.config.preset_options import Overrides
from ytdl_sub.config.preset_options import YTDLOptions from ytdl_sub.config.preset_options import YTDLOptions
from ytdl_sub.downloaders.url.validators import MultiUrlValidator from ytdl_sub.downloaders.url.validators import MultiUrlValidator
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.logger import Logger from ytdl_sub.utils.logger import Logger
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
@ -59,6 +60,8 @@ class BaseSubscription(ABC):
migrated_file_name=migrated_file_name, migrated_file_name=migrated_file_name,
) )
self._exception: Optional[Exception] = None
@property @property
def downloader_options(self) -> MultiUrlValidator: def downloader_options(self) -> MultiUrlValidator:
""" """
@ -167,6 +170,24 @@ class BaseSubscription(ABC):
""" """
return self._enhanced_download_archive.num_entries return self._enhanced_download_archive.num_entries
@property
def transaction_log(self) -> FileHandlerTransactionLog:
"""
Returns
-------
Transaction log from the subscription
"""
return self._enhanced_download_archive.get_file_handler_transaction_log()
@property
def exception(self) -> Optional[Exception]:
"""
Returns
-------
An exception if one occurred while processing the subscription
"""
return self._exception
def as_yaml(self) -> str: def as_yaml(self) -> str:
""" """
Returns Returns

View file

@ -326,7 +326,9 @@ class SubscriptionDownload(BaseSubscription, ABC):
If true, do not download any video/audio files or move anything to the output If true, do not download any video/audio files or move anything to the output
directory. directory.
""" """
self._exception = None
self._enhanced_download_archive.reinitialize(dry_run=dry_run) self._enhanced_download_archive.reinitialize(dry_run=dry_run)
plugins = self._initialize_plugins() plugins = self._initialize_plugins()
subscription_ytdl_options = SubscriptionYTDLOptions( subscription_ytdl_options = SubscriptionYTDLOptions(
@ -353,6 +355,19 @@ class SubscriptionDownload(BaseSubscription, ABC):
dry_run=dry_run, dry_run=dry_run,
) )
@contextlib.contextmanager
def exception_handling(self) -> None:
"""
Try to perform something on the subscription.
Store the error if one occurs.
"""
try:
yield
except Exception as exc: # pylint: disable=broad-except
self._exception = exc
return self.transaction_log
def update_with_info_json(self, dry_run: bool = False) -> FileHandlerTransactionLog: def update_with_info_json(self, dry_run: bool = False) -> FileHandlerTransactionLog:
""" """
Performs the subscription update using local info json files. Performs the subscription update using local info json files.
@ -362,7 +377,9 @@ class SubscriptionDownload(BaseSubscription, ABC):
dry_run dry_run
If true, do not modify any video/audio files or move anything to the output directory. If true, do not modify any video/audio files or move anything to the output directory.
""" """
self._exception = None
self._enhanced_download_archive.reinitialize(dry_run=dry_run) self._enhanced_download_archive.reinitialize(dry_run=dry_run)
plugins = self._initialize_plugins() plugins = self._initialize_plugins()
subscription_ytdl_options = SubscriptionYTDLOptions( subscription_ytdl_options = SubscriptionYTDLOptions(