From 67d0c25eab1c425d22549a5d3437380a7740aec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Fri, 11 Jan 2013 20:50:49 +0100 Subject: [PATCH] Add a PostProcessor for converting video format --- youtube_dl/PostProcessor.py | 75 ++++++++++++++++++++++++++----------- youtube_dl/__init__.py | 3 ++ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/youtube_dl/PostProcessor.py b/youtube_dl/PostProcessor.py index 79a0d7928..3d1093731 100644 --- a/youtube_dl/PostProcessor.py +++ b/youtube_dl/PostProcessor.py @@ -57,19 +57,17 @@ class PostProcessor(object): """ return information # by default, do nothing +class FFmpegPostProcessorError(BaseException): + def __init__(self, message): + self.message = message + class AudioConversionError(BaseException): def __init__(self, message): self.message = message -class FFmpegExtractAudioPP(PostProcessor): - def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, keepvideo=False, nopostoverwrites=False): +class FFmpegPostProcessor(PostProcessor): + def __init__(self,downloader=None): PostProcessor.__init__(self, downloader) - if preferredcodec is None: - preferredcodec = 'best' - self._preferredcodec = preferredcodec - self._preferredquality = preferredquality - self._keepvideo = keepvideo - self._nopostoverwrites = nopostoverwrites self._exes = self.detect_executables() @staticmethod @@ -83,6 +81,34 @@ class FFmpegExtractAudioPP(PostProcessor): programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] return dict((program, executable(program)) for program in programs) + def run_ffmpeg(self, path, out_path, opts): + if not self._exes['ffmpeg'] and not self._exes['avconv']: + raise FFmpegPostProcessorError('ffmpeg or avconv not found. Please install one.') + cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path)] + + opts + + [encodeFilename(self._ffmpeg_filename_argument(out_path))]) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout,stderr = p.communicate() + if p.returncode != 0: + msg = stderr.strip().split('\n')[-1] + raise FFmpegPostProcessorError(msg) + + def _ffmpeg_filename_argument(self, fn): + # ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details + if fn.startswith(u'-'): + return u'./' + fn + return fn + +class FFmpegExtractAudioPP(FFmpegPostProcessor): + def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, keepvideo=False, nopostoverwrites=False): + FFmpegPostProcessor.__init__(self, downloader) + if preferredcodec is None: + preferredcodec = 'best' + self._preferredcodec = preferredcodec + self._preferredquality = preferredquality + self._keepvideo = keepvideo + self._nopostoverwrites = nopostoverwrites + def get_audio_codec(self, path): if not self._exes['ffprobe'] and not self._exes['avprobe']: return None try: @@ -108,14 +134,11 @@ class FFmpegExtractAudioPP(PostProcessor): acodec_opts = [] else: acodec_opts = ['-acodec', codec] - cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path), '-vn'] - + acodec_opts + more_opts + - [encodeFilename(self._ffmpeg_filename_argument(out_path))]) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout,stderr = p.communicate() - if p.returncode != 0: - msg = stderr.strip().split('\n')[-1] - raise AudioConversionError(msg) + opts = ['-vn'] + acodec_opts + more_opts + try: + FFmpegPostProcessor.run_ffmpeg(self, path, out_path, opts) + except FFmpegPostProcessorError as err: + raise AudioConversionError(err.message) def run(self, information): path = information['filepath'] @@ -203,9 +226,19 @@ class FFmpegExtractAudioPP(PostProcessor): information['filepath'] = new_path return information - def _ffmpeg_filename_argument(self, fn): - # ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details - if fn.startswith(u'-'): - return u'./' + fn - return fn +class FFmpegVideoConvertor(FFmpegPostProcessor): + def __init__(self, downloader=None,preferedformat=None): + FFmpegPostProcessor.__init__(self,downloader) + self._preferedformat=preferedformat + def run(self, information): + path = information['filepath'] + prefix, sep, ext = path.rpartition(u'.') + outpath = prefix + sep + self._preferedformat + if not self._preferedformat or information['format'] == self._preferedformat: + return information + self._downloader.to_screen(u'['+'ffmpeg'+'] Converting video from %s to %s, Destination: ' % (information['format'], self._preferedformat) +outpath) + self.run_ffmpeg(path, outpath, []) + information['filepath'] = outpath + information['format'] = self._preferedformat + return information diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index d7ab0f086..0d9053db3 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -455,6 +455,9 @@ def _real_main(): if opts.extractaudio: fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, keepvideo=opts.keepvideo, nopostoverwrites=opts.nopostoverwrites)) + if opts.format: + fd.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.format)) + # Update version if opts.update_self: update_self(fd.to_screen, opts.verbose, sys.argv[0])