ytdl-sub/src/ytdl_sub/downloaders/soundcloud/albums_and_singles.py
Jesse Bannon 6033f0bc63 docs docs
2022-09-14 22:55:42 -07:00

137 lines
4.4 KiB
Python

from typing import Dict
from typing import Generator
from ytdl_sub.downloaders.downloader import Downloader
from ytdl_sub.downloaders.downloader import DownloaderValidator
from ytdl_sub.downloaders.generic.collection_validator import CollectionValidator
from ytdl_sub.entries.entry import Entry
from ytdl_sub.validators.url_validator import SoundcloudUsernameUrlValidator
from ytdl_sub.validators.validators import BoolValidator
class SoundcloudAlbumsAndSinglesDownloadOptions(DownloaderValidator):
"""
Downloads a soundcloud user's entire discography. Groups together album tracks and considers
any track not in an album as a single. Also includes any collaboration tracks.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
soundcloud:
# required
download_strategy: "albums_and_singles"
url: "soundcloud.com/username"
# optional
skip_premiere_tracks: True
"""
_required_keys = {"url"}
_optional_keys = {"skip_premiere_tracks"}
def __init__(self, name, value):
super().__init__(name, value)
self._url = self._validate_key(
key="url", validator=SoundcloudUsernameUrlValidator
).username_url
self._skip_premiere_tracks = self._validate_key(
"skip_premiere_tracks", BoolValidator, default=True
)
@property
def collection_validator(self) -> CollectionValidator:
"""Downloads the album tracks first, then the tracks"""
return CollectionValidator(
name=self._name,
value={
"urls": [
{
"url": f"{self._url}/tracks",
"variables": {
"track_number": "1",
"track_number_padded": "01",
"track_count": "1",
"album": "{title}",
"album_sanitized": "{title_sanitized}",
"album_year": "{upload_year}",
},
},
{
"url": f"{self._url}/albums",
"variables": {
"track_number": "{playlist_index}",
"track_number_padded": "{playlist_index_padded}",
"track_count": "{playlist_count}",
"album": "{playlist}",
"album_sanitized": "{playlist_sanitized}",
"album_year": "{playlist_max_upload_year}",
},
},
]
},
)
@property
def skip_premiere_tracks(self) -> bool:
"""
Optional. True to skip tracks that require purchasing. False otherwise. Defaults to True.
"""
return self._skip_premiere_tracks.value
@property
def url(self) -> str:
"""
Required. The Soundcloud user's url, i.e. ``soundcloud.com/the_username``
"""
return self._url
class SoundcloudAlbumsAndSinglesDownloader(
Downloader[SoundcloudAlbumsAndSinglesDownloadOptions, Entry]
):
downloader_options_type = SoundcloudAlbumsAndSinglesDownloadOptions
downloader_entry_type = Entry
supports_subtitles = False
supports_chapters = False
@classmethod
def ytdl_option_defaults(cls) -> Dict:
"""
Default `ytdl_options`_ for ``albums_and_singles``
.. code-block:: yaml
ytdl_options:
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
format: "bestaudio[ext=mp3]" # download format the best possible mp3
"""
return dict(
super().ytdl_option_defaults(),
**{
"format": "bestaudio[ext=mp3]",
},
)
def _should_skip(self, entry: Entry) -> bool:
if not self.download_options.skip_premiere_tracks:
return False
for url in [entry.kwargs_get("url", ""), entry.webpage_url]:
if "/preview/" in url:
return True
return False
def download(self) -> Generator[Entry, None, None]:
"""
Soundcloud subscription to download albums and tracks as singles.
"""
for entry in super().download():
if self._should_skip(entry):
continue
yield entry