Merge branch 'master' into rtmpdump
authorrzhxeo <rzhxeot7z81b4700@mailcatch.com>
Sun, 17 Nov 2013 23:27:06 +0000 (00:27 +0100)
committerrzhxeo <rzhxeot7z81b4700@mailcatch.com>
Sun, 17 Nov 2013 23:27:06 +0000 (00:27 +0100)
Conflicts:
youtube_dl/FileDownloader.py

Merge

1  2 
youtube_dl/FileDownloader.py

index 664b78662af3676571b35fc5c8c84c82b52be4a9,e5a542ed55d2d86f46b95c51da80bb66c17fa947..84a539b825051e268cacd828156b70eeee4a0fab
@@@ -4,12 -4,16 +4,16 @@@ import r
  import subprocess
  import sys
  import time
- import traceback
  
- if os.name == 'nt':
-     import ctypes
- from .utils import *
+ from .utils import (
+     compat_urllib_error,
+     compat_urllib_request,
+     ContentTooShortError,
+     determine_ext,
+     encodeFilename,
+     sanitize_open,
+     timeconvert,
+ )
  
  
  class FileDownloader(object):
      def to_stderr(self, message):
          self.ydl.to_screen(message)
  
-     def to_cons_title(self, message):
-         """Set console/terminal window title to message."""
-         if not self.params.get('consoletitle', False):
-             return
-         if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow():
-             # c_wchar_p() might not be necessary if `message` is
-             # already of type unicode()
-             ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
-         elif 'TERM' in os.environ:
-             self.to_screen('\033]0;%s\007' % message, skip_eol=True)
+     def to_console_title(self, message):
+         self.ydl.to_console_title(message)
  
      def trouble(self, *args, **kargs):
          self.ydl.trouble(*args, **kargs)
              if old_filename == new_filename:
                  return
              os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
-         except (IOError, OSError) as err:
+         except (IOError, OSError):
              self.report_error(u'unable to rename file')
  
      def try_utime(self, filename, last_modified_hdr):
          if self.params.get('noprogress', False):
              return
          clear_line = (u'\x1b[K' if sys.stderr.isatty() and os.name != 'nt' else u'')
-         eta_str = self.format_eta(eta)
-         percent_str = self.format_percent(percent)
+         if eta is not None:
+             eta_str = self.format_eta(eta)
+         else:
+             eta_str = 'Unknown ETA'
+         if percent is not None:
+             percent_str = self.format_percent(percent)
+         else:
+             percent_str = 'Unknown %'
          speed_str = self.format_speed(speed)
          if self.params.get('progress_with_newline', False):
              self.to_screen(u'[download] %s of %s at %s ETA %s' %
          else:
              self.to_screen(u'\r%s[download] %s of %s at %s ETA %s' %
                  (clear_line, percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
-         self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
+         self.to_console_title(u'youtube-dl - %s of %s at %s ETA %s' %
                  (percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
  
      def report_resuming_byte(self, resume_len):
          """Report file has already been fully downloaded."""
          try:
              self.to_screen(u'[download] %s has already been downloaded' % file_name)
-         except (UnicodeEncodeError) as err:
+         except UnicodeEncodeError:
              self.to_screen(u'[download] The file has already been downloaded')
  
      def report_unable_to_resume(self):
              self.to_screen(u'\r%s[download] 100%% of %s in %s' %
                  (clear_line, data_len_str, self.format_seconds(tot_time)))
  
-     def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path, tc_url):
+     def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path, tc_url, live):
 +        def run_rtmpdump(args):
 +            start = time.time()
 +            resume_percent = None
 +            resume_downloaded_data_len = None
 +            proc = subprocess.Popen(args, stderr=subprocess.PIPE)
 +            cursor_in_new_line = True
 +            proc_stderr_closed = False
 +            while not proc_stderr_closed:
 +                # read line from stderr
 +                line = u''
 +                while True:
 +                    char = proc.stderr.read(1)
 +                    if not char:
 +                        proc_stderr_closed = True
 +                        break
 +                    if char in [b'\r', b'\n']:
 +                        break
 +                    line += char.decode('ascii', 'replace')
 +                if not line:
 +                    # proc_stderr_closed is True
 +                    continue
 +                mobj = re.search(r'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec \(([0-9]{1,2}\.[0-9])%\)', line)
 +                if mobj:
 +                    downloaded_data_len = int(float(mobj.group(1))*1024)
 +                    percent = float(mobj.group(2))
 +                    if not resume_percent:
 +                        resume_percent = percent
 +                        resume_downloaded_data_len = downloaded_data_len
 +                    eta = self.calc_eta(start, time.time(), 100-resume_percent, percent-resume_percent)
 +                    speed = self.calc_speed(start, time.time(), downloaded_data_len-resume_downloaded_data_len)
 +                    data_len = None
 +                    if percent > 0:
 +                        data_len = int(downloaded_data_len * 100 / percent)
 +                    data_len_str = u'~'+self.format_bytes(data_len)
 +                    self.report_progress(percent, data_len_str, speed, eta)
 +                    cursor_in_new_line = False
 +                    self._hook_progress({
 +                        'downloaded_bytes': downloaded_data_len,
 +                        'total_bytes': data_len,
 +                        'tmpfilename': tmpfilename,
 +                        'filename': filename,
 +                        'status': 'downloading',
 +                        'eta': eta,
 +                        'speed': speed,
 +                    })
 +                elif self.params.get('verbose', False):
 +                    if not cursor_in_new_line:
 +                        self.to_screen(u'')
 +                    cursor_in_new_line = True
 +                    self.to_screen(u'[rtmpdump] '+line)
 +            proc.wait()
 +            if not cursor_in_new_line:
 +                self.to_screen(u'')
 +            return proc.returncode
 +
          self.report_destination(filename)
          tmpfilename = self.temp_name(filename)
          test = self.params.get('test', False)
          except (OSError, IOError):
              self.report_error(u'RTMP download detected but "rtmpdump" could not be run')
              return False
 -        verbosity_option = '--verbose' if self.params.get('verbose', False) else '--quiet'
  
          # Download using rtmpdump. rtmpdump returns exit code 2 when
          # the connection was interrumpted and resuming appears to be
          # possible. This is part of rtmpdump's normal usage, AFAIK.
 -        basic_args = ['rtmpdump', verbosity_option, '-r', url, '-o', tmpfilename]
 +        basic_args = ['rtmpdump', '--verbose', '-r', url, '-o', tmpfilename]
          if player_url is not None:
              basic_args += ['--swfVfy', player_url]
          if page_url is not None:
              basic_args += ['--tcUrl', url]
          if test:
              basic_args += ['--stop', '1']
+         if live:
+             basic_args += ['--live']
          args = basic_args + [[], ['--resume', '--skip', '1']][self.params.get('continuedl', False)]
          if self.params.get('verbose', False):
              try:
              except ImportError:
                  shell_quote = repr
              self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args))
 -        retval = subprocess.call(args)
 +
 +        retval = run_rtmpdump(args)
 +
          while (retval == 2 or retval == 1) and not test:
              prevsize = os.path.getsize(encodeFilename(tmpfilename))
 -            self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True)
 +            self.to_screen(u'[rtmpdump] %s bytes' % prevsize)
              time.sleep(5.0) # This seems to be needed
 -            retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1])
 +            retval = run_rtmpdump(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1])
              cursize = os.path.getsize(encodeFilename(tmpfilename))
              if prevsize == cursize and retval == 1:
                  break
               # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those
              if prevsize == cursize and retval == 2 and cursize > 1024:
 -                self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.')
 +                self.to_screen(u'[rtmpdump] Could not download the whole video. This can happen for some advertisements.')
                  retval = 0
                  break
          if retval == 0 or (test and retval == 2):
              fsize = os.path.getsize(encodeFilename(tmpfilename))
 -            self.to_screen(u'\r[rtmpdump] %s bytes' % fsize)
 +            self.to_screen(u'[rtmpdump] %s bytes' % fsize)
              self.try_rename(tmpfilename, filename)
              self._hook_progress({
                  'downloaded_bytes': fsize,
          self.report_destination(filename)
          tmpfilename = self.temp_name(filename)
  
-         args = ['ffmpeg', '-y', '-i', url, '-f', 'mp4', tmpfilename]
-         # Check for ffmpeg first
-         try:
-             subprocess.call(['ffmpeg', '-h'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
-         except (OSError, IOError):
-             self.report_error(u'm3u8 download detected but "%s" could not be run' % args[0] )
-             return False
+         args = ['-y', '-i', url, '-f', 'mp4', '-c', 'copy',
+             '-bsf:a', 'aac_adtstoasc', tmpfilename]
  
-         retval = subprocess.call(args)
+         for program in ['avconv', 'ffmpeg']:
+             try:
+                 subprocess.call([program, '-version'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
+                 break
+             except (OSError, IOError):
+                 pass
+         else:
+             self.report_error(u'm3u8 download detected but ffmpeg or avconv could not be found')
+         cmd = [program] + args
+         retval = subprocess.call(cmd)
          if retval == 0:
              fsize = os.path.getsize(encodeFilename(tmpfilename))
              self.to_screen(u'\r[%s] %s bytes' % (args[0], fsize))
                                                  info_dict.get('player_url', None),
                                                  info_dict.get('page_url', None),
                                                  info_dict.get('play_path', None),
-                                                 info_dict.get('tc_url', None))
+                                                 info_dict.get('tc_url', None),
+                                                 info_dict.get('rtmp_live', False))
  
          # Attempt to download using mplayer
          if url.startswith('mms') or url.startswith('rtsp'):
              # Progress message
              speed = self.calc_speed(start, time.time(), byte_counter - resume_len)
              if data_len is None:
-                 self.report_progress('Unknown %', data_len_str, speed_str, 'Unknown ETA')
-                 eta = None
+                 eta = percent = None
              else:
                  percent = self.calc_percent(byte_counter, data_len)
                  eta = self.calc_eta(start, time.time(), data_len - resume_len, byte_counter - resume_len)
-                 self.report_progress(percent, data_len_str, speed, eta)
+             self.report_progress(percent, data_len_str, speed, eta)
  
              self._hook_progress({
                  'downloaded_bytes': byte_counter,