diff options
Diffstat (limited to 'youtube_dl')
| -rwxr-xr-x | youtube_dl/YoutubeDL.py | 4 | ||||
| -rw-r--r-- | youtube_dl/__init__.py | 1 | ||||
| -rw-r--r-- | youtube_dl/downloader/hls.py | 7 | ||||
| -rw-r--r-- | youtube_dl/options.py | 4 | ||||
| -rw-r--r-- | youtube_dl/postprocessor/ffmpeg.py | 99 | 
5 files changed, 82 insertions, 33 deletions
diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 13d18e25e..dbb26272d 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -1298,7 +1298,7 @@ class YoutubeDL(object):                      downloaded = []                      success = True                      merger = FFmpegMergerPP(self, not self.params.get('keepvideo')) -                    if not merger._executable: +                    if not merger.available():                          postprocessors = []                          self.report_warning('You have requested multiple '                                              'formats but ffmpeg or avconv are not installed.' @@ -1647,7 +1647,7 @@ class YoutubeDL(object):          self._write_string('[debug] Python version %s - %s\n' % (              platform.python_version(), platform_name())) -        exe_versions = FFmpegPostProcessor.get_versions() +        exe_versions = FFmpegPostProcessor.get_versions(self)          exe_versions['rtmpdump'] = rtmpdump_version()          exe_str = ', '.join(              '%s %s' % (exe, v) diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index ed22f169f..108fb3c7a 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -350,6 +350,7 @@ def _real_main(argv=None):          'xattr_set_filesize': opts.xattr_set_filesize,          'match_filter': match_filter,          'no_color': opts.no_color, +        'ffmpeg_location': opts.ffmpeg_location,      }      with YoutubeDL(ydl_opts) as ydl: diff --git a/youtube_dl/downloader/hls.py b/youtube_dl/downloader/hls.py index e527ee425..8be4f4249 100644 --- a/youtube_dl/downloader/hls.py +++ b/youtube_dl/downloader/hls.py @@ -23,15 +23,14 @@ class HlsFD(FileDownloader):          tmpfilename = self.temp_name(filename)          ffpp = FFmpegPostProcessor(downloader=self) -        program = ffpp._executable -        if program is None: +        if not ffpp.available:              self.report_error('m3u8 download detected but ffmpeg or avconv could not be found. Please install one.')              return False          ffpp.check_version()          args = [              encodeArgument(opt) -            for opt in (program, '-y', '-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc')] +            for opt in (ffpp.executable, '-y', '-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc')]          args.append(encodeFilename(tmpfilename, True))          retval = subprocess.call(args) @@ -48,7 +47,7 @@ class HlsFD(FileDownloader):              return True          else:              self.to_stderr('\n') -            self.report_error('%s exited with code %d' % (program, retval)) +            self.report_error('%s exited with code %d' % (ffpp.basename, retval))              return False diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 873432bee..ba35399cf 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -736,6 +736,10 @@ def parseOpts(overrideArguments=None):          action='store_true', dest='prefer_ffmpeg',          help='Prefer ffmpeg over avconv for running the postprocessors')      postproc.add_option( +        '--ffmpeg-location', '--avconv-location', metavar='PATH', +        dest='ffmpeg_location', +        help='Location of the ffmpeg/avconv binary; either the path to the binary or its containing directory.') +    postproc.add_option(          '--exec',          metavar='CMD', dest='exec_cmd',          help='Execute a command on the file after downloading, similar to find\'s -exec syntax. Example: --exec \'adb push {} /sdcard/Music/ && rm {}\'') diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index 5238ce534..16babf6a5 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -30,54 +30,97 @@ class FFmpegPostProcessorError(PostProcessingError):  class FFmpegPostProcessor(PostProcessor):      def __init__(self, downloader=None, deletetempfiles=False):          PostProcessor.__init__(self, downloader) -        self._versions = self.get_versions()          self._deletetempfiles = deletetempfiles +        self._determine_executables()      def check_version(self): -        if not self._executable: +        if not self.available():              raise FFmpegPostProcessorError('ffmpeg or avconv not found. Please install one.')          required_version = '10-0' if self._uses_avconv() else '1.0'          if is_outdated_version( -                self._versions[self._executable], required_version): +                self._versions[self.basename], 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) +                self.basename, self.basename, required_version)              if self._downloader:                  self._downloader.report_warning(warning)      @staticmethod -    def get_versions(): -        programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] -        return dict((p, get_exe_version(p, args=['-version'])) for p in programs) - -    @property -    def available(self): -        return self._executable is not None +    def get_versions(downloader=None): +        return FFmpegPostProcessor(downloader)._versions -    @property -    def _executable(self): -        if self._downloader.params.get('prefer_ffmpeg', False): +    def _determine_executables(self): +        programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] +        prefer_ffmpeg = self._downloader.params.get('prefer_ffmpeg', False) + +        self.basename = None +        self.probe_basename = None + +        self._paths = None +        self._versions = None +        if self._downloader: +            location = self._downloader.params.get('ffmpeg_location') +            if location is not None: +                if not os.path.exists(location): +                    self._downloader.report_warning( +                        'ffmpeg-location %s does not exist! ' +                        'Continuing without avconv/ffmpeg.' % (location)) +                    self._versions = {} +                    return +                elif not os.path.isdir(location): +                    basename = os.path.splitext(os.path.basename(location))[0] +                    if basename not in programs: +                        self._downloader.report_warning( +                            'Cannot identify executable %s, its basename should be one of %s. ' +                            'Continuing without avconv/ffmpeg.' % +                            (location, ', '.join(programs))) +                        self._versions = {} +                        return None +                    location = os.path.dirname(os.path.abspath(location)) +                    if basename in ('ffmpeg', 'ffprobe'): +                        prefer_ffmpeg = True + +                self._paths = dict( +                    (p, os.path.join(location, p)) for p in programs) +                self._versions = dict( +                    (p, get_exe_version(self._paths[p], args=['-version'])) +                    for p in programs) +        if self._versions is None: +            self._versions = dict( +                (p, get_exe_version(p, args=['-version'])) for p in programs) +            self._paths = dict((p, p) for p in programs) + +        if prefer_ffmpeg:              prefs = ('ffmpeg', 'avconv')          else:              prefs = ('avconv', 'ffmpeg')          for p in prefs:              if self._versions[p]: -                return p -        return None +                self.basename = p +                break -    @property -    def _probe_executable(self): -        if self._downloader.params.get('prefer_ffmpeg', False): +        if prefer_ffmpeg:              prefs = ('ffprobe', 'avprobe')          else:              prefs = ('avprobe', 'ffprobe')          for p in prefs:              if self._versions[p]: -                return p -        return None +                self.probe_basename = p +                break + +    def available(self): +        return self.basename is not None      def _uses_avconv(self): -        return self._executable == 'avconv' +        return self.basename == 'avconv' + +    @property +    def executable(self): +        return self._paths[self.basename] + +    @property +    def probe_executable(self): +        return self._paths[self.probe_basename]      def run_ffmpeg_multiple_files(self, input_paths, out_path, opts):          self.check_version() @@ -88,7 +131,7 @@ class FFmpegPostProcessor(PostProcessor):          files_cmd = []          for path in input_paths:              files_cmd.extend([encodeArgument('-i'), encodeFilename(path, True)]) -        cmd = ([encodeFilename(self._executable, True), encodeArgument('-y')] + +        cmd = ([encodeFilename(self.executable, True), encodeArgument('-y')] +                 files_cmd +                 [encodeArgument(o) for o in opts] +                 [encodeFilename(self._ffmpeg_filename_argument(out_path), True)]) @@ -127,13 +170,15 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):      def get_audio_codec(self, path): -        if not self._probe_executable: +        if not self.probe_executable:              raise PostProcessingError('ffprobe or avprobe not found. Please install one.')          try:              cmd = [ -                encodeFilename(self._probe_executable, True), +                encodeFilename(self.probe_executable, True),                  encodeArgument('-show_streams'),                  encodeFilename(self._ffmpeg_filename_argument(path), True)] +            if self._downloader.params.get('verbose', False): +                self._downloader.to_screen('[debug] %s command line: %s' % (self.basename, shell_quote(cmd)))              handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), stdout=subprocess.PIPE, stdin=subprocess.PIPE)              output = handle.communicate()[0]              if handle.wait() != 0: @@ -223,14 +268,14 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):              if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):                  self._downloader.to_screen('[youtube] Post-process file %s exists, skipping' % new_path)              else: -                self._downloader.to_screen('[' + self._executable + '] Destination: ' + new_path) +                self._downloader.to_screen('[' + self.basename + '] Destination: ' + new_path)                  self.run_ffmpeg(path, new_path, acodec, more_opts)          except:              etype, e, tb = sys.exc_info()              if isinstance(e, AudioConversionError):                  msg = 'audio conversion failed: ' + e.msg              else: -                msg = 'error running ' + self._executable +                msg = 'error running ' + self.basename              raise PostProcessingError(msg)          # Try to update the date time for extracted audio file.  | 
