Add --dry-run summary (#92)
* tests updated with new class * fix nfo tags, no tests only manual inspection * txt * dry-run output tested * readability * all tests now have files except rolling recent * better * almost fixed * updated txt files w/indents * files updated, looking good * rolling recent dry-run test * Print in main
This commit is contained in:
parent
cb31156b42
commit
a0b46c0982
27 changed files with 1167 additions and 334 deletions
|
|
@ -8,6 +8,7 @@ from typing import Dict
|
|||
from typing import Generic
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TypeVar
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ from yt_dlp.utils import RejectedVideoReached
|
|||
from ytdl_sub.config.preset_options import Overrides
|
||||
from ytdl_sub.entries.base_entry import BaseEntry
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.utils.logger import Logger
|
||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import DownloadArchiver
|
||||
|
|
@ -197,7 +199,7 @@ class Downloader(DownloadArchiver, Generic[DownloaderOptionsT, DownloaderEntryT]
|
|||
return self._get_entry_dicts_from_info_json_files()
|
||||
|
||||
@abc.abstractmethod
|
||||
def download(self) -> List[DownloaderEntryT]:
|
||||
def download(self) -> List[DownloaderEntryT] | List[Tuple[DownloaderEntryT, FileMetadata]]:
|
||||
"""The function to perform the download of all media entries"""
|
||||
|
||||
def post_download(self, overrides: Overrides):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||
from ytdl_sub.downloaders.youtube.playlist import YoutubePlaylistDownloaderOptions
|
||||
|
|
@ -8,6 +9,7 @@ from ytdl_sub.entries.youtube import YoutubeVideo
|
|||
from ytdl_sub.utils.chapters import Chapters
|
||||
from ytdl_sub.utils.chapters import Timestamp
|
||||
from ytdl_sub.utils.ffmpeg import add_ffmpeg_metadata
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.validators.validators import BoolValidator
|
||||
|
||||
|
||||
|
|
@ -100,7 +102,7 @@ class YoutubeMergePlaylistDownloader(
|
|||
},
|
||||
)
|
||||
|
||||
def _add_chapters(self, merged_video: YoutubeVideo) -> None:
|
||||
def _get_chapters(self, merged_video: YoutubeVideo, add_chapters: bool) -> FileMetadata:
|
||||
titles: List[str] = []
|
||||
timestamps: List[Timestamp] = []
|
||||
|
||||
|
|
@ -111,14 +113,17 @@ class YoutubeMergePlaylistDownloader(
|
|||
|
||||
current_timestamp_sec += video_entry["duration"]
|
||||
|
||||
# TODO: return chapter metadata here
|
||||
if not self.is_dry_run:
|
||||
chapters = Chapters(timestamps=timestamps, titles=titles)
|
||||
|
||||
if not self.is_dry_run and add_chapters:
|
||||
add_ffmpeg_metadata(
|
||||
file_path=merged_video.get_download_file_path(),
|
||||
chapters=Chapters(timestamps=timestamps, titles=titles),
|
||||
chapters=chapters,
|
||||
file_duration_sec=merged_video.kwargs("duration"),
|
||||
)
|
||||
|
||||
return chapters.to_file_metadata(title="Timestamps of playlist videos in the merged file:")
|
||||
|
||||
def _to_merged_video(self, entry_dict: Dict) -> YoutubeVideo:
|
||||
"""
|
||||
Adds a few entries not included in a playlist entry to make it look like a merged video
|
||||
|
|
@ -138,13 +143,12 @@ class YoutubeMergePlaylistDownloader(
|
|||
)
|
||||
return YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)
|
||||
|
||||
def download(self) -> List[YoutubeVideo]:
|
||||
def download(self) -> List[Tuple[YoutubeVideo, FileMetadata]]:
|
||||
"""Download a single Youtube video, then split it into multiple videos"""
|
||||
merged_video = self._to_merged_video(
|
||||
entry_dict=self.extract_info(url=self.download_options.playlist_url)
|
||||
)
|
||||
|
||||
if self.download_options.add_chapters:
|
||||
self._add_chapters(merged_video=merged_video)
|
||||
|
||||
return [merged_video]
|
||||
merged_video_metadata = self._get_chapters(
|
||||
merged_video=merged_video, add_chapters=self.download_options.add_chapters
|
||||
)
|
||||
return [(merged_video, merged_video_metadata)]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import re
|
|||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||
from ytdl_sub.downloaders.youtube.video import YoutubeVideoDownloaderOptions
|
||||
|
|
@ -19,6 +20,7 @@ from ytdl_sub.utils.ffmpeg import FFMPEG
|
|||
# 01:00:00 title
|
||||
# where capture group 1 and 2 are the timestamp and title, respectively
|
||||
from ytdl_sub.utils.file_handler import FileHandler
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||
from ytdl_sub.validators.validators import StringValidator
|
||||
|
||||
|
|
@ -32,8 +34,8 @@ def _split_video_uid(source_uid: str, idx: int) -> str:
|
|||
def _split_video_ffmpeg_cmd(
|
||||
input_file: str, output_file: str, timestamps: List[Timestamp], idx: int
|
||||
) -> List[str]:
|
||||
timestamp_begin = timestamps[idx].timestamp_str
|
||||
timestamp_end = timestamps[idx + 1].timestamp_str if idx + 1 < len(timestamps) else ""
|
||||
timestamp_begin = timestamps[idx].standardized_str
|
||||
timestamp_end = timestamps[idx + 1].standardized_str if idx + 1 < len(timestamps) else ""
|
||||
|
||||
cmd = ["-i", input_file, "-ss", timestamp_begin]
|
||||
if timestamp_end:
|
||||
|
|
@ -121,15 +123,15 @@ class YoutubeSplitVideoDownloader(
|
|||
)
|
||||
|
||||
def _create_split_video_entry(
|
||||
self, source_entry_dict: Dict, title: str, idx: int, split_video_count: int
|
||||
) -> YoutubePlaylistVideo:
|
||||
self, source_entry_dict: Dict, title: str, idx: int, chapters: Chapters
|
||||
) -> Tuple[YoutubePlaylistVideo, FileMetadata]:
|
||||
"""
|
||||
Runs ffmpeg to create the split video
|
||||
"""
|
||||
entry_dict = copy.deepcopy(source_entry_dict)
|
||||
entry_dict["title"] = title
|
||||
entry_dict["playlist_index"] = idx + 1
|
||||
entry_dict["playlist_count"] = split_video_count
|
||||
entry_dict["playlist_count"] = len(chapters.timestamps)
|
||||
entry_dict["id"] = _split_video_uid(source_uid=entry_dict["id"], idx=idx)
|
||||
|
||||
# Remove track and artist since its now split
|
||||
|
|
@ -138,11 +140,20 @@ class YoutubeSplitVideoDownloader(
|
|||
if "artist" in entry_dict:
|
||||
del entry_dict["artist"]
|
||||
|
||||
return YoutubePlaylistVideo(entry_dict=entry_dict, working_directory=self.working_directory)
|
||||
timestamp_begin = chapters.timestamps[idx].readable_str
|
||||
timestamp_end = Timestamp(source_entry_dict["duration"]).readable_str
|
||||
if idx + 1 < len(chapters.timestamps):
|
||||
timestamp_end = chapters.timestamps[idx + 1].readable_str
|
||||
|
||||
def download(self) -> List[YoutubePlaylistVideo]:
|
||||
metadata = FileMetadata(metadata=f"{timestamp_begin} - {timestamp_end}")
|
||||
return (
|
||||
YoutubePlaylistVideo(entry_dict=entry_dict, working_directory=self.working_directory),
|
||||
metadata,
|
||||
)
|
||||
|
||||
def download(self) -> List[Tuple[YoutubePlaylistVideo, FileMetadata]]:
|
||||
"""Download a single Youtube video, then split it into multiple videos"""
|
||||
split_videos: List[YoutubePlaylistVideo] = []
|
||||
split_videos_and_metadata: List[Tuple[YoutubePlaylistVideo, FileMetadata]] = []
|
||||
|
||||
chapters = Chapters.from_file(chapters_file_path=self.download_options.split_timestamps)
|
||||
entry_dict = self.extract_info(url=self.download_options.video_url)
|
||||
|
|
@ -178,13 +189,10 @@ class YoutubeSplitVideoDownloader(
|
|||
)
|
||||
|
||||
# Format the split video as a YoutubePlaylistVideo
|
||||
split_videos.append(
|
||||
split_videos_and_metadata.append(
|
||||
self._create_split_video_entry(
|
||||
source_entry_dict=entry_dict,
|
||||
title=title,
|
||||
idx=idx,
|
||||
split_video_count=len(chapters.timestamps),
|
||||
source_entry_dict=entry_dict, title=title, idx=idx, chapters=chapters
|
||||
)
|
||||
)
|
||||
|
||||
return split_videos
|
||||
return split_videos_and_metadata
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import argparse
|
||||
import sys
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
from ytdl_sub.cli.download_args_parser import DownloadArgsParser
|
||||
from ytdl_sub.cli.main_args_parser import parser
|
||||
|
|
@ -8,12 +9,15 @@ from ytdl_sub.config.config_file import ConfigFile
|
|||
from ytdl_sub.config.preset import Preset
|
||||
from ytdl_sub.subscriptions.subscription import Subscription
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
from ytdl_sub.utils.logger import Logger
|
||||
|
||||
logger = Logger.get()
|
||||
|
||||
|
||||
def _download_subscriptions_from_yaml_files(config: ConfigFile, args: argparse.Namespace) -> None:
|
||||
def _download_subscriptions_from_yaml_files(
|
||||
config: ConfigFile, args: argparse.Namespace
|
||||
) -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
|
||||
"""
|
||||
Downloads all subscriptions from one or many subscription yaml files.
|
||||
|
||||
|
|
@ -23,10 +27,16 @@ def _download_subscriptions_from_yaml_files(config: ConfigFile, args: argparse.N
|
|||
Configuration file
|
||||
args
|
||||
Arguments from argparse
|
||||
|
||||
Returns
|
||||
-------
|
||||
List of (subscription, transaction_log)
|
||||
"""
|
||||
preset_paths: List[str] = args.subscription_paths
|
||||
presets: List[Preset] = []
|
||||
output: List[Tuple[Subscription, FileHandlerTransactionLog]] = []
|
||||
|
||||
# Load all of the presets first to perform all validation before downloading
|
||||
for preset_path in preset_paths:
|
||||
presets += Preset.from_file_path(config=config, subscription_path=preset_path)
|
||||
|
||||
|
|
@ -34,12 +44,16 @@ def _download_subscriptions_from_yaml_files(config: ConfigFile, args: argparse.N
|
|||
subscription = Subscription.from_preset(preset=preset, config=config)
|
||||
|
||||
logger.info("Beginning subscription download for %s", subscription.name)
|
||||
subscription.download(dry_run=args.dry_run)
|
||||
transaction_log = subscription.download(dry_run=args.dry_run)
|
||||
|
||||
output.append((subscription, transaction_log))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def _download_subscription_from_cli(
|
||||
config: ConfigFile, args: argparse.Namespace, extra_args: List[str]
|
||||
) -> None:
|
||||
) -> Tuple[Subscription, FileHandlerTransactionLog]:
|
||||
"""
|
||||
Downloads a one-off subscription using the CLI
|
||||
|
||||
|
|
@ -51,6 +65,10 @@ def _download_subscription_from_cli(
|
|||
Arguments from argparse
|
||||
extra_args
|
||||
Extra arguments from argparse that contain dynamic subscription options
|
||||
|
||||
Returns
|
||||
-------
|
||||
Subscription and its download transaction log
|
||||
"""
|
||||
dl_args_parser = DownloadArgsParser(
|
||||
extra_arguments=extra_args, config_options=config.config_options
|
||||
|
|
@ -69,7 +87,7 @@ def _download_subscription_from_cli(
|
|||
config=config,
|
||||
)
|
||||
|
||||
subscription.download(dry_run=args.dry_run)
|
||||
return subscription, subscription.download(dry_run=args.dry_run)
|
||||
|
||||
|
||||
def _main():
|
||||
|
|
@ -86,14 +104,22 @@ def _main():
|
|||
config: ConfigFile = ConfigFile.from_file_path(args.config).initialize()
|
||||
Logger.set_log_level(log_level_name=args.log_level)
|
||||
|
||||
transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]] = []
|
||||
if args.subparser == "sub":
|
||||
_download_subscriptions_from_yaml_files(config=config, args=args)
|
||||
logger.info("Subscription download complete!")
|
||||
transaction_logs = _download_subscriptions_from_yaml_files(config=config, args=args)
|
||||
|
||||
# One-off download
|
||||
if args.subparser == "dl":
|
||||
_download_subscription_from_cli(config=config, args=args, extra_args=extra_args)
|
||||
logger.info("Download complete!")
|
||||
transaction_logs.append(
|
||||
_download_subscription_from_cli(config=config, args=args, extra_args=extra_args)
|
||||
)
|
||||
|
||||
for subscription, transaction_log in transaction_logs:
|
||||
logger.info(
|
||||
"Downloads for %s:\n%s\n",
|
||||
subscription.name,
|
||||
transaction_log.to_output_message(subscription.output_directory),
|
||||
)
|
||||
|
||||
# Ran successfully, so we can delete the debug file
|
||||
Logger.cleanup(delete_debug_file=True)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class MusicTagsOptions(PluginOptions):
|
|||
class MusicTagsPlugin(Plugin[MusicTagsOptions]):
|
||||
plugin_options_type = MusicTagsOptions
|
||||
|
||||
def post_process_entry(self, entry: Entry):
|
||||
def post_process_entry(self, entry: Entry) -> FileMetadata:
|
||||
"""
|
||||
Tags the entry's audio file using values defined in the metadata options
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class NfoTagsOptions(PluginOptions):
|
|||
class NfoTagsPlugin(Plugin[NfoTagsOptions]):
|
||||
plugin_options_type = NfoTagsOptions
|
||||
|
||||
def post_process_entry(self, entry: Entry):
|
||||
def post_process_entry(self, entry: Entry) -> None:
|
||||
"""
|
||||
Creates an entry's NFO file using values defined in the metadata options
|
||||
|
||||
|
|
@ -115,7 +115,6 @@ class NfoTagsPlugin(Plugin[NfoTagsOptions]):
|
|||
with open(nfo_file_path, "wb") as nfo_file:
|
||||
nfo_file.write(xml)
|
||||
|
||||
# Archive the nfo's file name
|
||||
self.save_file(file_name=nfo_file_name, entry=entry)
|
||||
|
||||
return FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
|
||||
# Save the nfo file and log its metadata
|
||||
nfo_metadata = FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
|
||||
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry)
|
||||
|
|
|
|||
|
|
@ -106,5 +106,5 @@ class OutputDirectoryNfoTagsPlugin(Plugin[OutputDirectoryNfoTagsOptions]):
|
|||
with open(nfo_file_path, "wb") as nfo_file:
|
||||
nfo_file.write(xml)
|
||||
|
||||
self.save_file(file_name=nfo_file_name)
|
||||
return FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
|
||||
nfo_metadata = FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
|
||||
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata)
|
||||
|
|
|
|||
|
|
@ -267,10 +267,14 @@ class Subscription:
|
|||
)
|
||||
|
||||
for entry in downloader.download():
|
||||
# TODO: Add entry metadata from the downloader.download function
|
||||
entry_metadata = FileMetadata()
|
||||
if isinstance(entry, tuple):
|
||||
entry, entry_metadata = entry
|
||||
|
||||
for plugin in plugins:
|
||||
entry_metadata.extend(plugin.post_process_entry(entry))
|
||||
optional_plugin_entry_metadata = plugin.post_process_entry(entry)
|
||||
if optional_plugin_entry_metadata:
|
||||
entry_metadata.extend(optional_plugin_entry_metadata)
|
||||
|
||||
self._copy_entry_files_to_output_directory(
|
||||
entry=entry, entry_metadata=entry_metadata
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import os
|
||||
import re
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
|
||||
|
||||
class Timestamp:
|
||||
|
|
@ -48,12 +51,7 @@ class Timestamp:
|
|||
return self._timestamp_sec
|
||||
|
||||
@property
|
||||
def timestamp_str(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The timestamp in 'HH:MM:SS' format
|
||||
"""
|
||||
def _hours_minutes_seconds(self) -> Tuple[int, int, int]:
|
||||
seconds = self.timestamp_sec
|
||||
|
||||
hours = int(seconds / 3600)
|
||||
|
|
@ -62,6 +60,30 @@ class Timestamp:
|
|||
minutes = int(seconds / 60)
|
||||
seconds -= minutes * 60
|
||||
|
||||
return hours, minutes, seconds
|
||||
|
||||
@property
|
||||
def readable_str(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The timestamp in '0:SS' format (min trim).
|
||||
"""
|
||||
hours, minutes, seconds = self._hours_minutes_seconds
|
||||
if hours:
|
||||
return f"{str(hours)}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}"
|
||||
if minutes:
|
||||
return f"{str(minutes)}:{str(seconds).zfill(2)}"
|
||||
return f"0:{str(seconds).zfill(2)}"
|
||||
|
||||
@property
|
||||
def standardized_str(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The timestamp in 'HH:MM:SS' format
|
||||
"""
|
||||
hours, minutes, seconds = self._hours_minutes_seconds
|
||||
return f"{str(hours).zfill(2)}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}"
|
||||
|
||||
@classmethod
|
||||
|
|
@ -125,6 +147,22 @@ class Chapters:
|
|||
"""
|
||||
return self.timestamps[0].timestamp_sec == 0
|
||||
|
||||
def to_file_metadata(self, title: Optional[str] = None) -> FileMetadata:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
title
|
||||
Optional title
|
||||
|
||||
Returns
|
||||
-------
|
||||
Chapter metadata in the format of { readable_timestamp_str: title }
|
||||
"""
|
||||
return FileMetadata.from_dict(
|
||||
value_dict={ts.readable_str: title for ts, title in zip(self.timestamps, self.titles)},
|
||||
title=title,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, chapters_file_path: str) -> "Chapters":
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ class FileMetadata:
|
|||
Stores pretty-printed information about a file. Each line in the metadata represents a newline
|
||||
"""
|
||||
|
||||
def __init__(self, metadata: Optional[List[str]] = None):
|
||||
self.metadata: List[str] = metadata if metadata else []
|
||||
def __init__(self, metadata: Optional[Union[str, List[str]]] = None):
|
||||
self.metadata = []
|
||||
if isinstance(metadata, str):
|
||||
self.metadata = [metadata]
|
||||
elif isinstance(metadata, list):
|
||||
self.metadata = metadata
|
||||
|
||||
def append(self, line: str) -> "FileMetadata":
|
||||
"""
|
||||
|
|
@ -54,11 +58,19 @@ class FileMetadata:
|
|||
|
||||
def _recursive_add_dict_lines(rdict: Dict, indent: int):
|
||||
for key, value in sorted(rdict.items()):
|
||||
if isinstance(value, Dict):
|
||||
_recursive_add_dict_lines(rdict=value, indent=indent + 2)
|
||||
|
||||
_indent = " " * indent
|
||||
lines.append(f"{_indent}{key}: {value}")
|
||||
if isinstance(value, Dict):
|
||||
lines.append(f"{_indent}{key}:")
|
||||
_recursive_add_dict_lines(rdict=value, indent=indent + 2)
|
||||
else:
|
||||
value = str(value)
|
||||
# If there are newlines in the value, print them indented
|
||||
if "\n" in value:
|
||||
lines.append(f"{_indent}{key}:")
|
||||
for value_line in value.split("\n"):
|
||||
lines.append(f" {_indent}{value_line.strip()}")
|
||||
else:
|
||||
lines.append(f"{_indent}{key}: {value}")
|
||||
|
||||
_recursive_add_dict_lines(rdict=value_dict, indent=2)
|
||||
return cls(metadata=lines)
|
||||
|
|
@ -103,6 +115,46 @@ class FileHandlerTransactionLog:
|
|||
self.files_removed.add(file_name)
|
||||
return self
|
||||
|
||||
def to_output_message(self, output_directory: str) -> str:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
output_directory
|
||||
Path to the output directory. Included in the output message
|
||||
|
||||
Returns
|
||||
-------
|
||||
The output message to show users what was recorded in the transaction log
|
||||
"""
|
||||
lines: List[str] = []
|
||||
|
||||
def _indent_metadata_line(line: str) -> str:
|
||||
# Do not indent empty lines
|
||||
rstrip_line = line.rstrip()
|
||||
return f" {rstrip_line}" if rstrip_line else ""
|
||||
|
||||
if self.files_created:
|
||||
created_line = f"Files created in '{output_directory}'"
|
||||
created_line_dash = "-" * 40
|
||||
lines.extend([created_line, created_line_dash])
|
||||
for file_path, file_metadata in sorted(self.files_created.items()):
|
||||
lines.append(file_path)
|
||||
if file_metadata:
|
||||
lines.extend([_indent_metadata_line(line) for line in file_metadata.metadata])
|
||||
|
||||
if self.files_removed:
|
||||
# Add a blank line to separate created/removed files
|
||||
if self.files_created:
|
||||
lines.append("")
|
||||
|
||||
removed_line = f"Files removed from '{output_directory}'"
|
||||
removed_line_dash = "-" * 40
|
||||
lines.extend([removed_line, removed_line_dash])
|
||||
for file_path in sorted(self.files_removed):
|
||||
lines.append(file_path)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class FileHandler:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -618,7 +618,11 @@ class DownloadArchiver:
|
|||
return self.__enhanced_download_archive.is_dry_run
|
||||
|
||||
def save_file(
|
||||
self, file_name: str, output_file_name: Optional[str] = None, entry: Optional[Entry] = None
|
||||
self,
|
||||
file_name: str,
|
||||
file_metadata: Optional[FileMetadata] = None,
|
||||
output_file_name: Optional[str] = None,
|
||||
entry: Optional[Entry] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Saves a file in the working directory to the output directory.
|
||||
|
|
@ -627,6 +631,8 @@ class DownloadArchiver:
|
|||
----------
|
||||
file_name
|
||||
Name of the file relative to the working directory
|
||||
file_metadata
|
||||
Optional. Metadata to record to the transaction log for this file
|
||||
output_file_name
|
||||
Optional. Final name of the file in the output directory (does not include output
|
||||
directory path). If None, use the same working_directory file_name
|
||||
|
|
@ -634,5 +640,8 @@ class DownloadArchiver:
|
|||
Optional. Entry that the file belongs to
|
||||
"""
|
||||
self.__enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=file_name, output_file_name=output_file_name, entry=entry
|
||||
file_name=file_name,
|
||||
file_metadata=file_metadata,
|
||||
output_file_name=output_file_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
import hashlib
|
||||
import os.path
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
|
||||
|
||||
class ExpectedDownload:
|
||||
@dataclass
|
||||
class ExpectedDownloadFile:
|
||||
path: Path
|
||||
md5: Optional[Union[str, List[str]]] = None
|
||||
metadata: Optional[FileMetadata] = None
|
||||
|
||||
|
||||
class ExpectedDownloads:
|
||||
"""
|
||||
To test ytdl-sub downloads work, we compare each downloaded file's md5 hash to an
|
||||
expected md5 hash defined in this class.
|
||||
|
|
@ -18,12 +25,15 @@ class ExpectedDownload:
|
|||
try all the hashes (used in case the GitHub env produces different deterministic value).
|
||||
"""
|
||||
|
||||
def __init__(self, expected_md5_file_hashes: Dict[Path, Optional[Union[str, List[str]]]]):
|
||||
self.expected_md5_file_hashes = expected_md5_file_hashes
|
||||
def __init__(self, expected_downloads: List[ExpectedDownloadFile]):
|
||||
self.expected_downloads = expected_downloads
|
||||
|
||||
@property
|
||||
def file_count(self) -> int:
|
||||
return len(self.expected_md5_file_hashes)
|
||||
return len(self.expected_downloads)
|
||||
|
||||
def contains(self, relative_path: Path) -> bool:
|
||||
return sum(relative_path == download.path for download in self.expected_downloads) == 1
|
||||
|
||||
def assert_files_exist(self, relative_directory: Path):
|
||||
"""
|
||||
|
|
@ -35,38 +45,29 @@ class ExpectedDownload:
|
|||
num_files += 1
|
||||
|
||||
relative_path = Path(*path.parts[3:])
|
||||
assert (
|
||||
relative_path in self.expected_md5_file_hashes
|
||||
assert self.contains(
|
||||
relative_path
|
||||
), f"File {relative_path} was created but not expected"
|
||||
|
||||
assert num_files == self.file_count, "Mismatch in number of created files"
|
||||
|
||||
for relative_path, expected_md5_hash in self.expected_md5_file_hashes.items():
|
||||
full_path = Path(relative_directory) / relative_path
|
||||
for expected_download in self.expected_downloads:
|
||||
full_path = Path(relative_directory) / expected_download.path
|
||||
assert os.path.isfile(
|
||||
full_path
|
||||
), f"Expected {str(relative_path)} to be a file but it is not"
|
||||
), f"Expected {str(expected_download.path)} to be a file but it is not"
|
||||
|
||||
if expected_md5_hash is None:
|
||||
if expected_download.md5 is None:
|
||||
continue
|
||||
|
||||
with open(full_path, "rb") as file:
|
||||
md5_hash = hashlib.md5(file.read()).hexdigest()
|
||||
|
||||
if isinstance(expected_md5_hash, str):
|
||||
expected_md5_hash = [expected_md5_hash]
|
||||
expected_md5_hash = expected_download.md5
|
||||
if isinstance(expected_download.md5, str):
|
||||
expected_md5_hash = [expected_download.md5]
|
||||
|
||||
assert md5_hash in expected_md5_hash, (
|
||||
f"MD5 hash for {str(relative_path)} does not match: "
|
||||
f"MD5 hash for {str(expected_download.path)} does not match: "
|
||||
f"{md5_hash} != {expected_md5_hash}"
|
||||
)
|
||||
|
||||
def assert_dry_run_files_logged(self, transaction_log: FileHandlerTransactionLog):
|
||||
assert (
|
||||
len(transaction_log.files_created) == self.file_count
|
||||
), "Mismatch in number of created files"
|
||||
|
||||
for relative_path in self.expected_md5_file_hashes.keys():
|
||||
assert (
|
||||
str(relative_path) in transaction_log.files_created
|
||||
), f"Expected {str(relative_path)} to be a file but it is not"
|
||||
|
|
|
|||
55
tests/e2e/expected_transaction_log.py
Normal file
55
tests/e2e/expected_transaction_log.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
|
||||
_TRANSACTION_LOG_SUMMARY_PATH = Path("tests/e2e/resources/transaction_log_summaries")
|
||||
|
||||
|
||||
def assert_transaction_log_matches(
|
||||
output_directory: str,
|
||||
transaction_log: FileHandlerTransactionLog,
|
||||
transaction_log_summary_file_name: str,
|
||||
regenerate_transaction_log: bool = False,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
output_directory
|
||||
Output directory the files are saved to
|
||||
transaction_log
|
||||
Transaction log to check
|
||||
transaction_log_summary_file_name
|
||||
Name if the transaction log summary to compare.
|
||||
Lives in tests/e2e/resources/transaction_log_summaries
|
||||
regenerate_transaction_log
|
||||
USE WITH CAUTION - MANUALLY INSPECT CHANGED FILES TO ENSURE THEY LOOK GOOD!
|
||||
Updates the file with the input transaction log. Should only be used to update
|
||||
tests after an expected change is made.
|
||||
"""
|
||||
transaction_log_path = _TRANSACTION_LOG_SUMMARY_PATH / transaction_log_summary_file_name
|
||||
summary = transaction_log.to_output_message(output_directory=output_directory)
|
||||
|
||||
# Write the expected summary file if regenerate is True
|
||||
if regenerate_transaction_log:
|
||||
with open(transaction_log_path, "w", encoding="utf-8") as summary_file:
|
||||
summary_file.write(
|
||||
transaction_log.to_output_message(output_directory="{output_directory}")
|
||||
)
|
||||
|
||||
# Read the expected summary file
|
||||
with open(transaction_log_path, "r", encoding="utf-8") as summary_file:
|
||||
expected_summary = summary_file.read().format(output_directory=output_directory)
|
||||
|
||||
# Split, ensure there are the same number of new lines
|
||||
summary_lines: List[str] = summary.split("\n")
|
||||
expected_summary_lines: List[str] = expected_summary.split("\n")
|
||||
assert len(summary_lines) == len(
|
||||
expected_summary_lines
|
||||
), f"Summary number of lines differ: {len(summary_lines) != len(expected_summary_lines)}"
|
||||
|
||||
# Ensure each line equals
|
||||
for idx in range(len(summary_lines)):
|
||||
line = summary_lines[idx]
|
||||
expected_line = expected_summary_lines[idx]
|
||||
assert line == expected_line, f"Summary line {idx} differs: '{line}' != '{expected_line}'"
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-jb-download-archive.json
|
||||
j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3
|
||||
Music Tags:
|
||||
album: Baby Santana's Dorian Groove
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: Baby Santana's Dorian Groove
|
||||
track: 1
|
||||
year: 2021
|
||||
j_b/[2021] Baby Santana's Dorian Groove/folder.jpg
|
||||
j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3
|
||||
Music Tags:
|
||||
album: Purple Clouds
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: Purple Clouds
|
||||
track: 1
|
||||
year: 2021
|
||||
j_b/[2021] Purple Clouds/folder.jpg
|
||||
j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20160426 184214
|
||||
track: 1
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20160502 123150
|
||||
track: 2
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20160504 143832
|
||||
track: 3
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20160601 221234
|
||||
track: 4
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20160601 222440
|
||||
track: 5
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20170604 190236
|
||||
track: 6
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20170612 193646
|
||||
track: 7
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: 20170628 215206
|
||||
track: 8
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/09 - Finding Home.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: Finding Home
|
||||
track: 9
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: Shallow Water WIP
|
||||
track: 10
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/11 - Untold History.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
albumartist: j_b
|
||||
artist: j_b
|
||||
genre: Unset
|
||||
title: Untold History
|
||||
track: 11
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/folder.jpg
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-pz-download-archive.json
|
||||
Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1-thumb.jpg
|
||||
Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.mp4
|
||||
Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2010-08-13
|
||||
episode: 813
|
||||
plot:
|
||||
PLEASE WATCH IN HD!
|
||||
|
||||
This is the castle in the mod "Falcor" which we are currently working on.
|
||||
You can find some more information about the mod @ http://www.oblivionfalcormod.webs.com/
|
||||
|
||||
This is the first time I've really used the editor, and the beginning of our first mod. Hope you enjoy.
|
||||
season: 2010
|
||||
title: Oblivion Mod "Falcor" p.1
|
||||
year: 2010
|
||||
Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2-thumb.jpg
|
||||
Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.mp4
|
||||
Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2010-12-02
|
||||
episode: 1202
|
||||
plot:
|
||||
PLEASE WATCH IN HD.
|
||||
|
||||
Download Link:
|
||||
http://www.tesnexus.com/downloads/file.php?id=36306
|
||||
|
||||
Please keep in mind that the mod is not complete, so you'll see some blank areas during the video. I haven't made LOD (Level of Distance) yet either, so areas far away will not appear.
|
||||
season: 2010
|
||||
title: Oblivion Mod "Falcor" p.2
|
||||
year: 2010
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-02-01
|
||||
episode: 201
|
||||
plot:
|
||||
To join the server, you must apply at:
|
||||
http://www.jesseminecraft.webs.com/
|
||||
|
||||
This is just a brief video of the server as of Feb. 1, 2011.
|
||||
|
||||
Texture Pack I Use:
|
||||
http://www.minecraftforum.net/viewtopic.php?f=25&t=29164
|
||||
season: 2011
|
||||
title: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-02-27
|
||||
episode: 227
|
||||
plot:
|
||||
Website Link:
|
||||
http://jesseminecraft.webs.com/
|
||||
|
||||
All you have to do is read the rules, and fill out a quick, little application to join the Server so we know you read them. We do this to keep the griefers/newbs out, it only takes three minutes, it's not that big of a deal.
|
||||
|
||||
Jesse's Minecraft Server is a City Roleplay/Survival Server, there is a currency, public transportation, strict rules for immersive gameplay, and has a decent size population. Over 750 people have logged onto the server. It is very important you read the rules, we don't tolerate idiots.
|
||||
|
||||
There are over 200 empty properties that are ready for anyone to own! Join now!
|
||||
|
||||
This is the server state as of Feb. 27, 2011.
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
Texture Pack:
|
||||
http://www.minecraftforum.net/viewtopic.php?f=25&t=29164
|
||||
|
||||
Recording Software:
|
||||
http://www.fraps.com/download.php
|
||||
|
||||
Video Editing Software:
|
||||
http://explore.live.com/windows-live-movie-maker?os=other
|
||||
|
||||
Song:
|
||||
Given to Fly - Pearl Jam
|
||||
(Off of the 'Yield' album)
|
||||
|
||||
I claim no ownership of this song, all the credit goes to Pearl Jam and their producers.
|
||||
season: 2011
|
||||
title: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-03-21
|
||||
episode: 321
|
||||
plot:
|
||||
Website Link:
|
||||
http://jesseminecraft.webs.com/
|
||||
|
||||
To get on the whitelist, please look at the website linked above (^^^). Due to the overwhelming amount of people trying to join, I've made it so it costs $2 to become a member through paypal. All of it is explained on the website.
|
||||
|
||||
Jesse's Minecraft Server is a City Roleplay/Survival Server, there is a currency, public transportation, strict rules for immersive gameplay, and has a decent size population. Over 1000 people have logged onto the server. It is very important you read the rules, we don't tolerate idiots.
|
||||
|
||||
There are over 300 empty properties that are ready for anyone to own! Join now!
|
||||
|
||||
This is the server state as of Mar. 21, 2011.
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
Texture Pack:
|
||||
http://www.minecraftforum.net/viewtopic.php?f=25&t=29164
|
||||
|
||||
Recording Software:
|
||||
http://www.fraps.com/download.php
|
||||
|
||||
Video Editing Software:
|
||||
http://explore.live.com/windows-live-movie-maker?os=other
|
||||
|
||||
Song:
|
||||
Indifference - Pearl Jam
|
||||
(Off of the 'Vs.' album)
|
||||
|
||||
I claim no ownership of this song, all the credit goes to Pearl Jam and their producers.
|
||||
season: 2011
|
||||
title: Jesse's Minecraft Server [Trailer - Mar.21]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net)-thumb.jpg
|
||||
Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).mp4
|
||||
Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-05-29
|
||||
episode: 529
|
||||
plot:
|
||||
Currently in alpha testing.
|
||||
http://www.projectzombie.net/
|
||||
|
||||
Credits:
|
||||
-Master_Queef (Jesse)
|
||||
-Cheeseducksg
|
||||
-Vsalaka
|
||||
-Edwingsd
|
||||
-All Members on my Roleplay Server
|
||||
-All Mercenaries in Project Zombie
|
||||
|
||||
Song:
|
||||
Johnny Cash - God's Gonna Cut You Down
|
||||
|
||||
Texture Pack:
|
||||
http://www.minecraftforum.net/topic/26727-johnsmith-texture-pack-v73-32x32-16x-with-customizer/
|
||||
season: 2011
|
||||
title: Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net)
|
||||
year: 2011
|
||||
Season 2011/s2011.e0630 - Project Zombie _Fin-thumb.jpg
|
||||
Season 2011/s2011.e0630 - Project Zombie _Fin.mp4
|
||||
Season 2011/s2011.e0630 - Project Zombie _Fin.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-06-30
|
||||
episode: 630
|
||||
plot:
|
||||
Twas a good run while it lasted. If you're still interested in playing PZ, you can join my Roleplay Server, yes, Project Zombie is completely imported onto that one:
|
||||
http://www.jesseminecraft.webs.com/
|
||||
season: 2011
|
||||
title: Project Zombie |Fin|
|
||||
year: 2011
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC]-thumb.jpg
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].mp4
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2011-11-21
|
||||
episode: 1121
|
||||
plot:
|
||||
Mods I have used:
|
||||
http://skyrimnexus.com/downloads/file.php?id=329
|
||||
http://skyrimnexus.com/downloads/file.php?id=607
|
||||
http://skyrimnexus.com/downloads/file.php?id=711
|
||||
http://skyrimnexus.com/downloads/file.php?id=302
|
||||
http://skyrimnexus.com/downloads/file.php?id=141
|
||||
http://skyrimnexus.com/downloads/file.php?id=131
|
||||
http://skyrimnexus.com/downloads/file.php?id=85
|
||||
http://skyrimnexus.com/downloads/file.php?id=191
|
||||
season: 2011
|
||||
title: Skyrim 'Ultra HD w/Mods' [PC]
|
||||
year: 2011
|
||||
Season 2012/s2012.e0123 - Project Zombie _Map Trailer-thumb.jpg
|
||||
Season 2012/s2012.e0123 - Project Zombie _Map Trailer.mp4
|
||||
Season 2012/s2012.e0123 - Project Zombie _Map Trailer.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2012-01-23
|
||||
episode: 123
|
||||
plot:
|
||||
Faction Server IP: 72.9.157.88:25652
|
||||
Project Zombie IP: mc.projectzombie.beastnode.net
|
||||
Project Zombie Map: http://tinyurl.com/projectzombie1
|
||||
Website: http://jesseminecraft.webs.com/
|
||||
Map Download: http://www.planetminecraft.com/project/project-zombie-511865/
|
||||
|
||||
A few little mistakes, but awh well, the dynamic shadows look boss.
|
||||
|
||||
And the song is Comfortably Numb by Pink Floyd. If you didn't know that after the first 10 seconds of listening to it then you may need some help.
|
||||
season: 2012
|
||||
title: Project Zombie |Map Trailer|
|
||||
year: 2012
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer-thumb.jpg
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.mp4
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2013-07-19
|
||||
episode: 719
|
||||
plot:
|
||||
IP: 172.245.24.118
|
||||
http://www.projectzombie.net/
|
||||
|
||||
With almost a year of downtime, Project Zombie rises from its ashes. Rewind back to May 2011 and immerse yourself in the original Project Zombie with expansions. Open July 20th, 2013.
|
||||
season: 2013
|
||||
title: Project Zombie Rewind |Trailer|
|
||||
year: 2013
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2018-10-29
|
||||
episode: 1029
|
||||
plot:
|
||||
See the full trailer here: https://youtu.be/LN2e6idGluI
|
||||
Discord: https://discord.gg/BQN7SSe
|
||||
Website: https://www.projectzombie.net/
|
||||
|
||||
It has been about six years since the glory days of the original RP server. Hope to see many familiar faces in the revival of our great server.
|
||||
|
||||
Credits:
|
||||
ALISON - Space Echo
|
||||
https://www.youtube.com/watch?v=lPldcjlv3_Y
|
||||
season: 2018
|
||||
title: Jesse's Minecraft Server | Teaser Trailer
|
||||
year: 2018
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2018-11-02
|
||||
episode: 1102
|
||||
plot:
|
||||
IP: mc.jesse.id
|
||||
Live Map: http://mc.jesse.id:8123
|
||||
Discord: https://discord.gg/BQN7SSe
|
||||
Website: https://www.projectzombie.net/
|
||||
|
||||
It has been about six years since the glory days of the original RP server. Hope to see many familiar faces in the revival of our great server.
|
||||
|
||||
The MC version is 1.13.x AND/OR 1.12.x
|
||||
|
||||
Credits:
|
||||
ALISON - Space Echo
|
||||
https://www.youtube.com/watch?v=lPldcjlv3_Y
|
||||
season: 2018
|
||||
title: Jesse's Minecraft Server | IP mc.jesse.id
|
||||
year: 2018
|
||||
fanart.jpg
|
||||
poster.jpg
|
||||
tvshow.nfo
|
||||
NFO tags:
|
||||
tvshow:
|
||||
title: Project / Zombie
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-pz-download-archive.json
|
||||
fanart.jpg
|
||||
poster.jpg
|
||||
tvshow.nfo
|
||||
NFO tags:
|
||||
tvshow:
|
||||
title: Project / Zombie
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-pz-download-archive.json
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2018-10-29
|
||||
episode: 1029
|
||||
plot:
|
||||
See the full trailer here: https://youtu.be/LN2e6idGluI
|
||||
Discord: https://discord.gg/BQN7SSe
|
||||
Website: https://www.projectzombie.net/
|
||||
|
||||
It has been about six years since the glory days of the original RP server. Hope to see many familiar faces in the revival of our great server.
|
||||
|
||||
Credits:
|
||||
ALISON - Space Echo
|
||||
https://www.youtube.com/watch?v=lPldcjlv3_Y
|
||||
season: 2018
|
||||
title: Jesse's Minecraft Server | Teaser Trailer
|
||||
year: 2018
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo
|
||||
NFO tags:
|
||||
episodedetails:
|
||||
aired: 2018-11-02
|
||||
episode: 1102
|
||||
plot:
|
||||
IP: mc.jesse.id
|
||||
Live Map: http://mc.jesse.id:8123
|
||||
Discord: https://discord.gg/BQN7SSe
|
||||
Website: https://www.projectzombie.net/
|
||||
|
||||
It has been about six years since the glory days of the original RP server. Hope to see many familiar faces in the revival of our great server.
|
||||
|
||||
The MC version is 1.13.x AND/OR 1.12.x
|
||||
|
||||
Credits:
|
||||
ALISON - Space Echo
|
||||
https://www.youtube.com/watch?v=lPldcjlv3_Y
|
||||
season: 2018
|
||||
title: Jesse's Minecraft Server | IP mc.jesse.id
|
||||
year: 2018
|
||||
fanart.jpg
|
||||
poster.jpg
|
||||
tvshow.nfo
|
||||
NFO tags:
|
||||
tvshow:
|
||||
title: Project / Zombie
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-pz-download-archive.json
|
||||
fanart.jpg
|
||||
poster.jpg
|
||||
tvshow.nfo
|
||||
NFO tags:
|
||||
tvshow:
|
||||
title: Project / Zombie
|
||||
|
||||
Files removed from '{output_directory}'
|
||||
----------------------------------------
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - Jesse's Minecraft Server-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server.mkv
|
||||
Timestamps of playlist videos in the merged file:
|
||||
0:00: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
5:23: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
9:23: Jesse's Minecraft Server [Trailer - Mar.21]
|
||||
JMC - Jesse's Minecraft Server.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: JMC
|
||||
title: Jesse's Minecraft Server
|
||||
year: 2011
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-jmc-download-archive.json
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: JMC
|
||||
title: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
year: 2011
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: JMC
|
||||
title: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
year: 2011
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: JMC
|
||||
title: Jesse's Minecraft Server [Trailer - Mar.21]
|
||||
year: 2011
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
Project Zombie - 1-6.Intro.mp4
|
||||
0:00 - 0:10
|
||||
Project Zombie - 2-6.Part 1.mp4
|
||||
0:10 - 0:20
|
||||
Project Zombie - 3-6.Part 2.mp4
|
||||
0:20 - 0:30
|
||||
Project Zombie - 4-6.Part 3.mp4
|
||||
0:30 - 0:40
|
||||
Project Zombie - 5-6.Part 4.mp4
|
||||
0:40 - 1:01
|
||||
Project Zombie - 6-6.Part 5.mp4
|
||||
1:01 - 1:28
|
||||
Project Zombie - Intro-thumb.jpg
|
||||
Project Zombie - Intro.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Intro
|
||||
year: 2010
|
||||
Project Zombie - Part 1-thumb.jpg
|
||||
Project Zombie - Part 1.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Part 1
|
||||
year: 2010
|
||||
Project Zombie - Part 2-thumb.jpg
|
||||
Project Zombie - Part 2.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Part 2
|
||||
year: 2010
|
||||
Project Zombie - Part 3-thumb.jpg
|
||||
Project Zombie - Part 3.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Part 3
|
||||
year: 2010
|
||||
Project Zombie - Part 4-thumb.jpg
|
||||
Project Zombie - Part 4.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Part 4
|
||||
year: 2010
|
||||
Project Zombie - Part 5-thumb.jpg
|
||||
Project Zombie - Part 5.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: Project Zombie
|
||||
title: Part 5
|
||||
year: 2010
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - Oblivion Mod 'Falcor' p.1-thumb.jpg
|
||||
JMC - Oblivion Mod 'Falcor' p.1.mp4
|
||||
JMC - Oblivion Mod 'Falcor' p.1.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
album: Music Videos
|
||||
artist: JMC
|
||||
title: Oblivion Mod "Falcor" p.1
|
||||
year: 2010
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
from e2e.expected_download import ExpectedDownloadFile
|
||||
from e2e.expected_download import ExpectedDownloads
|
||||
from e2e.expected_transaction_log import assert_transaction_log_matches
|
||||
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
from ytdl_sub.config.preset import Preset
|
||||
|
|
@ -56,34 +58,32 @@ def discography_subscription(config, subscription_name, subscription_dict):
|
|||
def expected_discography_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-jb-download-archive.json"): "1a99156e9ece62539fb2608416a07200",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-jb-download-archive.json"), md5="1a99156e9ece62539fb2608416a07200"),
|
||||
|
||||
# Entry files (singles)
|
||||
Path("j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3"): "bffbd558e12c6a9e029dc136a88342c4",
|
||||
Path("j_b/[2021] Baby Santana's Dorian Groove/folder.jpg"): "967892be44b8c47e1be73f055a7c6f08",
|
||||
ExpectedDownloadFile(path=Path("j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3"), md5="bffbd558e12c6a9e029dc136a88342c4"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2021] Baby Santana's Dorian Groove/folder.jpg"), md5="967892be44b8c47e1be73f055a7c6f08"),
|
||||
|
||||
Path("j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3"): "038db58aebe2ba875b733932b42a94d6",
|
||||
Path("j_b/[2021] Purple Clouds/folder.jpg"): "967892be44b8c47e1be73f055a7c6f08",
|
||||
ExpectedDownloadFile(path=Path("j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3"), md5="038db58aebe2ba875b733932b42a94d6"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2021] Purple Clouds/folder.jpg"), md5="967892be44b8c47e1be73f055a7c6f08"),
|
||||
|
||||
# Entry files (albums)
|
||||
Path("j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3"): "e145f0a2f6012768280c38655ca58065",
|
||||
Path("j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3"): "60c8b8817a197a13e4bb90903af612c5",
|
||||
Path("j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3"): "8265b7e4f79878af877bc6ecd9757efe",
|
||||
Path("j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3"): "accf46b76891d2954b893d0f91d82816",
|
||||
Path("j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3"): "e1f584f523336160d5c1104a61de77f3",
|
||||
Path("j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3"): "f6885b25901177f0357649afe97328cc",
|
||||
Path("j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3"): "fa057f221cbe4cf2442cd2fdb960743e",
|
||||
Path("j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3"): "7794ae812c64580e2ac8fc457d5cc85f",
|
||||
Path("j_b/[2022] Acoustic Treats/09 - Finding Home.mp3"): "adbf02eddb2090c008eb497d13ff84b9",
|
||||
Path("j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3"): "65bb10c84366c71498161734f953e93d",
|
||||
Path("j_b/[2022] Acoustic Treats/11 - Untold History.mp3"): "6904b2918e5dc38d9a9f72d967eb74bf",
|
||||
Path("j_b/[2022] Acoustic Treats/folder.jpg"): "967892be44b8c47e1be73f055a7c6f08",
|
||||
|
||||
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3"), md5="e145f0a2f6012768280c38655ca58065"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3"), md5="60c8b8817a197a13e4bb90903af612c5"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3"), md5="8265b7e4f79878af877bc6ecd9757efe"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3"), md5="accf46b76891d2954b893d0f91d82816"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3"), md5="e1f584f523336160d5c1104a61de77f3"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3"), md5="f6885b25901177f0357649afe97328cc"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3"), md5="fa057f221cbe4cf2442cd2fdb960743e"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3"), md5="7794ae812c64580e2ac8fc457d5cc85f"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/09 - Finding Home.mp3"), md5="adbf02eddb2090c008eb497d13ff84b9"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3"), md5="65bb10c84366c71498161734f953e93d"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/11 - Untold History.mp3"), md5="6904b2918e5dc38d9a9f72d967eb74bf"),
|
||||
ExpectedDownloadFile(path=Path("j_b/[2022] Acoustic Treats/folder.jpg"), md5="967892be44b8c47e1be73f055a7c6f08"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -94,14 +94,15 @@ class TestSoundcloudDiscography:
|
|||
expected md5 file hashes.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_discography_download(
|
||||
self, discography_subscription, expected_discography_download, output_directory
|
||||
self, discography_subscription, expected_discography_download, output_directory, dry_run
|
||||
):
|
||||
discography_subscription.download()
|
||||
expected_discography_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
def test_discography_dry_run(
|
||||
self, discography_subscription, expected_discography_download, output_directory
|
||||
):
|
||||
transaction_log = discography_subscription.download(dry_run=True)
|
||||
expected_discography_download.assert_dry_run_files_logged(transaction_log=transaction_log)
|
||||
transaction_log = discography_subscription.download(dry_run=dry_run)
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="soundcloud/test_soundcloud_discography.txt",
|
||||
)
|
||||
if not dry_run:
|
||||
expected_discography_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ from pathlib import Path
|
|||
import mergedeep
|
||||
import pytest
|
||||
from conftest import assert_debug_log
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
from e2e.expected_download import ExpectedDownloadFile
|
||||
from e2e.expected_download import ExpectedDownloads
|
||||
from e2e.expected_transaction_log import assert_transaction_log_matches
|
||||
|
||||
import ytdl_sub.downloaders.downloader
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
|
|
@ -65,65 +67,65 @@ def full_channel_subscription(config, subscription_name, subscription_dict):
|
|||
def expected_full_channel_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-pz-download-archive.json"): "dd3c6236a107a665b884f701b8d14d4d",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-pz-download-archive.json"), md5="dd3c6236a107a665b884f701b8d14d4d"),
|
||||
|
||||
# Output directory files
|
||||
Path("fanart.jpg"): "c16b8b88a82cbd47d217ee80f6a8b5f3",
|
||||
Path("poster.jpg"): "e92872ff94c96ad49e9579501c791578",
|
||||
Path("tvshow.nfo"): "83c7db96081ac5bdf289fcf396bec157",
|
||||
ExpectedDownloadFile(path=Path("fanart.jpg"), md5="c16b8b88a82cbd47d217ee80f6a8b5f3"),
|
||||
ExpectedDownloadFile(path=Path("poster.jpg"), md5="e92872ff94c96ad49e9579501c791578"),
|
||||
ExpectedDownloadFile(path=Path("tvshow.nfo"), md5="83c7db96081ac5bdf289fcf396bec157"),
|
||||
|
||||
# Entry files
|
||||
Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1-thumb.jpg"): "fb95b510681676e81c321171fc23143e",
|
||||
Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.mp4"): "931a705864c57d21d6fedebed4af6bbc",
|
||||
Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.nfo"): "67d8d71d048039080acbba3bce4febaa",
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1-thumb.jpg"), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.mp4"), md5="931a705864c57d21d6fedebed4af6bbc"),
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e0813 - Oblivion Mod 'Falcor' p.1.nfo"), md5="67d8d71d048039080acbba3bce4febaa"),
|
||||
|
||||
Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2-thumb.jpg"): "8b32ee9c037fa669e444a0ac181525a1",
|
||||
Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.mp4"): "d3469b4dca7139cb3dbc38712b6796bf",
|
||||
Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.nfo"): "d81f49cedbd7edaee987521e89b37904",
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2-thumb.jpg"), md5="8b32ee9c037fa669e444a0ac181525a1"),
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.mp4"), md5="d3469b4dca7139cb3dbc38712b6796bf"),
|
||||
ExpectedDownloadFile(path=Path("Season 2010/s2010.e1202 - Oblivion Mod 'Falcor' p.2.nfo"), md5="d81f49cedbd7edaee987521e89b37904"),
|
||||
|
||||
Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg"): "b232d253df621aa770b780c1301d364d",
|
||||
Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4"): "e66287b9832277b6a4d1554e29d9fdcc",
|
||||
Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo"): "f7c0de89038f8c491bded8a3968720a2",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg"), md5="b232d253df621aa770b780c1301d364d"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4"), md5="e66287b9832277b6a4d1554e29d9fdcc"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo"), md5="f7c0de89038f8c491bded8a3968720a2"),
|
||||
|
||||
Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg"): "d17c379ea8b362f5b97c6b213b0342cb",
|
||||
Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4"): "04ab5cb3cc12325d0c96a7cd04a8b91d",
|
||||
Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo"): "ee1eda78fa0980bc703e602b5012dd1f",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg"), md5="d17c379ea8b362f5b97c6b213b0342cb"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4"), md5="04ab5cb3cc12325d0c96a7cd04a8b91d"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo"), md5="ee1eda78fa0980bc703e602b5012dd1f"),
|
||||
|
||||
Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg"): "e7830aa8a64b0cde65ba3f7e5fc56530",
|
||||
Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4"): "025de6099a5c98e6397153c7a62d517d",
|
||||
Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo"): "61eb6369430da0ab6134d78829a7621b",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg"), md5="e7830aa8a64b0cde65ba3f7e5fc56530"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4"), md5="025de6099a5c98e6397153c7a62d517d"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo"), md5="61eb6369430da0ab6134d78829a7621b"),
|
||||
|
||||
Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net)-thumb.jpg"): "c956192a379b3661595c9920972d4819",
|
||||
Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).mp4"): "3d9c19835b03355d6fd5d00cd59dbe5b",
|
||||
Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).nfo"): "60f72b99f5c69f9e03a071a12160928f",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net)-thumb.jpg"), md5="c956192a379b3661595c9920972d4819"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).mp4"), md5="3d9c19835b03355d6fd5d00cd59dbe5b"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0529 - Project Zombie _Official Trailer_ (IP - mc.projectzombie.beastnode.net).nfo"), md5="60f72b99f5c69f9e03a071a12160928f"),
|
||||
|
||||
Path("Season 2011/s2011.e0630 - Project Zombie _Fin-thumb.jpg"): "00ed383591779ffe98291de60f198fe9",
|
||||
Path("Season 2011/s2011.e0630 - Project Zombie _Fin.mp4"): "4971cb2d4fa29460361031f3fa8e1ea9",
|
||||
Path("Season 2011/s2011.e0630 - Project Zombie _Fin.nfo"): "a7b5d9e57d20852f5daf360a1373bb7a",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0630 - Project Zombie _Fin-thumb.jpg"), md5="00ed383591779ffe98291de60f198fe9"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0630 - Project Zombie _Fin.mp4"), md5="4971cb2d4fa29460361031f3fa8e1ea9"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e0630 - Project Zombie _Fin.nfo"), md5="a7b5d9e57d20852f5daf360a1373bb7a"),
|
||||
|
||||
Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC]-thumb.jpg"): "1718599d5189c65f7d8cf6acfa5ea851",
|
||||
Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].mp4"): "55e9b0add08c48c9c66105da0def2426",
|
||||
Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].nfo"): "fe60e2b6b564f9316b6c7c183e1cf300",
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC]-thumb.jpg"), md5="1718599d5189c65f7d8cf6acfa5ea851"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].mp4"), md5="55e9b0add08c48c9c66105da0def2426"),
|
||||
ExpectedDownloadFile(path=Path("Season 2011/s2011.e1121 - Skyrim 'Ultra HD w_Mods' [PC].nfo"), md5="fe60e2b6b564f9316b6c7c183e1cf300"),
|
||||
|
||||
Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer-thumb.jpg"): "54ebe9df801b278fdd17b21afa8373a6",
|
||||
Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer.mp4"): "65e4ce53ed5ec4139995469f99477a50",
|
||||
Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer.nfo"): "c8900adcca83c473c79a4afbc7ad2de1",
|
||||
ExpectedDownloadFile(path=Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer-thumb.jpg"), md5="54ebe9df801b278fdd17b21afa8373a6"),
|
||||
ExpectedDownloadFile(path=Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer.mp4"), md5="65e4ce53ed5ec4139995469f99477a50"),
|
||||
ExpectedDownloadFile(path=Path("Season 2012/s2012.e0123 - Project Zombie _Map Trailer.nfo"), md5="c8900adcca83c473c79a4afbc7ad2de1"),
|
||||
|
||||
Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer-thumb.jpg"): "e29d49433175de8a761af35c5307791f",
|
||||
Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.mp4"): "18620a8257a686beda65e54add4d4cd1",
|
||||
Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.nfo"): "1c993c41d4308a6049333154d0adee16",
|
||||
ExpectedDownloadFile(path=Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer-thumb.jpg"), md5="e29d49433175de8a761af35c5307791f"),
|
||||
ExpectedDownloadFile(path=Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.mp4"), md5="18620a8257a686beda65e54add4d4cd1"),
|
||||
ExpectedDownloadFile(path=Path("Season 2013/s2013.e0719 - Project Zombie Rewind _Trailer.nfo"), md5="1c993c41d4308a6049333154d0adee16"),
|
||||
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg"): "6f8f5e1e031ec2a04b0a4906c04a19ee",
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4"): "82f6ee7253e1dbb83ae7215af08ffacc",
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo"): "cc7886aae3af6b7b0facd82f95390242",
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg"), md5="6f8f5e1e031ec2a04b0a4906c04a19ee"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4"), md5="82f6ee7253e1dbb83ae7215af08ffacc"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo"), md5="cc7886aae3af6b7b0facd82f95390242"),
|
||||
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"): "49cc64b25314155c1b8ab0361ac0c34f",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"): "e733b4cc385b953b08c8eb0f47e03c1e",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"): "2b3ccb3f1ef81ee49fe1afb88f275a09",
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"), md5="49cc64b25314155c1b8ab0361ac0c34f"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"), md5="e733b4cc385b953b08c8eb0f47e03c1e"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"), md5="2b3ccb3f1ef81ee49fe1afb88f275a09"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -161,25 +163,25 @@ def recent_channel_subscription(config, subscription_name, recent_channel_subscr
|
|||
def expected_recent_channel_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-pz-download-archive.json"): "91534d1c5921d121aa35d7a197ba1940",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-pz-download-archive.json"), md5="91534d1c5921d121aa35d7a197ba1940"),
|
||||
|
||||
# Output directory files
|
||||
Path("fanart.jpg"): "c16b8b88a82cbd47d217ee80f6a8b5f3",
|
||||
Path("poster.jpg"): "e92872ff94c96ad49e9579501c791578",
|
||||
Path("tvshow.nfo"): "83c7db96081ac5bdf289fcf396bec157",
|
||||
ExpectedDownloadFile(path=Path("fanart.jpg"), md5="c16b8b88a82cbd47d217ee80f6a8b5f3"),
|
||||
ExpectedDownloadFile(path=Path("poster.jpg"), md5="e92872ff94c96ad49e9579501c791578"),
|
||||
ExpectedDownloadFile(path=Path("tvshow.nfo"), md5="83c7db96081ac5bdf289fcf396bec157"),
|
||||
|
||||
# Recent Entry files
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg"): "6f8f5e1e031ec2a04b0a4906c04a19ee",
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4"): "82f6ee7253e1dbb83ae7215af08ffacc",
|
||||
Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo"): "cc7886aae3af6b7b0facd82f95390242",
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer-thumb.jpg"), md5="6f8f5e1e031ec2a04b0a4906c04a19ee"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.mp4"), md5="82f6ee7253e1dbb83ae7215af08ffacc"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1029 - Jesse's Minecraft Server _ Teaser Trailer.nfo"), md5="cc7886aae3af6b7b0facd82f95390242"),
|
||||
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"): "49cc64b25314155c1b8ab0361ac0c34f",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"): "e733b4cc385b953b08c8eb0f47e03c1e",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"): "2b3ccb3f1ef81ee49fe1afb88f275a09",
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"), md5="49cc64b25314155c1b8ab0361ac0c34f"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"), md5="e733b4cc385b953b08c8eb0f47e03c1e"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"), md5="2b3ccb3f1ef81ee49fe1afb88f275a09"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -219,16 +221,16 @@ def recent_channel_no_vids_in_range_subscription(
|
|||
def expected_recent_channel_no_vids_in_range_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-pz-download-archive.json"): "99914b932bd37a50b983c5e7c90ae93b",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-pz-download-archive.json"), md5="99914b932bd37a50b983c5e7c90ae93b"),
|
||||
|
||||
# Output directory files
|
||||
Path("fanart.jpg"): "c16b8b88a82cbd47d217ee80f6a8b5f3",
|
||||
Path("poster.jpg"): "e92872ff94c96ad49e9579501c791578",
|
||||
Path("tvshow.nfo"): "83c7db96081ac5bdf289fcf396bec157",
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("fanart.jpg"), md5="c16b8b88a82cbd47d217ee80f6a8b5f3"),
|
||||
ExpectedDownloadFile(path=Path("poster.jpg"), md5="e92872ff94c96ad49e9579501c791578"),
|
||||
ExpectedDownloadFile(path=Path("tvshow.nfo"), md5="83c7db96081ac5bdf289fcf396bec157"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -266,21 +268,21 @@ def rolling_recent_channel_subscription(
|
|||
def expected_rolling_recent_channel_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-pz-download-archive.json"): "97ff47a7c5d89a426a653493ad1a3f06",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-pz-download-archive.json"), md5="97ff47a7c5d89a426a653493ad1a3f06"),
|
||||
|
||||
# Output directory files
|
||||
Path("fanart.jpg"): "c16b8b88a82cbd47d217ee80f6a8b5f3",
|
||||
Path("poster.jpg"): "e92872ff94c96ad49e9579501c791578",
|
||||
Path("tvshow.nfo"): "83c7db96081ac5bdf289fcf396bec157",
|
||||
ExpectedDownloadFile(path=Path("fanart.jpg"), md5="c16b8b88a82cbd47d217ee80f6a8b5f3"),
|
||||
ExpectedDownloadFile(path=Path("poster.jpg"), md5="e92872ff94c96ad49e9579501c791578"),
|
||||
ExpectedDownloadFile(path=Path("tvshow.nfo"), md5="83c7db96081ac5bdf289fcf396bec157"),
|
||||
|
||||
# Rolling Recent Entry files
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"): "49cc64b25314155c1b8ab0361ac0c34f",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"): "e733b4cc385b953b08c8eb0f47e03c1e",
|
||||
Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"): "2b3ccb3f1ef81ee49fe1afb88f275a09",
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id-thumb.jpg"), md5="49cc64b25314155c1b8ab0361ac0c34f"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.mp4"), md5="e733b4cc385b953b08c8eb0f47e03c1e"),
|
||||
ExpectedDownloadFile(path=Path("Season 2018/s2018.e1102 - Jesse's Minecraft Server _ IP mc.jesse.id.nfo"), md5="2b3ccb3f1ef81ee49fe1afb88f275a09"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -291,68 +293,75 @@ class TestChannelAsKodiTvShow:
|
|||
expected md5 file hashes.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_full_channel_download(
|
||||
self, full_channel_subscription, expected_full_channel_download, output_directory
|
||||
self, full_channel_subscription, expected_full_channel_download, output_directory, dry_run
|
||||
):
|
||||
full_channel_subscription.download()
|
||||
expected_full_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
def test_full_channel_dry_run(
|
||||
self, full_channel_subscription, expected_full_channel_download, output_directory
|
||||
):
|
||||
transaction_log = full_channel_subscription.download(dry_run=True)
|
||||
expected_full_channel_download.assert_dry_run_files_logged(transaction_log=transaction_log)
|
||||
transaction_log = full_channel_subscription.download(dry_run=dry_run)
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_full.txt",
|
||||
)
|
||||
if not dry_run:
|
||||
expected_full_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_recent_channel_download(
|
||||
self, recent_channel_subscription, expected_recent_channel_download, output_directory
|
||||
self,
|
||||
recent_channel_subscription,
|
||||
expected_recent_channel_download,
|
||||
output_directory,
|
||||
dry_run,
|
||||
):
|
||||
recent_channel_subscription.download()
|
||||
expected_recent_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
# try downloading again, ensure nothing more was downloaded
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
recent_channel_subscription.download()
|
||||
transaction_log = recent_channel_subscription.download(dry_run=dry_run)
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_recent.txt",
|
||||
)
|
||||
if not dry_run:
|
||||
expected_recent_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
def test_recent_channel_dry_run(
|
||||
self, recent_channel_subscription, expected_recent_channel_download, output_directory
|
||||
):
|
||||
transaction_log = recent_channel_subscription.download(dry_run=True)
|
||||
expected_recent_channel_download.assert_dry_run_files_logged(
|
||||
transaction_log=transaction_log
|
||||
)
|
||||
# try downloading again, ensure nothing more was downloaded
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
transaction_log = recent_channel_subscription.download()
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name=(
|
||||
"youtube/test_channel_no_additional_downloads.txt"
|
||||
),
|
||||
)
|
||||
expected_recent_channel_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_recent_channel_download__no_vids_in_range(
|
||||
self,
|
||||
recent_channel_no_vids_in_range_subscription,
|
||||
expected_recent_channel_no_vids_in_range_download,
|
||||
output_directory,
|
||||
dry_run,
|
||||
):
|
||||
recent_channel_no_vids_in_range_subscription.download()
|
||||
expected_recent_channel_no_vids_in_range_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
||||
# Try again, make sure its the same
|
||||
recent_channel_no_vids_in_range_subscription.download()
|
||||
expected_recent_channel_no_vids_in_range_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
||||
def test_recent_channel_dry_run__no_vids_in_range(
|
||||
self,
|
||||
recent_channel_no_vids_in_range_subscription,
|
||||
expected_recent_channel_no_vids_in_range_download,
|
||||
output_directory,
|
||||
):
|
||||
transaction_log = recent_channel_no_vids_in_range_subscription.download(dry_run=True)
|
||||
expected_recent_channel_no_vids_in_range_download.assert_dry_run_files_logged(
|
||||
transaction_log=transaction_log
|
||||
)
|
||||
# Run twice, ensure nothing changes between runs
|
||||
for _ in range(2):
|
||||
transaction_log = recent_channel_no_vids_in_range_subscription.download(dry_run=dry_run)
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_no_additional_downloads.txt",
|
||||
)
|
||||
if not dry_run:
|
||||
expected_recent_channel_no_vids_in_range_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_rolling_recent_channel_download(
|
||||
self,
|
||||
recent_channel_subscription,
|
||||
|
|
@ -360,14 +369,22 @@ class TestChannelAsKodiTvShow:
|
|||
expected_recent_channel_download,
|
||||
expected_rolling_recent_channel_download,
|
||||
output_directory,
|
||||
dry_run,
|
||||
):
|
||||
# First, download recent vids
|
||||
# First, download recent vids. Always download since we want to test dry-run
|
||||
# on the rolling recent portion.
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="RejectedVideoReached, stopping additional downloads",
|
||||
):
|
||||
recent_channel_subscription.download()
|
||||
expected_recent_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
transaction_log = recent_channel_subscription.download()
|
||||
|
||||
expected_recent_channel_download.assert_files_exist(relative_directory=output_directory)
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_recent.txt",
|
||||
)
|
||||
|
||||
# Then, download the rolling recent vids subscription. This should remove one of the
|
||||
# two videos
|
||||
|
|
@ -375,18 +392,33 @@ class TestChannelAsKodiTvShow:
|
|||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
rolling_recent_channel_subscription.download()
|
||||
transaction_log = rolling_recent_channel_subscription.download(dry_run=dry_run)
|
||||
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_rolling_recent.txt",
|
||||
regenerate_transaction_log=True,
|
||||
)
|
||||
if not dry_run:
|
||||
expected_rolling_recent_channel_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
||||
# Invoke the rolling download again, ensure downloading stopped early from it already
|
||||
# existing
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
rolling_recent_channel_subscription.download()
|
||||
if not dry_run:
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
transaction_log = rolling_recent_channel_subscription.download()
|
||||
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_channel_no_additional_downloads.txt",
|
||||
)
|
||||
expected_rolling_recent_channel_download.assert_files_exist(
|
||||
relative_directory=output_directory
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
from e2e.expected_download import ExpectedDownloadFile
|
||||
from e2e.expected_download import ExpectedDownloads
|
||||
from e2e.expected_transaction_log import assert_transaction_log_matches
|
||||
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
from ytdl_sub.config.preset import Preset
|
||||
|
|
@ -63,16 +65,15 @@ def playlist_subscription(config, subscription_name, subscription_dict):
|
|||
|
||||
@pytest.fixture
|
||||
def expected_playlist_download():
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
Path("JMC - Jesse's Minecraft Server-thumb.jpg"): "a3f1910f9c51f6442f845a528e190829",
|
||||
Path("JMC - Jesse's Minecraft Server.mkv"): [
|
||||
"6053c47a8690519b0a33c13fa4b01ac0",
|
||||
"3ab42b3e6be0a44deb3a9a28e6ebaf16",
|
||||
],
|
||||
Path("JMC - Jesse's Minecraft Server.nfo"): "10df5dcdb65ab18ecf21b3503c77e48b",
|
||||
}
|
||||
# fmt: off
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server-thumb.jpg"), md5="a3f1910f9c51f6442f845a528e190829"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server.mkv"), md5=["6053c47a8690519b0a33c13fa4b01ac0", "3ab42b3e6be0a44deb3a9a28e6ebaf16"]),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server.nfo"), md5="10df5dcdb65ab18ecf21b3503c77e48b"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
||||
class TestYoutubeMergePlaylist:
|
||||
|
|
@ -81,14 +82,16 @@ class TestYoutubeMergePlaylist:
|
|||
files exist and have the expected md5 file hashes.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_merge_playlist_download(
|
||||
self, playlist_subscription, expected_playlist_download, output_directory
|
||||
self, playlist_subscription, expected_playlist_download, output_directory, dry_run
|
||||
):
|
||||
playlist_subscription.download()
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
transaction_log = playlist_subscription.download()
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_merge_playlist.txt",
|
||||
)
|
||||
|
||||
def test_merge_playlist_dry_run(
|
||||
self, playlist_subscription, expected_playlist_download, output_directory
|
||||
):
|
||||
transaction_log = playlist_subscription.download(dry_run=True)
|
||||
expected_playlist_download.assert_dry_run_files_logged(transaction_log=transaction_log)
|
||||
if not dry_run:
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ from pathlib import Path
|
|||
import mergedeep
|
||||
import pytest
|
||||
from conftest import assert_debug_log
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
from e2e.expected_download import ExpectedDownloadFile
|
||||
from e2e.expected_download import ExpectedDownloads
|
||||
from e2e.expected_transaction_log import assert_transaction_log_matches
|
||||
|
||||
import ytdl_sub.downloaders.downloader
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
|
|
@ -63,24 +65,24 @@ def playlist_subscription(config, subscription_name, subscription_dict):
|
|||
def expected_playlist_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-jmc-download-archive.json"): "9f785c29194a6ecfba6a6b4018763ddc",
|
||||
ExpectedDownloadFile(path=Path(".ytdl-sub-jmc-download-archive.json"), md5="9f785c29194a6ecfba6a6b4018763ddc"),
|
||||
|
||||
# Entry files
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg"): "b232d253df621aa770b780c1301d364d",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4"): "e66287b9832277b6a4d1554e29d9fdcc",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo"): "3d272fe58487b6011ad049b6000b046f",
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg"), md5="b232d253df621aa770b780c1301d364d"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4"), md5="e66287b9832277b6a4d1554e29d9fdcc"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo"), md5="3d272fe58487b6011ad049b6000b046f"),
|
||||
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg"): "d17c379ea8b362f5b97c6b213b0342cb",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4"): "04ab5cb3cc12325d0c96a7cd04a8b91d",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo"): "6f99af10bef67276a507d1d9770c5e92",
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg"), md5="d17c379ea8b362f5b97c6b213b0342cb"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4"), md5="04ab5cb3cc12325d0c96a7cd04a8b91d"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo"), md5="6f99af10bef67276a507d1d9770c5e92"),
|
||||
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg"): "e7830aa8a64b0cde65ba3f7e5fc56530",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4"): "025de6099a5c98e6397153c7a62d517d",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo"): "beec3c1326654bd8c858cecf4e40977a",
|
||||
}
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg"), md5="e7830aa8a64b0cde65ba3f7e5fc56530"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4"), md5="025de6099a5c98e6397153c7a62d517d"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo"), md5="beec3c1326654bd8c858cecf4e40977a"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -118,12 +120,12 @@ def single_video_subscription(config, subscription_name, single_video_subscripti
|
|||
def expected_single_video_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
Path("JMC - Oblivion Mod 'Falcor' p.1-thumb.jpg"): "fb95b510681676e81c321171fc23143e",
|
||||
Path("JMC - Oblivion Mod 'Falcor' p.1.mp4"): "931a705864c57d21d6fedebed4af6bbc",
|
||||
Path("JMC - Oblivion Mod 'Falcor' p.1.nfo"): "89f509a8a3d9003e22a9091abeeae5dc",
|
||||
}
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
ExpectedDownloadFile(path=Path("JMC - Oblivion Mod 'Falcor' p.1-thumb.jpg"), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Oblivion Mod 'Falcor' p.1.mp4"), md5="931a705864c57d21d6fedebed4af6bbc"),
|
||||
ExpectedDownloadFile(path=Path("JMC - Oblivion Mod 'Falcor' p.1.nfo"), md5="89f509a8a3d9003e22a9091abeeae5dc"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -134,36 +136,37 @@ class TestPlaylistAsKodiMusicVideo:
|
|||
files exist and have the expected md5 file hashes.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_playlist_download(
|
||||
self, playlist_subscription, expected_playlist_download, output_directory
|
||||
self, playlist_subscription, expected_playlist_download, output_directory, dry_run
|
||||
):
|
||||
playlist_subscription.download()
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
transaction_log = playlist_subscription.download()
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_playlist.txt",
|
||||
)
|
||||
|
||||
# After the playlist is downloaded, ensure another invocation will hit ExistingVideoReached
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
playlist_subscription.download()
|
||||
if not dry_run:
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
def test_playlist_dry_run(
|
||||
self, playlist_subscription, expected_playlist_download, output_directory
|
||||
):
|
||||
file_transaction_log = playlist_subscription.download(dry_run=True)
|
||||
expected_playlist_download.assert_dry_run_files_logged(transaction_log=file_transaction_log)
|
||||
# Ensure another invocation will hit ExistingVideoReached
|
||||
with assert_debug_log(
|
||||
logger=ytdl_sub.downloaders.downloader.logger,
|
||||
expected_message="ExistingVideoReached, stopping additional downloads",
|
||||
):
|
||||
playlist_subscription.download()
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_single_video_download(
|
||||
self, single_video_subscription, expected_single_video_download, output_directory
|
||||
self, single_video_subscription, expected_single_video_download, output_directory, dry_run
|
||||
):
|
||||
single_video_subscription.download()
|
||||
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
def test_single_video_dry_run(
|
||||
self, single_video_subscription, expected_single_video_download, output_directory
|
||||
):
|
||||
file_transaction_log = single_video_subscription.download(dry_run=True)
|
||||
expected_single_video_download.assert_dry_run_files_logged(
|
||||
transaction_log=file_transaction_log
|
||||
transaction_log = single_video_subscription.download()
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_video.txt",
|
||||
)
|
||||
if not dry_run:
|
||||
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ from tempfile import NamedTemporaryFile
|
|||
import mergedeep
|
||||
import pytest
|
||||
from conftest import assert_debug_log
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
from e2e.expected_download import ExpectedDownloadFile
|
||||
from e2e.expected_download import ExpectedDownloads
|
||||
from e2e.expected_transaction_log import assert_transaction_log_matches
|
||||
|
||||
import ytdl_sub.downloaders.downloader
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
|
|
@ -88,27 +90,27 @@ def single_video_subscription(config, subscription_name, subscription_dict):
|
|||
def expected_single_video_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
Path('Project Zombie - 1-6.Intro.mp4'): "eaec6f50f364b13ef1a201e736ec9c05",
|
||||
Path('Project Zombie - 2-6.Part 1.mp4'): "5850b19acb250cc13db36f80fa1bba5a",
|
||||
Path('Project Zombie - 3-6.Part 2.mp4'): "445d95eba437db6df284df7e1ab633e8",
|
||||
Path('Project Zombie - 4-6.Part 3.mp4'): "2b6e7532d515c9e64ed2a33d850cf199",
|
||||
Path('Project Zombie - 5-6.Part 4.mp4'): "842bf3c4d1fcc4c5ab110635935dac66",
|
||||
Path('Project Zombie - 6-6.Part 5.mp4'): "238de99f00f829ab72f042b79da9a33a",
|
||||
Path('Project Zombie - Intro-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Intro.nfo'): "ded59ac906f579312cc3cf98a57e7ea3",
|
||||
Path('Project Zombie - Part 1-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Part 1.nfo'): "70ff5cd0092b8bc22dc4db93a824789b",
|
||||
Path('Project Zombie - Part 2-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Part 2.nfo'): "54450c18a2cbb9d6d2ee5d0a1fb3f279",
|
||||
Path('Project Zombie - Part 3-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Part 3.nfo'): "0effb13fc4039363a95969d1048dde57",
|
||||
Path('Project Zombie - Part 4-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Part 4.nfo'): "74bd0d7c12105469838768a0cc323a8c",
|
||||
Path('Project Zombie - Part 5-thumb.jpg'): "fb95b510681676e81c321171fc23143e",
|
||||
Path('Project Zombie - Part 5.nfo'): "a8cf2e77721335ea7c18e22734e7996c",
|
||||
}
|
||||
return ExpectedDownloads(
|
||||
expected_downloads=[
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 1-6.Intro.mp4'), md5="eaec6f50f364b13ef1a201e736ec9c05"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 2-6.Part 1.mp4'), md5="5850b19acb250cc13db36f80fa1bba5a"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 3-6.Part 2.mp4'), md5="445d95eba437db6df284df7e1ab633e8"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 4-6.Part 3.mp4'), md5="2b6e7532d515c9e64ed2a33d850cf199"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 5-6.Part 4.mp4'), md5="842bf3c4d1fcc4c5ab110635935dac66"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - 6-6.Part 5.mp4'), md5="238de99f00f829ab72f042b79da9a33a"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Intro-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Intro.nfo'), md5="ded59ac906f579312cc3cf98a57e7ea3"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 1-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 1.nfo'), md5="70ff5cd0092b8bc22dc4db93a824789b"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 2-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 2.nfo'), md5="54450c18a2cbb9d6d2ee5d0a1fb3f279"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 3-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 3.nfo'), md5="0effb13fc4039363a95969d1048dde57"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 4-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 4.nfo'), md5="74bd0d7c12105469838768a0cc323a8c"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 5-thumb.jpg'), md5="fb95b510681676e81c321171fc23143e"),
|
||||
ExpectedDownloadFile(path=Path('Project Zombie - Part 5.nfo'), md5="a8cf2e77721335ea7c18e22734e7996c"),
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -119,12 +121,16 @@ class TestPlaylistAsKodiMusicVideo:
|
|||
files exist and have the expected md5 file hashes.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
def test_split_video_download(
|
||||
self, single_video_subscription, expected_single_video_download, output_directory
|
||||
self, single_video_subscription, expected_single_video_download, output_directory, dry_run
|
||||
):
|
||||
single_video_subscription.download()
|
||||
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
||||
transaction_log = single_video_subscription.download()
|
||||
assert_transaction_log_matches(
|
||||
output_directory=output_directory,
|
||||
transaction_log=transaction_log,
|
||||
transaction_log_summary_file_name="youtube/test_split_video.txt",
|
||||
)
|
||||
|
||||
def test_split_video_dry_run(self, single_video_subscription, expected_single_video_download):
|
||||
transaction_log = single_video_subscription.download(dry_run=True)
|
||||
expected_single_video_download.assert_dry_run_files_logged(transaction_log=transaction_log)
|
||||
if not dry_run:
|
||||
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
|
|
|||
Loading…
Reference in a new issue