all tests passing, lint 10, should test dry-run arg
This commit is contained in:
parent
179540093c
commit
685ea60607
7 changed files with 92 additions and 46 deletions
|
|
@ -100,8 +100,7 @@ class YoutubeMergePlaylistDownloader(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
def _add_chapters(self, merged_video: YoutubeVideo) -> None:
|
||||||
def _add_chapters(cls, merged_video: YoutubeVideo) -> None:
|
|
||||||
titles: List[str] = []
|
titles: List[str] = []
|
||||||
timestamps: List[Timestamp] = []
|
timestamps: List[Timestamp] = []
|
||||||
|
|
||||||
|
|
@ -112,11 +111,13 @@ class YoutubeMergePlaylistDownloader(
|
||||||
|
|
||||||
current_timestamp_sec += video_entry["duration"]
|
current_timestamp_sec += video_entry["duration"]
|
||||||
|
|
||||||
add_ffmpeg_metadata(
|
# TODO: return chapter metadata here
|
||||||
file_path=merged_video.get_download_file_path(),
|
if not self.is_dry_run:
|
||||||
chapters=Chapters(timestamps=timestamps, titles=titles),
|
add_ffmpeg_metadata(
|
||||||
file_duration_sec=merged_video.kwargs("duration"),
|
file_path=merged_video.get_download_file_path(),
|
||||||
)
|
chapters=Chapters(timestamps=timestamps, titles=titles),
|
||||||
|
file_duration_sec=merged_video.kwargs("duration"),
|
||||||
|
)
|
||||||
|
|
||||||
def _to_merged_video(self, entry_dict: Dict) -> YoutubeVideo:
|
def _to_merged_video(self, entry_dict: Dict) -> YoutubeVideo:
|
||||||
"""
|
"""
|
||||||
|
|
@ -130,7 +131,11 @@ class YoutubeMergePlaylistDownloader(
|
||||||
entry_dict["duration"] = sum(
|
entry_dict["duration"] = sum(
|
||||||
playlist_entry["duration"] for playlist_entry in entry_dict["entries"]
|
playlist_entry["duration"] for playlist_entry in entry_dict["entries"]
|
||||||
)
|
)
|
||||||
entry_dict["ext"] = entry_dict["requested_downloads"][0]["ext"]
|
entry_dict["ext"] = (
|
||||||
|
entry_dict["requested_downloads"][0]["ext"]
|
||||||
|
if "requested_downloads" in entry_dict
|
||||||
|
else "mkv"
|
||||||
|
)
|
||||||
return YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)
|
return YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)
|
||||||
|
|
||||||
def download(self) -> List[YoutubeVideo]:
|
def download(self) -> List[YoutubeVideo]:
|
||||||
|
|
|
||||||
|
|
@ -155,26 +155,26 @@ class YoutubeSplitVideoDownloader(
|
||||||
for idx, title in enumerate(chapters.titles):
|
for idx, title in enumerate(chapters.titles):
|
||||||
new_uid = _split_video_uid(source_uid=entry.uid, idx=idx)
|
new_uid = _split_video_uid(source_uid=entry.uid, idx=idx)
|
||||||
|
|
||||||
# Get the input/output file paths
|
if not self.is_dry_run:
|
||||||
input_file = entry.get_download_file_path()
|
# Get the input/output file paths
|
||||||
output_file = str(Path(self.working_directory) / f"{new_uid}.{entry.ext}")
|
input_file = entry.get_download_file_path()
|
||||||
output_thumbnail_file = str(
|
output_file = str(Path(self.working_directory) / f"{new_uid}.{entry.ext}")
|
||||||
Path(self.working_directory) / f"{new_uid}.{entry.thumbnail_ext}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run ffmpeg to create the split the video
|
# Run ffmpeg to create the split the video
|
||||||
FFMPEG.run(
|
FFMPEG.run(
|
||||||
_split_video_ffmpeg_cmd(
|
_split_video_ffmpeg_cmd(
|
||||||
input_file=input_file,
|
input_file=input_file,
|
||||||
output_file=output_file,
|
output_file=output_file,
|
||||||
timestamps=chapters.timestamps,
|
timestamps=chapters.timestamps,
|
||||||
idx=idx,
|
idx=idx,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
# Copy the thumbnail
|
# Copy the original vid thumbnail to the working directory with the new uid. This so
|
||||||
|
# downstream logic thinks this split video has its own thumbnail
|
||||||
FileHandler.copy(
|
FileHandler.copy(
|
||||||
src_file_path=entry.get_download_thumbnail_path(),
|
src_file_path=entry.get_download_thumbnail_path(),
|
||||||
dst_file_path=output_thumbnail_file,
|
dst_file_path=Path(self.working_directory) / f"{new_uid}.{entry.thumbnail_ext}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Format the split video as a YoutubePlaylistVideo
|
# Format the split video as a YoutubePlaylistVideo
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.plugins.plugin import Plugin
|
from ytdl_sub.plugins.plugin import Plugin
|
||||||
from ytdl_sub.plugins.plugin import PluginOptions
|
from ytdl_sub.plugins.plugin import PluginOptions
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||||
|
|
||||||
|
|
@ -145,7 +147,9 @@ class Subscription:
|
||||||
and self.downloader_class.supports_download_archive
|
and self.downloader_class.supports_download_archive
|
||||||
)
|
)
|
||||||
|
|
||||||
def _copy_entry_files_to_output_directory(self, entry: Entry):
|
def _copy_entry_files_to_output_directory(
|
||||||
|
self, entry: Entry, entry_metadata: Optional[FileMetadata] = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Helper function to move the media file and optionally thumbnail file to the output directory
|
Helper function to move the media file and optionally thumbnail file to the output directory
|
||||||
for a single entry.
|
for a single entry.
|
||||||
|
|
@ -154,13 +158,18 @@ class Subscription:
|
||||||
----------
|
----------
|
||||||
entry:
|
entry:
|
||||||
The entry with files to move
|
The entry with files to move
|
||||||
|
entry_metadata
|
||||||
|
Optional. Metadata to record to the transaction log for this entry
|
||||||
"""
|
"""
|
||||||
# Move the file after all direct file modifications are complete
|
# Move the file after all direct file modifications are complete
|
||||||
output_file_name = self.overrides.apply_formatter(
|
output_file_name = self.overrides.apply_formatter(
|
||||||
formatter=self.output_options.file_name, entry=entry
|
formatter=self.output_options.file_name, entry=entry
|
||||||
)
|
)
|
||||||
self._enhanced_download_archive.save_file_to_output_directory(
|
self._enhanced_download_archive.save_file_to_output_directory(
|
||||||
file_name=entry.get_download_file_name(), output_file_name=output_file_name, entry=entry
|
file_name=entry.get_download_file_name(),
|
||||||
|
file_metadata=entry_metadata,
|
||||||
|
output_file_name=output_file_name,
|
||||||
|
entry=entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.output_options.thumbnail_name:
|
if self.output_options.thumbnail_name:
|
||||||
|
|
@ -257,17 +266,19 @@ class Subscription:
|
||||||
ytdl_options=ytdl_options,
|
ytdl_options=ytdl_options,
|
||||||
)
|
)
|
||||||
|
|
||||||
entries = downloader.download()
|
for entry in downloader.download():
|
||||||
for plugin in plugins:
|
# TODO: Add entry metadata from the downloader.download function
|
||||||
for entry in entries:
|
entry_metadata = FileMetadata()
|
||||||
plugin.post_process_entry(entry)
|
for plugin in plugins:
|
||||||
|
entry_metadata.extend(plugin.post_process_entry(entry))
|
||||||
|
|
||||||
plugin.post_process_subscription()
|
self._copy_entry_files_to_output_directory(
|
||||||
|
entry=entry, entry_metadata=entry_metadata
|
||||||
for entry in entries:
|
)
|
||||||
self._copy_entry_files_to_output_directory(entry=entry)
|
|
||||||
|
|
||||||
downloader.post_download(overrides=self.overrides)
|
downloader.post_download(overrides=self.overrides)
|
||||||
|
for plugin in plugins:
|
||||||
|
plugin.post_process_subscription()
|
||||||
|
|
||||||
return self._enhanced_download_archive.get_file_handler_transaction_log()
|
return self._enhanced_download_archive.get_file_handler_transaction_log()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from typing import Union
|
||||||
|
|
||||||
class FileMetadata:
|
class FileMetadata:
|
||||||
"""
|
"""
|
||||||
Stores pretty-printed information about a file
|
Stores pretty-printed information about a file. Each line in the metadata represents a newline
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, metadata: Optional[List[str]] = None):
|
def __init__(self, metadata: Optional[List[str]] = None):
|
||||||
|
|
@ -27,14 +27,15 @@ class FileMetadata:
|
||||||
self.metadata.append(line)
|
self.metadata.append(line)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def extend(self, other: "FileMetadata") -> "FileMetadata":
|
def extend(self, other: Optional["FileMetadata"]) -> "FileMetadata":
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
other
|
other
|
||||||
Other metadata to extend to this one in its entirety
|
Other metadata to extend to this one in its entirety
|
||||||
"""
|
"""
|
||||||
self.metadata.extend(other.metadata)
|
if other is not None:
|
||||||
|
self.metadata.extend(other.metadata)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -146,7 +147,9 @@ class FileHandler:
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|
||||||
def copy_file_to_output_directory(self, file_name: str, output_file_name: str):
|
def copy_file_to_output_directory(
|
||||||
|
self, file_name: str, output_file_name: str, file_metadata: Optional[FileMetadata] = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Copies a file from the working directory to the output directory.
|
Copies a file from the working directory to the output directory.
|
||||||
All file copies from working to output directory should use this function for tracking and
|
All file copies from working to output directory should use this function for tracking and
|
||||||
|
|
@ -158,8 +161,12 @@ class FileHandler:
|
||||||
File in the working directory
|
File in the working directory
|
||||||
output_file_name
|
output_file_name
|
||||||
Desired output file name in the output_directory
|
Desired output file name in the output_directory
|
||||||
|
file_metadata
|
||||||
|
Optional. Metadata to record to the transaction log for this file
|
||||||
"""
|
"""
|
||||||
self._file_handler_transaction_log.log_created_file(output_file_name)
|
self._file_handler_transaction_log.log_created_file(
|
||||||
|
file_name=output_file_name, file_metadata=file_metadata
|
||||||
|
)
|
||||||
|
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
output_file_path = Path(self.output_directory) / output_file_name
|
output_file_path = Path(self.output_directory) / output_file_name
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from yt_dlp import DateRange
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.utils.file_handler import FileHandler
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -547,15 +548,22 @@ class EnhancedDownloadArchive:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def save_file_to_output_directory(
|
def save_file_to_output_directory(
|
||||||
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,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Saves a file from the working directory to the output directory
|
Saves a file from the working directory to the output directory and record it in the
|
||||||
|
transaction log.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
file_name
|
file_name
|
||||||
Name of the file to move (does not include working directory path)
|
Name of the file to move (does not include working directory path)
|
||||||
|
file_metadata
|
||||||
|
Optional. Metadata to record to the transaction log for this file
|
||||||
output_file_name
|
output_file_name
|
||||||
Optional. Final name of the file in the output directory (does not include output
|
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
|
directory path). If None, use the same working_directory file_name
|
||||||
|
|
@ -569,7 +577,7 @@ class EnhancedDownloadArchive:
|
||||||
self.mapping.add_entry(entry=entry, entry_file_path=output_file_name)
|
self.mapping.add_entry(entry=entry, entry_file_path=output_file_name)
|
||||||
|
|
||||||
self._file_handler.copy_file_to_output_directory(
|
self._file_handler.copy_file_to_output_directory(
|
||||||
file_name=file_name, output_file_name=output_file_name
|
file_name=file_name, file_metadata=file_metadata, output_file_name=output_file_name
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_file_handler_transaction_log(self) -> FileHandlerTransactionLog:
|
def get_file_handler_transaction_log(self) -> FileHandlerTransactionLog:
|
||||||
|
|
@ -609,7 +617,9 @@ class DownloadArchiver:
|
||||||
"""
|
"""
|
||||||
return self.__enhanced_download_archive.is_dry_run
|
return self.__enhanced_download_archive.is_dry_run
|
||||||
|
|
||||||
def save_file(self, file_name: str, entry: Optional[Entry] = None) -> None:
|
def save_file(
|
||||||
|
self, file_name: str, output_file_name: Optional[str] = None, entry: Optional[Entry] = None
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Saves a file in the working directory to the output directory.
|
Saves a file in the working directory to the output directory.
|
||||||
|
|
||||||
|
|
@ -617,9 +627,12 @@ class DownloadArchiver:
|
||||||
----------
|
----------
|
||||||
file_name
|
file_name
|
||||||
Name of the file relative to the working directory
|
Name of the file relative to the working directory
|
||||||
|
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
|
||||||
entry
|
entry
|
||||||
Optional. Entry that the file belongs to
|
Optional. Entry that the file belongs to
|
||||||
"""
|
"""
|
||||||
self.__enhanced_download_archive.save_file_to_output_directory(
|
self.__enhanced_download_archive.save_file_to_output_directory(
|
||||||
file_name=file_name, entry=entry
|
file_name=file_name, output_file_name=output_file_name, entry=entry
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,14 @@ class TestYoutubeMergePlaylist:
|
||||||
files exist and have the expected md5 file hashes.
|
files exist and have the expected md5 file hashes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_playlist_download(
|
def test_merge_playlist_download(
|
||||||
self, playlist_subscription, expected_playlist_download, output_directory
|
self, playlist_subscription, expected_playlist_download, output_directory
|
||||||
):
|
):
|
||||||
playlist_subscription.download()
|
playlist_subscription.download()
|
||||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,12 @@ class TestPlaylistAsKodiMusicVideo:
|
||||||
files exist and have the expected md5 file hashes.
|
files exist and have the expected md5 file hashes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_single_video_download(
|
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
|
||||||
):
|
):
|
||||||
single_video_subscription.download()
|
single_video_subscription.download()
|
||||||
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
expected_single_video_download.assert_files_exist(relative_directory=output_directory)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue