diff options
Diffstat (limited to 'youtube_dl/downloader/external.py')
| -rw-r--r-- | youtube_dl/downloader/external.py | 121 | 
1 files changed, 121 insertions, 0 deletions
| diff --git a/youtube_dl/downloader/external.py b/youtube_dl/downloader/external.py index 2bc011266..30277dc20 100644 --- a/youtube_dl/downloader/external.py +++ b/youtube_dl/downloader/external.py @@ -2,8 +2,11 @@ from __future__ import unicode_literals  import os.path  import subprocess +import sys +import re  from .common import FileDownloader +from ..postprocessor.ffmpeg import FFmpegPostProcessor, EXT_TO_OUT_FORMATS  from ..utils import (      cli_option,      cli_valueless_option, @@ -11,6 +14,8 @@ from ..utils import (      cli_configuration_args,      encodeFilename,      encodeArgument, +    handle_youtubedl_headers, +    check_executable,  ) @@ -46,9 +51,17 @@ class ExternalFD(FileDownloader):          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') +    @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) @@ -76,6 +89,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(): @@ -89,6 +104,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(): @@ -99,6 +116,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(): @@ -112,6 +131,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([ @@ -130,12 +151,112 @@ 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():              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') + +    @classmethod +    def available(cls): +        return FFmpegPostProcessor().available + +    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'] + +        args += self._configuration_args() + +        # 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: +            # [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())] + +        protocol = info_dict.get('protocol') + +        if protocol == 'rtmp': +            player_url = info_dict.get('player_url') +            page_url = info_dict.get('page_url') +            app = info_dict.get('app') +            play_path = info_dict.get('play_path') +            tc_url = info_dict.get('tc_url') +            flash_version = info_dict.get('flash_version') +            live = info_dict.get('rtmp_live', False) +            if player_url is not None: +                args += ['-rtmp_swfverify', player_url] +            if page_url is not None: +                args += ['-rtmp_pageurl', page_url] +            if app is not None: +                args += ['-rtmp_app', app] +            if play_path is not None: +                args += ['-rtmp_playpath', play_path] +            if tc_url is not None: +                args += ['-rtmp_tcurl', tc_url] +            if flash_version is not None: +                args += ['-rtmp_flashver', flash_version] +            if live: +                args += ['-rtmp_live', 'live'] + +        args += ['-i', url, '-c', 'copy'] +        if protocol == 'm3u8': +            if self.params.get('hls_use_mpegts', False): +                args += ['-f', 'mpegts'] +            else: +                args += ['-f', 'mp4', '-bsf:a', 'aac_adtstoasc'] +        elif protocol == 'rtmp': +            args += ['-f', 'flv'] +        else: +            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)) + +        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() | 
