[FEATURE] file_convert plugin (#267)
This commit is contained in:
parent
713f55bb3c
commit
9f6b3d4647
5 changed files with 153 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ from ytdl_sub.downloaders.youtube.video import YoutubeVideoDownloader
|
||||||
from ytdl_sub.plugins.audio_extract import AudioExtractPlugin
|
from ytdl_sub.plugins.audio_extract import AudioExtractPlugin
|
||||||
from ytdl_sub.plugins.chapters import ChaptersPlugin
|
from ytdl_sub.plugins.chapters import ChaptersPlugin
|
||||||
from ytdl_sub.plugins.date_range import DateRangePlugin
|
from ytdl_sub.plugins.date_range import DateRangePlugin
|
||||||
|
from ytdl_sub.plugins.file_convert import FileConvertPlugin
|
||||||
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
|
||||||
|
|
@ -117,6 +118,7 @@ class PluginMapping:
|
||||||
_MAPPING: Dict[str, Type[Plugin]] = {
|
_MAPPING: Dict[str, Type[Plugin]] = {
|
||||||
"audio_extract": AudioExtractPlugin,
|
"audio_extract": AudioExtractPlugin,
|
||||||
"date_range": DateRangePlugin,
|
"date_range": DateRangePlugin,
|
||||||
|
"file_convert": FileConvertPlugin,
|
||||||
"music_tags": MusicTagsPlugin,
|
"music_tags": MusicTagsPlugin,
|
||||||
"video_tags": VideoTagsPlugin,
|
"video_tags": VideoTagsPlugin,
|
||||||
"nfo_tags": NfoTagsPlugin,
|
"nfo_tags": NfoTagsPlugin,
|
||||||
|
|
|
||||||
84
src/ytdl_sub/plugins/file_convert.py
Normal file
84
src/ytdl_sub/plugins/file_convert.py
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
from typing import Dict
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from ytdl_sub.entries.entry import Entry
|
||||||
|
from ytdl_sub.plugins.plugin import Plugin
|
||||||
|
from ytdl_sub.plugins.plugin import PluginOptions
|
||||||
|
from ytdl_sub.plugins.plugin import PluginPriority
|
||||||
|
from ytdl_sub.utils.ffmpeg import FFMPEG
|
||||||
|
from ytdl_sub.utils.file_handler import FileHandler
|
||||||
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
|
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||||
|
|
||||||
|
|
||||||
|
class FileConvertOptions(PluginOptions):
|
||||||
|
"""
|
||||||
|
Converts media files from one extension to another.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
presets:
|
||||||
|
my_example_preset:
|
||||||
|
file_converter:
|
||||||
|
convert:
|
||||||
|
webm: "mp4"
|
||||||
|
"""
|
||||||
|
|
||||||
|
_required_keys = {"convert"}
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self._convert = self._validate_key(key="convert", validator=LiteralDictValidator).dict
|
||||||
|
|
||||||
|
@property
|
||||||
|
def convert(self) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
Convert from one extension to another
|
||||||
|
"""
|
||||||
|
return self._convert
|
||||||
|
|
||||||
|
|
||||||
|
class FileConvertPlugin(Plugin[FileConvertOptions]):
|
||||||
|
plugin_options_type = FileConvertOptions
|
||||||
|
priority = PluginPriority(modify_entry=0)
|
||||||
|
|
||||||
|
def modify_entry(self, entry: Entry) -> Entry:
|
||||||
|
"""
|
||||||
|
If the entry is of the specified file type, convert it
|
||||||
|
"""
|
||||||
|
for convert_from, convert_to in self.plugin_options.convert.items():
|
||||||
|
# Entry ext does not need conversion
|
||||||
|
if entry.ext != convert_from:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Entry ext needs conversion
|
||||||
|
input_file_path = entry.get_download_file_path()
|
||||||
|
output_file_path = input_file_path.removesuffix(entry.ext) + convert_to
|
||||||
|
|
||||||
|
ffmpeg_args = ["-bitexact", "-i", input_file_path, output_file_path]
|
||||||
|
|
||||||
|
if not self.is_dry_run:
|
||||||
|
FFMPEG.run(ffmpeg_args)
|
||||||
|
FileHandler.delete(input_file_path)
|
||||||
|
|
||||||
|
entry.add_kwargs(
|
||||||
|
{
|
||||||
|
"ext": convert_to,
|
||||||
|
"__converted_from": convert_from,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def post_process_entry(self, entry: Entry) -> Optional[FileMetadata]:
|
||||||
|
"""
|
||||||
|
Add metadata about conversion if it happened
|
||||||
|
"""
|
||||||
|
if converted_from := entry.kwargs_get("__converted_from"):
|
||||||
|
return FileMetadata(f"Converted from {converted_from}")
|
||||||
|
|
||||||
|
return None
|
||||||
47
tests/e2e/plugins/test_file_convert.py
Normal file
47
tests/e2e/plugins/test_file_convert.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import pytest
|
||||||
|
from expected_download import assert_expected_downloads
|
||||||
|
from expected_transaction_log import assert_transaction_log_matches
|
||||||
|
|
||||||
|
from ytdl_sub.subscriptions.subscription import Subscription
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def preset_dict(output_directory):
|
||||||
|
return {
|
||||||
|
"preset": "yt_music_video",
|
||||||
|
"youtube": {"video_url": "https://www.youtube.com/watch?v=2zYF9JLHDmA"},
|
||||||
|
"output_options": {"output_directory": output_directory},
|
||||||
|
# download the worst format so it is fast
|
||||||
|
"ytdl_options": {
|
||||||
|
"postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility
|
||||||
|
},
|
||||||
|
"file_convert": {"convert": {"webm": "mp4"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileConvert:
|
||||||
|
@pytest.mark.parametrize("dry_run", [True, False])
|
||||||
|
def test_file_convert(
|
||||||
|
self,
|
||||||
|
music_video_config,
|
||||||
|
preset_dict,
|
||||||
|
output_directory,
|
||||||
|
dry_run,
|
||||||
|
):
|
||||||
|
subscription = Subscription.from_dict(
|
||||||
|
config=music_video_config,
|
||||||
|
preset_name="file_convert_test",
|
||||||
|
preset_dict=preset_dict,
|
||||||
|
)
|
||||||
|
|
||||||
|
transaction_log = subscription.download(dry_run=dry_run)
|
||||||
|
assert_transaction_log_matches(
|
||||||
|
output_directory=output_directory,
|
||||||
|
transaction_log=transaction_log,
|
||||||
|
transaction_log_summary_file_name="plugins/file_convert/output.txt",
|
||||||
|
)
|
||||||
|
assert_expected_downloads(
|
||||||
|
output_directory=output_directory,
|
||||||
|
dry_run=dry_run,
|
||||||
|
expected_download_summary_file_name="plugins/file_convert/output.json",
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3-thumb.jpg": "662fcaadf6e80d63591bac19a5fdffb0",
|
||||||
|
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.info.json": "c0cdbc58669859a3f85e1bd4f0d1c424",
|
||||||
|
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.mp4": "14d83cb2ee79d372b3b862d95c2f7a43",
|
||||||
|
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.nfo": "cacf09ab38f9b3085da9c5af516cf22a"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
Files created:
|
||||||
|
----------------------------------------
|
||||||
|
{output_directory}
|
||||||
|
Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3-thumb.jpg
|
||||||
|
Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.info.json
|
||||||
|
Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.mp4
|
||||||
|
Converted from webm
|
||||||
|
Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.nfo
|
||||||
|
NFO tags:
|
||||||
|
musicvideo:
|
||||||
|
album: Music Videos
|
||||||
|
artist: Beyond The Guitar
|
||||||
|
title: When you hear Hugh Jackman is returning as Wolverine in Deadpool 3
|
||||||
|
year: 2022
|
||||||
Loading…
Reference in a new issue