Merge remote-tracking branch 'rzhxeo/rtmpdump'
authorPhilipp Hagemeister <phihag@phihag.de>
Mon, 25 Nov 2013 05:16:18 +0000 (06:16 +0100)
committerPhilipp Hagemeister <phihag@phihag.de>
Mon, 25 Nov 2013 05:16:18 +0000 (06:16 +0100)
1  2 
youtube_dl/FileDownloader.py

index 2b4fb0b31322f617fe5139a9ca59700407f8b49c,84a539b825051e268cacd828156b70eeee4a0fab..c6276d1942105bc9a856dd98c0ca6417b827a7da
@@@ -1,3 -1,4 +1,3 @@@
 -import math
  import os
  import re
  import subprocess
@@@ -10,7 -11,6 +10,7 @@@ from .utils import 
      ContentTooShortError,
      determine_ext,
      encodeFilename,
 +    format_bytes,
      sanitize_open,
      timeconvert,
  )
@@@ -53,6 -53,20 +53,6 @@@ class FileDownloader(object)
          self._progress_hooks = []
          self.params = params
  
 -    @staticmethod
 -    def format_bytes(bytes):
 -        if bytes is None:
 -            return 'N/A'
 -        if type(bytes) is str:
 -            bytes = float(bytes)
 -        if bytes == 0.0:
 -            exponent = 0
 -        else:
 -            exponent = int(math.log(bytes, 1024.0))
 -        suffix = ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'][exponent]
 -        converted = float(bytes) / float(1024 ** exponent)
 -        return '%.2f%s' % (converted, suffix)
 -
      @staticmethod
      def format_seconds(seconds):
          (mins, secs) = divmod(seconds, 60)
      def format_speed(speed):
          if speed is None:
              return '%10s' % '---b/s'
 -        return '%10s' % ('%s/s' % FileDownloader.format_bytes(speed))
 +        return '%10s' % ('%s/s' % format_bytes(speed))
  
      @staticmethod
      def best_block_size(elapsed_time, bytes):
                  (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, 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:
              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.to_screen(u'\r[download] File is larger than max-filesize (%s bytes > %s bytes). Aborting.' % (data_len, max_data_len))
                  return False
  
 -        data_len_str = self.format_bytes(data_len)
 +        data_len_str = format_bytes(data_len)
          byte_counter = 0 + resume_len
          block_size = self.params.get('buffersize', 1024)
          start = time.time()