[REFACTOR] Move downloaders to separate files (#91)
This commit is contained in:
parent
9df40497e2
commit
607ad9f9de
11 changed files with 267 additions and 270 deletions
|
|
@ -45,13 +45,13 @@ for available source variables to use.
|
||||||
|
|
||||||
channel
|
channel
|
||||||
_______
|
_______
|
||||||
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions()
|
.. autoclass:: ytdl_sub.downloaders.youtube.channel.YoutubeChannelDownloaderOptions()
|
||||||
:members:
|
:members:
|
||||||
:member-order: bysource
|
:member-order: bysource
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:exclude-members: get_date_range
|
:exclude-members: get_date_range
|
||||||
|
|
||||||
.. autofunction:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloader.ytdl_option_defaults()
|
.. autofunction:: ytdl_sub.downloaders.youtube.channel.YoutubeChannelDownloader.ytdl_option_defaults()
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -59,23 +59,23 @@ _______
|
||||||
|
|
||||||
playlist
|
playlist
|
||||||
________
|
________
|
||||||
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions()
|
.. autoclass:: ytdl_sub.downloaders.youtube.playlist.YoutubePlaylistDownloaderOptions()
|
||||||
:members:
|
:members:
|
||||||
:member-order: bysource
|
:member-order: bysource
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
.. autofunction:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloader.ytdl_option_defaults()
|
.. autofunction:: ytdl_sub.downloaders.youtube.playlist.YoutubePlaylistDownloader.ytdl_option_defaults()
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
video
|
video
|
||||||
_____
|
_____
|
||||||
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions()
|
.. autoclass:: ytdl_sub.downloaders.youtube.video.YoutubeVideoDownloaderOptions()
|
||||||
:members:
|
:members:
|
||||||
:member-order: bysource
|
:member-order: bysource
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
.. autofunction:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloader.ytdl_option_defaults()
|
.. autofunction:: ytdl_sub.downloaders.youtube.video.YoutubeVideoDownloader.ytdl_option_defaults()
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -109,12 +109,12 @@ for available source variables to use.
|
||||||
|
|
||||||
albums_and_singles
|
albums_and_singles
|
||||||
__________________
|
__________________
|
||||||
.. autoclass:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloadOptions()
|
.. autoclass:: ytdl_sub.downloaders.soundcloud.albums_and_singles.SoundcloudAlbumsAndSinglesDownloadOptions()
|
||||||
:members:
|
:members:
|
||||||
:member-order: bysource
|
:member-order: bysource
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
.. autofunction:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloader.ytdl_option_defaults()
|
.. autofunction:: ytdl_sub.downloaders.soundcloud.albums_and_singles.SoundcloudAlbumsAndSinglesDownloader.ytdl_option_defaults()
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ from typing import List
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from ytdl_sub.downloaders.downloader import Downloader
|
from ytdl_sub.downloaders.downloader import Downloader
|
||||||
from ytdl_sub.downloaders.soundcloud_downloader import SoundcloudAlbumsAndSinglesDownloader
|
from ytdl_sub.downloaders.soundcloud.albums_and_singles import SoundcloudAlbumsAndSinglesDownloader
|
||||||
|
from ytdl_sub.downloaders.youtube.channel import YoutubeChannelDownloader
|
||||||
from ytdl_sub.downloaders.youtube.merge_playlist import YoutubeMergePlaylistDownloader
|
from ytdl_sub.downloaders.youtube.merge_playlist import YoutubeMergePlaylistDownloader
|
||||||
|
from ytdl_sub.downloaders.youtube.playlist import YoutubePlaylistDownloader
|
||||||
from ytdl_sub.downloaders.youtube.split_video import YoutubeSplitVideoDownloader
|
from ytdl_sub.downloaders.youtube.split_video import YoutubeSplitVideoDownloader
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeChannelDownloader
|
from ytdl_sub.downloaders.youtube.video import YoutubeVideoDownloader
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubePlaylistDownloader
|
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeVideoDownloader
|
|
||||||
from ytdl_sub.plugins.music_tags import MusicTagsPlugin
|
from ytdl_sub.plugins.music_tags import MusicTagsPlugin
|
||||||
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
|
from ytdl_sub.plugins.nfo_tags import NfoTagsPlugin
|
||||||
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
|
from ytdl_sub.plugins.output_directory_nfo_tags import OutputDirectoryNfoTagsPlugin
|
||||||
|
|
|
||||||
0
src/ytdl_sub/downloaders/soundcloud/__init__.py
Normal file
0
src/ytdl_sub/downloaders/soundcloud/__init__.py
Normal file
65
src/ytdl_sub/downloaders/soundcloud/abc.py
Normal file
65
src/ytdl_sub/downloaders/soundcloud/abc.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Generic
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from ytdl_sub.downloaders.downloader import Downloader
|
||||||
|
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
||||||
|
from ytdl_sub.entries.soundcloud import SoundcloudTrack
|
||||||
|
from ytdl_sub.validators.validators import BoolValidator
|
||||||
|
|
||||||
|
|
||||||
|
class SoundcloudDownloaderOptions(DownloaderValidator, 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
|
||||||
|
)
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
|
||||||
|
SoundcloudDownloaderOptionsT = TypeVar(
|
||||||
|
"SoundcloudDownloaderOptionsT", bound=SoundcloudDownloaderOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SoundcloudDownloader(
|
||||||
|
Downloader[SoundcloudDownloaderOptionsT, SoundcloudTrack],
|
||||||
|
Generic[SoundcloudDownloaderOptionsT],
|
||||||
|
ABC,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Class that handles downloading soundcloud entries via ytdl and converting them into
|
||||||
|
SoundcloudTrack / SoundcloudAlbumTrack objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
downloader_entry_type = SoundcloudTrack
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def artist_albums_url(cls, artist_url: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Full artist album url
|
||||||
|
"""
|
||||||
|
return f"{artist_url}/albums"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def artist_tracks_url(cls, artist_url: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Full artist tracks url
|
||||||
|
"""
|
||||||
|
return f"{artist_url}/tracks"
|
||||||
|
|
@ -1,79 +1,11 @@
|
||||||
from abc import ABC
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Generic
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import TypeVar
|
|
||||||
|
|
||||||
from ytdl_sub.downloaders.downloader import Downloader
|
from ytdl_sub.downloaders.soundcloud.abc import SoundcloudDownloader
|
||||||
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
from ytdl_sub.downloaders.soundcloud.abc import SoundcloudDownloaderOptions
|
||||||
from ytdl_sub.entries.soundcloud import SoundcloudAlbum
|
from ytdl_sub.entries.soundcloud import SoundcloudAlbum
|
||||||
from ytdl_sub.entries.soundcloud import SoundcloudTrack
|
from ytdl_sub.entries.soundcloud import SoundcloudTrack
|
||||||
from ytdl_sub.validators.url_validator import SoundcloudUsernameUrlValidator
|
from ytdl_sub.validators.url_validator import SoundcloudUsernameUrlValidator
|
||||||
from ytdl_sub.validators.validators import BoolValidator
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Abstract Soundcloud downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudDownloaderOptions(DownloaderValidator, 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
|
|
||||||
)
|
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
|
|
||||||
SoundcloudDownloaderOptionsT = TypeVar(
|
|
||||||
"SoundcloudDownloaderOptionsT", bound=SoundcloudDownloaderOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudDownloader(
|
|
||||||
Downloader[SoundcloudDownloaderOptionsT, SoundcloudTrack],
|
|
||||||
Generic[SoundcloudDownloaderOptionsT],
|
|
||||||
ABC,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Class that handles downloading soundcloud entries via ytdl and converting them into
|
|
||||||
SoundcloudTrack / SoundcloudAlbumTrack objects
|
|
||||||
"""
|
|
||||||
|
|
||||||
downloader_entry_type = SoundcloudTrack
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def artist_albums_url(cls, artist_url: str) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full artist album url
|
|
||||||
"""
|
|
||||||
return f"{artist_url}/albums"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def artist_tracks_url(cls, artist_url: str) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full artist tracks url
|
|
||||||
"""
|
|
||||||
return f"{artist_url}/tracks"
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Soundcloud albums and singles downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudAlbumsAndSinglesDownloadOptions(SoundcloudDownloaderOptions):
|
class SoundcloudAlbumsAndSinglesDownloadOptions(SoundcloudDownloaderOptions):
|
||||||
29
src/ytdl_sub/downloaders/youtube/abc.py
Normal file
29
src/ytdl_sub/downloaders/youtube/abc.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Generic
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from ytdl_sub.downloaders.downloader import Downloader
|
||||||
|
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
||||||
|
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeDownloaderOptions(DownloaderValidator, ABC):
|
||||||
|
"""
|
||||||
|
Abstract source validator for all soundcloud sources.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
YoutubeDownloaderOptionsT = TypeVar("YoutubeDownloaderOptionsT", bound=YoutubeDownloaderOptions)
|
||||||
|
YoutubeVideoT = TypeVar("YoutubeVideoT", bound=YoutubeVideo)
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeDownloader(
|
||||||
|
Downloader[YoutubeDownloaderOptionsT, YoutubeVideoT],
|
||||||
|
Generic[YoutubeDownloaderOptionsT, YoutubeVideoT],
|
||||||
|
ABC,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Class that handles downloading youtube entries via ytdl and converting them into
|
||||||
|
YoutubeVideo like objects. Reserved for any future logic that is shared amongst all YT
|
||||||
|
downloaders.
|
||||||
|
"""
|
||||||
|
|
@ -1,205 +1,23 @@
|
||||||
from abc import ABC
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Generic
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import TypeVar
|
|
||||||
|
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
from ytdl_sub.config.preset_options import Overrides
|
||||||
from ytdl_sub.downloaders.downloader import Downloader
|
|
||||||
from ytdl_sub.downloaders.downloader import DownloaderOptionsT
|
from ytdl_sub.downloaders.downloader import DownloaderOptionsT
|
||||||
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||||
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloaderOptions
|
||||||
from ytdl_sub.entries.youtube import YoutubeChannel
|
from ytdl_sub.entries.youtube import YoutubeChannel
|
||||||
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
|
||||||
from ytdl_sub.entries.youtube import YoutubeVideo
|
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
from ytdl_sub.utils.thumbnail import convert_url_thumbnail
|
from ytdl_sub.utils.thumbnail import convert_url_thumbnail
|
||||||
from ytdl_sub.validators.date_range_validator import DateRangeValidator
|
from ytdl_sub.validators.date_range_validator import DateRangeValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
||||||
from ytdl_sub.validators.url_validator import YoutubeChannelUrlValidator
|
from ytdl_sub.validators.url_validator import YoutubeChannelUrlValidator
|
||||||
from ytdl_sub.validators.url_validator import YoutubePlaylistUrlValidator
|
|
||||||
from ytdl_sub.validators.url_validator import YoutubeVideoUrlValidator
|
|
||||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||||
|
|
||||||
logger = Logger.get()
|
logger = Logger.get()
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Abstract Youtube downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDownloaderOptions(DownloaderValidator, ABC):
|
|
||||||
"""
|
|
||||||
Abstract source validator for all soundcloud sources.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
YoutubeDownloaderOptionsT = TypeVar("YoutubeDownloaderOptionsT", bound=YoutubeDownloaderOptions)
|
|
||||||
YoutubeVideoT = TypeVar("YoutubeVideoT", bound=YoutubeVideo)
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDownloader(
|
|
||||||
Downloader[YoutubeDownloaderOptionsT, YoutubeVideoT],
|
|
||||||
Generic[YoutubeDownloaderOptionsT, YoutubeVideoT],
|
|
||||||
ABC,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Class that handles downloading youtube entries via ytdl and converting them into
|
|
||||||
YoutubeVideo like objects. Reserved for any future logic that is shared amongst all YT
|
|
||||||
downloaders.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Youtube single video downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
|
|
||||||
"""
|
|
||||||
Downloads a single youtube video. This download strategy is intended for CLI usage performing
|
|
||||||
a one-time download of a video, not a subscription.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
presets:
|
|
||||||
example_preset:
|
|
||||||
youtube:
|
|
||||||
# required
|
|
||||||
download_strategy: "video"
|
|
||||||
video_url: "youtube.com/watch?v=VMAPTo7RVDo"
|
|
||||||
|
|
||||||
CLI usage:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ytdl-sub dl --preset "example_preset" --youtube.video_url "youtube.com/watch?v=VMAPTo7RVDo"
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
_required_keys = {"video_url"}
|
|
||||||
|
|
||||||
def __init__(self, name, value):
|
|
||||||
super().__init__(name, value)
|
|
||||||
self._video_url = self._validate_key("video_url", YoutubeVideoUrlValidator).video_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def video_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Required. The url of the video, i.e. ``youtube.com/watch?v=VMAPTo7RVDo``.
|
|
||||||
"""
|
|
||||||
return self._video_url
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, YoutubeVideo]):
|
|
||||||
downloader_options_type = YoutubeVideoDownloaderOptions
|
|
||||||
downloader_entry_type = YoutubeVideo
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def ytdl_option_defaults(cls) -> Dict:
|
|
||||||
"""
|
|
||||||
Default `ytdl_options`_ for ``video``
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
ytdl_options:
|
|
||||||
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
|
|
||||||
"""
|
|
||||||
return dict(
|
|
||||||
super().ytdl_option_defaults(),
|
|
||||||
**{"break_on_existing": True},
|
|
||||||
)
|
|
||||||
|
|
||||||
def download(self) -> List[YoutubeVideo]:
|
|
||||||
"""Download a single Youtube video"""
|
|
||||||
entry_dict = self.extract_info(url=self.download_options.video_url)
|
|
||||||
return [YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)]
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Youtube playlist downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
|
|
||||||
"""
|
|
||||||
Downloads all videos from a youtube playlist.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
presets:
|
|
||||||
my_example_preset:
|
|
||||||
youtube:
|
|
||||||
# required
|
|
||||||
download_strategy: "playlist"
|
|
||||||
playlist_url: "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg"
|
|
||||||
"""
|
|
||||||
|
|
||||||
_required_keys = {"playlist_url"}
|
|
||||||
|
|
||||||
def __init__(self, name, value):
|
|
||||||
super().__init__(name, value)
|
|
||||||
self._playlist_url = self._validate_key(
|
|
||||||
"playlist_url", YoutubePlaylistUrlValidator
|
|
||||||
).playlist_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def playlist_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Required. The playlist's url, i.e.
|
|
||||||
``https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg``.
|
|
||||||
"""
|
|
||||||
return self._playlist_url
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistDownloader(
|
|
||||||
YoutubeDownloader[YoutubePlaylistDownloaderOptions, YoutubePlaylistVideo]
|
|
||||||
):
|
|
||||||
downloader_options_type = YoutubePlaylistDownloaderOptions
|
|
||||||
downloader_entry_type = YoutubePlaylistVideo
|
|
||||||
|
|
||||||
# pylint: disable=line-too-long
|
|
||||||
@classmethod
|
|
||||||
def ytdl_option_defaults(cls) -> Dict:
|
|
||||||
"""
|
|
||||||
Default `ytdl_options`_ for ``playlist``
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
ytdl_options:
|
|
||||||
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
|
|
||||||
break_on_existing: True # stop downloads (newest to oldest) if a video is already downloaded
|
|
||||||
"""
|
|
||||||
return dict(
|
|
||||||
super().ytdl_option_defaults(),
|
|
||||||
**{"break_on_existing": True},
|
|
||||||
)
|
|
||||||
|
|
||||||
# pylint: enable=line-too-long
|
|
||||||
|
|
||||||
def download(self) -> List[YoutubePlaylistVideo]:
|
|
||||||
"""
|
|
||||||
Downloads all videos in a Youtube playlist
|
|
||||||
"""
|
|
||||||
playlist_videos: List[YoutubePlaylistVideo] = []
|
|
||||||
|
|
||||||
entry_dicts = self.extract_info_via_info_json(url=self.download_options.playlist_url)
|
|
||||||
for entry_dict in entry_dicts:
|
|
||||||
if entry_dict.get("extractor") == "youtube":
|
|
||||||
playlist_videos.append(
|
|
||||||
YoutubePlaylistVideo(
|
|
||||||
entry_dict=entry_dict, working_directory=self.working_directory
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return playlist_videos
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Youtube channel downloader + options
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidator):
|
class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidator):
|
||||||
"""
|
"""
|
||||||
|
|
@ -2,8 +2,8 @@ from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeDownloader
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubePlaylistDownloaderOptions
|
from ytdl_sub.downloaders.youtube.playlist import YoutubePlaylistDownloaderOptions
|
||||||
from ytdl_sub.entries.youtube import YoutubeVideo
|
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||||
from ytdl_sub.utils.chapters import Chapters
|
from ytdl_sub.utils.chapters import Chapters
|
||||||
from ytdl_sub.utils.chapters import Timestamp
|
from ytdl_sub.utils.chapters import Timestamp
|
||||||
|
|
|
||||||
83
src/ytdl_sub/downloaders/youtube/playlist.py
Normal file
83
src/ytdl_sub/downloaders/youtube/playlist.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||||
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloaderOptions
|
||||||
|
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
||||||
|
from ytdl_sub.validators.url_validator import YoutubePlaylistUrlValidator
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
|
||||||
|
"""
|
||||||
|
Downloads all videos from a youtube playlist.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
presets:
|
||||||
|
my_example_preset:
|
||||||
|
youtube:
|
||||||
|
# required
|
||||||
|
download_strategy: "playlist"
|
||||||
|
playlist_url: "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg"
|
||||||
|
"""
|
||||||
|
|
||||||
|
_required_keys = {"playlist_url"}
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self._playlist_url = self._validate_key(
|
||||||
|
"playlist_url", YoutubePlaylistUrlValidator
|
||||||
|
).playlist_url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def playlist_url(self) -> str:
|
||||||
|
"""
|
||||||
|
Required. The playlist's url, i.e.
|
||||||
|
``https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg``.
|
||||||
|
"""
|
||||||
|
return self._playlist_url
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubePlaylistDownloader(
|
||||||
|
YoutubeDownloader[YoutubePlaylistDownloaderOptions, YoutubePlaylistVideo]
|
||||||
|
):
|
||||||
|
downloader_options_type = YoutubePlaylistDownloaderOptions
|
||||||
|
downloader_entry_type = YoutubePlaylistVideo
|
||||||
|
|
||||||
|
# pylint: disable=line-too-long
|
||||||
|
@classmethod
|
||||||
|
def ytdl_option_defaults(cls) -> Dict:
|
||||||
|
"""
|
||||||
|
Default `ytdl_options`_ for ``playlist``
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
ytdl_options:
|
||||||
|
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
|
||||||
|
break_on_existing: True # stop downloads (newest to oldest) if a video is already downloaded
|
||||||
|
"""
|
||||||
|
return dict(
|
||||||
|
super().ytdl_option_defaults(),
|
||||||
|
**{"break_on_existing": True},
|
||||||
|
)
|
||||||
|
|
||||||
|
# pylint: enable=line-too-long
|
||||||
|
|
||||||
|
def download(self) -> List[YoutubePlaylistVideo]:
|
||||||
|
"""
|
||||||
|
Downloads all videos in a Youtube playlist
|
||||||
|
"""
|
||||||
|
playlist_videos: List[YoutubePlaylistVideo] = []
|
||||||
|
|
||||||
|
entry_dicts = self.extract_info_via_info_json(url=self.download_options.playlist_url)
|
||||||
|
for entry_dict in entry_dicts:
|
||||||
|
if entry_dict.get("extractor") == "youtube":
|
||||||
|
playlist_videos.append(
|
||||||
|
YoutubePlaylistVideo(
|
||||||
|
entry_dict=entry_dict, working_directory=self.working_directory
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return playlist_videos
|
||||||
|
|
@ -4,8 +4,8 @@ from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeDownloader
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeVideoDownloaderOptions
|
from ytdl_sub.downloaders.youtube.video import YoutubeVideoDownloaderOptions
|
||||||
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
||||||
from ytdl_sub.entries.youtube import YoutubeVideo
|
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||||
from ytdl_sub.utils.chapters import Chapters
|
from ytdl_sub.utils.chapters import Chapters
|
||||||
|
|
|
||||||
70
src/ytdl_sub/downloaders/youtube/video.py
Normal file
70
src/ytdl_sub/downloaders/youtube/video.py
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloader
|
||||||
|
from ytdl_sub.downloaders.youtube.abc import YoutubeDownloaderOptions
|
||||||
|
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||||
|
from ytdl_sub.validators.url_validator import YoutubeVideoUrlValidator
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
|
||||||
|
"""
|
||||||
|
Downloads a single youtube video. This download strategy is intended for CLI usage performing
|
||||||
|
a one-time download of a video, not a subscription.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
presets:
|
||||||
|
example_preset:
|
||||||
|
youtube:
|
||||||
|
# required
|
||||||
|
download_strategy: "video"
|
||||||
|
video_url: "youtube.com/watch?v=VMAPTo7RVDo"
|
||||||
|
|
||||||
|
CLI usage:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ytdl-sub dl --preset "example_preset" --youtube.video_url "youtube.com/watch?v=VMAPTo7RVDo"
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
_required_keys = {"video_url"}
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self._video_url = self._validate_key("video_url", YoutubeVideoUrlValidator).video_url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def video_url(self) -> str:
|
||||||
|
"""
|
||||||
|
Required. The url of the video, i.e. ``youtube.com/watch?v=VMAPTo7RVDo``.
|
||||||
|
"""
|
||||||
|
return self._video_url
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, YoutubeVideo]):
|
||||||
|
downloader_options_type = YoutubeVideoDownloaderOptions
|
||||||
|
downloader_entry_type = YoutubeVideo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ytdl_option_defaults(cls) -> Dict:
|
||||||
|
"""
|
||||||
|
Default `ytdl_options`_ for ``video``
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
ytdl_options:
|
||||||
|
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
|
||||||
|
"""
|
||||||
|
return dict(
|
||||||
|
super().ytdl_option_defaults(),
|
||||||
|
**{"break_on_existing": True},
|
||||||
|
)
|
||||||
|
|
||||||
|
def download(self) -> List[YoutubeVideo]:
|
||||||
|
"""Download a single Youtube video"""
|
||||||
|
entry_dict = self.extract_info(url=self.download_options.video_url)
|
||||||
|
return [YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)]
|
||||||
Loading…
Reference in a new issue