make download strategy a psuedo validator

This commit is contained in:
jbannon 2022-04-09 06:01:01 +00:00
parent 4b05a13714
commit 2f3a647775
10 changed files with 125 additions and 128 deletions

View file

@ -7,13 +7,10 @@ from ytdl_subscribe.subscriptions.subscription import Subscription
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import ( from ytdl_subscribe.validators.config.source_options.soundcloud_validators import (
SoundcloudAlbumsAndSinglesDownloadValidator, SoundcloudAlbumsAndSinglesDownloadValidator,
) )
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import (
SoundcloudSourceValidator,
)
class SoundcloudAlbumsAndSinglesSubscription( class SoundcloudAlbumsAndSinglesSubscription(
Subscription[SoundcloudSourceValidator, SoundcloudAlbumsAndSinglesDownloadValidator] Subscription[SoundcloudAlbumsAndSinglesDownloadValidator]
): ):
def extract_info(self): def extract_info(self):
tracks: List[SoundcloudTrack] = [] tracks: List[SoundcloudTrack] = []
@ -22,7 +19,7 @@ class SoundcloudAlbumsAndSinglesSubscription(
# Get the album info first. This tells us which track ids belong # Get the album info first. This tells us which track ids belong
# to an album. Unfortunately we cannot use download_archive or info.json for this # to an album. Unfortunately we cannot use download_archive or info.json for this
albums: List[SoundcloudAlbum] = downloader.download_albums( albums: List[SoundcloudAlbum] = downloader.download_albums(
artist_name=self.download_strategy_options.username.value artist_name=self.source_options.username.value
) )
for album in albums: for album in albums:
@ -31,9 +28,7 @@ class SoundcloudAlbumsAndSinglesSubscription(
) )
# only add tracks that are not part of an album # only add tracks that are not part of an album
single_tracks = downloader.download_tracks( single_tracks = downloader.download_tracks(artist_name=self.source_options.username.value)
artist_name=self.download_strategy_options.username.value
)
tracks += [ tracks += [
track for track in single_tracks if not any(album.contains(track) for album in albums) track for track in single_tracks if not any(album.contains(track) for album in albums)
] ]

View file

@ -24,17 +24,13 @@ from ytdl_subscribe.validators.config.output_options.output_options_validator im
) )
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator from ytdl_subscribe.validators.config.overrides.overrides_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.source_options.source_validator import ( from ytdl_subscribe.validators.config.source_options.source_validators import SourceValidator
DownloadStrategyValidator,
)
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
T = TypeVar("T", bound=SourceValidator) T = TypeVar("T", bound=SourceValidator)
U = TypeVar("U", bound=Downloader) U = TypeVar("U", bound=Downloader)
V = TypeVar("V", bound=DownloadStrategyValidator)
class Subscription(Generic[T, V], ABC): class Subscription(Generic[T], ABC):
def __init__( def __init__(
self, self,
name: str, name: str,
@ -65,11 +61,6 @@ class Subscription(Generic[T, V], ABC):
ytdl_options=self.__preset_options.ytdl_options.dict, ytdl_options=self.__preset_options.ytdl_options.dict,
) )
@property
def download_strategy_options(self) -> V:
"""Returns the download strategy options defined for this subscription"""
return self.source_options.download_strategy
@property @property
def output_options(self) -> OutputOptionsValidator: def output_options(self) -> OutputOptionsValidator:
"""Returns the output options defined for this subscription""" """Returns the output options defined for this subscription"""

View file

@ -3,17 +3,12 @@ from ytdl_subscribe.subscriptions.subscription import Subscription
from ytdl_subscribe.validators.config.source_options.youtube_validators import ( from ytdl_subscribe.validators.config.source_options.youtube_validators import (
YoutubePlaylistDownloadValidator, YoutubePlaylistDownloadValidator,
) )
from ytdl_subscribe.validators.config.source_options.youtube_validators import (
YoutubeSourceValidator,
)
class YoutubePlaylistSubscription( class YoutubePlaylistSubscription(Subscription[YoutubePlaylistDownloadValidator]):
Subscription[YoutubeSourceValidator, YoutubePlaylistDownloadValidator]
):
def extract_info(self): def extract_info(self):
entries = self.get_downloader(YoutubeDownloader).download_playlist( entries = self.get_downloader(YoutubeDownloader).download_playlist(
playlist_id=self.download_strategy_options.playlist_id.value playlist_id=self.source_options.playlist_id.value
) )
for entry in entries: for entry in entries:

View file

@ -12,21 +12,24 @@ from ytdl_subscribe.validators.config.output_options.output_options_validator im
OutputOptionsValidator, OutputOptionsValidator,
) )
from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator from ytdl_subscribe.validators.config.overrides.overrides_validator import OverridesValidator
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import ( from ytdl_subscribe.validators.config.source_options.download_strategy_validators import (
SoundcloudSourceValidator, DownloadStrategyValidator,
) )
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator from ytdl_subscribe.validators.config.source_options.download_strategy_validators import (
from ytdl_subscribe.validators.config.source_options.youtube_validators import ( SoundcloudDownloadStrategyValidator,
YoutubeSourceValidator,
) )
from ytdl_subscribe.validators.config.source_options.download_strategy_validators import (
YoutubeDownloadStrategyValidator,
)
from ytdl_subscribe.validators.config.source_options.source_validators import SourceValidator
from ytdl_subscribe.validators.config.ytdl_options.ytdl_options_validator import ( from ytdl_subscribe.validators.config.ytdl_options.ytdl_options_validator import (
YTDLOptionsValidator, YTDLOptionsValidator,
) )
from ytdl_subscribe.validators.exceptions import ValidationException from ytdl_subscribe.validators.exceptions import ValidationException
PRESET_SOURCE_VALIDATOR_MAPPING: Dict[str, Type[SourceValidator]] = { PRESET_SOURCE_VALIDATOR_MAPPING: Dict[str, Type[DownloadStrategyValidator]] = {
"soundcloud": SoundcloudSourceValidator, "soundcloud": SoundcloudDownloadStrategyValidator,
"youtube": YoutubeSourceValidator, "youtube": YoutubeDownloadStrategyValidator,
} }
PRESET_REQUIRED_KEYS = {"output_options"} PRESET_REQUIRED_KEYS = {"output_options"}
@ -47,28 +50,29 @@ class PresetValidator(StrictDictValidator):
return sorted(list(PRESET_SOURCE_VALIDATOR_MAPPING.keys())) return sorted(list(PRESET_SOURCE_VALIDATOR_MAPPING.keys()))
def __validate_and_get_subscription_source(self) -> SourceValidator: def __validate_and_get_subscription_source(self) -> SourceValidator:
subscription_source: Optional[SourceValidator] = None download_strategy_validator: Optional[DownloadStrategyValidator] = None
for key in self._keys: for key in self._keys:
if key in self.__available_sources and subscription_source: # Ensure there are not multiple sources, i.e. youtube and soundcloud
if key in self.__available_sources and download_strategy_validator:
raise ValidationException( raise ValidationException(
f"'{self._name}' can only have one of the following sources: " f"'{self._name}' can only have one of the following sources: "
f"{', '.join(self.__available_sources)}" f"{', '.join(self.__available_sources)}"
) )
if key in PRESET_SOURCE_VALIDATOR_MAPPING: if key in PRESET_SOURCE_VALIDATOR_MAPPING:
subscription_source = self._validate_key( download_strategy_validator = self._validate_key(
key=key, validator=PRESET_SOURCE_VALIDATOR_MAPPING[key] key=key, validator=PRESET_SOURCE_VALIDATOR_MAPPING[key]
) )
# If subscription source was not set, error # If subscription source was not set, error
if not subscription_source: if not download_strategy_validator:
raise ValidationException( raise ValidationException(
f"'{self._name} must have one of the following sources: " f"'{self._name} must have one of the following sources: "
f"{', '.join(self.__available_sources)}" f"{', '.join(self.__available_sources)}"
) )
return subscription_source return download_strategy_validator.source_validator
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)

View file

@ -0,0 +1,64 @@
import copy
from abc import ABC
from typing import Any
from typing import Dict
from typing import Type
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
from ytdl_subscribe.validators.base.validators import StringValidator
from ytdl_subscribe.validators.config.source_options.soundcloud_validators import (
SoundcloudAlbumsAndSinglesDownloadValidator,
)
from ytdl_subscribe.validators.config.source_options.source_validators import SourceValidator
from ytdl_subscribe.validators.config.source_options.youtube_validators import (
YoutubePlaylistDownloadValidator,
)
class DownloadStrategyValidator(StrictDictValidator, ABC):
"""
Validates the download strategy of a source. Does not validate the source options.
"""
# All media sources must define a download strategy
_required_keys = {"download_strategy"}
# Extra fields will be strict-validated using other StictDictValidators
_allow_extra_keys = True
_download_strategy_to_source_mapping: Dict[str, Type[SourceValidator]] = {}
def __init__(self, name: str, value: Any):
super().__init__(name=name, value=value)
download_strategy_name = self._validate_key(
key="download_strategy",
validator=StringValidator,
).value
if download_strategy_name not in self._possible_download_strategies:
raise self._validation_exception(
f"download_strategy must be one of the following: "
f"{', '.join(self._possible_download_strategies)}"
)
# Remove the 'download_strategy' key before passing the other keys to the actual
# source validator
source_validator_dict = copy.deepcopy(self._dict)
del source_validator_dict["download_strategy"]
source_validator_class = self._download_strategy_to_source_mapping[download_strategy_name]
self.source_validator = source_validator_class(name=self._name, value=source_validator_dict)
@property
def _possible_download_strategies(self):
return sorted(list(self._download_strategy_to_source_mapping.keys()))
class SoundcloudDownloadStrategyValidator(DownloadStrategyValidator):
_download_strategy_to_source_mapping = {
"albums_and_singles": SoundcloudAlbumsAndSinglesDownloadValidator
}
class YoutubeDownloadStrategyValidator(DownloadStrategyValidator):
_download_strategy_to_source_mapping = {"playlist": YoutubePlaylistDownloadValidator}

View file

@ -1,28 +1,12 @@
from ytdl_subscribe.validators.base.validators import BoolValidator
from ytdl_subscribe.validators.base.validators import StringValidator 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_validators import (
DownloadStrategyValidator, SoundcloudSourceValidator,
) )
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
class SoundcloudAlbumsAndSinglesDownloadValidator(DownloadStrategyValidator): class SoundcloudAlbumsAndSinglesDownloadValidator(SoundcloudSourceValidator):
_required_keys = {"username"} _required_keys = {"username"}
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
self.username = self._validate_key(key="username", validator=StringValidator) self.username = self._validate_key(key="username", validator=StringValidator)
class SoundcloudSourceValidator(SourceValidator):
_optional_keys = {"skip_premiere_tracks"}
_download_strategy_validator_mapping = {
"albums_and_singles": SoundcloudAlbumsAndSinglesDownloadValidator
}
def __init__(self, name: str, value: dict):
super().__init__(name=name, value=value)
self.skip_premiere_tracks = self._validate_key(
"skip_premiere_tracks", BoolValidator, default=True
)

View file

@ -1,48 +0,0 @@
import copy
from typing import Any
from typing import Dict
from typing import Type
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
from ytdl_subscribe.validators.base.validators import StringValidator
class DownloadStrategyValidator(StrictDictValidator):
pass
class SourceValidator(StrictDictValidator):
# All media sources must define a download strategy
_required_keys = {"download_strategy"}
# Extra fields will be strict-validated using other StictDictValidators
_allow_extra_keys = True
_download_strategy_validator_mapping: Dict[str, Type[DownloadStrategyValidator]] = {}
def __init__(self, name: str, value: Any):
super().__init__(name=name, value=value)
download_strategy_name = self._validate_key(
key="download_strategy",
validator=StringValidator,
).value
if download_strategy_name not in self._possible_download_strategies:
raise self._validation_exception(
f"download_strategy must be one of the following: "
f"{', '.join(self._possible_download_strategies)}"
)
# Remove all non-download strategy keys before passing the dict to the validator
download_strategy_dict = copy.deepcopy(self._dict)
for key_to_delete in self._allowed_keys:
del download_strategy_dict[key_to_delete]
download_strategy_class = self._download_strategy_validator_mapping[download_strategy_name]
self.download_strategy = download_strategy_class(
name=self._name, value=download_strategy_dict
)
@property
def _possible_download_strategies(self):
return sorted(list(self._download_strategy_validator_mapping.keys()))

View file

@ -0,0 +1,30 @@
from abc import ABC
from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator
from ytdl_subscribe.validators.base.validators import BoolValidator
class SourceValidator(StrictDictValidator):
"""
Abstract class for any source validator
"""
class SoundcloudSourceValidator(SourceValidator, ABC):
"""
Abstract source validator for all soundcloud sources.
"""
_optional_keys = {"skip_premiere_tracks"}
def __init__(self, name: str, value: dict):
super().__init__(name=name, value=value)
self.skip_premiere_tracks = self._validate_key(
"skip_premiere_tracks", BoolValidator, default=True
)
class YoutubeSourceValidator(SourceValidator, ABC):
"""
Abstract source validator for all soundcloud sources.
"""

View file

@ -1,22 +1,10 @@
from typing import Any
from ytdl_subscribe.validators.base.validators import StringValidator 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_validators import YoutubeSourceValidator
DownloadStrategyValidator,
)
from ytdl_subscribe.validators.config.source_options.source_validator import SourceValidator
class YoutubePlaylistDownloadValidator(DownloadStrategyValidator): class YoutubePlaylistDownloadValidator(YoutubeSourceValidator):
_required_keys = {"playlist_id"} _required_keys = {"playlist_id"}
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
self.playlist_id = self._validate_key("playlist_id", StringValidator) self.playlist_id = self._validate_key("playlist_id", StringValidator)
class YoutubeSourceValidator(SourceValidator):
_download_strategy_validator_mapping = {"playlist": YoutubePlaylistDownloadValidator}
def __init__(self, name: str, value: Any):
super().__init__(name=name, value=value)

View file

@ -65,15 +65,9 @@ class SubscriptionValidator(StrictDictValidator):
) )
def to_subscription(self) -> Subscription: def to_subscription(self) -> Subscription:
if isinstance( if isinstance(self.preset.subscription_source, SoundcloudAlbumsAndSinglesDownloadValidator):
self.preset.subscription_source.download_strategy,
SoundcloudAlbumsAndSinglesDownloadValidator,
):
subscription_class = SoundcloudAlbumsAndSinglesSubscription subscription_class = SoundcloudAlbumsAndSinglesSubscription
elif isinstance( elif isinstance(self.preset.subscription_source, YoutubePlaylistDownloadValidator):
self.preset.subscription_source.download_strategy,
YoutubePlaylistDownloadValidator,
):
subscription_class = YoutubePlaylistSubscription subscription_class = YoutubePlaylistSubscription
else: else:
raise ValueError("subscription source class not found") raise ValueError("subscription source class not found")