diff --git a/rwx/sw/youtube/__init__.py b/rwx/sw/youtube/__init__.py index 06f5bdc..2db1edb 100644 --- a/rwx/sw/youtube/__init__.py +++ b/rwx/sw/youtube/__init__.py @@ -1,4 +1,4 @@ -import yt_dlp +"""YouTube DownLoad.""" # playlists # … @@ -14,7 +14,6 @@ import yt_dlp # playlist / entries # … - # videos # id # channel @@ -36,8 +35,80 @@ import yt_dlp # thumbnails # view_count +from abc import ABC, abstractmethod +from enum import Enum +from rwx import Object +from rwx.log import stream as log + +import yt_dlp + + +class Action(Enum): + DOWNLOAD_VIDEO = 0 + EXTRACT_PLAYLIST = 1 + EXTRACT_PLAYLISTS = 2 + EXTRACT_VIDEO = 3 + EXTRACT_VIDEOS = 4 + + +class Tab(Object, ABC): + """YouTube Tab.""" + + URL_ROOT = "https://youtube.com" + + def __init__(self, object_id: str) -> None: + """Set object id. + + :param object_id: object identifier + :type object_id: str + """ + self.object_id = object_id + log.info(self.object_id) + self.url = self.get_url() + log.info(self.url) + + def get_options(self) -> dict: + """Return options for the action. + + :rtype: dict + """ + return { + "extract_flat": True, + "skip_download": True, + } + + @abstractmethod + def get_url(self) -> str: + """Return URL to access for object. + + :rtype: str + """ + + @staticmethod + def dl(opt: dict) -> yt_dlp.YoutubeDL: + options = {**opt, "ignoreerrors": False, "quiet": False} + log.info(options) + return yt_dlp.YoutubeDL(options) + + +class Playlist(Tab): + + def __init__(self, playlist_id: str) -> None: + self.playlist_id = playlist_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/playlist?list={self.playlist_id}" + + +class Playlists(Tab): + + def __init__(self, channel_id: str) -> None: + self.channel_id = channel_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/channel/{self.channel_id}/playlists" + -# video # id # title # formats @@ -50,93 +121,57 @@ import yt_dlp # categories # tags # fulltitle +class Video(Tab): + + def __init__(self, video_id: str) -> None: + self.video_id = video_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/watch?v={self.video_id}" -def extract_playlist(playlist_id): - options = { - "quiet": False, - "extract_flat": True, - "force_generic_extractor": False, - "ignoreerrors": False, - } - url = f"https://youtube.com/playlist?list={playlist_id}" - print(f"""\ -{options} +class Videos(Tab): -{url} + def __init__(self, channel_id: str) -> None: + self.channel_id = channel_id -""") - with yt_dlp.YoutubeDL(options) as y: - return y.extract_info(url, download=False) + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/channel/{self.channel_id}/videos" -def extract_playlists(channel_id): - options = { - "quiet": False, - "extract_flat": True, - "force_generic_extractor": False, - "ignoreerrors": False, - "skip_download": True, - } - url = f"https://youtube.com/channel/{channel_id}/playlists" - print(f"""\ -{options} - -{url} - -""") - with yt_dlp.YoutubeDL(options) as y: - return y.extract_info(url, download=False) +def command(action: Action): + match action: + case Action.DOWNLOAD_VIDEO: + options = { + "format": "bestvideo[ext=mp4]+bestaudio[ext=mp4]", + "outtmpl": "%(id)s.%(ext)s", + "writesubtitles": True, + "writethumbnail": True, + } + log.info(options) + with dl(options) as youtube: + match action: + case Action.DOWNLOAD_VIDEO: + youtube.download([url]) + case _: + youtube.extract_info(url, download=False) -def download_video(video_id): - options = { - "format": "bestvideo[ext=mp4]+bestaudio[ext=mp4]", - "outtmpl": "%(id)s.%(ext)s", - "writesubtitles": True, - "writethumbnail": True, - "ignoreerrors": False, - } - url = f"https://youtu.be/{video_id}" - print(f"""\ -{options} - -{url} - -""") - with yt_dlp.YoutubeDL(options) as y: - return y.download([url]) +def download_video(video_id: str): + return command(Action.DOWNLOAD_VIDEO, video_id) -def extract_video(video_id): - options = { - "quiet": False, - "ignoreerrors": False, - } - url = f"https://youtu.be/{video_id}" - print(f"""\ -{options} - -{url} - -""") - with yt_dlp.YoutubeDL(options) as y: - return y.extract_info(url, download=False) +def extract_playlist(playlist_id: str): + return command(Action.EXTRACT_PLAYLIST, playlist_id) -def extract_videos(channel_id): - options = { - "quiet": False, - "extract_flat": True, - "force_generic_extractor": False, - "ignoreerrors": False, - } - url = f"https://youtube.com/channel/{channel_id}/videos" - print(f"""\ -{options} +def extract_playlists(channel_id: str): + return command(Action.EXTRACT_PLAYLISTS, channel_id) -{url} -""") - with yt_dlp.YoutubeDL(options) as y: - return y.extract_info(url, download=False) +def extract_video(video_id: str): + return command(Action.EXTRACT_VIDEO, video_id) + + +def extract_videos(channel_id: str): + return command(Action.EXTRACT_VIDEOS, channel_id) diff --git a/rwx/sw/yt_dlp/__init__.py b/rwx/sw/yt_dlp/__init__.py new file mode 100644 index 0000000..df744eb --- /dev/null +++ b/rwx/sw/yt_dlp/__init__.py @@ -0,0 +1,177 @@ +"""YouTube.""" + +# playlists +# … +# entries + +# playlists / entries +# title +# id + +# playlist +# entries + +# playlist / entries +# … + +# videos +# id +# channel +# channel_id +# title +# channel_follower_count +# description +# tags +# thumbnails +# uploader_id +# uploader +# entries + +# videos / entries +# id +# title +# description truncated +# duration +# thumbnails +# view_count + +from abc import ABC, abstractmethod +from enum import Enum +from rwx import Object +from rwx.log import stream as log + +import yt_dlp + + +class Action(Enum): + DOWNLOAD_VIDEO = 0 + EXTRACT_PLAYLIST = 1 + EXTRACT_PLAYLISTS = 2 + EXTRACT_VIDEO = 3 + EXTRACT_VIDEOS = 4 + + +class Tab(Object, ABC): + """YouTube Tab.""" + + URL_ROOT = "https://youtube.com" + + def __init__(self, object_id: str) -> None: + """Set object id. + + :param object_id: object identifier + :type object_id: str + """ + self.object_id = object_id + log.info(self.object_id) + self.url = self.get_url() + log.info(self.url) + + def get_options(self) -> dict: + """Return options for the action. + + :rtype: dict + """ + return { + "extract_flat": True, + "skip_download": True, + } + + @abstractmethod + def get_url(self) -> str: + """Return URL to access for object. + + :rtype: str + """ + + @staticmethod + def dl(opt: dict) -> yt_dlp.YoutubeDL: + options = {**opt, "ignoreerrors": False, "quiet": False} + log.info(options) + return yt_dlp.YoutubeDL(options) + + +class Playlist(Tab): + + def __init__(self, playlist_id: str) -> None: + self.playlist_id = playlist_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/playlist?list={self.playlist_id}" + + +class Playlists(Tab): + + def __init__(self, channel_id: str) -> None: + self.channel_id = channel_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/channel/{self.channel_id}/playlists" + + +# id +# title +# formats +# thumbnails +# thumbnail +# description +# +# duration +# view_count +# categories +# tags +# fulltitle +class Video(Tab): + + def __init__(self, video_id: str) -> None: + self.video_id = video_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/watch?v={self.video_id}" + + +class Videos(Tab): + + def __init__(self, channel_id: str) -> None: + self.channel_id = channel_id + + def get_url(self) -> str: + return f"{Tab.URL_ROOT}/channel/{self.channel_id}/videos" + + +def command(action: Action): + match action: + case Action.DOWNLOAD_VIDEO: + options = { + "format": "bestvideo[ext=mp4]+bestaudio[ext=mp4]", + "outtmpl": "%(id)s.%(ext)s", + "writesubtitles": True, + "writethumbnail": True, + } + log.info(options) + with dl(options) as youtube: + match action: + case Action.DOWNLOAD_VIDEO: + youtube.download([url]) + case _: + youtube.extract_info(url, download=False) + + +def download_video(video_id: str): + return command(Action.DOWNLOAD_VIDEO, video_id) + + +def extract_playlist(playlist_id: str): + return command(Action.EXTRACT_PLAYLIST, playlist_id) + + +def extract_playlists(channel_id: str): + return command(Action.EXTRACT_PLAYLISTS, channel_id) + + +def extract_video(video_id: str): + return command(Action.EXTRACT_VIDEO, video_id) + + +def extract_videos(channel_id: str): + return command(Action.EXTRACT_VIDEOS, channel_id) diff --git a/rwx/sw/yt_dlp/video.py b/rwx/sw/yt_dlp/video.py new file mode 100644 index 0000000..e69de29