diff options
Diffstat (limited to 'youtube_dl/postprocessor')
| -rw-r--r-- | youtube_dl/postprocessor/__init__.py | 12 | ||||
| -rw-r--r-- | youtube_dl/postprocessor/common.py | 2 | ||||
| -rw-r--r-- | youtube_dl/postprocessor/execafterdownload.py | 3 | ||||
| -rw-r--r-- | youtube_dl/postprocessor/ffmpeg.py | 117 | ||||
| -rw-r--r-- | youtube_dl/postprocessor/xattrpp.py | 3 | 
5 files changed, 87 insertions, 50 deletions
diff --git a/youtube_dl/postprocessor/__init__.py b/youtube_dl/postprocessor/__init__.py index 6ac67cbae..f8507951c 100644 --- a/youtube_dl/postprocessor/__init__.py +++ b/youtube_dl/postprocessor/__init__.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals  from .atomicparsley import AtomicParsleyPP  from .ffmpeg import ( @@ -5,22 +6,29 @@ from .ffmpeg import (      FFmpegAudioFixPP,      FFmpegEmbedSubtitlePP,      FFmpegExtractAudioPP, +    FFmpegFixupStretchedPP,      FFmpegMergerPP,      FFmpegMetadataPP, -    FFmpegVideoConvertor, +    FFmpegVideoConvertorPP,  )  from .xattrpp import XAttrMetadataPP  from .execafterdownload import ExecAfterDownloadPP + +def get_postprocessor(key): +    return globals()[key + 'PP'] + +  __all__ = [      'AtomicParsleyPP',      'ExecAfterDownloadPP',      'FFmpegAudioFixPP',      'FFmpegEmbedSubtitlePP',      'FFmpegExtractAudioPP', +    'FFmpegFixupStretchedPP',      'FFmpegMergerPP',      'FFmpegMetadataPP',      'FFmpegPostProcessor', -    'FFmpegVideoConvertor', +    'FFmpegVideoConvertorPP',      'XAttrMetadataPP',  ] diff --git a/youtube_dl/postprocessor/common.py b/youtube_dl/postprocessor/common.py index 788f94d02..e54ae678d 100644 --- a/youtube_dl/postprocessor/common.py +++ b/youtube_dl/postprocessor/common.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  from ..utils import PostProcessingError diff --git a/youtube_dl/postprocessor/execafterdownload.py b/youtube_dl/postprocessor/execafterdownload.py index baf1b1945..75c0f7bbe 100644 --- a/youtube_dl/postprocessor/execafterdownload.py +++ b/youtube_dl/postprocessor/execafterdownload.py @@ -14,7 +14,7 @@ class ExecAfterDownloadPP(PostProcessor):      def run(self, information):          cmd = self.exec_cmd -        if not '{}' in cmd: +        if '{}' not in cmd:              cmd += ' {}'          cmd = cmd.replace('{}', shlex_quote(information['filepath'])) @@ -26,4 +26,3 @@ class ExecAfterDownloadPP(PostProcessor):                  'Command returned error code %d' % retCode)          return None, information  # by default, keep file and do nothing - diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index d44de84cf..8bf5bebc3 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  import os  import subprocess  import sys @@ -33,13 +35,13 @@ class FFmpegPostProcessor(PostProcessor):      def check_version(self):          if not self._executable: -            raise FFmpegPostProcessorError(u'ffmpeg or avconv not found. Please install one.') +            raise FFmpegPostProcessorError('ffmpeg or avconv not found. Please install one.') -        REQUIRED_VERSION = '1.0' +        required_version = '10-0' if self._uses_avconv() else '1.0'          if is_outdated_version( -                self._versions[self._executable], REQUIRED_VERSION): -            warning = u'Your copy of %s is outdated, update %s to version %s or newer if you encounter any errors.' % ( -                self._executable, self._executable, REQUIRED_VERSION) +                self._versions[self._executable], required_version): +            warning = 'Your copy of %s is outdated, update %s to version %s or newer if you encounter any errors.' % ( +                self._executable, self._executable, required_version)              if self._downloader:                  self._downloader.report_warning(warning) @@ -49,6 +51,10 @@ class FFmpegPostProcessor(PostProcessor):          return dict((p, get_exe_version(p, args=['-version'])) for p in programs)      @property +    def available(self): +        return self._executable is not None + +    @property      def _executable(self):          if self._downloader.params.get('prefer_ffmpeg', False):              prefs = ('ffmpeg', 'avconv') @@ -76,16 +82,18 @@ class FFmpegPostProcessor(PostProcessor):      def run_ffmpeg_multiple_files(self, input_paths, out_path, opts):          self.check_version() +        oldest_mtime = min(os.stat(path).st_mtime for path in input_paths) +          files_cmd = []          for path in input_paths: -            files_cmd.extend(['-i', encodeFilename(path, True)]) -        oldest_mtime = min(os.stat(path).st_mtime for path in input_paths) -        cmd = ([self._executable, '-y'] + files_cmd -               + [encodeArgument(o) for o in opts] + +            files_cmd.extend([encodeArgument('-i'), encodeFilename(path, True)]) +        cmd = ([encodeFilename(self._executable, True), encodeArgument('-y')] + +               files_cmd + +               [encodeArgument(o) for o in opts] +                 [encodeFilename(self._ffmpeg_filename_argument(out_path), True)])          if self._downloader.params.get('verbose', False): -            self._downloader.to_screen(u'[debug] ffmpeg command line: %s' % shell_quote(cmd)) +            self._downloader.to_screen('[debug] ffmpeg command line: %s' % shell_quote(cmd))          p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)          stdout, stderr = p.communicate()          if p.returncode != 0: @@ -102,8 +110,8 @@ class FFmpegPostProcessor(PostProcessor):      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 +        if fn.startswith('-'): +            return './' + fn          return fn @@ -119,11 +127,11 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):      def get_audio_codec(self, path):          if not self._probe_executable: -            raise PostProcessingError(u'ffprobe or avprobe not found. Please install one.') +            raise PostProcessingError('ffprobe or avprobe not found. Please install one.')          try:              cmd = [ -                self._probe_executable, -                '-show_streams', +                encodeFilename(self._probe_executable, True), +                encodeArgument('-show_streams'),                  encodeFilename(self._ffmpeg_filename_argument(path), True)]              handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), stdout=subprocess.PIPE)              output = handle.communicate()[0] @@ -155,7 +163,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):          filecodec = self.get_audio_codec(path)          if filecodec is None: -            raise PostProcessingError(u'WARNING: unable to obtain file audio codec with ffprobe') +            raise PostProcessingError('WARNING: unable to obtain file audio codec with ffprobe')          uses_avconv = self._uses_avconv()          more_opts = [] @@ -204,7 +212,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):                  extension = 'wav'                  more_opts += ['-f', 'wav'] -        prefix, sep, ext = path.rpartition(u'.') # not os.path.splitext, since the latter does not work on unicode in all setups +        prefix, sep, ext = path.rpartition('.')  # not os.path.splitext, since the latter does not work on unicode in all setups          new_path = prefix + sep + extension          # If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly. @@ -213,16 +221,16 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):          try:              if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)): -                self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path) +                self._downloader.to_screen('[youtube] Post-process file %s exists, skipping' % new_path)              else: -                self._downloader.to_screen(u'[' + self._executable + '] Destination: ' + new_path) +                self._downloader.to_screen('[' + self._executable + '] Destination: ' + new_path)                  self.run_ffmpeg(path, new_path, acodec, more_opts)          except: -            etype,e,tb = sys.exc_info() +            etype, e, tb = sys.exc_info()              if isinstance(e, AudioConversionError): -                msg = u'audio conversion failed: ' + e.msg +                msg = 'audio conversion failed: ' + e.msg              else: -                msg = u'error running ' + self._executable +                msg = 'error running ' + self._executable              raise PostProcessingError(msg)          # Try to update the date time for extracted audio file. @@ -230,30 +238,30 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):              try:                  os.utime(encodeFilename(new_path), (time.time(), information['filetime']))              except: -                self._downloader.report_warning(u'Cannot update utime of audio file') +                self._downloader.report_warning('Cannot update utime of audio file')          information['filepath'] = new_path -        return self._nopostoverwrites,information +        return self._nopostoverwrites, information -class FFmpegVideoConvertor(FFmpegPostProcessor): -    def __init__(self, downloader=None,preferedformat=None): -        super(FFmpegVideoConvertor, self).__init__(downloader) -        self._preferedformat=preferedformat +class FFmpegVideoConvertorPP(FFmpegPostProcessor): +    def __init__(self, downloader=None, preferedformat=None): +        super(FFmpegVideoConvertorPP, self).__init__(downloader) +        self._preferedformat = preferedformat      def run(self, information):          path = information['filepath'] -        prefix, sep, ext = path.rpartition(u'.') +        prefix, sep, ext = path.rpartition('.')          outpath = prefix + sep + self._preferedformat          if information['ext'] == self._preferedformat: -            self._downloader.to_screen(u'[ffmpeg] Not converting video file %s - already is in target format %s' % (path, self._preferedformat)) -            return True,information -        self._downloader.to_screen(u'['+'ffmpeg'+'] Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) +outpath) +            self._downloader.to_screen('[ffmpeg] Not converting video file %s - already is in target format %s' % (path, self._preferedformat)) +            return True, information +        self._downloader.to_screen('[' + 'ffmpeg' + '] Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath)          self.run_ffmpeg(path, outpath, [])          information['filepath'] = outpath          information['format'] = self._preferedformat          information['ext'] = self._preferedformat -        return False,information +        return False, information  class FFmpegEmbedSubtitlePP(FFmpegPostProcessor): @@ -455,11 +463,11 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):          return cls._lang_map.get(code[:2])      def run(self, information): -        if information['ext'] != u'mp4': -            self._downloader.to_screen(u'[ffmpeg] Subtitles can only be embedded in mp4 files') +        if information['ext'] != 'mp4': +            self._downloader.to_screen('[ffmpeg] Subtitles can only be embedded in mp4 files')              return True, information          if not information.get('subtitles'): -            self._downloader.to_screen(u'[ffmpeg] There aren\'t any subtitles to embed')  +            self._downloader.to_screen('[ffmpeg] There aren\'t any subtitles to embed')              return True, information          sub_langs = [key for key in information['subtitles']] @@ -468,14 +476,14 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):          opts = ['-map', '0:0', '-map', '0:1', '-c:v', 'copy', '-c:a', 'copy']          for (i, lang) in enumerate(sub_langs): -            opts.extend(['-map', '%d:0' % (i+1), '-c:s:%d' % i, 'mov_text']) +            opts.extend(['-map', '%d:0' % (i + 1), '-c:s:%d' % i, 'mov_text'])              lang_code = self._conver_lang_code(lang)              if lang_code is not None:                  opts.extend(['-metadata:s:s:%d' % i, 'language=%s' % lang_code])          opts.extend(['-f', 'mp4']) -        temp_filename = filename + u'.temp' -        self._downloader.to_screen(u'[ffmpeg] Embedding subtitles in \'%s\'' % filename) +        temp_filename = filename + '.temp' +        self._downloader.to_screen('[ffmpeg] Embedding subtitles in \'%s\'' % filename)          self.run_ffmpeg_multiple_files(input_files, temp_filename, opts)          os.remove(encodeFilename(filename))          os.rename(encodeFilename(temp_filename), encodeFilename(filename)) @@ -496,13 +504,13 @@ class FFmpegMetadataPP(FFmpegPostProcessor):              metadata['artist'] = info['uploader_id']          if not metadata: -            self._downloader.to_screen(u'[ffmpeg] There isn\'t any metadata to add') +            self._downloader.to_screen('[ffmpeg] There isn\'t any metadata to add')              return True, info          filename = info['filepath']          temp_filename = prepend_extension(filename, 'temp') -        if info['ext'] == u'm4a': +        if info['ext'] == 'm4a':              options = ['-vn', '-acodec', 'copy']          else:              options = ['-c', 'copy'] @@ -510,7 +518,7 @@ class FFmpegMetadataPP(FFmpegPostProcessor):          for (name, value) in metadata.items():              options.extend(['-metadata', '%s=%s' % (name, value)]) -        self._downloader.to_screen(u'[ffmpeg] Adding metadata to \'%s\'' % filename) +        self._downloader.to_screen('[ffmpeg] Adding metadata to \'%s\'' % filename)          self.run_ffmpeg(filename, temp_filename, options)          os.remove(encodeFilename(filename))          os.rename(encodeFilename(temp_filename), encodeFilename(filename)) @@ -520,8 +528,8 @@ class FFmpegMetadataPP(FFmpegPostProcessor):  class FFmpegMergerPP(FFmpegPostProcessor):      def run(self, info):          filename = info['filepath'] -        args = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0', '-shortest'] -        self._downloader.to_screen(u'[ffmpeg] Merging formats into "%s"' % filename) +        args = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0'] +        self._downloader.to_screen('[ffmpeg] Merging formats into "%s"' % filename)          self.run_ffmpeg_multiple_files(info['__files_to_merge'], filename, args)          return True, info @@ -532,7 +540,26 @@ class FFmpegAudioFixPP(FFmpegPostProcessor):          temp_filename = prepend_extension(filename, 'temp')          options = ['-vn', '-acodec', 'copy'] -        self._downloader.to_screen(u'[ffmpeg] Fixing audio file "%s"' % filename) +        self._downloader.to_screen('[ffmpeg] Fixing audio file "%s"' % filename) +        self.run_ffmpeg(filename, temp_filename, options) + +        os.remove(encodeFilename(filename)) +        os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + +        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)) diff --git a/youtube_dl/postprocessor/xattrpp.py b/youtube_dl/postprocessor/xattrpp.py index b5cae41c8..f6c63fe97 100644 --- a/youtube_dl/postprocessor/xattrpp.py +++ b/youtube_dl/postprocessor/xattrpp.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals +  import os  import subprocess  import sys @@ -108,4 +110,3 @@ class XAttrMetadataPP(PostProcessor):          except (subprocess.CalledProcessError, OSError):              self._downloader.report_error("This filesystem doesn't support extended attributes. (You may have to enable them in your /etc/fstab)")              return False, info -  | 
