From 12b84ac8c13754baeeead907d8c9d239141f8706 Mon Sep 17 00:00:00 2001 From: remitamine Date: Fri, 19 Feb 2016 19:29:24 +0100 Subject: [PATCH 01/13] [downloader/external] Add FFmpegFD(fixes #622) - replace HlsFD and RtspFD - add basic support for downloading part of the video or audio --- youtube_dl/downloader/__init__.py | 20 +++++---- youtube_dl/downloader/external.py | 65 +++++++++++++++++++++++++++ youtube_dl/downloader/hls.py | 74 ++----------------------------- youtube_dl/downloader/rtsp.py | 45 ------------------- 4 files changed, 80 insertions(+), 124 deletions(-) delete mode 100644 youtube_dl/downloader/rtsp.py diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index dccc59212..bb6afb1f8 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -1,14 +1,15 @@ from __future__ import unicode_literals from .common import FileDownloader -from .external import get_external_downloader from .f4m import F4mFD from .hls import HlsFD -from .hls import NativeHlsFD from .http import HttpFD -from .rtsp import RtspFD from .rtmp import RtmpFD from .dash import DashSegmentsFD +from .external import ( + get_external_downloader, + FFmpegFD, +) from ..utils import ( determine_protocol, @@ -16,10 +17,10 @@ PROTOCOL_MAP = { 'rtmp': RtmpFD, - 'm3u8_native': NativeHlsFD, - 'm3u8': HlsFD, - 'mms': RtspFD, - 'rtsp': RtspFD, + 'm3u8_native': HlsFD, + 'm3u8': FFmpegFD, + 'mms': FFmpegFD, + 'rtsp': FFmpegFD, 'f4m': F4mFD, 'http_dash_segments': DashSegmentsFD, } @@ -30,6 +31,9 @@ def get_suitable_downloader(info_dict, params={}): protocol = determine_protocol(info_dict) info_dict['protocol'] = protocol + if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.supports(info_dict): + return FFmpegFD + external_downloader = params.get('external_downloader') if external_downloader is not None: ed = get_external_downloader(external_downloader) @@ -37,7 +41,7 @@ def get_suitable_downloader(info_dict, params={}): return ed if protocol == 'm3u8' and params.get('hls_prefer_native'): - return NativeHlsFD + return HlsFD return PROTOCOL_MAP.get(protocol, HttpFD) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 2bc011266..bb43677b7 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -2,8 +2,12 @@ import os.path import subprocess +import sys +import re from .common import FileDownloader +from ..postprocessor.ffmpeg import FFmpegPostProcessor +from ..compat import compat_str from ..utils import ( cli_option, cli_valueless_option, @@ -11,6 +15,7 @@ cli_configuration_args, encodeFilename, encodeArgument, + handle_youtubedl_headers, ) @@ -136,6 +141,66 @@ def _make_cmd(self, tmpfilename, info_dict): cmd += ['%s:%s' % (key, val)] return cmd + +class FFmpegFD(ExternalFD): + @classmethod + def supports(cls, info_dict): + return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') + + def _call_downloader(self, tmpfilename, info_dict): + url = info_dict['url'] + ffpp = FFmpegPostProcessor(downloader=self) + ffpp.check_version() + + args = [ffpp.executable, '-y'] + + start_time = info_dict.get('start_time', 0) + if start_time: + args += ['-ss', compat_str(start_time)] + end_time = info_dict.get('end_time') + if end_time: + args += ['-t', compat_str(end_time - start_time)] + + if info_dict['http_headers'] and re.match(r'^https?://', url): + # Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv: + # [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header. + headers = handle_youtubedl_headers(info_dict['http_headers']) + args += [ + '-headers', + ''.join('%s: %s\r\n' % (key, val) for key, val in headers.items())] + + args += ['-i', url, '-c', 'copy'] + if info_dict.get('protocol') == 'm3u8': + if self.params.get('hls_use_mpegts', False): + args += ['-f', 'mpegts'] + else: + args += ['-f', 'mp4', '-bsf:a', 'aac_adtstoasc'] + else: + args += ['-f', info_dict['ext']] + + args = [encodeArgument(opt) for opt in args] + args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True)) + + self._debug_cmd(args) + + proc = subprocess.Popen(args, stdin=subprocess.PIPE) + try: + retval = proc.wait() + except KeyboardInterrupt: + # subprocces.run would send the SIGKILL signal to ffmpeg and the + # mp4 file couldn't be played, but if we ask ffmpeg to quit it + # produces a file that is playable (this is mostly useful for live + # streams). Note that Windows is not affected and produces playable + # files (see https://github.com/rg3/youtube-dl/issues/8300). + if sys.platform != 'win32': + proc.communicate(b'q') + raise + return retval + + +class AVconvFD(FFmpegFD): + pass + _BY_NAME = dict( (klass.get_basename(), klass) for name, klass in globals().items() diff --git a/youtube_dl/downloader/hls.py b/youtube_dl/downloader/hls.py index 2a775bf00..a01dac031 100644 --- a/youtube_dl/downloader/hls.py +++ b/youtube_dl/downloader/hls.py @@ -1,87 +1,19 @@ from __future__ import unicode_literals -import os +import os.path import re -import subprocess -import sys -from .common import FileDownloader from .fragment import FragmentFD from ..compat import compat_urlparse -from ..postprocessor.ffmpeg import FFmpegPostProcessor from ..utils import ( - encodeArgument, encodeFilename, sanitize_open, - handle_youtubedl_headers, ) -class HlsFD(FileDownloader): - def real_download(self, filename, info_dict): - url = info_dict['url'] - self.report_destination(filename) - tmpfilename = self.temp_name(filename) - - ffpp = FFmpegPostProcessor(downloader=self) - 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 = [ffpp.executable, '-y'] - - if info_dict['http_headers'] and re.match(r'^https?://', url): - # Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv: - # [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header. - headers = handle_youtubedl_headers(info_dict['http_headers']) - args += [ - '-headers', - ''.join('%s: %s\r\n' % (key, val) for key, val in headers.items())] - - args += ['-i', url, '-c', 'copy'] - if self.params.get('hls_use_mpegts', False): - args += ['-f', 'mpegts'] - else: - args += ['-f', 'mp4', '-bsf:a', 'aac_adtstoasc'] - - args = [encodeArgument(opt) for opt in args] - args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True)) - - self._debug_cmd(args) - - proc = subprocess.Popen(args, stdin=subprocess.PIPE) - try: - retval = proc.wait() - except KeyboardInterrupt: - # subprocces.run would send the SIGKILL signal to ffmpeg and the - # mp4 file couldn't be played, but if we ask ffmpeg to quit it - # produces a file that is playable (this is mostly useful for live - # streams). Note that Windows is not affected and produces playable - # files (see https://github.com/rg3/youtube-dl/issues/8300). - if sys.platform != 'win32': - proc.communicate(b'q') - raise - if retval == 0: - fsize = os.path.getsize(encodeFilename(tmpfilename)) - self.to_screen('\r[%s] %s bytes' % (args[0], fsize)) - self.try_rename(tmpfilename, filename) - self._hook_progress({ - 'downloaded_bytes': fsize, - 'total_bytes': fsize, - 'filename': filename, - 'status': 'finished', - }) - return True - else: - self.to_stderr('\n') - self.report_error('%s exited with code %d' % (ffpp.basename, retval)) - return False - - -class NativeHlsFD(FragmentFD): - """ A more limited implementation that does not require ffmpeg """ +class HlsFD(FragmentFD): + """ A limited implementation that does not require ffmpeg """ FD_NAME = 'hlsnative' diff --git a/youtube_dl/downloader/rtsp.py b/youtube_dl/downloader/rtsp.py deleted file mode 100644 index 3eb29526c..000000000 --- a/youtube_dl/downloader/rtsp.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import unicode_literals - -import os -import subprocess - -from .common import FileDownloader -from ..utils import ( - check_executable, - encodeFilename, -) - - -class RtspFD(FileDownloader): - def real_download(self, filename, info_dict): - url = info_dict['url'] - self.report_destination(filename) - tmpfilename = self.temp_name(filename) - - if check_executable('mplayer', ['-h']): - args = [ - 'mplayer', '-really-quiet', '-vo', 'null', '-vc', 'dummy', - '-dumpstream', '-dumpfile', tmpfilename, url] - elif check_executable('mpv', ['-h']): - args = [ - 'mpv', '-really-quiet', '--vo=null', '--stream-dump=' + tmpfilename, url] - else: - self.report_error('MMS or RTSP download detected but neither "mplayer" nor "mpv" could be run. Please install any.') - return False - - retval = subprocess.call(args) - if retval == 0: - fsize = os.path.getsize(encodeFilename(tmpfilename)) - self.to_screen('\r[%s] %s bytes' % (args[0], fsize)) - self.try_rename(tmpfilename, filename) - self._hook_progress({ - 'downloaded_bytes': fsize, - 'total_bytes': fsize, - 'filename': filename, - 'status': 'finished', - }) - return True - else: - self.to_stderr('\n') - self.report_error('%s exited with code %d' % (args[0], retval)) - return False From 99cbe98ce8617c119c2fb6a567b0e6ef7eae8859 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sat, 20 Feb 2016 07:58:25 +0100 Subject: [PATCH 02/13] [downloader/external] check for external downloaders availability --- youtube_dl/downloader/__init__.py | 4 ++-- youtube_dl/downloader/external.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index bb6afb1f8..67c2840a5 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -31,13 +31,13 @@ def get_suitable_downloader(info_dict, params={}): protocol = determine_protocol(info_dict) info_dict['protocol'] = protocol - if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.supports(info_dict): + if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.available() and FFmpegFD.supports(info_dict): return FFmpegFD external_downloader = params.get('external_downloader') if external_downloader is not None: ed = get_external_downloader(external_downloader) - if ed.supports(info_dict): + if ed.available() and ed.supports(info_dict): return ed if protocol == 'm3u8' and params.get('hls_prefer_native'): diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index bb43677b7..edf85483b 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -16,6 +16,7 @@ encodeFilename, encodeArgument, handle_youtubedl_headers, + check_executable, ) @@ -50,6 +51,10 @@ def get_basename(cls): def exe(self): return self.params.get('external_downloader') + @classmethod + def available(cls): + return check_executable(cls.get_basename(), cls.available_opt) + @classmethod def supports(cls, info_dict): return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps') @@ -81,6 +86,8 @@ def _call_downloader(self, tmpfilename, info_dict): class CurlFD(ExternalFD): + available_opt = ['-V'] + def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '--location', '-o', tmpfilename] for key, val in info_dict['http_headers'].items(): @@ -94,6 +101,8 @@ def _make_cmd(self, tmpfilename, info_dict): class AxelFD(ExternalFD): + available_opt = ['-V'] + def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-o', tmpfilename] for key, val in info_dict['http_headers'].items(): @@ -104,6 +113,8 @@ def _make_cmd(self, tmpfilename, info_dict): class WgetFD(ExternalFD): + available_opt = ['--version'] + def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-O', tmpfilename, '-nv', '--no-cookies'] for key, val in info_dict['http_headers'].items(): @@ -117,6 +128,8 @@ def _make_cmd(self, tmpfilename, info_dict): class Aria2cFD(ExternalFD): + available_opt = ['-v'] + def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-c'] cmd += self._configuration_args([ @@ -135,6 +148,10 @@ def _make_cmd(self, tmpfilename, info_dict): class HttpieFD(ExternalFD): + @classmethod + def available(cls): + return check_executable('http', ['--version']) + def _make_cmd(self, tmpfilename, info_dict): cmd = ['http', '--download', '--output', tmpfilename, info_dict['url']] for key, val in info_dict['http_headers'].items(): @@ -147,6 +164,10 @@ class FFmpegFD(ExternalFD): def supports(cls, info_dict): return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') + @classmethod + def available(cls): + return FFmpegPostProcessor().available + def _call_downloader(self, tmpfilename, info_dict): url = info_dict['url'] ffpp = FFmpegPostProcessor(downloader=self) From f34294fa0c0097cea7f6388d5d691d5a54950491 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sat, 20 Feb 2016 08:06:12 +0100 Subject: [PATCH 03/13] [downloader/external:ffmpegfd] check for None value of start_time --- youtube_dl/downloader/external.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index edf85483b..a4fdf1af8 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -175,7 +175,7 @@ def _call_downloader(self, tmpfilename, info_dict): args = [ffpp.executable, '-y'] - start_time = info_dict.get('start_time', 0) + start_time = info_dict.get('start_time') or 0 if start_time: args += ['-ss', compat_str(start_time)] end_time = info_dict.get('end_time') From a755f82549cb6bffd8ff9545e0b0a4883b7422d2 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 12:15:29 +0100 Subject: [PATCH 04/13] [ffmpeg] convert format ext to ffmpeg output formats codes --- youtube_dl/downloader/external.py | 4 ++-- youtube_dl/postprocessor/ffmpeg.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index a4fdf1af8..fcb956e6c 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -6,7 +6,7 @@ import re from .common import FileDownloader -from ..postprocessor.ffmpeg import FFmpegPostProcessor +from ..postprocessor.ffmpeg import FFmpegPostProcessor, EXT_TO_OUT_FORMATS from ..compat import compat_str from ..utils import ( cli_option, @@ -197,7 +197,7 @@ def _call_downloader(self, tmpfilename, info_dict): else: args += ['-f', 'mp4', '-bsf:a', 'aac_adtstoasc'] else: - args += ['-f', info_dict['ext']] + args += ['-f', EXT_TO_OUT_FORMATS.get(info_dict['ext'], info_dict['ext'])] args = [encodeArgument(opt) for opt in args] args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True)) diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index 380bc6f29..cc7aaeda3 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -25,6 +25,19 @@ ) +EXT_TO_OUT_FORMATS = { + "aac": "adts", + "m4a": "ipod", + "mka": "matroska", + "mkv": "matroska", + "mpg": "mpeg", + "ogv": "ogg", + "ts": "mpegts", + "wma": "asf", + "wmv": "asf", +} + + class FFmpegPostProcessorError(PostProcessingError): pass From 634415ca1791b22ddb8a272266084d8b22199d09 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 12:23:10 +0100 Subject: [PATCH 05/13] [downloader/external] skip FFmpegFD when requesting multiple formats --- youtube_dl/downloader/external.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index fcb956e6c..4e299b4ee 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -162,7 +162,7 @@ def _make_cmd(self, tmpfilename, info_dict): class FFmpegFD(ExternalFD): @classmethod def supports(cls, info_dict): - return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') + return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') and not info_dict.get('requested_formats') @classmethod def available(cls): From 91ee320bfaa279571b27bf44730c8677ff2b3980 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 14:37:45 +0100 Subject: [PATCH 06/13] [downloader/external] wrap available_opt in a list --- youtube_dl/downloader/external.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 4e299b4ee..daedf66de 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -53,7 +53,7 @@ def exe(self): @classmethod def available(cls): - return check_executable(cls.get_basename(), cls.available_opt) + return check_executable(cls.get_basename(), [cls.AVAILABLE_OPT]) @classmethod def supports(cls, info_dict): @@ -86,7 +86,7 @@ def _call_downloader(self, tmpfilename, info_dict): class CurlFD(ExternalFD): - available_opt = ['-V'] + AVAILABLE_OPT = '-V' def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '--location', '-o', tmpfilename] @@ -101,7 +101,7 @@ def _make_cmd(self, tmpfilename, info_dict): class AxelFD(ExternalFD): - available_opt = ['-V'] + AVAILABLE_OPT = '-V' def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-o', tmpfilename] @@ -113,7 +113,7 @@ def _make_cmd(self, tmpfilename, info_dict): class WgetFD(ExternalFD): - available_opt = ['--version'] + AVAILABLE_OPT = '--version' def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-O', tmpfilename, '-nv', '--no-cookies'] @@ -128,7 +128,7 @@ def _make_cmd(self, tmpfilename, info_dict): class Aria2cFD(ExternalFD): - available_opt = ['-v'] + AVAILABLE_OPT = '-v' def _make_cmd(self, tmpfilename, info_dict): cmd = [self.exe, '-c'] From 2cb99ebbd0284f0de4bafd03179653bb9599b080 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 14:53:17 +0100 Subject: [PATCH 07/13] [downloader/external] add can_download mathod for checking downloader availibilty and support --- youtube_dl/downloader/__init__.py | 4 ++-- youtube_dl/downloader/external.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index 67c2840a5..daa5498d4 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -31,13 +31,13 @@ def get_suitable_downloader(info_dict, params={}): protocol = determine_protocol(info_dict) info_dict['protocol'] = protocol - if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.available() and FFmpegFD.supports(info_dict): + if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.can_download(info_dict): return FFmpegFD external_downloader = params.get('external_downloader') if external_downloader is not None: ed = get_external_downloader(external_downloader) - if ed.available() and ed.supports(info_dict): + if ed.can_download(info_dict): return ed if protocol == 'm3u8' and params.get('hls_prefer_native'): diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index daedf66de..fe2a0198c 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -59,6 +59,10 @@ def available(cls): def supports(cls, info_dict): return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps') + @classmethod + def can_download(cls, info_dict): + return cls.available() and cls.supports(info_dict) + def _option(self, command_option, param): return cli_option(self.params, command_option, param) From be24916a7f6af9540076a6bc39bc78a71d6264bb Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 15:24:02 +0100 Subject: [PATCH 08/13] [downloader/rtsp] Add rtsp and mms downloader --- youtube_dl/downloader/__init__.py | 5 ++-- youtube_dl/downloader/rtsp.py | 45 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 youtube_dl/downloader/rtsp.py diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index daa5498d4..0b65aa3e2 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -6,6 +6,7 @@ from .http import HttpFD from .rtmp import RtmpFD from .dash import DashSegmentsFD +from .rtsp import RtspFD from .external import ( get_external_downloader, FFmpegFD, @@ -19,8 +20,8 @@ 'rtmp': RtmpFD, 'm3u8_native': HlsFD, 'm3u8': FFmpegFD, - 'mms': FFmpegFD, - 'rtsp': FFmpegFD, + 'mms': RtspFD, + 'rtsp': RtspFD, 'f4m': F4mFD, 'http_dash_segments': DashSegmentsFD, } diff --git a/youtube_dl/downloader/rtsp.py b/youtube_dl/downloader/rtsp.py new file mode 100644 index 000000000..3eb29526c --- /dev/null +++ b/youtube_dl/downloader/rtsp.py @@ -0,0 +1,45 @@ +from __future__ import unicode_literals + +import os +import subprocess + +from .common import FileDownloader +from ..utils import ( + check_executable, + encodeFilename, +) + + +class RtspFD(FileDownloader): + def real_download(self, filename, info_dict): + url = info_dict['url'] + self.report_destination(filename) + tmpfilename = self.temp_name(filename) + + if check_executable('mplayer', ['-h']): + args = [ + 'mplayer', '-really-quiet', '-vo', 'null', '-vc', 'dummy', + '-dumpstream', '-dumpfile', tmpfilename, url] + elif check_executable('mpv', ['-h']): + args = [ + 'mpv', '-really-quiet', '--vo=null', '--stream-dump=' + tmpfilename, url] + else: + self.report_error('MMS or RTSP download detected but neither "mplayer" nor "mpv" could be run. Please install any.') + return False + + retval = subprocess.call(args) + if retval == 0: + fsize = os.path.getsize(encodeFilename(tmpfilename)) + self.to_screen('\r[%s] %s bytes' % (args[0], fsize)) + self.try_rename(tmpfilename, filename) + self._hook_progress({ + 'downloaded_bytes': fsize, + 'total_bytes': fsize, + 'filename': filename, + 'status': 'finished', + }) + return True + else: + self.to_stderr('\n') + self.report_error('%s exited with code %d' % (args[0], retval)) + return False From da1973a0383ddc8125702e74a88d3a35bbfcf06d Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 16:16:26 +0100 Subject: [PATCH 09/13] [extractor/__init__] disable time range downloading --- youtube_dl/downloader/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index 0b65aa3e2..19471a250 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -32,8 +32,8 @@ def get_suitable_downloader(info_dict, params={}): protocol = determine_protocol(info_dict) info_dict['protocol'] = protocol - if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.can_download(info_dict): - return FFmpegFD + # if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.can_download(info_dict): + # return FFmpegFD external_downloader = params.get('external_downloader') if external_downloader is not None: From 6ae27bed01111ddd3c533fb94ae187306e7cf940 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 20:25:39 +0100 Subject: [PATCH 10/13] [download/external] move the check for multiple selected formats to get_suitable_downloader --- youtube_dl/downloader/__init__.py | 2 +- youtube_dl/downloader/external.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/youtube_dl/downloader/__init__.py b/youtube_dl/downloader/__init__.py index 19471a250..73b34fdae 100644 --- a/youtube_dl/downloader/__init__.py +++ b/youtube_dl/downloader/__init__.py @@ -32,7 +32,7 @@ def get_suitable_downloader(info_dict, params={}): protocol = determine_protocol(info_dict) info_dict['protocol'] = protocol - # if (info_dict.get('start_time') or info_dict.get('end_time')) and FFmpegFD.can_download(info_dict): + # if (info_dict.get('start_time') or info_dict.get('end_time')) and not info_dict.get('requested_formats') and FFmpegFD.can_download(info_dict): # return FFmpegFD external_downloader = params.get('external_downloader') diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index fe2a0198c..85cf834c7 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -166,7 +166,7 @@ def _make_cmd(self, tmpfilename, info_dict): class FFmpegFD(ExternalFD): @classmethod def supports(cls, info_dict): - return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') and not info_dict.get('requested_formats') + return info_dict['protocol'] in ('http', 'https', 'ftp', 'ftps', 'm3u8', 'rtsp', 'rtmp', 'mms') @classmethod def available(cls): From 77dea16ac8bb38d735ac6f002eaba7570a67d361 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 20:30:23 +0100 Subject: [PATCH 11/13] [downloader/external] check for ffmpeg availablity when it used for m3u8 download --- youtube_dl/downloader/external.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 85cf834c7..845a63e93 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -175,6 +175,9 @@ def available(cls): def _call_downloader(self, tmpfilename, info_dict): url = info_dict['url'] ffpp = FFmpegPostProcessor(downloader=self) + 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 = [ffpp.executable, '-y'] From 694c47b2619b9d3698e0bc7bba45bb937f97bd3c Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 21:11:19 +0100 Subject: [PATCH 12/13] [external/downloader] don't pass -t and -ss to ffmpeg --- youtube_dl/downloader/external.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 845a63e93..2ddd76882 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -7,7 +7,6 @@ from .common import FileDownloader from ..postprocessor.ffmpeg import FFmpegPostProcessor, EXT_TO_OUT_FORMATS -from ..compat import compat_str from ..utils import ( cli_option, cli_valueless_option, @@ -182,12 +181,12 @@ def _call_downloader(self, tmpfilename, info_dict): args = [ffpp.executable, '-y'] - start_time = info_dict.get('start_time') or 0 - if start_time: - args += ['-ss', compat_str(start_time)] - end_time = info_dict.get('end_time') - if end_time: - args += ['-t', compat_str(end_time - start_time)] + # start_time = info_dict.get('start_time') or 0 + # if start_time: + # args += ['-ss', compat_str(start_time)] + # end_time = info_dict.get('end_time') + # if end_time: + # args += ['-t', compat_str(end_time - start_time)] if info_dict['http_headers'] and re.match(r'^https?://', url): # Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv: From d8515fd41c504baea129de26d60ca55dd69ae3f8 Mon Sep 17 00:00:00 2001 From: remitamine Date: Sun, 13 Mar 2016 21:13:50 +0100 Subject: [PATCH 13/13] [downloader/external] pass configuration args to ffmpeg --- youtube_dl/downloader/external.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 2ddd76882..697f81e3f 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -181,6 +181,8 @@ def _call_downloader(self, tmpfilename, info_dict): args = [ffpp.executable, '-y'] + args += self._configuration_args() + # start_time = info_dict.get('start_time') or 0 # if start_time: # args += ['-ss', compat_str(start_time)]