From 12b84ac8c13754baeeead907d8c9d239141f8706 Mon Sep 17 00:00:00 2001
From: remitamine <remitamine@gmail.com>
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 @@ from ..utils import (
 
 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 @@ from __future__ import unicode_literals
 
 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 @@ from ..utils import (
     cli_configuration_args,
     encodeFilename,
     encodeArgument,
+    handle_youtubedl_headers,
 )
 
 
@@ -136,6 +141,66 @@ class HttpieFD(ExternalFD):
             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 <remitamine@gmail.com>
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 @@ from ..utils import (
     encodeFilename,
     encodeArgument,
     handle_youtubedl_headers,
+    check_executable,
 )
 
 
@@ -50,6 +51,10 @@ class ExternalFD(FileDownloader):
     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 @@ class ExternalFD(FileDownloader):
 
 
 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 @@ class CurlFD(ExternalFD):
 
 
 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 @@ class AxelFD(ExternalFD):
 
 
 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 @@ class WgetFD(ExternalFD):
 
 
 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 @@ class Aria2cFD(ExternalFD):
 
 
 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 <remitamine@gmail.com>
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 @@ class FFmpegFD(ExternalFD):
 
         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 <remitamine@gmail.com>
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 sys
 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 @@ class FFmpegFD(ExternalFD):
             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 @@ from ..utils import (
 )
 
 
+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 <remitamine@gmail.com>
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 @@ class HttpieFD(ExternalFD):
 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 <remitamine@gmail.com>
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 @@ class ExternalFD(FileDownloader):
 
     @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 @@ class ExternalFD(FileDownloader):
 
 
 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 @@ class CurlFD(ExternalFD):
 
 
 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 @@ class AxelFD(ExternalFD):
 
 
 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 @@ class WgetFD(ExternalFD):
 
 
 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 <remitamine@gmail.com>
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 @@ class ExternalFD(FileDownloader):
     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 <remitamine@gmail.com>
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 .hls import HlsFD
 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 @@ PROTOCOL_MAP = {
     '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 <remitamine@gmail.com>
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 <remitamine@gmail.com>
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 @@ class HttpieFD(ExternalFD):
 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 <remitamine@gmail.com>
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 @@ class FFmpegFD(ExternalFD):
     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 <remitamine@gmail.com>
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 @@ import re
 
 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 @@ class FFmpegFD(ExternalFD):
 
         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 <remitamine@gmail.com>
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 @@ class FFmpegFD(ExternalFD):
 
         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)]