mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 06:35:12 +00:00 
			
		
		
		
	[youtube|ffmpeg] Automatically correct video with non-square pixels (Fixes #4674)
This commit is contained in:
		| @@ -70,6 +70,7 @@ from .extractor import get_info_extractor, gen_extractors | |||||||
| from .downloader import get_suitable_downloader | from .downloader import get_suitable_downloader | ||||||
| from .downloader.rtmp import rtmpdump_version | from .downloader.rtmp import rtmpdump_version | ||||||
| from .postprocessor import ( | from .postprocessor import ( | ||||||
|  |     FFmpegFixupStretchedPP, | ||||||
|     FFmpegMergerPP, |     FFmpegMergerPP, | ||||||
|     FFmpegPostProcessor, |     FFmpegPostProcessor, | ||||||
|     get_postprocessor, |     get_postprocessor, | ||||||
| @@ -204,6 +205,12 @@ class YoutubeDL(object): | |||||||
|                        Progress hooks are guaranteed to be called at least once |                        Progress hooks are guaranteed to be called at least once | ||||||
|                        (with status "finished") if the download is successful. |                        (with status "finished") if the download is successful. | ||||||
|     merge_output_format: Extension to use when merging formats. |     merge_output_format: Extension to use when merging formats. | ||||||
|  |     fixup:             Automatically correct known faults of the file. | ||||||
|  |                        One of: | ||||||
|  |                        - "never": do nothing | ||||||
|  |                        - "warn": only emit a warning | ||||||
|  |                        - "detect_or_warn": check whether we can do anything | ||||||
|  |                                            about it, warn otherwise | ||||||
|  |  | ||||||
|  |  | ||||||
|     The following parameters are not used by YoutubeDL itself, they are used by |     The following parameters are not used by YoutubeDL itself, they are used by | ||||||
| @@ -924,6 +931,7 @@ class YoutubeDL(object): | |||||||
|                                 'fps': formats_info[0].get('fps'), |                                 'fps': formats_info[0].get('fps'), | ||||||
|                                 'vcodec': formats_info[0].get('vcodec'), |                                 'vcodec': formats_info[0].get('vcodec'), | ||||||
|                                 'vbr': formats_info[0].get('vbr'), |                                 'vbr': formats_info[0].get('vbr'), | ||||||
|  |                                 'stretched_ratio': formats_info[0].get('stretched_ratio'), | ||||||
|                                 'acodec': formats_info[1].get('acodec'), |                                 'acodec': formats_info[1].get('acodec'), | ||||||
|                                 'abr': formats_info[1].get('abr'), |                                 'abr': formats_info[1].get('abr'), | ||||||
|                                 'ext': output_ext, |                                 'ext': output_ext, | ||||||
| @@ -1154,6 +1162,27 @@ class YoutubeDL(object): | |||||||
|                     return |                     return | ||||||
|  |  | ||||||
|             if success: |             if success: | ||||||
|  |                 # Fixup content | ||||||
|  |                 stretched_ratio = info_dict.get('stretched_ratio') | ||||||
|  |                 if stretched_ratio is not None and stretched_ratio != 1: | ||||||
|  |                     fixup_policy = self.params.get('fixup') | ||||||
|  |                     if fixup_policy is None: | ||||||
|  |                         fixup_policy = 'detect_or_warn' | ||||||
|  |                     if fixup_policy == 'warn': | ||||||
|  |                         self.report_warning('%s: Non-uniform pixel ratio (%s)' % ( | ||||||
|  |                             info_dict['id'], stretched_ratio)) | ||||||
|  |                     elif fixup_policy == 'detect_or_warn': | ||||||
|  |                         stretched_pp = FFmpegFixupStretchedPP(self) | ||||||
|  |                         if stretched_pp.available: | ||||||
|  |                             info_dict.setdefault('__postprocessors', []) | ||||||
|  |                             info_dict['__postprocessors'].append(stretched_pp) | ||||||
|  |                         else: | ||||||
|  |                             self.report_warning( | ||||||
|  |                                 '%s: Non-uniform pixel ratio (%s). Install ffmpeg or avconv to fix this automatically.' % ( | ||||||
|  |                                     info_dict['id'], stretched_ratio)) | ||||||
|  |                     else: | ||||||
|  |                         assert fixup_policy == 'ignore' | ||||||
|  |  | ||||||
|                 try: |                 try: | ||||||
|                     self.post_process(filename, info_dict) |                     self.post_process(filename, info_dict) | ||||||
|                 except (PostProcessingError) as err: |                 except (PostProcessingError) as err: | ||||||
|   | |||||||
| @@ -326,6 +326,7 @@ def _real_main(argv=None): | |||||||
|         'extract_flat': opts.extract_flat, |         'extract_flat': opts.extract_flat, | ||||||
|         'merge_output_format': opts.merge_output_format, |         'merge_output_format': opts.merge_output_format, | ||||||
|         'postprocessors': postprocessors, |         'postprocessors': postprocessors, | ||||||
|  |         'fixup': opts.fixup, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     with YoutubeDL(ydl_opts) as ydl: |     with YoutubeDL(ydl_opts) as ydl: | ||||||
|   | |||||||
| @@ -114,6 +114,9 @@ class InfoExtractor(object): | |||||||
|                                  to add to the request. |                                  to add to the request. | ||||||
|                     * http_post_data  Additional data to send with a POST |                     * http_post_data  Additional data to send with a POST | ||||||
|                                  request. |                                  request. | ||||||
|  |                     * stretched_ratio  If given and not 1, indicates that the | ||||||
|  |                                        video's pixels are not square. | ||||||
|  |                                        width : height ratio as float. | ||||||
|     url:            Final video URL. |     url:            Final video URL. | ||||||
|     ext:            Video filename extension. |     ext:            Video filename extension. | ||||||
|     format:         The video format, defaults to ext (used for --get-format) |     format:         The video format, defaults to ext (used for --get-format) | ||||||
|   | |||||||
| @@ -465,6 +465,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): | |||||||
|                 'skip_download': 'requires avconv', |                 'skip_download': 'requires avconv', | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         # Non-square pixels | ||||||
|  |         { | ||||||
|  |             'url': 'https://www.youtube.com/watch?v=_b-2C3KPAM0', | ||||||
|  |             'info_dict': { | ||||||
|  |                 'id': '_b-2C3KPAM0', | ||||||
|  |                 'ext': 'mp4', | ||||||
|  |                 'stretched_ratio': 16 / 9., | ||||||
|  |                 'upload_date': '20110310', | ||||||
|  |                 'uploader_id': 'AllenMeow', | ||||||
|  |                 'description': 'made by Wacom from Korea | 字幕&加油添醋 by TY\'s Allen | 感謝heylisa00cavey1001同學熱情提供梗及翻譯', | ||||||
|  |                 'uploader': '孫艾倫', | ||||||
|  |                 'title': '[A-made] 變態妍字幕版 太妍 我就是這樣的人', | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
| @@ -1051,6 +1065,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): | |||||||
|                             f['preference'] = f.get('preference', 0) - 10000 |                             f['preference'] = f.get('preference', 0) - 10000 | ||||||
|                     formats.extend(dash_formats) |                     formats.extend(dash_formats) | ||||||
|  |  | ||||||
|  |         # Check for malformed aspect ratio | ||||||
|  |         stretched_m = re.search( | ||||||
|  |             r'<meta\s+property="og:video:tag".*?content="yt:stretch=(?P<w>[0-9]+):(?P<h>[0-9]+)">', | ||||||
|  |             video_webpage) | ||||||
|  |         if stretched_m: | ||||||
|  |             ratio = float(stretched_m.group('w')) / float(stretched_m.group('h')) | ||||||
|  |             for f in formats: | ||||||
|  |                 if f.get('vcodec') != 'none': | ||||||
|  |                     f['stretched_ratio'] = ratio | ||||||
|  |  | ||||||
|         self._sort_formats(formats) |         self._sort_formats(formats) | ||||||
|  |  | ||||||
|         return { |         return { | ||||||
|   | |||||||
| @@ -631,6 +631,13 @@ def parseOpts(overrideArguments=None): | |||||||
|         '--xattrs', |         '--xattrs', | ||||||
|         action='store_true', dest='xattrs', default=False, |         action='store_true', dest='xattrs', default=False, | ||||||
|         help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)') |         help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)') | ||||||
|  |     postproc.add_option( | ||||||
|  |         '--fixup', | ||||||
|  |         metavar='POLICY', dest='fixup', default='detect_or_warn', | ||||||
|  |         help='(experimental) Automatically correct known faults of the file. ' | ||||||
|  |              'One of never (do nothing), warn (only emit a warning), ' | ||||||
|  |              'detect_or_warn(check whether we can do anything about it, warn ' | ||||||
|  |              'otherwise') | ||||||
|     postproc.add_option( |     postproc.add_option( | ||||||
|         '--prefer-avconv', |         '--prefer-avconv', | ||||||
|         action='store_false', dest='prefer_ffmpeg', |         action='store_false', dest='prefer_ffmpeg', | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ from .ffmpeg import ( | |||||||
|     FFmpegAudioFixPP, |     FFmpegAudioFixPP, | ||||||
|     FFmpegEmbedSubtitlePP, |     FFmpegEmbedSubtitlePP, | ||||||
|     FFmpegExtractAudioPP, |     FFmpegExtractAudioPP, | ||||||
|  |     FFmpegFixupStretchedPP, | ||||||
|     FFmpegMergerPP, |     FFmpegMergerPP, | ||||||
|     FFmpegMetadataPP, |     FFmpegMetadataPP, | ||||||
|     FFmpegVideoConvertorPP, |     FFmpegVideoConvertorPP, | ||||||
| @@ -24,6 +25,7 @@ __all__ = [ | |||||||
|     'FFmpegAudioFixPP', |     'FFmpegAudioFixPP', | ||||||
|     'FFmpegEmbedSubtitlePP', |     'FFmpegEmbedSubtitlePP', | ||||||
|     'FFmpegExtractAudioPP', |     'FFmpegExtractAudioPP', | ||||||
|  |     'FFmpegFixupStretchedPP', | ||||||
|     'FFmpegMergerPP', |     'FFmpegMergerPP', | ||||||
|     'FFmpegMetadataPP', |     'FFmpegMetadataPP', | ||||||
|     'FFmpegPostProcessor', |     'FFmpegPostProcessor', | ||||||
|   | |||||||
| @@ -50,6 +50,10 @@ class FFmpegPostProcessor(PostProcessor): | |||||||
|         programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] |         programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] | ||||||
|         return dict((p, get_exe_version(p, args=['-version'])) for p in programs) |         return dict((p, get_exe_version(p, args=['-version'])) for p in programs) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def available(self): | ||||||
|  |         return self._executable is not None | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def _executable(self): |     def _executable(self): | ||||||
|         if self._downloader.params.get('prefer_ffmpeg', False): |         if self._downloader.params.get('prefer_ffmpeg', False): | ||||||
| @@ -540,3 +544,22 @@ class FFmpegAudioFixPP(FFmpegPostProcessor): | |||||||
|         os.rename(encodeFilename(temp_filename), encodeFilename(filename)) |         os.rename(encodeFilename(temp_filename), encodeFilename(filename)) | ||||||
|  |  | ||||||
|         return True, info |         return True, info | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FFmpegFixupStretchedPP(FFmpegPostProcessor): | ||||||
|  |     def run(self, info): | ||||||
|  |         stretched_ratio = info.get('stretched_ratio') | ||||||
|  |         if stretched_ratio is None or stretched_ratio == 1: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         filename = info['filepath'] | ||||||
|  |         temp_filename = prepend_extension(filename, 'temp') | ||||||
|  |  | ||||||
|  |         options = ['-c', 'copy', '-aspect', '%f' % stretched_ratio] | ||||||
|  |         self._downloader.to_screen('[ffmpeg] Fixing aspect ratio in "%s"' % filename) | ||||||
|  |         self.run_ffmpeg(filename, temp_filename, options) | ||||||
|  |  | ||||||
|  |         os.remove(encodeFilename(filename)) | ||||||
|  |         os.rename(encodeFilename(temp_filename), encodeFilename(filename)) | ||||||
|  |  | ||||||
|  |         return True, info | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Hagemeister
					Philipp Hagemeister