mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-14 07:12:41 +00:00
[ffmpeg] Add --ffmpeg-location
This commit is contained in:
parent
c80b9cd280
commit
73fac4e911
5 changed files with 80 additions and 31 deletions
|
@ -1298,7 +1298,7 @@ class YoutubeDL(object):
|
||||||
downloaded = []
|
downloaded = []
|
||||||
success = True
|
success = True
|
||||||
merger = FFmpegMergerPP(self, not self.params.get('keepvideo'))
|
merger = FFmpegMergerPP(self, not self.params.get('keepvideo'))
|
||||||
if not merger._executable:
|
if not merger.available():
|
||||||
postprocessors = []
|
postprocessors = []
|
||||||
self.report_warning('You have requested multiple '
|
self.report_warning('You have requested multiple '
|
||||||
'formats but ffmpeg or avconv are not installed.'
|
'formats but ffmpeg or avconv are not installed.'
|
||||||
|
@ -1647,7 +1647,7 @@ class YoutubeDL(object):
|
||||||
self._write_string('[debug] Python version %s - %s\n' % (
|
self._write_string('[debug] Python version %s - %s\n' % (
|
||||||
platform.python_version(), platform_name()))
|
platform.python_version(), platform_name()))
|
||||||
|
|
||||||
exe_versions = FFmpegPostProcessor.get_versions()
|
exe_versions = FFmpegPostProcessor.get_versions(self)
|
||||||
exe_versions['rtmpdump'] = rtmpdump_version()
|
exe_versions['rtmpdump'] = rtmpdump_version()
|
||||||
exe_str = ', '.join(
|
exe_str = ', '.join(
|
||||||
'%s %s' % (exe, v)
|
'%s %s' % (exe, v)
|
||||||
|
|
|
@ -350,6 +350,7 @@ def _real_main(argv=None):
|
||||||
'xattr_set_filesize': opts.xattr_set_filesize,
|
'xattr_set_filesize': opts.xattr_set_filesize,
|
||||||
'match_filter': match_filter,
|
'match_filter': match_filter,
|
||||||
'no_color': opts.no_color,
|
'no_color': opts.no_color,
|
||||||
|
'ffmpeg_location': opts.ffmpeg_location,
|
||||||
}
|
}
|
||||||
|
|
||||||
with YoutubeDL(ydl_opts) as ydl:
|
with YoutubeDL(ydl_opts) as ydl:
|
||||||
|
|
|
@ -23,15 +23,14 @@ class HlsFD(FileDownloader):
|
||||||
tmpfilename = self.temp_name(filename)
|
tmpfilename = self.temp_name(filename)
|
||||||
|
|
||||||
ffpp = FFmpegPostProcessor(downloader=self)
|
ffpp = FFmpegPostProcessor(downloader=self)
|
||||||
program = ffpp._executable
|
if not ffpp.available:
|
||||||
if program is None:
|
|
||||||
self.report_error('m3u8 download detected but ffmpeg or avconv could not be found. Please install one.')
|
self.report_error('m3u8 download detected but ffmpeg or avconv could not be found. Please install one.')
|
||||||
return False
|
return False
|
||||||
ffpp.check_version()
|
ffpp.check_version()
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
encodeArgument(opt)
|
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))
|
args.append(encodeFilename(tmpfilename, True))
|
||||||
|
|
||||||
retval = subprocess.call(args)
|
retval = subprocess.call(args)
|
||||||
|
@ -48,7 +47,7 @@ class HlsFD(FileDownloader):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.to_stderr('\n')
|
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
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -735,6 +735,10 @@ def parseOpts(overrideArguments=None):
|
||||||
'--prefer-ffmpeg',
|
'--prefer-ffmpeg',
|
||||||
action='store_true', dest='prefer_ffmpeg',
|
action='store_true', dest='prefer_ffmpeg',
|
||||||
help='Prefer ffmpeg over avconv for running the postprocessors')
|
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(
|
postproc.add_option(
|
||||||
'--exec',
|
'--exec',
|
||||||
metavar='CMD', dest='exec_cmd',
|
metavar='CMD', dest='exec_cmd',
|
||||||
|
|
|
@ -30,54 +30,97 @@ class FFmpegPostProcessorError(PostProcessingError):
|
||||||
class FFmpegPostProcessor(PostProcessor):
|
class FFmpegPostProcessor(PostProcessor):
|
||||||
def __init__(self, downloader=None, deletetempfiles=False):
|
def __init__(self, downloader=None, deletetempfiles=False):
|
||||||
PostProcessor.__init__(self, downloader)
|
PostProcessor.__init__(self, downloader)
|
||||||
self._versions = self.get_versions()
|
|
||||||
self._deletetempfiles = deletetempfiles
|
self._deletetempfiles = deletetempfiles
|
||||||
|
self._determine_executables()
|
||||||
|
|
||||||
def check_version(self):
|
def check_version(self):
|
||||||
if not self._executable:
|
if not self.available():
|
||||||
raise FFmpegPostProcessorError('ffmpeg or avconv not found. Please install one.')
|
raise FFmpegPostProcessorError('ffmpeg or avconv not found. Please install one.')
|
||||||
|
|
||||||
required_version = '10-0' if self._uses_avconv() else '1.0'
|
required_version = '10-0' if self._uses_avconv() else '1.0'
|
||||||
if is_outdated_version(
|
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.' % (
|
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:
|
if self._downloader:
|
||||||
self._downloader.report_warning(warning)
|
self._downloader.report_warning(warning)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_versions():
|
def get_versions(downloader=None):
|
||||||
|
return FFmpegPostProcessor(downloader)._versions
|
||||||
|
|
||||||
|
def _determine_executables(self):
|
||||||
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
|
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
|
||||||
return dict((p, get_exe_version(p, args=['-version'])) for p in programs)
|
prefer_ffmpeg = self._downloader.params.get('prefer_ffmpeg', False)
|
||||||
|
|
||||||
@property
|
self.basename = None
|
||||||
def available(self):
|
self.probe_basename = None
|
||||||
return self._executable is not None
|
|
||||||
|
|
||||||
@property
|
self._paths = None
|
||||||
def _executable(self):
|
self._versions = None
|
||||||
if self._downloader.params.get('prefer_ffmpeg', False):
|
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')
|
prefs = ('ffmpeg', 'avconv')
|
||||||
else:
|
else:
|
||||||
prefs = ('avconv', 'ffmpeg')
|
prefs = ('avconv', 'ffmpeg')
|
||||||
for p in prefs:
|
for p in prefs:
|
||||||
if self._versions[p]:
|
if self._versions[p]:
|
||||||
return p
|
self.basename = p
|
||||||
return None
|
break
|
||||||
|
|
||||||
@property
|
if prefer_ffmpeg:
|
||||||
def _probe_executable(self):
|
|
||||||
if self._downloader.params.get('prefer_ffmpeg', False):
|
|
||||||
prefs = ('ffprobe', 'avprobe')
|
prefs = ('ffprobe', 'avprobe')
|
||||||
else:
|
else:
|
||||||
prefs = ('avprobe', 'ffprobe')
|
prefs = ('avprobe', 'ffprobe')
|
||||||
for p in prefs:
|
for p in prefs:
|
||||||
if self._versions[p]:
|
if self._versions[p]:
|
||||||
return p
|
self.probe_basename = p
|
||||||
return None
|
break
|
||||||
|
|
||||||
|
def available(self):
|
||||||
|
return self.basename is not None
|
||||||
|
|
||||||
def _uses_avconv(self):
|
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):
|
def run_ffmpeg_multiple_files(self, input_paths, out_path, opts):
|
||||||
self.check_version()
|
self.check_version()
|
||||||
|
@ -88,7 +131,7 @@ class FFmpegPostProcessor(PostProcessor):
|
||||||
files_cmd = []
|
files_cmd = []
|
||||||
for path in input_paths:
|
for path in input_paths:
|
||||||
files_cmd.extend([encodeArgument('-i'), encodeFilename(path, True)])
|
files_cmd.extend([encodeArgument('-i'), encodeFilename(path, True)])
|
||||||
cmd = ([encodeFilename(self._executable, True), encodeArgument('-y')] +
|
cmd = ([encodeFilename(self.executable, True), encodeArgument('-y')] +
|
||||||
files_cmd +
|
files_cmd +
|
||||||
[encodeArgument(o) for o in opts] +
|
[encodeArgument(o) for o in opts] +
|
||||||
[encodeFilename(self._ffmpeg_filename_argument(out_path), True)])
|
[encodeFilename(self._ffmpeg_filename_argument(out_path), True)])
|
||||||
|
@ -127,13 +170,15 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
def get_audio_codec(self, path):
|
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.')
|
raise PostProcessingError('ffprobe or avprobe not found. Please install one.')
|
||||||
try:
|
try:
|
||||||
cmd = [
|
cmd = [
|
||||||
encodeFilename(self._probe_executable, True),
|
encodeFilename(self.probe_executable, True),
|
||||||
encodeArgument('-show_streams'),
|
encodeArgument('-show_streams'),
|
||||||
encodeFilename(self._ffmpeg_filename_argument(path), True)]
|
encodeFilename(self._ffmpeg_filename_argument(path), True)]
|
||||||
|
if self._downloader.params.get('verbose', False):
|
||||||
|
self._downloader.to_screen('[debug] ffprobe command line: %s' % shell_quote(cmd))
|
||||||
handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), stdout=subprocess.PIPE)
|
handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), stdout=subprocess.PIPE)
|
||||||
output = handle.communicate()[0]
|
output = handle.communicate()[0]
|
||||||
if handle.wait() != 0:
|
if handle.wait() != 0:
|
||||||
|
@ -223,14 +268,14 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
||||||
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
|
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
|
||||||
self._downloader.to_screen('[youtube] Post-process file %s exists, skipping' % new_path)
|
self._downloader.to_screen('[youtube] Post-process file %s exists, skipping' % new_path)
|
||||||
else:
|
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)
|
self.run_ffmpeg(path, new_path, acodec, more_opts)
|
||||||
except:
|
except:
|
||||||
etype, e, tb = sys.exc_info()
|
etype, e, tb = sys.exc_info()
|
||||||
if isinstance(e, AudioConversionError):
|
if isinstance(e, AudioConversionError):
|
||||||
msg = 'audio conversion failed: ' + e.msg
|
msg = 'audio conversion failed: ' + e.msg
|
||||||
else:
|
else:
|
||||||
msg = 'error running ' + self._executable
|
msg = 'error running ' + self.basename
|
||||||
raise PostProcessingError(msg)
|
raise PostProcessingError(msg)
|
||||||
|
|
||||||
# Try to update the date time for extracted audio file.
|
# Try to update the date time for extracted audio file.
|
||||||
|
|
Loading…
Reference in a new issue