diff --git a/src/ytdl_sub/downloaders/youtube/merge_playlist.py b/src/ytdl_sub/downloaders/youtube/merge_playlist.py index 7f3f879a..ba1f6157 100644 --- a/src/ytdl_sub/downloaders/youtube/merge_playlist.py +++ b/src/ytdl_sub/downloaders/youtube/merge_playlist.py @@ -1,5 +1,6 @@ from typing import Dict from typing import List +from typing import Optional from ytdl_sub.downloaders.youtube_downloader import YoutubeDownloader from ytdl_sub.downloaders.youtube_downloader import YoutubePlaylistDownloaderOptions @@ -26,7 +27,7 @@ class YoutubeMergePlaylistDownloaderOptions(YoutubePlaylistDownloaderOptions): youtube: # required download_strategy: "merge_playlist" - playlist_url: "TODO" + playlist_url: "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg" # optional add_chapters: False @@ -36,22 +37,8 @@ class YoutubeMergePlaylistDownloaderOptions(YoutubePlaylistDownloaderOptions): ytdl-sub dl \ --preset "example_preset" \ - --youtube.video_url "youtube.com/watch?v=VMAPTo7RVDo" \ - --youtube.split_timestamps "path/to/timestamps.txt" - - ``split_timestamps`` file format: - - .. code-block:: markdown - - 0:00 Intro - 0:24 Blackwater Park - 10:23 Bleak - 16:39 Jokes - 1:02:23 Ending - - The above will create 5 videos in total. The first timestamp must start with ``0:00`` - and the last timestamp, in this example, would create a video starting at ``1:02:23`` and - end at Youtube video's ending. + --youtube.playlist_url "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg" \ + --youtube.add_chapters True """ _required_keys = {"playlist_url"} @@ -64,7 +51,7 @@ class YoutubeMergePlaylistDownloaderOptions(YoutubePlaylistDownloaderOptions): ).value @property - def add_chapters(self) -> bool: + def add_chapters(self) -> Optional[bool]: """ Whether to add chapters using each video's title in the merged playlist. Defaults to false. """ @@ -87,6 +74,15 @@ class YoutubeMergePlaylistDownloader( ytdl_options: ignoreerrors: True # ignore errors like hidden videos, age restriction, etc + playlistreverse: True # Sort the playlist so it begins with the first entry + postprocessors: + # Convert the videos to mkv format + - key: "FFmpegVideoConvertor" + when: "post_process" + preferedformat: "mkv" + # Concatenate all the playlist videos into a single file + - key: "FFmpegConcat" + when: "playlist" """ return dict( super().ytdl_option_defaults(), @@ -106,7 +102,8 @@ class YoutubeMergePlaylistDownloader( }, ) - def _add_chapters(self, merged_video: YoutubeVideo) -> None: + @classmethod + def _add_chapters(cls, merged_video: YoutubeVideo) -> None: titles: List[str] = [] timestamps: List[Timestamp] = [] diff --git a/src/ytdl_sub/utils/chapters.py b/src/ytdl_sub/utils/chapters.py index 2dff1c9a..ccb5164f 100644 --- a/src/ytdl_sub/utils/chapters.py +++ b/src/ytdl_sub/utils/chapters.py @@ -40,10 +40,20 @@ class Timestamp: @property def timestamp_sec(self) -> int: + """ + Returns + ------- + Timestamp in seconds + """ return self._timestamp_sec @property def timestamp_str(self) -> str: + """ + Returns + ------- + The timestamp in 'HH:MM:SS' format + """ seconds = self.timestamp_sec hours = int(seconds / 3600) @@ -71,6 +81,11 @@ class Timestamp: ---------- timestamp_str Timestamp in the form of "HH:MM:SS" + + Raises + ------ + ValueError + Invalid timestamp string format """ hour_minute_second = cls._normalize_timestamp_str(timestamp_str).split(":") if len(hour_minute_second) != 3: @@ -103,10 +118,26 @@ class Chapters: raise ValueError("Timestamps must be in ascending order") def contains_zero_timestamp(self) -> bool: + """ + Returns + ------- + True if the first timestamp starts at 0. False otherwise. + """ return self.timestamps[0].timestamp_sec == 0 @classmethod def from_file(cls, chapters_file_path: str) -> "Chapters": + """ + Parameters + ---------- + chapters_file_path + Path to file containing chapters + + Raises + ------ + ValidationException + File path does not exist or contains invalid formatting + """ if not os.path.isfile(chapters_file_path): raise ValidationException( f"chapter/timestamp file path '{chapters_file_path}' does not exist." @@ -126,9 +157,9 @@ class Chapters: break if len(line_split) != 2: - raise ValueError( - f"Chapter/Timestamp file line could not be parsed: '{line}'. " - f"Must be in the format of 'HH:MM:SS title" + raise ValidationException( + f"Chapter/Timestamp file '{chapters_file_path}' could not parse '{line}': " + f"must be in the format of 'HH:MM:SS title" ) timestamp_str, title = tuple(x for x in line_split)