[FEATURE] Simplify `music_tags` plugin

This commit is contained in:
Jesse Bannon 2023-07-24 12:45:25 -07:00
parent 0cbd2af4d3
commit ad540f6005
9 changed files with 119 additions and 40 deletions

View file

@ -4,17 +4,19 @@ Deprecation Notices
July 2023 July 2023
--------- ---------
embed_thumbnail music_tags
^^^^^^^^^^^^^^^ ^^^^^^^^^^
Embedding thumbnails has its own dedicated plugin now, which supports both audio and video files. Music tags is getting simplified. ``tags`` will now reside directly under music_tags, and
It will be removed from ``music_tags`` in October 2023. Convert from: ``embed_thumbnail`` is getting moved to its own plugin (supports video files as well). Convert from:
.. code-block:: yaml .. code-block:: yaml
my_example_preset: my_example_preset:
music_tags: music_tags:
embed_thumbnail: True embed_thumbnail: True
tags:
artist: "Elvis Presley"
To the following: To the following:
@ -22,4 +24,7 @@ To the following:
my_example_preset: my_example_preset:
embed_thumbnail: True embed_thumbnail: True
music_tags:
artist: "Elvis Presley"
The old format will be removed in October 2023.

View file

@ -64,7 +64,6 @@ presets:
# It is recommended to keep most of this as-is, and use override # It is recommended to keep most of this as-is, and use override
# variables to set them to be what you want. # variables to set them to be what you want.
music_tags: music_tags:
tags:
artist: "{track_artist}" artist: "{track_artist}"
artists: "{track_artist}" artists: "{track_artist}"
albumartist: "{track_artist}" albumartist: "{track_artist}"
@ -76,6 +75,9 @@ presets:
year: "{track_year}" year: "{track_year}"
genre: "{track_genre}" genre: "{track_genre}"
# Optionally embed the thumbnail into the track
embed_thumbnail: False
# For every configurable field, make it an override variable, # For every configurable field, make it an override variable,
# so we can carefully tune different use-cases by only modifying override variables. # so we can carefully tune different use-cases by only modifying override variables.
overrides: overrides:

View file

@ -1,5 +1,5 @@
from collections import defaultdict from collections import defaultdict
from typing import Any from typing import Any, Optional
from typing import Dict from typing import Dict
from typing import List from typing import List
@ -72,25 +72,27 @@ class MusicTagsOptions(OptionsDictValidator):
- "ytdl-sub" - "ytdl-sub"
""" """
_required_keys = {"tags"} _optional_keys = {"tags", "embed_thumbnail"}
_optional_keys = {"embed_thumbnail"} _allow_extra_keys = True
@classmethod
def partial_validate(cls, name: str, value: Any) -> None:
"""
Partially validate music tags
"""
if isinstance(value, dict):
value["tags"] = value.get("tags", {})
_ = cls(name, value)
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
self._tags = self._validate_key(key="tags", validator=MusicTagsValidator) tags_validator: Optional[MusicTagsValidator] = self._validate_key_if_present(
key="tags", validator=MusicTagsValidator
)
self._embed_thumbnail = self._validate_key_if_present( self._embed_thumbnail = self._validate_key_if_present(
key="embed_thumbnail", validator=BoolValidator, default=False key="embed_thumbnail", validator=BoolValidator
).value )
# New format where tags are the keys, no "tags" or "embed_thumbnail" present
if tags_validator is None and self._embed_thumbnail is None:
self._tags = MusicTagsValidator(name=name, value=value)
self._is_old_format = False
else:
self._is_old_format = True
self._tags = tags_validator or MusicTagsValidator(name=name, value={})
@property @property
def tags(self) -> MusicTagsValidator: def tags(self) -> MusicTagsValidator:
@ -105,7 +107,9 @@ class MusicTagsOptions(OptionsDictValidator):
""" """
Optional. Whether to embed the thumbnail into the audio file. Optional. Whether to embed the thumbnail into the audio file.
""" """
return self._embed_thumbnail if self._embed_thumbnail is None:
return False
return self._embed_thumbnail.value
class MusicTagsPlugin(Plugin[MusicTagsOptions]): class MusicTagsPlugin(Plugin[MusicTagsOptions]):
@ -122,6 +126,17 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
f"to audio using the audio_extract plugin." f"to audio using the audio_extract plugin."
) )
if self.plugin_options._is_old_format:
logger.warning(
"music_tags.tags is now deprecated. Place your tags directly under music_tags "
"instead. The old format will be removed in October of 2023."
)
if self.plugin_options.embed_thumbnail:
logger.warning(
"music_tags.embed_thumbnail is also deprecated. Use the dedicated "
"embed_thumbnail plugin instead. This will be removed in October of 2023."
)
# Resolve the tags into this dict # Resolve the tags into this dict
tags_to_write: Dict[str, List[str]] = defaultdict(list) tags_to_write: Dict[str, List[str]] = defaultdict(list)
for tag_name, tag_formatters in self.plugin_options.tags.as_lists.items(): for tag_name, tag_formatters in self.plugin_options.tags.as_lists.items():
@ -147,10 +162,6 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
setattr(audio_file, tag_name, tag_value[0]) setattr(audio_file, tag_name, tag_value[0])
if self.plugin_options.embed_thumbnail: if self.plugin_options.embed_thumbnail:
logger.warning(
"music_tags.embed_thumbnail is now deprecated. Use the dedicated "
"embed_thumbnail plugin instead. This will be removed in October of 2023."
)
# convert the entry thumbnail so it is embedded as jpg # convert the entry thumbnail so it is embedded as jpg
convert_download_thumbnail(entry=entry) convert_download_thumbnail(entry=entry)

View file

@ -6,7 +6,7 @@ from ytdl_sub.subscriptions.subscription import Subscription
@pytest.fixture @pytest.fixture
def single_song_preset_dict(output_directory): def single_song_preset_dict_old_format(output_directory):
return { return {
"preset": "single", "preset": "single",
# test multi-tags # test multi-tags
@ -24,6 +24,24 @@ def single_song_preset_dict(output_directory):
}, },
} }
@pytest.fixture
def single_song_preset_dict(output_directory):
return {
"preset": "single",
# test multi-tags
"music_tags": {"genres": ["multi_tag_1", "multi_tag_2"]},
# test the new embed_thumbnail plugin
"embed_thumbnail": True,
# download the worst format so it is fast
"ytdl_options": {
"format": "worst[ext=mp4]",
"postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility
},
"overrides": {
"url": "https://www.youtube.com/watch?v=2lAe1cqCOXo",
"music_directory": output_directory,
},
}
@pytest.fixture @pytest.fixture
def multiple_songs_preset_dict(output_directory): def multiple_songs_preset_dict(output_directory):
@ -44,7 +62,33 @@ def multiple_songs_preset_dict(output_directory):
class TestAudioExtract: class TestAudioExtract:
@pytest.mark.parametrize("dry_run", [True, False]) @pytest.mark.parametrize("dry_run", [True, False])
def test_audio_extract_single_song( def test_audio_extract_single_song_old_format(
self,
music_audio_config,
single_song_preset_dict_old_format,
output_directory,
dry_run,
):
subscription = Subscription.from_dict(
config=music_audio_config,
preset_name="single_song_test",
preset_dict=single_song_preset_dict_old_format,
)
transaction_log = subscription.download(dry_run=dry_run)
assert_transaction_log_matches(
output_directory=output_directory,
transaction_log=transaction_log,
transaction_log_summary_file_name="plugins/test_audio_extract_single_old_format.txt",
)
assert_expected_downloads(
output_directory=output_directory,
dry_run=dry_run,
expected_download_summary_file_name="plugins/test_audio_extract_single_old_format.json",
)
@pytest.mark.parametrize("dry_run", [True, False])
def test_audio_extract_single_song_new_format(
self, self,
music_audio_config, music_audio_config,
single_song_preset_dict, single_song_preset_dict,

View file

@ -15,7 +15,7 @@ def single_song_video_dict(output_directory):
}, },
"output_options": {"output_directory": output_directory, "file_name": "will_error.mp4"}, "output_options": {"output_directory": output_directory, "file_name": "will_error.mp4"},
# test multi-tags # test multi-tags
"music_tags": {"embed_thumbnail": True, "tags": {"genres": ["multi_tag_1", "multi_tag_2"]}}, "music_tags": {"genres": ["multi_tag_1", "multi_tag_2"]},
# download the worst format so it is fast # download the worst format so it is fast
"ytdl_options": { "ytdl_options": {
"format": "worst[ext=mp4]", "format": "worst[ext=mp4]",

View file

@ -1,5 +1,5 @@
{ {
".ytdl-sub-single_song_test-download-archive.json": "c8ff22ec3304c9f8dab18cedaed4e8b4", ".ytdl-sub-single_song_test-download-archive.json": "c8ff22ec3304c9f8dab18cedaed4e8b4",
"YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3": "37b38834eda1293ad503e00dcff7c4dc", "YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3": "33c69b0dce605e9f78fe23246ce442dd",
"YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/folder.jpg": "50ee47c80f679029f5d3503bb91b045a" "YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/folder.jpg": "50ee47c80f679029f5d3503bb91b045a"
} }

View file

@ -0,0 +1,5 @@
{
".ytdl-sub-single_song_test-download-archive.json": "c8ff22ec3304c9f8dab18cedaed4e8b4",
"YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3": "7dcad720d29f5036eb6f75e533757f80",
"YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind/folder.jpg": "50ee47c80f679029f5d3503bb91b045a"
}

View file

@ -4,7 +4,7 @@ Files created:
.ytdl-sub-single_song_test-download-archive.json .ytdl-sub-single_song_test-download-archive.json
{output_directory}/YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind {output_directory}/YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind
01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3 01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3
Embedded Thumbnail, Music Tags: Music Tags:
album: YouTube Rewind 2019: For the Record | #YouTubeRewind album: YouTube Rewind 2019: For the Record | #YouTubeRewind
albumartist: YouTube albumartist: YouTube
albumartists: YouTube albumartists: YouTube

View file

@ -0,0 +1,12 @@
Files created:
----------------------------------------
{output_directory}
.ytdl-sub-single_song_test-download-archive.json
{output_directory}/YouTube/[2019] YouTube Rewind 2019 For the Record #YouTubeRewind
01 - YouTube Rewind 2019 For the Record #YouTubeRewind.mp3
Embedded Thumbnail, Music Tags:
genres:
- multi_tag_1
- multi_tag_2
Embedded thumbnail
folder.jpg