[BUGFIX] Use yt-dlp remuxer instead of our own (#275)

* [BUGFIX] Use yt-dlps remuxer instead of our own

* docstring
This commit is contained in:
Jesse Bannon 2022-10-07 17:16:54 -07:00 committed by GitHub
parent a1149bd655
commit 9883907bf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 45 deletions

View file

@ -8,6 +8,7 @@ from typing import final
from ytdl_sub.entries.base_entry import BaseEntry
from ytdl_sub.entries.variables.entry_variables import EntryVariables
from ytdl_sub.validators.audo_codec_validator import AUDIO_CODEC_EXTS
from ytdl_sub.validators.audo_codec_validator import VIDEO_CODEC_EXTS
class Entry(EntryVariables, BaseEntry):
@ -89,11 +90,11 @@ class Entry(EntryVariables, BaseEntry):
"""
file_exists = os.path.isfile(self.get_download_file_path())
# HACK: yt-dlp does not record extracted audio extensions anywhere. If the file is not
# found, try it using audio extensions
# HACK: yt-dlp does not record extracted/converted extensions anywhere. If the file is not
# found, try it using all possible extensions
if not file_exists:
for audio_ext in AUDIO_CODEC_EXTS:
if os.path.isfile(self.get_download_file_path().removesuffix(self.ext) + audio_ext):
for ext in AUDIO_CODEC_EXTS.union(VIDEO_CODEC_EXTS):
if os.path.isfile(self.get_download_file_path().removesuffix(self.ext) + ext):
file_exists = True
break

View file

@ -100,9 +100,6 @@ class AudioExtractPlugin(Plugin[AudioExtractOptions]):
if not os.path.isfile(extracted_audio_file):
raise FileNotDownloadedException("Failed to find the extracted audio file")
# TODO: create entry function to update kwargs
# pylint: disable=protected-access
entry._kwargs["ext"] = new_ext
# pylint: enable=protected-access
entry.add_kwargs({"ext": new_ext})
return entry

View file

@ -1,14 +1,14 @@
import os
from typing import Dict
from typing import Optional
from ytdl_sub.entries.entry import Entry
from ytdl_sub.entries.variables.kwargs import EXT
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.exceptions import FileNotDownloadedException
from ytdl_sub.utils.file_handler import FileMetadata
from ytdl_sub.validators.validators import LiteralDictValidator
from ytdl_sub.validators.audo_codec_validator import VideoCodecTypeValidator
class FileConvertOptions(PluginOptions):
@ -22,55 +22,74 @@ class FileConvertOptions(PluginOptions):
presets:
my_example_preset:
file_converter:
convert:
webm: "mp4"
convert_to: "mp4"
"""
_required_keys = {"convert"}
_required_keys = {"convert_to"}
def __init__(self, name, value):
super().__init__(name, value)
self._convert = self._validate_key(key="convert", validator=LiteralDictValidator).dict
self._convert_to = self._validate_key(
key="convert_to", validator=VideoCodecTypeValidator
).value
@property
def convert(self) -> Dict[str, str]:
def convert_to(self) -> str:
"""
Convert from one extension to another
Convert to a desired extension
"""
return self._convert
return self._convert_to
class FileConvertPlugin(Plugin[FileConvertOptions]):
plugin_options_type = FileConvertOptions
priority = PluginPriority(modify_entry=0)
def modify_entry(self, entry: Entry) -> Entry:
def ytdl_options(self) -> Optional[Dict]:
"""
If the entry is of the specified file type, convert it
Returns
-------
ffmpeg video remuxing post processing dict
"""
for convert_from, convert_to in self.plugin_options.convert.items():
# Entry ext does not need conversion
if entry.ext != convert_from:
continue
return {
"postprocessors": [
{
"key": "FFmpegVideoRemuxer",
"when": "post_process",
"preferedformat": self.plugin_options.convert_to,
}
]
}
# Entry ext needs conversion
input_file_path = entry.get_download_file_path()
output_file_path = input_file_path.removesuffix(entry.ext) + convert_to
def modify_entry(self, entry: Entry) -> Optional[Entry]:
"""
Parameters
----------
entry
Entry with extracted audio
ffmpeg_args = ["-bitexact", "-i", input_file_path, "-c", "copy", output_file_path]
Returns
-------
Entry with updated 'ext' source variable
if not self.is_dry_run:
FFMPEG.run(ffmpeg_args)
FileHandler.delete(input_file_path)
Raises
------
FileNotDownloadedException
If the audio file is not found
"""
new_ext = self.plugin_options.convert_to
converted_video_file = entry.get_download_file_path().removesuffix(entry.ext) + new_ext
if not self.is_dry_run:
if not os.path.isfile(converted_video_file):
raise FileNotDownloadedException("Failed to find the converted video file")
if entry.ext != new_ext:
entry.add_kwargs(
{
"ext": convert_to,
"__converted_from": convert_from,
"__converted_from": entry.ext,
}
)
return entry
entry.add_kwargs({EXT: new_ext})
return entry

View file

@ -5,8 +5,3 @@ presets:
kodi_safe: True
output_directory_nfo_tags:
kodi_safe: True
_plex_base:
file_convert:
convert:
webm: "mp4"

View file

@ -2,12 +2,13 @@ presets:
_plex_tv_show:
preset:
- "_plex_base"
- "_episode_video_tags"
overrides:
tv_show_poster_file_name: "poster.jpg"
tv_show_fanart_file_name: "fanart.jpg"
season_poster_file_name: "Season{season_number_padded}.jpg"
file_convert: # Plex currently does not support webm
convert_to: "mp4"
_jellyfin_tv_show:
preset:

View file

@ -11,6 +11,7 @@ from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
from ytdl_sub.plugins.audio_extract import AudioExtractPlugin
from ytdl_sub.plugins.chapters import ChaptersPlugin
from ytdl_sub.plugins.date_range import DateRangePlugin
from ytdl_sub.plugins.file_convert import FileConvertPlugin
from ytdl_sub.plugins.plugin import Plugin
from ytdl_sub.plugins.subtitles import SubtitlesPlugin
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
@ -119,6 +120,13 @@ class SubscriptionYTDLOptions:
return date_range_plugin.ytdl_options()
@property
def _file_convert_options(self) -> Dict:
if not (file_convert_plugin := self._get_plugin(FileConvertPlugin)):
return {}
return file_convert_plugin.ytdl_options()
@property
def _user_ytdl_options(self) -> Dict:
return self._preset.ytdl_options.dict
@ -134,6 +142,7 @@ class SubscriptionYTDLOptions:
if self._dry_run:
ytdl_options_builder.add(
self._date_range_options,
self._file_convert_options,
self._subtitle_options,
self._chapter_options,
self._user_ytdl_options, # user ytdl options...
@ -143,6 +152,7 @@ class SubscriptionYTDLOptions:
ytdl_options_builder.add(
self._output_options,
self._date_range_options,
self._file_convert_options,
self._subtitle_options,
self._chapter_options,
self._audio_extract_options,

View file

@ -16,7 +16,30 @@ AUDIO_CODEC_TYPES_EXTENSION_MAPPING: Dict[str, str] = {
AUDIO_CODEC_TYPES: Set[str] = set(AUDIO_CODEC_TYPES_EXTENSION_MAPPING.keys())
AUDIO_CODEC_EXTS: Set[str] = set(AUDIO_CODEC_TYPES_EXTENSION_MAPPING.values())
VIDEO_CODEC_EXTS: Set[str] = {
"avi",
"flv",
"mkv",
"mov",
"mp4",
"webm",
"3g2",
"3gp",
"f4v",
"mk3d",
"divx",
"mpg",
"ogv",
"m4v",
"wmv",
}
class CodecTypeValidator(StringSelectValidator):
_expected_value_type_name = "codec"
_select_values = AUDIO_CODEC_TYPES
class VideoCodecTypeValidator(StringSelectValidator):
_expected_value_type_name = "codec"
_select_values = VIDEO_CODEC_EXTS

View file

@ -15,7 +15,7 @@ def preset_dict(output_directory):
"ytdl_options": {
"postprocessor_args": {"ffmpeg": ["-bitexact"]}, # Must add this for reproducibility
},
"file_convert": {"convert": {"webm": "mp4"}},
"file_convert": {"convert_to": "mp4"},
}

View file

@ -1,6 +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": "c6f7178b1efc57282dabbca958785d69",
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.mp4": "3c4e0cf1cefe6bf55adbbb1d809bbd04",
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.info.json": "4d51260489eca1f969107cfb4957b368",
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.mp4": "390177bcc076e309d1534b8bb5afdcc7",
"Beyond The Guitar - When you hear Hugh Jackman is returning as Wolverine in Deadpool 3.nfo": "cacf09ab38f9b3085da9c5af516cf22a"
}