begin
This commit is contained in:
parent
1620c3a42d
commit
fc7a234f68
3 changed files with 211 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ from typing import Type
|
|||
|
||||
from ytdl_sub.downloaders.downloader import Downloader
|
||||
from ytdl_sub.downloaders.soundcloud_downloader import SoundcloudAlbumsAndSinglesDownloader
|
||||
from ytdl_sub.downloaders.youtube.merge_playlist import YoutubeMergePlaylistDownloader
|
||||
from ytdl_sub.downloaders.youtube.split_video import YoutubeSplitVideoDownloader
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeChannelDownloader
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubePlaylistDownloader
|
||||
|
|
@ -25,6 +26,7 @@ class DownloadStrategyMapping:
|
|||
"playlist": YoutubePlaylistDownloader,
|
||||
"channel": YoutubeChannelDownloader,
|
||||
"split_video": YoutubeSplitVideoDownloader,
|
||||
"merge_playlist": YoutubeMergePlaylistDownloader,
|
||||
},
|
||||
"soundcloud": {
|
||||
"albums_and_singles": SoundcloudAlbumsAndSinglesDownloader,
|
||||
|
|
|
|||
106
src/ytdl_sub/downloaders/youtube/merge_playlist.py
Normal file
106
src/ytdl_sub/downloaders/youtube/merge_playlist.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import copy
|
||||
import os.path
|
||||
import re
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeDownloader, \
|
||||
YoutubePlaylistDownloaderOptions
|
||||
from ytdl_sub.downloaders.youtube_downloader import YoutubeVideoDownloaderOptions
|
||||
from ytdl_sub.entries.youtube import YoutubePlaylistVideo
|
||||
from ytdl_sub.entries.youtube import YoutubeVideo
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.ffmpeg import FFMPEG
|
||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||
from ytdl_sub.validators.validators import StringValidator
|
||||
|
||||
###############################################################################
|
||||
# Youtube split video downloader + options
|
||||
|
||||
|
||||
class YoutubeMergePlaylistDownloaderOptions(YoutubePlaylistDownloaderOptions):
|
||||
"""
|
||||
Downloads all videos in a playlist and merges them into a single video.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
presets:
|
||||
example_preset:
|
||||
youtube:
|
||||
# required
|
||||
download_strategy: "merge_playlist"
|
||||
playlist_url: "TODO"
|
||||
chapter_name: "{title}"
|
||||
|
||||
CLI usage:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
_required_keys = {"playlist_url"}
|
||||
|
||||
# def __init__(self, name, value):
|
||||
# super().__init__(name, value)
|
||||
|
||||
|
||||
class YoutubeMergePlaylistDownloader(
|
||||
YoutubeDownloader[YoutubeMergePlaylistDownloaderOptions, YoutubeVideo]
|
||||
):
|
||||
downloader_options_type = YoutubeMergePlaylistDownloaderOptions
|
||||
downloader_entry_type = YoutubeVideo
|
||||
|
||||
@classmethod
|
||||
def ytdl_option_defaults(cls) -> Dict:
|
||||
"""
|
||||
Default `ytdl_options`_ for ``split_video``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ytdl_options:
|
||||
ignoreerrors: True # ignore errors like hidden videos, age restriction, etc
|
||||
"""
|
||||
return dict(
|
||||
super().ytdl_option_defaults(),
|
||||
**{"postprocessors": [
|
||||
{
|
||||
"key": "FFmpegVideoRemuxer",
|
||||
"when": "post_process",
|
||||
"preferedformat": "mkv",
|
||||
},
|
||||
{
|
||||
"key": "FFmpegConcat",
|
||||
"when": "playlist",
|
||||
}
|
||||
]},
|
||||
)
|
||||
|
||||
def download(self) -> List[YoutubeVideo]:
|
||||
"""Download a single Youtube video, then split it into multiple videos"""
|
||||
split_videos: List[YoutubePlaylistVideo] = []
|
||||
entry_dict = self.extract_info(url=self.download_options.playlist_url)
|
||||
|
||||
return []
|
||||
103
tests/e2e/youtube/test_merge_playlist.py
Normal file
103
tests/e2e/youtube/test_merge_playlist.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
from pathlib import Path
|
||||
|
||||
import mergedeep
|
||||
import pytest
|
||||
from conftest import assert_debug_log
|
||||
from e2e.expected_download import ExpectedDownload
|
||||
|
||||
import ytdl_sub.downloaders.downloader
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
from ytdl_sub.config.preset import Preset
|
||||
from ytdl_sub.subscriptions.subscription import Subscription
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_path():
|
||||
return "examples/kodi_music_videos_config.yaml"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def subscription_name():
|
||||
return "jmc"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config(config_path):
|
||||
return ConfigFile.from_file_path(config_path=config_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def subscription_dict(output_directory, subscription_name):
|
||||
return {
|
||||
"preset": "yt_music_video_playlist",
|
||||
"youtube": {
|
||||
"download_strategy": "merge_playlist",
|
||||
"playlist_url": "https://youtube.com/playlist?list=PL5BC0FC26BECA5A35"
|
||||
},
|
||||
# override the output directory with our fixture-generated dir
|
||||
"output_options": {"output_directory": output_directory},
|
||||
# download the worst format so it is fast
|
||||
"ytdl_options": {
|
||||
"format": "worst",
|
||||
},
|
||||
"overrides": {"artist": "JMC"},
|
||||
}
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# PLAYLIST FIXTURES
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def playlist_subscription(config, subscription_name, subscription_dict):
|
||||
playlist_preset = Preset.from_dict(
|
||||
config=config,
|
||||
preset_name=subscription_name,
|
||||
preset_dict=subscription_dict,
|
||||
)
|
||||
|
||||
return Subscription.from_preset(
|
||||
preset=playlist_preset,
|
||||
config=config,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def expected_playlist_download():
|
||||
# turn off black formatter here for readability
|
||||
# fmt: off
|
||||
return ExpectedDownload(
|
||||
expected_md5_file_hashes={
|
||||
# Download mapping
|
||||
Path(".ytdl-sub-jmc-download-archive.json"): "7541aa75606b86bff5ff276895520cf0",
|
||||
|
||||
# Entry files
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].jpg"): "048a19cf0f674437351872c3f312ebf1",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4"): "e66287b9832277b6a4d1554e29d9fdcc",
|
||||
Path("JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo"): "3d272fe58487b6011ad049b6000b046f",
|
||||
|
||||
Path("JMC - Given to Fly.jpg"): "2e58e4d5f06ce5d1c3336fa493470135",
|
||||
Path("JMC - Given to Fly.mp4"): "04ab5cb3cc12325d0c96a7cd04a8b91d",
|
||||
Path("JMC - Given to Fly.nfo"): "0dc578bf5f1ceb6e069a57d329894f35",
|
||||
|
||||
Path("JMC - Indifference (Remastered).jpg"): "9baaddc6b62f5b9ae3781eb4eef0e3b3",
|
||||
Path("JMC - Indifference (Remastered).mp4"): "025de6099a5c98e6397153c7a62d517d",
|
||||
Path("JMC - Indifference (Remastered).nfo"): "061b86d9dc8fb39d39feab3292dafeb0",
|
||||
}
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
||||
|
||||
class TestYoutubeMergePlaylist:
|
||||
"""
|
||||
Downloads my old minecraft youtube channel, pretends they are music videos. Ensure the above
|
||||
files exist and have the expected md5 file hashes.
|
||||
"""
|
||||
|
||||
def test_playlist_download(
|
||||
self, playlist_subscription, expected_playlist_download, output_directory
|
||||
):
|
||||
playlist_subscription.download()
|
||||
expected_playlist_download.assert_files_exist(relative_directory=output_directory)
|
||||
|
||||
Loading…
Reference in a new issue