Everything passing besides channel

This commit is contained in:
jbannon 2022-07-02 05:28:51 +00:00
parent 30ad9501e6
commit d89cb1c177
9 changed files with 96 additions and 13 deletions

View file

@ -43,9 +43,9 @@ presets:
# store previously downloaded song IDs to tell YTDL not to re-download # store previously downloaded song IDs to tell YTDL not to re-download
# them on a successive invocation. # them on a successive invocation.
output_options: output_options:
output_directory: "/{music_directory}/{artist_sanitized}" output_directory: "{music_directory}"
file_name: "{album_directory_name}/{track_number_padded} - {title_sanitized}.{ext}" file_name: "{artist_sanitized}/{album_directory_name}/{track_number_padded} - {title_sanitized}.{ext}"
thumbnail_name: "{album_directory_name}/folder.jpg" thumbnail_name: "{artist_sanitized}/{album_directory_name}/folder.jpg"
maintain_download_archive: True maintain_download_archive: True
# For each song downloaded, populate the audio file with music tags. # For each song downloaded, populate the audio file with music tags.

View file

@ -1,8 +1,11 @@
from typing import Dict
import mediafile import mediafile
from ytdl_sub.entries.entry import Entry 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 FileMetadata
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.validators import StringValidator from ytdl_sub.validators.validators import StringValidator
@ -57,17 +60,28 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
""" """
Tags the entry's audio file using values defined in the metadata options Tags the entry's audio file using values defined in the metadata options
""" """
audio_file = mediafile.MediaFile(entry.get_download_file_path()) supported_fields = list(mediafile.MediaFile.sorted_fields())
for tag, tag_formatter in self.plugin_options.tags.dict.items(): tags_to_write: Dict[str, str] = {}
if tag not in audio_file.fields(): for tag_name, tag_formatter in self.plugin_options.tags.dict.items():
if tag_name not in supported_fields:
# TODO: Add support for custom fields
self._logger.warning( self._logger.warning(
"tag '%s' is not supported for %s files. Supported tags: %s", "tag '%s' is not supported for %s files. Supported tags: %s",
tag, tag_name,
entry.ext, entry.ext,
", ".join(audio_file.sorted_fields()), ", ".join(sorted(supported_fields)),
) )
continue
tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry) tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
setattr(audio_file, tag, tag_value) tags_to_write[tag_name] = tag_value
audio_file.save() # write the actual tags if its not a dry run
if not self.is_dry_run:
audio_file = mediafile.MediaFile(entry.get_download_file_path())
for tag_name, tag_value in tags_to_write.items():
setattr(audio_file, tag_name, tag_value)
audio_file.save()
# report the tags written
return FileMetadata.from_dict(value_dict=tags_to_write, title="Music Tags:")

View file

@ -6,6 +6,7 @@ import dicttoxml
from ytdl_sub.entries.entry import Entry 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 FileMetadata
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
@ -116,3 +117,5 @@ class NfoTagsPlugin(Plugin[NfoTagsOptions]):
# Archive the nfo's file name # Archive the nfo's file name
self.save_file(file_name=nfo_file_name, entry=entry) self.save_file(file_name=nfo_file_name, entry=entry)
return FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")

View file

@ -5,6 +5,7 @@ import dicttoxml
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 FileMetadata
from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
@ -106,3 +107,4 @@ class OutputDirectoryNfoTagsPlugin(Plugin[OutputDirectoryNfoTagsOptions]):
nfo_file.write(xml) nfo_file.write(xml)
self.save_file(file_name=nfo_file_name) self.save_file(file_name=nfo_file_name)
return FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")

View file

@ -46,6 +46,14 @@ class Plugin(Generic[PluginOptionsT], ABC):
def working_directory(self) -> str: def working_directory(self) -> str:
return self.__enhanced_download_archive.working_directory return self.__enhanced_download_archive.working_directory
@property
def output_directory(self) -> str:
return self.__enhanced_download_archive.output_directory
@property
def is_dry_run(self) -> bool:
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, 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.

View file

@ -12,10 +12,31 @@ class FileMetadata:
def __init__(self, metadata: Optional[List[str]] = None): def __init__(self, metadata: Optional[List[str]] = None):
self.metadata: List[str] = metadata if metadata else [] self.metadata: List[str] = metadata if metadata else []
def append(self, other: "FileMetadata") -> "FileMetadata": def append(self, line: str) -> "FileMetadata":
self.metadata.append(line)
return self
def extend(self, other: "FileMetadata") -> "FileMetadata":
self.metadata.extend(other.metadata) self.metadata.extend(other.metadata)
return self return self
@classmethod
def from_dict(cls, value_dict: Dict[str, str], title: Optional[str] = None) -> "FileMetadata":
lines: List[str] = []
if title is not None:
lines.append(title)
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}")
_recursive_add_dict_lines(rdict=value_dict, indent=2)
return cls(metadata=lines)
class FileHandlerTransactionLog: class FileHandlerTransactionLog:
""" """

View file

@ -373,6 +373,10 @@ class EnhancedDownloadArchive:
self._download_mapping = DownloadMappings() self._download_mapping = DownloadMappings()
return self return self
@property
def is_dry_run(self) -> bool:
return self._file_handler.dry_run
@property @property
def archive_file_name(self) -> str: def archive_file_name(self) -> str:
""" """

View file

@ -29,7 +29,7 @@ def subscription_dict(output_directory, subscription_name):
"preset": "sc_discography", "preset": "sc_discography",
"soundcloud": {"url": "https://soundcloud.com/jessebannon"}, "soundcloud": {"url": "https://soundcloud.com/jessebannon"},
# override the output directory with our fixture-generated dir # override the output directory with our fixture-generated dir
"output_options": {"output_directory": output_directory + "/{artist_sanitized}"}, "output_options": {"output_directory": output_directory},
# download the worst format so it is fast # download the worst format so it is fast
"ytdl_options": { "ytdl_options": {
"format": "worst[ext=mp3]", "format": "worst[ext=mp3]",
@ -59,7 +59,7 @@ def expected_discography_download():
return ExpectedDownload( return ExpectedDownload(
expected_md5_file_hashes={ expected_md5_file_hashes={
# Download mapping # Download mapping
Path("j_b/.ytdl-sub-jb-download-archive.json"): "ae55de93b71267b5712c9a3d06c07c26", Path(".ytdl-sub-jb-download-archive.json"): "1a99156e9ece62539fb2608416a07200",
# Entry files (singles) # 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/01 - Baby Santana's Dorian Groove.mp3"): "bffbd558e12c6a9e029dc136a88342c4",
@ -99,3 +99,9 @@ class TestSoundcloudDiscography:
): ):
discography_subscription.download() discography_subscription.download()
expected_discography_download.assert_files_exist(relative_directory=output_directory) 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)

View file

@ -297,6 +297,12 @@ class TestChannelAsKodiTvShow:
full_channel_subscription.download() full_channel_subscription.download()
expected_full_channel_download.assert_files_exist(relative_directory=output_directory) 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)
def test_recent_channel_download( 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
): ):
@ -311,6 +317,14 @@ class TestChannelAsKodiTvShow:
recent_channel_subscription.download() recent_channel_subscription.download()
expected_recent_channel_download.assert_files_exist(relative_directory=output_directory) 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
)
def test_recent_channel_download__no_vids_in_range( def test_recent_channel_download__no_vids_in_range(
self, self,
recent_channel_no_vids_in_range_subscription, recent_channel_no_vids_in_range_subscription,
@ -328,6 +342,17 @@ class TestChannelAsKodiTvShow:
relative_directory=output_directory 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
)
def test_rolling_recent_channel_download( def test_rolling_recent_channel_download(
self, self,
recent_channel_subscription, recent_channel_subscription,