X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=youtube_dl%2Fpostprocessor%2Fffmpeg.py;h=45328ed43ef44052fedb9dcf5f2de012aa3007ae;hb=01b4b745749bb92b4a56b4201d699740cbf450ab;hp=a7fe5f346978bef4625fc86ac7b749ca200910b2;hpb=d70ad093af56330d19eabe54483cbbd44b1bf6c1;p=youtube-dl diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index a7fe5f346..45328ed43 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -9,6 +9,7 @@ from .common import AudioConversionError, PostProcessor from ..utils import ( check_executable, compat_subprocess_get_DEVNULL, + encodeArgument, encodeFilename, PostProcessingError, prepend_extension, @@ -31,21 +32,30 @@ class FFmpegPostProcessor(PostProcessor): programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] return dict((program, check_executable(program, ['-version'])) for program in programs) + def _get_executable(self): + if self._downloader.params.get('prefer_ffmpeg', False): + return self._exes['ffmpeg'] or self._exes['avconv'] + else: + return self._exes['avconv'] or self._exes['ffmpeg'] + + def _uses_avconv(self): + return self._get_executable() == self._exes['avconv'] + def run_ffmpeg_multiple_files(self, input_paths, out_path, opts): - if not self._exes['ffmpeg'] and not self._exes['avconv']: + if not self._get_executable(): raise FFmpegPostProcessorError(u'ffmpeg or avconv not found. Please install one.') files_cmd = [] for path in input_paths: files_cmd.extend(['-i', encodeFilename(path, True)]) - cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y'] + files_cmd - + opts + + cmd = ([self._get_executable(), '-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)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout,stderr = p.communicate() + stdout, stderr = p.communicate() if p.returncode != 0: stderr = stderr.decode('utf-8', 'replace') msg = stderr.strip().split('\n')[-1] @@ -93,8 +103,6 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): return None def run_ffmpeg(self, path, out_path, codec, more_opts): - if not self._exes['ffmpeg'] and not self._exes['avconv']: - raise AudioConversionError('ffmpeg or avconv not found. Please install one.') if codec is None: acodec_opts = [] else: @@ -112,13 +120,14 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): if filecodec is None: raise PostProcessingError(u'WARNING: unable to obtain file audio codec with ffprobe') + uses_avconv = self._uses_avconv() more_opts = [] if self._preferredcodec == 'best' or self._preferredcodec == filecodec or (self._preferredcodec == 'm4a' and filecodec == 'aac'): if filecodec == 'aac' and self._preferredcodec in ['m4a', 'best']: # Lossless, but in another container acodec = 'copy' extension = 'm4a' - more_opts = [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc'] + more_opts = ['-bsf:a' if uses_avconv else '-absf', 'aac_adtstoasc'] elif filecodec in ['aac', 'mp3', 'vorbis', 'opus']: # Lossless if possible acodec = 'copy' @@ -134,9 +143,9 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): more_opts = [] if self._preferredquality is not None: if int(self._preferredquality) < 10: - more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality] + more_opts += ['-q:a' if uses_avconv else '-aq', self._preferredquality] else: - more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k'] + more_opts += ['-b:a' if uses_avconv else '-ab', self._preferredquality + 'k'] else: # We convert the audio (lossy) acodec = {'mp3': 'libmp3lame', 'aac': 'aac', 'm4a': 'aac', 'opus': 'opus', 'vorbis': 'libvorbis', 'wav': None}[self._preferredcodec] @@ -145,13 +154,13 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): if self._preferredquality is not None: # The opus codec doesn't support the -aq option if int(self._preferredquality) < 10 and extension != 'opus': - more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality] + more_opts += ['-q:a' if uses_avconv else '-aq', self._preferredquality] else: - more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k'] + more_opts += ['-b:a' if uses_avconv else '-ab', self._preferredquality + 'k'] if self._preferredcodec == 'aac': more_opts += ['-f', 'adts'] if self._preferredcodec == 'm4a': - more_opts += [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc'] + more_opts += ['-bsf:a' if uses_avconv else '-absf', 'aac_adtstoasc'] if self._preferredcodec == 'vorbis': extension = 'ogg' if self._preferredcodec == 'wav': @@ -169,14 +178,14 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)): self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path) else: - self._downloader.to_screen(u'[' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + '] Destination: ' + new_path) + self._downloader.to_screen(u'[' + self._get_executable() + '] Destination: ' + new_path) self.run_ffmpeg(path, new_path, acodec, more_opts) except: etype,e,tb = sys.exc_info() if isinstance(e, AudioConversionError): msg = u'audio conversion failed: ' + e.msg else: - msg = u'error running ' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + msg = u'error running ' + self._get_executable() raise PostProcessingError(msg) # Try to update the date time for extracted audio file. @@ -456,7 +465,11 @@ class FFmpegMetadataPP(FFmpegPostProcessor): filename = info['filepath'] temp_filename = prepend_extension(filename, 'temp') - options = ['-c', 'copy'] + if info['ext'] == u'm4a': + options = ['-vn', '-acodec', 'copy'] + else: + options = ['-c', 'copy'] + for (name, value) in metadata.items(): options.extend(['-metadata', '%s=%s' % (name, value)]) @@ -471,6 +484,21 @@ class FFmpegMergerPP(FFmpegPostProcessor): def run(self, info): filename = info['filepath'] args = ['-c', 'copy'] + self._downloader.to_screen(u'[ffmpeg] Merging formats into "%s"' % filename) self.run_ffmpeg_multiple_files(info['__files_to_merge'], filename, args) return True, info + +class FFmpegAudioFixPP(FFmpegPostProcessor): + def run(self, info): + filename = info['filepath'] + temp_filename = prepend_extension(filename, 'temp') + + options = ['-vn', '-acodec', 'copy'] + self._downloader.to_screen(u'[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