update black line-len to 100
This commit is contained in:
parent
05737d2b19
commit
2b9ab7c3af
26 changed files with 77 additions and 204 deletions
|
|
@ -2,3 +2,6 @@
|
||||||
profile = "black"
|
profile = "black"
|
||||||
line_length = 100
|
line_length = 100
|
||||||
force_single_line = true
|
force_single_line = true
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line_length = 100
|
||||||
|
|
@ -18,15 +18,15 @@
|
||||||
# artist: "DeLorra"
|
# artist: "DeLorra"
|
||||||
# genre: "Synthwave / Electronic"
|
# genre: "Synthwave / Electronic"
|
||||||
#
|
#
|
||||||
#bl00dwave:
|
bl00dwave:
|
||||||
# preset: "soundcloud_with_id3_tags"
|
preset: "soundcloud_with_id3_tags"
|
||||||
# soundcloud:
|
soundcloud:
|
||||||
# username: bl00dwave
|
username: bl00dwave
|
||||||
# output_options:
|
output_options:
|
||||||
# output_directory: "/tmp/bl00dwave"
|
output_directory: "/tmp/bl00dwave"
|
||||||
# overrides:
|
overrides:
|
||||||
# artist: "bl00dwave"
|
artist: "bl00dwave"
|
||||||
# genre: "Synthwave / Electronic"
|
genre: "Synthwave / Electronic"
|
||||||
|
|
||||||
tom_petty:
|
tom_petty:
|
||||||
preset: "music_videos"
|
preset: "music_videos"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ytdl_subscribe.entries.entry import Entry
|
from ytdl_subscribe.entries.entry import Entry
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -47,9 +45,7 @@ def download_file_name(uid, ext):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_entry_to_dict(
|
def mock_entry_to_dict(uid, title, ext, upload_date, upload_year, thumbnail, thumbnail_ext):
|
||||||
uid, title, ext, upload_date, upload_year, thumbnail, thumbnail_ext
|
|
||||||
):
|
|
||||||
return {
|
return {
|
||||||
"uid": uid,
|
"uid": uid,
|
||||||
"title": title,
|
"title": title,
|
||||||
|
|
@ -114,9 +110,7 @@ def validate_entry_dict_contains_valid_formatters():
|
||||||
format_string = f"test {{{key}}} formatting works"
|
format_string = f"test {{{key}}} formatting works"
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
entry.apply_formatter(
|
entry.apply_formatter(StringFormatterValidator(name="test", value=format_string))
|
||||||
StringFormatterValidator(name="test", value=format_string)
|
|
||||||
)
|
|
||||||
== expected_string
|
== expected_string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,8 @@ import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
StringFormatterValidator,
|
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.overrides.overrides_validator import (
|
|
||||||
OverridesValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
||||||
from ytdl_subscribe.validators.exceptions import ValidationException
|
from ytdl_subscribe.validators.exceptions import ValidationException
|
||||||
|
|
||||||
|
|
@ -47,9 +43,7 @@ class TestEntry(object):
|
||||||
mock_entry.kwargs(key)
|
mock_entry.kwargs(key)
|
||||||
|
|
||||||
def test_entry_formatter_fails_missing_field(self, mock_entry):
|
def test_entry_formatter_fails_missing_field(self, mock_entry):
|
||||||
format_string = StringFormatterValidator(
|
format_string = StringFormatterValidator(name="test", value=f"prefix {{bah_humbug}} suffix")
|
||||||
name="test", value=f"prefix {{bah_humbug}} suffix"
|
|
||||||
)
|
|
||||||
available_fields = ", ".join(sorted(mock_entry.to_dict().keys()))
|
available_fields = ", ".join(sorted(mock_entry.to_dict().keys()))
|
||||||
expected_error_msg = (
|
expected_error_msg = (
|
||||||
f"Validation error in test: Format variable 'bah_humbug' does not exist. "
|
f"Validation error in test: Format variable 'bah_humbug' does not exist. "
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ from ytdl_subscribe.validators.exceptions import ValidationException
|
||||||
|
|
||||||
|
|
||||||
class TestDictValidator:
|
class TestDictValidator:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize("value", [{}, {"key": "value"}, {"b": {}, "a": "keys_out_of_order"}])
|
||||||
"value", [{}, {"key": "value"}, {"b": {}, "a": "keys_out_of_order"}]
|
|
||||||
)
|
|
||||||
def test_dict_validator(self, value):
|
def test_dict_validator(self, value):
|
||||||
dict_validator = DictValidator(name="good_dict_validator", value=value)
|
dict_validator = DictValidator(name="good_dict_validator", value=value)
|
||||||
assert dict_validator._name == "good_dict_validator"
|
assert dict_validator._name == "good_dict_validator"
|
||||||
|
|
@ -31,9 +29,7 @@ class TestDictValidator:
|
||||||
)
|
)
|
||||||
def test_dict_validator_validate_key(self, value, validator_class):
|
def test_dict_validator_validate_key(self, value, validator_class):
|
||||||
dict_validator = DictValidator(name="validate_key", value={"key_name": value})
|
dict_validator = DictValidator(name="validate_key", value={"key_name": value})
|
||||||
validated_key = dict_validator._validate_key(
|
validated_key = dict_validator._validate_key(key="key_name", validator=validator_class)
|
||||||
key="key_name", validator=validator_class
|
|
||||||
)
|
|
||||||
|
|
||||||
assert isinstance(validated_key, validator_class)
|
assert isinstance(validated_key, validator_class)
|
||||||
assert validated_key.value == value
|
assert validated_key.value == value
|
||||||
|
|
@ -95,9 +91,7 @@ class TestDictValidator:
|
||||||
|
|
||||||
|
|
||||||
class TestLiteralDictValidator:
|
class TestLiteralDictValidator:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize("value", [{}, {"key": "value"}, {"b": {}, "a": "keys_out_of_order"}])
|
||||||
"value", [{}, {"key": "value"}, {"b": {}, "a": "keys_out_of_order"}]
|
|
||||||
)
|
|
||||||
def test_literal_dict_validator_dict_and_keys(self, value):
|
def test_literal_dict_validator_dict_and_keys(self, value):
|
||||||
dict_validator = LiteralDictValidator(name="good_dict_validator", value=value)
|
dict_validator = LiteralDictValidator(name="good_dict_validator", value=value)
|
||||||
assert dict_validator._name == "good_dict_validator"
|
assert dict_validator._name == "good_dict_validator"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
||||||
DictFormatterValidator,
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
from ytdl_subscribe.validators.exceptions import StringFormattingException
|
||||||
from ytdl_subscribe.validators.exceptions import ValidationException
|
from ytdl_subscribe.validators.exceptions import ValidationException
|
||||||
|
|
||||||
|
|
@ -29,9 +25,7 @@ def error_message_unequal_regex_matches_str():
|
||||||
class TestStringFormatterValidator(object):
|
class TestStringFormatterValidator(object):
|
||||||
def test_parse(self):
|
def test_parse(self):
|
||||||
format_string = "Here is my {var_one} and {var_two} 💩"
|
format_string = "Here is my {var_one} and {var_two} 💩"
|
||||||
validator = StringFormatterValidator(
|
validator = StringFormatterValidator(name="test_format_variables", value=format_string)
|
||||||
name="test_format_variables", value=format_string
|
|
||||||
)
|
|
||||||
|
|
||||||
assert validator.format_string == format_string
|
assert validator.format_string == format_string
|
||||||
assert validator.format_variables == ["var_one", "var_two"]
|
assert validator.format_variables == ["var_one", "var_two"]
|
||||||
|
|
@ -54,12 +48,8 @@ class TestStringFormatterValidator(object):
|
||||||
"Try }var_one} and {var_one}",
|
"Try }var_one} and {var_one}",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_validate_fail_uneven_brackets(
|
def test_validate_fail_uneven_brackets(self, format_string, error_message_unequal_brackets_str):
|
||||||
self, format_string, error_message_unequal_brackets_str
|
expected_error_msg = f"Validation error in fail: {error_message_unequal_brackets_str}"
|
||||||
):
|
|
||||||
expected_error_msg = (
|
|
||||||
f"Validation error in fail: {error_message_unequal_brackets_str}"
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(ValidationException, match=expected_error_msg):
|
with pytest.raises(ValidationException, match=expected_error_msg):
|
||||||
_ = StringFormatterValidator(name="fail", value=format_string)
|
_ = StringFormatterValidator(name="fail", value=format_string)
|
||||||
|
|
@ -78,9 +68,7 @@ class TestStringFormatterValidator(object):
|
||||||
def test_validate_fail_bad_variable(
|
def test_validate_fail_bad_variable(
|
||||||
self, format_string, error_message_unequal_regex_matches_str
|
self, format_string, error_message_unequal_regex_matches_str
|
||||||
):
|
):
|
||||||
expected_error_msg = (
|
expected_error_msg = f"Validation error in fail: {error_message_unequal_regex_matches_str}"
|
||||||
f"Validation error in fail: {error_message_unequal_regex_matches_str}"
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(ValidationException, match=expected_error_msg):
|
with pytest.raises(ValidationException, match=expected_error_msg):
|
||||||
_ = StringFormatterValidator(name="fail", value=format_string)
|
_ = StringFormatterValidator(name="fail", value=format_string)
|
||||||
|
|
@ -92,9 +80,7 @@ class TestStringFormatterValidator(object):
|
||||||
("{try} {valid_var}", "try"),
|
("{try} {valid_var}", "try"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_validate_fail_variable_keyword_or_not_identifier(
|
def test_validate_fail_variable_keyword_or_not_identifier(self, format_string, bad_variable):
|
||||||
self, format_string, bad_variable
|
|
||||||
):
|
|
||||||
expected_error_msg = (
|
expected_error_msg = (
|
||||||
f"Validation error in fail: "
|
f"Validation error in fail: "
|
||||||
f"'{bad_variable}' is a Python keyword and cannot be used as a variable."
|
f"'{bad_variable}' is a Python keyword and cannot be used as a variable."
|
||||||
|
|
@ -105,14 +91,10 @@ class TestStringFormatterValidator(object):
|
||||||
|
|
||||||
def test_string_formatter_single_field(self):
|
def test_string_formatter_single_field(self):
|
||||||
uid = "this uid"
|
uid = "this uid"
|
||||||
format_string = StringFormatterValidator(
|
format_string = StringFormatterValidator(name="test", value=f"prefix {{uid}} suffix")
|
||||||
name="test", value=f"prefix {{uid}} suffix"
|
|
||||||
)
|
|
||||||
expected_string = f"prefix {uid} suffix"
|
expected_string = f"prefix {uid} suffix"
|
||||||
|
|
||||||
assert (
|
assert format_string.apply_formatter(variable_dict={"uid": uid}) == expected_string
|
||||||
format_string.apply_formatter(variable_dict={"uid": uid}) == expected_string
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_entry_formatter_duplicate_fields(self):
|
def test_entry_formatter_duplicate_fields(self):
|
||||||
upload_year = "2022"
|
upload_year = "2022"
|
||||||
|
|
@ -133,15 +115,10 @@ class TestStringFormatterValidator(object):
|
||||||
"level_c": "level c and {level_b}",
|
"level_c": "level c and {level_b}",
|
||||||
}
|
}
|
||||||
|
|
||||||
format_string = StringFormatterValidator(
|
format_string = StringFormatterValidator(name="test", value="level d and {level_c}")
|
||||||
name="test", value="level d and {level_c}"
|
|
||||||
)
|
|
||||||
expected_string = "level d and level c and level b and level a"
|
expected_string = "level d and level c and level b and level a"
|
||||||
|
|
||||||
assert (
|
assert format_string.apply_formatter(variable_dict=variable_dict) == expected_string
|
||||||
format_string.apply_formatter(variable_dict=variable_dict)
|
|
||||||
== expected_string
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_entry_formatter_override_recursive_fail_cycle(self):
|
def test_entry_formatter_override_recursive_fail_cycle(self):
|
||||||
variable_dict = {
|
variable_dict = {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,7 @@ class Downloader:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _configure_ytdl_options(
|
def _configure_ytdl_options(cls, ytdl_options: Optional[Dict], working_directory: str) -> Dict:
|
||||||
cls, ytdl_options: Optional[Dict], working_directory: str
|
|
||||||
) -> Dict:
|
|
||||||
"""Configure the ytdl options for the downloader"""
|
"""Configure the ytdl options for the downloader"""
|
||||||
if ytdl_options is None:
|
if ytdl_options is None:
|
||||||
ytdl_options = {}
|
ytdl_options = {}
|
||||||
|
|
@ -56,9 +54,7 @@ class Downloader:
|
||||||
)
|
)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def ytdl_downloader(
|
def ytdl_downloader(self, ytdl_options_overrides: Optional[Dict] = None) -> ytdl.YoutubeDL:
|
||||||
self, ytdl_options_overrides: Optional[Dict] = None
|
|
||||||
) -> ytdl.YoutubeDL:
|
|
||||||
"""
|
"""
|
||||||
Context manager to interact with yt_dlp.
|
Context manager to interact with yt_dlp.
|
||||||
"""
|
"""
|
||||||
|
|
@ -69,9 +65,7 @@ class Downloader:
|
||||||
with ytdl.YoutubeDL(ytdl_options) as ytdl_downloader:
|
with ytdl.YoutubeDL(ytdl_options) as ytdl_downloader:
|
||||||
yield ytdl_downloader
|
yield ytdl_downloader
|
||||||
|
|
||||||
def extract_info(
|
def extract_info(self, ytdl_options_overrides: Optional[Dict] = None, **kwargs) -> Dict:
|
||||||
self, ytdl_options_overrides: Optional[Dict] = None, **kwargs
|
|
||||||
) -> Dict:
|
|
||||||
"""
|
"""
|
||||||
Wrapper around yt_dlp.YoutubeDL.YoutubeDL.extract_info
|
Wrapper around yt_dlp.YoutubeDL.YoutubeDL.extract_info
|
||||||
All kwargs will passed to the extract_info function.
|
All kwargs will passed to the extract_info function.
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,7 @@ class YoutubeDownloader(Downloader):
|
||||||
not fetch the metadata (maybe there is a way??)
|
not fetch the metadata (maybe there is a way??)
|
||||||
"""
|
"""
|
||||||
ytdl_metadata_override = {
|
ytdl_metadata_override = {
|
||||||
"download_archive": str(
|
"download_archive": str(Path(self.output_directory) / "ytdl-download-archive.txt"),
|
||||||
Path(self.output_directory) / "ytdl-download-archive.txt"
|
|
||||||
),
|
|
||||||
"writeinfojson": True,
|
"writeinfojson": True,
|
||||||
}
|
}
|
||||||
_ = self.extract_info(ytdl_options_overrides=ytdl_metadata_override, url=url)
|
_ = self.extract_info(ytdl_options_overrides=ytdl_metadata_override, url=url)
|
||||||
|
|
@ -45,12 +43,8 @@ class YoutubeDownloader(Downloader):
|
||||||
|
|
||||||
# Load the entries from info.json, ignore the playlist entry
|
# Load the entries from info.json, ignore the playlist entry
|
||||||
for file_name in os.listdir(self.output_directory):
|
for file_name in os.listdir(self.output_directory):
|
||||||
if file_name.endswith(".info.json") and not file_name.startswith(
|
if file_name.endswith(".info.json") and not file_name.startswith(playlist_id):
|
||||||
playlist_id
|
with open(Path(self.output_directory) / file_name, "r", encoding="utf-8") as file:
|
||||||
):
|
|
||||||
with open(
|
|
||||||
Path(self.output_directory) / file_name, "r", encoding="utf-8"
|
|
||||||
) as file:
|
|
||||||
entries.append(YoutubeVideo(**json.load(file)))
|
entries.append(YoutubeVideo(**json.load(file)))
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,8 @@ from typing import Optional
|
||||||
|
|
||||||
from sanitize_filename import sanitize
|
from sanitize_filename import sanitize
|
||||||
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
StringFormatterValidator,
|
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.overrides.overrides_validator import (
|
|
||||||
OverridesValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.exceptions import ValidationException
|
from ytdl_subscribe.validators.exceptions import ValidationException
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -33,9 +29,7 @@ class Entry:
|
||||||
def kwargs(self, key) -> Any:
|
def kwargs(self, key) -> Any:
|
||||||
"""Returns an internal kwarg value supplied from ytdl"""
|
"""Returns an internal kwarg value supplied from ytdl"""
|
||||||
if not self.kwargs_contains(key):
|
if not self.kwargs_contains(key):
|
||||||
raise KeyError(
|
raise KeyError(f"Expected '{key}' in {self.__class__.__name__} but does not exist.")
|
||||||
f"Expected '{key}' in {self.__class__.__name__} but does not exist."
|
|
||||||
)
|
|
||||||
return self._kwargs[key]
|
return self._kwargs[key]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,7 @@ class SoundcloudAlbum(Entry):
|
||||||
"""
|
"""
|
||||||
return [SoundcloudTrack(**entry) for entry in self.kwargs("entries")]
|
return [SoundcloudTrack(**entry) for entry in self.kwargs("entries")]
|
||||||
|
|
||||||
def album_tracks(
|
def album_tracks(self, skip_premiere_tracks: bool = True) -> List[SoundcloudAlbumTrack]:
|
||||||
self, skip_premiere_tracks: bool = True
|
|
||||||
) -> List[SoundcloudAlbumTrack]:
|
|
||||||
"""
|
"""
|
||||||
Returns all tracks in the album represented as album-tracks. They will share the
|
Returns all tracks in the album represented as album-tracks. They will share the
|
||||||
same album name, have ordered track numbers, and a shared album year.
|
same album name, have ordered track numbers, and a shared album year.
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ from typing import List
|
||||||
###################################################################################################
|
###################################################################################################
|
||||||
# GLOBAL PARSER
|
# GLOBAL PARSER
|
||||||
from ytdl_subscribe.validators.config.config_file_validator import ConfigFileValidator
|
from ytdl_subscribe.validators.config.config_file_validator import ConfigFileValidator
|
||||||
from ytdl_subscribe.validators.config.subscription_validator import (
|
from ytdl_subscribe.validators.config.subscription_validator import SubscriptionValidator
|
||||||
SubscriptionValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="YoutubeDL-Subscribe: Download and organize your favorite media hassle-free."
|
description="YoutubeDL-Subscribe: Download and organize your favorite media hassle-free."
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,7 @@ class SoundcloudAlbumsAndSinglesSubscription(SoundcloudSubscription):
|
||||||
artist_name=self.download_strategy_options.username.value
|
artist_name=self.download_strategy_options.username.value
|
||||||
)
|
)
|
||||||
tracks += [
|
tracks += [
|
||||||
track
|
track for track in single_tracks if not any(album.contains(track) for album in albums)
|
||||||
for track in single_tracks
|
|
||||||
if not any(album.contains(track) for album in albums)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for entry in tracks:
|
for entry in tracks:
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@ from PIL import Image
|
||||||
|
|
||||||
from ytdl_subscribe.downloaders.downloader import Downloader
|
from ytdl_subscribe.downloaders.downloader import Downloader
|
||||||
from ytdl_subscribe.entries.entry import Entry
|
from ytdl_subscribe.entries.entry import Entry
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.config_options.config_options_validator import (
|
from ytdl_subscribe.validators.config.config_options.config_options_validator import (
|
||||||
ConfigOptionsValidator,
|
ConfigOptionsValidator,
|
||||||
)
|
)
|
||||||
|
|
@ -20,9 +18,7 @@ from ytdl_subscribe.validators.config.preset_validator import PresetValidator
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
||||||
DownloadStrategyValidator,
|
DownloadStrategyValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
|
||||||
SourceValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
SOURCE_T = TypeVar("SOURCE_T", bound=SourceValidator)
|
SOURCE_T = TypeVar("SOURCE_T", bound=SourceValidator)
|
||||||
DOWNLOAD_STRATEGY_T = TypeVar("DOWNLOAD_STRATEGY_T", bound=DownloadStrategyValidator)
|
DOWNLOAD_STRATEGY_T = TypeVar("DOWNLOAD_STRATEGY_T", bound=DownloadStrategyValidator)
|
||||||
|
|
@ -83,9 +79,7 @@ class Subscription(object):
|
||||||
ytdl_options=self.ytdl_options.dict,
|
ytdl_options=self.ytdl_options.dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _apply_formatter(
|
def _apply_formatter(self, entry: Entry, formatter: StringFormatterValidator) -> str:
|
||||||
self, entry: Entry, formatter: StringFormatterValidator
|
|
||||||
) -> str:
|
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
@ -103,13 +97,9 @@ class Subscription(object):
|
||||||
|
|
||||||
def _post_process_tagging(self, entry: Entry):
|
def _post_process_tagging(self, entry: Entry):
|
||||||
id3_options = self.metadata_options.id3
|
id3_options = self.metadata_options.id3
|
||||||
audio_file = music_tag.load_file(
|
audio_file = music_tag.load_file(entry.file_path(relative_directory=self.working_directory))
|
||||||
entry.file_path(relative_directory=self.working_directory)
|
|
||||||
)
|
|
||||||
for tag, tag_formatter in id3_options.tags.dict.items():
|
for tag, tag_formatter in id3_options.tags.dict.items():
|
||||||
audio_file[tag] = self._apply_formatter(
|
audio_file[tag] = self._apply_formatter(entry=entry, formatter=tag_formatter)
|
||||||
entry=entry, formatter=tag_formatter
|
|
||||||
)
|
|
||||||
audio_file.save()
|
audio_file.save()
|
||||||
|
|
||||||
def _post_process_nfo(self, entry):
|
def _post_process_nfo(self, entry):
|
||||||
|
|
@ -128,9 +118,7 @@ class Subscription(object):
|
||||||
attr_type=False,
|
attr_type=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
nfo_file_name = self._apply_formatter(
|
nfo_file_name = self._apply_formatter(entry=entry, formatter=nfo_options.nfo_name)
|
||||||
entry=entry, formatter=nfo_options.nfo_name
|
|
||||||
)
|
|
||||||
output_directory = self._apply_formatter(
|
output_directory = self._apply_formatter(
|
||||||
entry=entry, formatter=self.output_options.output_directory
|
entry=entry, formatter=self.output_options.output_directory
|
||||||
)
|
)
|
||||||
|
|
@ -151,9 +139,7 @@ class Subscription(object):
|
||||||
self._post_process_tagging(entry)
|
self._post_process_tagging(entry)
|
||||||
|
|
||||||
# Move the file after all direct file modifications are complete
|
# Move the file after all direct file modifications are complete
|
||||||
entry_source_file_path = entry.file_path(
|
entry_source_file_path = entry.file_path(relative_directory=self.working_directory)
|
||||||
relative_directory=self.working_directory
|
|
||||||
)
|
|
||||||
|
|
||||||
output_directory = self._apply_formatter(
|
output_directory = self._apply_formatter(
|
||||||
entry=entry, formatter=self.output_options.output_directory
|
entry=entry, formatter=self.output_options.output_directory
|
||||||
|
|
@ -169,9 +155,7 @@ class Subscription(object):
|
||||||
|
|
||||||
# Download the thumbnail if its present
|
# Download the thumbnail if its present
|
||||||
if self.output_options.thumbnail_name:
|
if self.output_options.thumbnail_name:
|
||||||
source_thumbnail_path = entry.thumbnail_path(
|
source_thumbnail_path = entry.thumbnail_path(relative_directory=self.working_directory)
|
||||||
relative_directory=self.working_directory
|
|
||||||
)
|
|
||||||
|
|
||||||
output_thumbnail_name = self._apply_formatter(
|
output_thumbnail_name = self._apply_formatter(
|
||||||
entry=entry, formatter=self.output_options.thumbnail_name
|
entry=entry, formatter=self.output_options.thumbnail_name
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,7 @@ class StrictDictValidator(DictValidator):
|
||||||
# Ensure all required keys are present
|
# Ensure all required keys are present
|
||||||
for required_key in self._required_keys:
|
for required_key in self._required_keys:
|
||||||
if required_key not in self._dict:
|
if required_key not in self._dict:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(f"missing the required field '{required_key}'")
|
||||||
f"missing the required field '{required_key}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure an empty dict was not passed as the value
|
# Ensure an empty dict was not passed as the value
|
||||||
if not self._dict:
|
if not self._dict:
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,7 @@ class DictFormatterValidator(LiteralDictValidator):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
|
|
||||||
for key in self._keys:
|
for key in self._keys:
|
||||||
self._value[key] = self._validate_key(
|
self._value[key] = self._validate_key(key=key, validator=StringFormatterValidator)
|
||||||
key=key, validator=StringFormatterValidator
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dict(self) -> Dict[str, StringFormatterValidator]:
|
def dict(self) -> Dict[str, StringFormatterValidator]:
|
||||||
|
|
@ -134,7 +132,4 @@ class DictFormatterValidator(LiteralDictValidator):
|
||||||
@property
|
@property
|
||||||
def dict_with_format_strings(self) -> Dict[str, str]:
|
def dict_with_format_strings(self) -> Dict[str, str]:
|
||||||
"""Returns dict with the format strings themselves"""
|
"""Returns dict with the format strings themselves"""
|
||||||
return {
|
return {key: string_formatter.format_string for key, string_formatter in self.dict.items()}
|
||||||
key: string_formatter.format_string
|
|
||||||
for key, string_formatter in self.dict.items()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,7 @@ class Validator(ABC):
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._value = copy.deepcopy(
|
self._value = copy.deepcopy(value) # Always deep copy to avoid editing references
|
||||||
value
|
|
||||||
) # Always deep copy to avoid editing references
|
|
||||||
|
|
||||||
if not isinstance(self._value, self._expected_value_type):
|
if not isinstance(self._value, self._expected_value_type):
|
||||||
expected_value_type_name = self._expected_value_type_name or str(
|
expected_value_type_name = self._expected_value_type_name or str(
|
||||||
|
|
@ -147,9 +145,7 @@ class DictValidator(Validator):
|
||||||
An instance of the specified validator
|
An instance of the specified validator
|
||||||
"""
|
"""
|
||||||
if key not in self._dict and default is None:
|
if key not in self._dict and default is None:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(f"{key} is missing when it should be present.")
|
||||||
f"{key} is missing when it should be present."
|
|
||||||
)
|
|
||||||
|
|
||||||
return validator(
|
return validator(
|
||||||
name=f"{self._name}.{key}",
|
name=f"{self._name}.{key}",
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ class ConfigFileValidator(StrictDictValidator):
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
self.config_options = self._validate_key(
|
self.config_options = self._validate_key("configuration", ConfigOptionsValidator)
|
||||||
"configuration", ConfigOptionsValidator
|
|
||||||
)
|
|
||||||
self.presets = self._validate_key("presets", ConfigPresetsValidator)
|
self.presets = self._validate_key("presets", ConfigPresetsValidator)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
||||||
DictFormatterValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
||||||
from ytdl_subscribe.validators.base.validators import StringValidator
|
from ytdl_subscribe.validators.base.validators import StringValidator
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
||||||
DictFormatterValidator,
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NFOValidator(StrictDictValidator):
|
class NFOValidator(StrictDictValidator):
|
||||||
|
|
@ -13,10 +9,6 @@ class NFOValidator(StrictDictValidator):
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
|
|
||||||
self.nfo_name = self._validate_key(
|
self.nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
|
||||||
key="nfo_name", validator=StringFormatterValidator
|
self.nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
|
||||||
)
|
|
||||||
self.nfo_root = self._validate_key(
|
|
||||||
key="nfo_root", validator=StringFormatterValidator
|
|
||||||
)
|
|
||||||
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
|
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
import sanitize_filename
|
import sanitize_filename
|
||||||
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
from ytdl_subscribe.validators.base.string_formatter_validators import DictFormatterValidator
|
||||||
DictFormatterValidator,
|
from ytdl_subscribe.validators.base.string_formatter_validators import StringFormatterValidator
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.base.string_formatter_validators import (
|
|
||||||
StringFormatterValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OverridesValidator(DictFormatterValidator):
|
class OverridesValidator(DictFormatterValidator):
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,11 @@ from ytdl_subscribe.validators.config.metadata_options.metadata_options_validato
|
||||||
from ytdl_subscribe.validators.config.output_options.output_options_validator import (
|
from ytdl_subscribe.validators.config.output_options.output_options_validator import (
|
||||||
OutputOptionsValidator,
|
OutputOptionsValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.overrides.overrides_validator import (
|
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator
|
||||||
OverridesValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import (
|
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import (
|
||||||
SoundcloudSourceValidator,
|
SoundcloudSourceValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
|
||||||
SourceValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.source_options.youtube_validators import (
|
from ytdl_subscribe.validators.config.source_options.youtube_validators import (
|
||||||
YoutubeSourceValidator,
|
YoutubeSourceValidator,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ from ytdl_subscribe.validators.base.validators import StringValidator
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
||||||
DownloadStrategyValidator,
|
DownloadStrategyValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
|
||||||
SourceValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudAlbumsAndSinglesDownloadValidator(DownloadStrategyValidator):
|
class SoundcloudAlbumsAndSinglesDownloadValidator(DownloadStrategyValidator):
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ class SourceValidator(StrictDictValidator):
|
||||||
# Extra fields will be strict-validated using other StictDictValidators
|
# Extra fields will be strict-validated using other StictDictValidators
|
||||||
_allow_extra_keys = True
|
_allow_extra_keys = True
|
||||||
|
|
||||||
_download_strategy_validator_mapping: Dict[
|
_download_strategy_validator_mapping: Dict[str, Type[DownloadStrategyValidator]] = {}
|
||||||
str, Type[DownloadStrategyValidator]
|
|
||||||
] = {}
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
super().__init__(name=name, value=value)
|
super().__init__(name=name, value=value)
|
||||||
|
|
@ -39,9 +37,7 @@ class SourceValidator(StrictDictValidator):
|
||||||
for key_to_delete in self._allowed_keys:
|
for key_to_delete in self._allowed_keys:
|
||||||
del download_strategy_dict[key_to_delete]
|
del download_strategy_dict[key_to_delete]
|
||||||
|
|
||||||
download_strategy_class = self._download_strategy_validator_mapping[
|
download_strategy_class = self._download_strategy_validator_mapping[download_strategy_name]
|
||||||
download_strategy_name
|
|
||||||
]
|
|
||||||
self.download_strategy = download_strategy_class(
|
self.download_strategy = download_strategy_class(
|
||||||
name=self._name, value=download_strategy_dict
|
name=self._name, value=download_strategy_dict
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@ from ytdl_subscribe.validators.base.validators import StringValidator
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
||||||
DownloadStrategyValidator,
|
DownloadStrategyValidator,
|
||||||
)
|
)
|
||||||
from ytdl_subscribe.validators.config.source_options.source_validator import (
|
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
|
||||||
SourceValidator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistDownloadValidator(DownloadStrategyValidator):
|
class YoutubePlaylistDownloadValidator(DownloadStrategyValidator):
|
||||||
|
|
@ -18,9 +16,7 @@ class YoutubePlaylistDownloadValidator(DownloadStrategyValidator):
|
||||||
|
|
||||||
|
|
||||||
class YoutubeSourceValidator(SourceValidator):
|
class YoutubeSourceValidator(SourceValidator):
|
||||||
_download_strategy_validator_mapping = {
|
_download_strategy_validator_mapping = {"playlist": YoutubePlaylistDownloadValidator}
|
||||||
"playlist": YoutubePlaylistDownloadValidator
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
super().__init__(name=name, value=value)
|
super().__init__(name=name, value=value)
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,13 @@ from typing import List
|
||||||
import yaml
|
import yaml
|
||||||
from mergedeep import mergedeep
|
from mergedeep import mergedeep
|
||||||
|
|
||||||
from ytdl_subscribe.subscriptions.soundcloud import (
|
from ytdl_subscribe.subscriptions.soundcloud import SoundcloudAlbumsAndSinglesSubscription
|
||||||
SoundcloudAlbumsAndSinglesSubscription,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.subscriptions.subscription import Subscription
|
from ytdl_subscribe.subscriptions.subscription import Subscription
|
||||||
from ytdl_subscribe.subscriptions.youtube import YoutubePlaylistSubscription
|
from ytdl_subscribe.subscriptions.youtube import YoutubePlaylistSubscription
|
||||||
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_subscribe.validators.base.validators import StringValidator
|
from ytdl_subscribe.validators.base.validators import StringValidator
|
||||||
from ytdl_subscribe.validators.config.config_file_validator import ConfigFileValidator
|
from ytdl_subscribe.validators.config.config_file_validator import ConfigFileValidator
|
||||||
from ytdl_subscribe.validators.config.overrides.overrides_validator import (
|
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator
|
||||||
OverridesValidator,
|
|
||||||
)
|
|
||||||
from ytdl_subscribe.validators.config.preset_validator import PRESET_OPTIONAL_KEYS
|
from ytdl_subscribe.validators.config.preset_validator import PRESET_OPTIONAL_KEYS
|
||||||
from ytdl_subscribe.validators.config.preset_validator import PRESET_REQUIRED_KEYS
|
from ytdl_subscribe.validators.config.preset_validator import PRESET_REQUIRED_KEYS
|
||||||
from ytdl_subscribe.validators.config.preset_validator import PresetValidator
|
from ytdl_subscribe.validators.config.preset_validator import PresetValidator
|
||||||
|
|
@ -60,9 +56,7 @@ class SubscriptionValidator(StrictDictValidator):
|
||||||
# A little hacky, we will override the preset with the contents of this subscription,
|
# A little hacky, we will override the preset with the contents of this subscription,
|
||||||
# then validate it
|
# then validate it
|
||||||
preset_dict = copy.deepcopy(self.config.presets.dict[preset_name])
|
preset_dict = copy.deepcopy(self.config.presets.dict[preset_name])
|
||||||
preset_dict = mergedeep.merge(
|
preset_dict = mergedeep.merge(preset_dict, self._dict, strategy=mergedeep.Strategy.REPLACE)
|
||||||
preset_dict, self._dict, strategy=mergedeep.Strategy.REPLACE
|
|
||||||
)
|
|
||||||
del preset_dict["preset"]
|
del preset_dict["preset"]
|
||||||
|
|
||||||
self.preset = PresetValidator(
|
self.preset = PresetValidator(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue