From 165efb823b3a8a6a6788cfe23e6b93dfbe150568 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Sat, 4 Sep 2021 01:37:41 +0530 Subject: [PATCH] [ModifyChapters] fixes (See desc) * [docs] Fix typo * Do not enable `sponskrub` by default * Fix `--force-keyframes-at-cuts` * Don't embed subtitles if the video has been cut. Previously, running `--remove-chapters` with `--embed-subs` multiple times caused repeated cuts and out-of-sync subtitles * Store `_real_duration` to prevent running ffprobe multiple times --- yt_dlp/options.py | 4 ++-- yt_dlp/postprocessor/ffmpeg.py | 22 ++++++++++++++++++++++ yt_dlp/postprocessor/modify_chapters.py | 18 +++++------------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 505160cec..c2d7a74ff 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -1394,7 +1394,7 @@ def parseOpts(overrideArguments=None): 'SponsorBlock categories to create chapters for, separated by commas. ' 'Available categories are all, %s. You can prefix the category with a "-" to exempt it. ' 'See https://wiki.sponsor.ajay.app/index.php/Segment_Categories for description of the categories. ' - 'Eg: --sponsorblock-query all,-preview' % ', '.join(SponsorBlockPP.CATEGORIES.keys()))) + 'Eg: --sponsorblock-mark all,-preview' % ', '.join(SponsorBlockPP.CATEGORIES.keys()))) sponsorblock.add_option( '--sponsorblock-remove', metavar='CATS', dest='sponsorblock_remove', default=set(), action='callback', type='str', @@ -1421,7 +1421,7 @@ def parseOpts(overrideArguments=None): sponsorblock.add_option( '--sponskrub', - action='store_true', dest='sponskrub', default=None, + action='store_true', dest='sponskrub', default=False, help=optparse.SUPPRESS_HELP) sponsorblock.add_option( '--no-sponskrub', diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py index 806334645..25488e58b 100644 --- a/yt_dlp/postprocessor/ffmpeg.py +++ b/yt_dlp/postprocessor/ffmpeg.py @@ -15,6 +15,7 @@ from ..utils import ( dfxp2srt, encodeArgument, encodeFilename, + float_or_none, get_exe_version, is_outdated_version, ISO639Utils, @@ -233,6 +234,23 @@ class FFmpegPostProcessor(PostProcessor): None) return num, len(streams) + def _get_real_video_duration(self, info, fatal=True): + try: + if '_real_duration' not in info: + info['_real_duration'] = float_or_none( + traverse_obj(self.get_metadata_object(info['filepath']), ('format', 'duration'))) + if not info['_real_duration']: + raise PostProcessingError('ffprobe returned empty duration') + except PostProcessingError as e: + if fatal: + raise PostProcessingError(f'Unable to determine video duration; {e}') + return info.setdefault('_real_duration', None) + + def _duration_mismatch(self, d1, d2): + if not d1 or not d2: + return None + return abs(d1 - d2) > 1 + def run_ffmpeg_multiple_files(self, input_paths, out_path, opts, **kwargs): return self.real_run_ffmpeg( [(path, []) for path in input_paths], @@ -528,6 +546,10 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor): return [], information filename = information['filepath'] + if self._duration_mismatch( + self._get_real_video_duration(information, False), information['duration']): + self.to_screen(f'Skipping {self.pp_key()} since the real and expected durations mismatch') + return [], information ext = information['ext'] sub_langs, sub_names, sub_filenames = [], [], [] diff --git a/yt_dlp/postprocessor/modify_chapters.py b/yt_dlp/postprocessor/modify_chapters.py index 3d6493b68..9a7ba8eff 100644 --- a/yt_dlp/postprocessor/modify_chapters.py +++ b/yt_dlp/postprocessor/modify_chapters.py @@ -9,11 +9,9 @@ from .ffmpeg import ( ) from .sponsorblock import SponsorBlockPP from ..utils import ( - float_or_none, orderedSet, PostProcessingError, prepend_extension, - traverse_obj, ) @@ -37,7 +35,7 @@ class ModifyChaptersPP(FFmpegPostProcessor): if not chapters and not sponsor_chapters: return [], info - real_duration = self._get_real_video_duration(info['filepath']) + real_duration = self._get_real_video_duration(info) if not chapters: chapters = [{'start_time': 0, 'end_time': real_duration, 'title': info['title']}] @@ -45,8 +43,8 @@ class ModifyChaptersPP(FFmpegPostProcessor): if not cuts: return [], info - if abs(real_duration - info['duration']) > 1: - if abs(real_duration - info['chapters'][-1]['end_time']) < 1: + if self._duration_mismatch(real_duration, info.get('duration')): + if not self._duration_mismatch(real_duration, info['chapters'][-1]['end_time']): self.to_screen(f'Skipping {self.pp_key()} since the video appears to be already cut') return [], info if not info.get('__real_download'): @@ -72,6 +70,7 @@ class ModifyChaptersPP(FFmpegPostProcessor): os.replace(out_file, in_file) files_to_remove.append(uncut_file) + info['_real_duration'] = info['chapters'][-1]['end_time'] return files_to_remove, info def _mark_chapters_to_remove(self, chapters, sponsor_chapters): @@ -101,13 +100,6 @@ class ModifyChaptersPP(FFmpegPostProcessor): return chapters, sponsor_chapters - def _get_real_video_duration(self, filename): - duration = float_or_none( - traverse_obj(self.get_metadata_object(filename), ('format', 'duration'))) - if duration is None: - raise PostProcessingError('ffprobe returned empty duration') - return duration - def _get_supported_subs(self, info): for sub in (info.get('requested_subtitles') or {}).values(): sub_file = sub.get('filepath') @@ -311,7 +303,7 @@ class ModifyChaptersPP(FFmpegPostProcessor): in_file = filename out_file = prepend_extension(in_file, 'temp') if force_keyframes: - in_file = self.force_keyframes(in_file, (t for r in ranges_to_cut for t in r)) + in_file = self.force_keyframes(in_file, (t for c in ranges_to_cut for t in (c['start_time'], c['end_time']))) self.to_screen(f'Removing chapters from {filename}') self.concat_files([in_file] * len(concat_opts), out_file, concat_opts) if in_file != filename: