Add a PostProcessor for converting video format

This commit is contained in:
Jaime Marquínez Ferrándiz 2013-01-11 20:50:49 +01:00
parent 09f9552b40
commit 67d0c25eab
2 changed files with 57 additions and 21 deletions

View file

@ -57,19 +57,17 @@ class PostProcessor(object):
""" """
return information # by default, do nothing return information # by default, do nothing
class FFmpegPostProcessorError(BaseException):
def __init__(self, message):
self.message = message
class AudioConversionError(BaseException): class AudioConversionError(BaseException):
def __init__(self, message): def __init__(self, message):
self.message = message self.message = message
class FFmpegExtractAudioPP(PostProcessor): class FFmpegPostProcessor(PostProcessor):
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, keepvideo=False, nopostoverwrites=False): def __init__(self,downloader=None):
PostProcessor.__init__(self, downloader) 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() self._exes = self.detect_executables()
@staticmethod @staticmethod
@ -83,6 +81,34 @@ class FFmpegExtractAudioPP(PostProcessor):
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
return dict((program, executable(program)) for program in programs) 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): def get_audio_codec(self, path):
if not self._exes['ffprobe'] and not self._exes['avprobe']: return None if not self._exes['ffprobe'] and not self._exes['avprobe']: return None
try: try:
@ -108,14 +134,11 @@ class FFmpegExtractAudioPP(PostProcessor):
acodec_opts = [] acodec_opts = []
else: else:
acodec_opts = ['-acodec', codec] acodec_opts = ['-acodec', codec]
cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path), '-vn'] opts = ['-vn'] + acodec_opts + more_opts
+ acodec_opts + more_opts + try:
[encodeFilename(self._ffmpeg_filename_argument(out_path))]) FFmpegPostProcessor.run_ffmpeg(self, path, out_path, opts)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except FFmpegPostProcessorError as err:
stdout,stderr = p.communicate() raise AudioConversionError(err.message)
if p.returncode != 0:
msg = stderr.strip().split('\n')[-1]
raise AudioConversionError(msg)
def run(self, information): def run(self, information):
path = information['filepath'] path = information['filepath']
@ -203,9 +226,19 @@ class FFmpegExtractAudioPP(PostProcessor):
information['filepath'] = new_path information['filepath'] = new_path
return information return information
def _ffmpeg_filename_argument(self, fn): class FFmpegVideoConvertor(FFmpegPostProcessor):
# ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details def __init__(self, downloader=None,preferedformat=None):
if fn.startswith(u'-'): FFmpegPostProcessor.__init__(self,downloader)
return u'./' + fn self._preferedformat=preferedformat
return fn
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

View file

@ -455,6 +455,9 @@ def _real_main():
if opts.extractaudio: if opts.extractaudio:
fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, keepvideo=opts.keepvideo, nopostoverwrites=opts.nopostoverwrites)) 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 # Update version
if opts.update_self: if opts.update_self:
update_self(fd.to_screen, opts.verbose, sys.argv[0]) update_self(fd.to_screen, opts.verbose, sys.argv[0])