Fix validators and output directory
This commit is contained in:
parent
3a22a9bf10
commit
374021d027
8 changed files with 61 additions and 50 deletions
44
config.yaml
44
config.yaml
|
|
@ -8,17 +8,21 @@ presets:
|
||||||
skip_premiere_tracks: True
|
skip_premiere_tracks: True
|
||||||
ytdl_options:
|
ytdl_options:
|
||||||
format: 'bestaudio[ext=mp3]'
|
format: 'bestaudio[ext=mp3]'
|
||||||
post_process:
|
output_options:
|
||||||
file_name: "[{album_year}] {sanitized_album}/{track_number_padded} - {sanitized_title}.{ext}"
|
file_name: "[{album_year}] {sanitized_album}/{track_number_padded} - {sanitized_title}.{ext}"
|
||||||
thumbnail_name: "[{album_year}] {sanitized_album}/folder.{thumbnail_ext}"
|
convert_thumbnail: "jpeg"
|
||||||
tagging:
|
thumbnail_name: "[{album_year}] {sanitized_album}/folder.jpeg"
|
||||||
artist: "{artist}"
|
metadata_options:
|
||||||
albumartist: "{artist}"
|
id3:
|
||||||
title: "{title}"
|
id3_version: "2.4"
|
||||||
album: "{album}"
|
tags:
|
||||||
tracknumber: "{track_number}"
|
artist: "{artist}"
|
||||||
year: "{album_year}"
|
albumartist: "{artist}"
|
||||||
genre: "{genre}"
|
title: "{title}"
|
||||||
|
album: "{album}"
|
||||||
|
tracknumber: "{track_number}"
|
||||||
|
year: "{album_year}"
|
||||||
|
genre: "{genre}"
|
||||||
|
|
||||||
music_videos:
|
music_videos:
|
||||||
youtube:
|
youtube:
|
||||||
|
|
@ -26,14 +30,16 @@ presets:
|
||||||
ytdl_options:
|
ytdl_options:
|
||||||
format: 'best'
|
format: 'best'
|
||||||
ignoreerrors: True
|
ignoreerrors: True
|
||||||
post_process:
|
output_options:
|
||||||
file_name: "{sanitized_artist} - {sanitized_title}.{ext}"
|
file_name: "{sanitized_artist} - {sanitized_title}.{ext}"
|
||||||
convert_thumbnail: "jpg"
|
convert_thumbnail: "jpeg"
|
||||||
thumbnail_name: "{sanitized_artist} - {sanitized_title}.jpg"
|
thumbnail_name: "{sanitized_artist} - {sanitized_title}.jpeg"
|
||||||
nfo_name: "{sanitized_artist} - {sanitized_title}.nfo"
|
metadata_options:
|
||||||
nfo_root: "musicvideo"
|
|
||||||
nfo:
|
nfo:
|
||||||
artist: "{artist}"
|
nfo_name: "{sanitized_artist} - {sanitized_title}.nfo"
|
||||||
title: "{title}"
|
nfo_root: "musicvideo"
|
||||||
album: "Music Videos"
|
tags:
|
||||||
year: "{upload_year}"
|
artist: "{artist}"
|
||||||
|
title: "{title}"
|
||||||
|
album: "Music Videos"
|
||||||
|
year: "{upload_year}"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ ALISON:
|
||||||
preset: "soundcloud_with_id3_tags"
|
preset: "soundcloud_with_id3_tags"
|
||||||
soundcloud:
|
soundcloud:
|
||||||
username: alis_on
|
username: alis_on
|
||||||
output_path: "/mnt/nas/Downloads/ytdl/music/ALISON"
|
output_options:
|
||||||
|
output_directory: "/tmp/ALISON"
|
||||||
overrides:
|
overrides:
|
||||||
artist: "A.L.I.S.O.N."
|
artist: "A.L.I.S.O.N."
|
||||||
genre: "Synthwave / Electronic"
|
genre: "Synthwave / Electronic"
|
||||||
|
|
@ -11,7 +12,8 @@ delorra:
|
||||||
preset: "soundcloud_with_id3_tags"
|
preset: "soundcloud_with_id3_tags"
|
||||||
soundcloud:
|
soundcloud:
|
||||||
username: delorra
|
username: delorra
|
||||||
output_path: "/mnt/nas/Downloads/ytdl/music/DeLorra"
|
output_options:
|
||||||
|
output_directory: "/tmp/DeLorra"
|
||||||
overrides:
|
overrides:
|
||||||
artist: "DeLorra"
|
artist: "DeLorra"
|
||||||
genre: "Synthwave / Electronic"
|
genre: "Synthwave / Electronic"
|
||||||
|
|
@ -20,23 +22,26 @@ bl00dwave:
|
||||||
preset: "soundcloud_with_id3_tags"
|
preset: "soundcloud_with_id3_tags"
|
||||||
soundcloud:
|
soundcloud:
|
||||||
username: bl00dwave
|
username: bl00dwave
|
||||||
output_path: "/tmp/bl00dwave"
|
output_options:
|
||||||
|
output_directory: "/tmp/bl00dwave"
|
||||||
overrides:
|
overrides:
|
||||||
artist: "bl00dwave"
|
artist: "bl00dwave"
|
||||||
genre: "Synthwave / Electronic"
|
genre: "Synthwave / Electronic"
|
||||||
|
|
||||||
tom_petty:
|
tom_petty:
|
||||||
|
preset: "music_videos"
|
||||||
youtube:
|
youtube:
|
||||||
playlist_id: PLoopXDarluPBnuxs4PTC55Sc_2ShAXC0i
|
playlist_id: PLoopXDarluPBnuxs4PTC55Sc_2ShAXC0i
|
||||||
preset: "music_videos"
|
output_options:
|
||||||
output_path: "/tmp/Tom Petty"
|
output_directory: "/tmp/Tom Petty"
|
||||||
overrides:
|
overrides:
|
||||||
artist: "Tom Petty and the Heartbreakers"
|
artist: "Tom Petty and the Heartbreakers"
|
||||||
|
|
||||||
rammstein:
|
rammstein:
|
||||||
|
preset: "music_videos"
|
||||||
youtube:
|
youtube:
|
||||||
playlist_id: "PLVTLbc6i-h_iuhdwUfuPDLFLXG2QQnz-x"
|
playlist_id: "PLVTLbc6i-h_iuhdwUfuPDLFLXG2QQnz-x"
|
||||||
preset: "music_videos"
|
output_options:
|
||||||
output_path: "/mnt/nas/Downloads/ytdl/music_videos/Rammstein"
|
output_directory: "/tmp/Rammstein"
|
||||||
overrides:
|
overrides:
|
||||||
artist: "Rammstein"
|
artist: "Rammstein"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
@ -129,38 +130,44 @@ class Subscription(object):
|
||||||
relative_directory=self.config_options.working_directory.value
|
relative_directory=self.config_options.working_directory.value
|
||||||
)
|
)
|
||||||
|
|
||||||
entry_destination_file_path = entry.apply_formatter(
|
output_directory = entry.apply_formatter(
|
||||||
|
format_string=self.output_options.output_directory.format_string,
|
||||||
|
overrides=self.overrides.dict
|
||||||
|
)
|
||||||
|
output_file_name = entry.apply_formatter(
|
||||||
format_string=self.output_options.file_name.format_string,
|
format_string=self.output_options.file_name.format_string,
|
||||||
overrides=self.overrides.dict,
|
overrides=self.overrides.dict,
|
||||||
)
|
)
|
||||||
|
entry_destination_file_path = Path(output_directory) / Path(output_file_name)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(entry_destination_file_path), exist_ok=True)
|
os.makedirs(os.path.dirname(entry_destination_file_path), exist_ok=True)
|
||||||
copyfile(entry_source_file_path, entry_destination_file_path)
|
copyfile(entry_source_file_path, entry_destination_file_path)
|
||||||
|
|
||||||
# Download the thumbnail if its present
|
# Download the thumbnail if its present
|
||||||
if self.output_options.thumbnail_name:
|
if self.output_options.thumbnail_name:
|
||||||
thumbnail_source_path = entry.thumbnail_path(
|
source_thumbnail_path = entry.thumbnail_path(
|
||||||
relative_directory=self.config_options.working_directory.value
|
relative_directory=self.config_options.working_directory.value
|
||||||
)
|
)
|
||||||
|
|
||||||
thumbnail_destination_path = entry.apply_formatter(
|
output_thumbnail_name = entry.apply_formatter(
|
||||||
format_string=self.output_options.thumbnail_name.format_string,
|
format_string=self.output_options.thumbnail_name.format_string,
|
||||||
overrides=self.overrides.dict,
|
overrides=self.overrides.dict,
|
||||||
)
|
)
|
||||||
|
output_thumbnail_path = Path(output_directory) / Path(output_thumbnail_name)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(entry_destination_file_path), exist_ok=True)
|
os.makedirs(os.path.dirname(output_thumbnail_path), exist_ok=True)
|
||||||
|
|
||||||
# If the thumbnail is to be converted, then save the converted thumbnail to the
|
# If the thumbnail is to be converted, then save the converted thumbnail to the
|
||||||
# output filepath
|
# output filepath
|
||||||
if self.output_options.convert_thumbnail:
|
if self.output_options.convert_thumbnail:
|
||||||
im = Image.open(thumbnail_source_path).convert("RGB")
|
im = Image.open(source_thumbnail_path).convert("RGB")
|
||||||
im.save(
|
im.save(
|
||||||
fp=thumbnail_destination_path,
|
fp=output_thumbnail_name,
|
||||||
format=self.output_options.convert_thumbnail.value,
|
format=self.output_options.convert_thumbnail.value,
|
||||||
)
|
)
|
||||||
# Otherwise, just copy the downloaded thumbnail
|
# Otherwise, just copy the downloaded thumbnail
|
||||||
else:
|
else:
|
||||||
copyfile(thumbnail_source_path, thumbnail_destination_path)
|
copyfile(source_thumbnail_path, output_thumbnail_name)
|
||||||
|
|
||||||
if self.metadata_options.nfo:
|
if self.metadata_options.nfo:
|
||||||
self._post_process_nfo(entry)
|
self._post_process_nfo(entry)
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ class StringFormatterValidator(StringValidator):
|
||||||
|
|
||||||
return format_variables
|
return format_variables
|
||||||
|
|
||||||
def __init__(self, name, format_string: str):
|
def __init__(self, name, value: str):
|
||||||
super().__init__(name=name, value=format_string)
|
super().__init__(name=name, value=value)
|
||||||
self.format_variables = self.__validate_and_get_format_variables()
|
self.format_variables = self.__validate_and_get_format_variables()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ class StringSelectValidator(StringValidator):
|
||||||
|
|
||||||
select_values: Set[str] = set()
|
select_values: Set[str] = set()
|
||||||
|
|
||||||
def __init__(self, name, format_string: str):
|
def __init__(self, name, value: str):
|
||||||
super().__init__(name=name, value=format_string)
|
super().__init__(name=name, value=value)
|
||||||
|
|
||||||
if self.value not in self.select_values:
|
if self.value not in self.select_values:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,3 @@ class MetadataOptionsValidator(StrictDictValidator):
|
||||||
|
|
||||||
self.id3 = self.validate_key_if_present(key="id3", validator=Id3Validator)
|
self.id3 = self.validate_key_if_present(key="id3", validator=Id3Validator)
|
||||||
self.nfo = self.validate_key_if_present(key="nfo", validator=NFOValidator)
|
self.nfo = self.validate_key_if_present(key="nfo", validator=NFOValidator)
|
||||||
|
|
||||||
self.output_directory: StringFormatterValidator = self.validate_key(
|
|
||||||
key="output_directory", validator=StringFormatterValidator
|
|
||||||
)
|
|
||||||
|
|
||||||
self.file_name: StringFormatterValidator = self.validate_key(
|
|
||||||
key="file_name", validator=StringFormatterValidator
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class OverridesValidator(DictFormatterValidator):
|
||||||
def dict(self) -> dict:
|
def dict(self) -> dict:
|
||||||
"""For overrides, create sanitized versions of each entry for convenience"""
|
"""For overrides, create sanitized versions of each entry for convenience"""
|
||||||
output_dict = copy.deepcopy(super().dict)
|
output_dict = copy.deepcopy(super().dict)
|
||||||
for key in self.keys:
|
for key in list(output_dict.keys()):
|
||||||
output_dict[f"sanitized_{key}"] = sanitize_filename.sanitize(
|
output_dict[f"sanitized_{key}"] = sanitize_filename.sanitize(
|
||||||
output_dict[key]
|
output_dict[key]
|
||||||
)
|
)
|
||||||
|
|
@ -47,8 +47,9 @@ class OverridesValidator(DictFormatterValidator):
|
||||||
|
|
||||||
|
|
||||||
class PresetValidator(StrictDictValidator):
|
class PresetValidator(StrictDictValidator):
|
||||||
required_keys = {"output_options, metadata_options"}
|
required_keys = {"output_options"}
|
||||||
optional_keys = {
|
optional_keys = {
|
||||||
|
"metadata_options",
|
||||||
"ytdl_options",
|
"ytdl_options",
|
||||||
"overrides",
|
"overrides",
|
||||||
*SubscriptionSourceName.all(),
|
*SubscriptionSourceName.all(),
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from ytdl_subscribe.validators.config.config_validator import ConfigValidator
|
||||||
from ytdl_subscribe.validators.config.preset_validator import OverridesValidator
|
from ytdl_subscribe.validators.config.preset_validator import OverridesValidator
|
||||||
from ytdl_subscribe.validators.config.preset_validator import PresetValidator
|
from ytdl_subscribe.validators.config.preset_validator import PresetValidator
|
||||||
from ytdl_subscribe.validators.config.sources.soundcloud_validators import (
|
from ytdl_subscribe.validators.config.sources.soundcloud_validators import (
|
||||||
SoundcloudSourceValidator,
|
SoundcloudSourceValidator, SoundcloudAlbumsAndSinglesDownloadValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.sources.youtube_validators import (
|
from ytdl_subscribe.validators.config.sources.youtube_validators import (
|
||||||
YoutubePlaylistDownloadValidator,
|
YoutubePlaylistDownloadValidator,
|
||||||
|
|
@ -64,14 +64,14 @@ class SubscriptionValidator(StrictDictValidator):
|
||||||
del preset_dict["preset"]
|
del preset_dict["preset"]
|
||||||
|
|
||||||
self.preset = PresetValidator(
|
self.preset = PresetValidator(
|
||||||
name=self.name,
|
name=f"{self.name}.{preset_name}",
|
||||||
value=preset_dict,
|
value=preset_dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_subscription(self) -> Subscription:
|
def to_subscription(self) -> Subscription:
|
||||||
if isinstance(
|
if isinstance(
|
||||||
self.preset.subscription_source.download_strategy,
|
self.preset.subscription_source.download_strategy,
|
||||||
SoundcloudAlbumsAndSinglesSubscription,
|
SoundcloudAlbumsAndSinglesDownloadValidator,
|
||||||
):
|
):
|
||||||
subscription_class = SoundcloudAlbumsAndSinglesSubscription
|
subscription_class = SoundcloudAlbumsAndSinglesSubscription
|
||||||
elif isinstance(
|
elif isinstance(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue