1 from __future__ 
import unicode_literals
 
   9 from .common 
import FileDownloader
 
  17 class RtmpFD(FileDownloader
): 
  18     def real_download(self
, filename
, info_dict
): 
  19         def run_rtmpdump(args
): 
  22             resume_downloaded_data_len 
= None 
  23             proc 
= subprocess
.Popen(args
, stderr
=subprocess
.PIPE
) 
  24             cursor_in_new_line 
= True 
  25             proc_stderr_closed 
= False 
  26             while not proc_stderr_closed
: 
  27                 # read line from stderr 
  30                     char 
= proc
.stderr
.read(1) 
  32                         proc_stderr_closed 
= True 
  34                     if char 
in [b
'\r', b
'\n']: 
  36                     line 
+= char
.decode('ascii', 'replace') 
  38                     # proc_stderr_closed is True 
  40                 mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec \(([0-9]{1,2}\.[0-9])%\)', line
) 
  42                     downloaded_data_len 
= int(float(mobj
.group(1))*1024) 
  43                     percent 
= float(mobj
.group(2)) 
  44                     if not resume_percent
: 
  45                         resume_percent 
= percent
 
  46                         resume_downloaded_data_len 
= downloaded_data_len
 
  47                     eta 
= self
.calc_eta(start
, time
.time(), 100-resume_percent
, percent
-resume_percent
) 
  48                     speed 
= self
.calc_speed(start
, time
.time(), downloaded_data_len
-resume_downloaded_data_len
) 
  51                         data_len 
= int(downloaded_data_len 
* 100 / percent
) 
  52                     data_len_str 
= '~' + format_bytes(data_len
) 
  53                     self
.report_progress(percent
, data_len_str
, speed
, eta
) 
  54                     cursor_in_new_line 
= False 
  56                         'downloaded_bytes': downloaded_data_len
, 
  57                         'total_bytes': data_len
, 
  58                         'tmpfilename': tmpfilename
, 
  60                         'status': 'downloading', 
  65                     # no percent for live streams 
  66                     mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec', line
) 
  68                         downloaded_data_len 
= int(float(mobj
.group(1))*1024) 
  69                         time_now 
= time
.time() 
  70                         speed 
= self
.calc_speed(start
, time_now
, downloaded_data_len
) 
  71                         self
.report_progress_live_stream(downloaded_data_len
, speed
, time_now 
- start
) 
  72                         cursor_in_new_line 
= False 
  74                             'downloaded_bytes': downloaded_data_len
, 
  75                             'tmpfilename': tmpfilename
, 
  77                             'status': 'downloading', 
  80                     elif self
.params
.get('verbose', False): 
  81                         if not cursor_in_new_line
: 
  83                         cursor_in_new_line 
= True 
  84                         self
.to_screen('[rtmpdump] '+line
) 
  86             if not cursor_in_new_line
: 
  88             return proc
.returncode
 
  90         url 
= info_dict
['url'] 
  91         player_url 
= info_dict
.get('player_url', None) 
  92         page_url 
= info_dict
.get('page_url', None) 
  93         app 
= info_dict
.get('app', None) 
  94         play_path 
= info_dict
.get('play_path', None) 
  95         tc_url 
= info_dict
.get('tc_url', None) 
  96         flash_version 
= info_dict
.get('flash_version', None) 
  97         live 
= info_dict
.get('rtmp_live', False) 
  98         conn 
= info_dict
.get('rtmp_conn', None) 
  99         protocol 
= info_dict
.get('rtmp_protocol', None) 
 101         self
.report_destination(filename
) 
 102         tmpfilename 
= self
.temp_name(filename
) 
 103         test 
= self
.params
.get('test', False) 
 105         # Check for rtmpdump first 
 107             subprocess
.call(['rtmpdump', '-h'], stdout
=(open(os
.path
.devnull
, 'w')), stderr
=subprocess
.STDOUT
) 
 108         except (OSError, IOError): 
 109             self
.report_error('RTMP download detected but "rtmpdump" could not be run') 
 112         # Download using rtmpdump. rtmpdump returns exit code 2 when 
 113         # the connection was interrumpted and resuming appears to be 
 114         # possible. This is part of rtmpdump's normal usage, AFAIK. 
 115         basic_args 
= ['rtmpdump', '--verbose', '-r', url
, '-o', tmpfilename
] 
 116         if player_url 
is not None: 
 117             basic_args 
+= ['--swfVfy', player_url
] 
 118         if page_url 
is not None: 
 119             basic_args 
+= ['--pageUrl', page_url
] 
 121             basic_args 
+= ['--app', app
] 
 122         if play_path 
is not None: 
 123             basic_args 
+= ['--playpath', play_path
] 
 124         if tc_url 
is not None: 
 125             basic_args 
+= ['--tcUrl', url
] 
 127             basic_args 
+= ['--stop', '1'] 
 128         if flash_version 
is not None: 
 129             basic_args 
+= ['--flashVer', flash_version
] 
 131             basic_args 
+= ['--live'] 
 132         if isinstance(conn
, list): 
 134                 basic_args 
+= ['--conn', entry
] 
 135         elif isinstance(conn
, compat_str
): 
 136             basic_args 
+= ['--conn', conn
] 
 137         if protocol 
is not None: 
 138             basic_args 
+= ['--protocol', protocol
] 
 139         args 
= basic_args 
+ [[], ['--resume', '--skip', '1']][not live 
and self
.params
.get('continuedl', False)] 
 141         if sys
.platform 
== 'win32' and sys
.version_info 
< (3, 0): 
 142             # Windows subprocess module does not actually support Unicode 
 144             # See http://stackoverflow.com/a/9951851/35070 
 145             subprocess_encoding 
= sys
.getfilesystemencoding() 
 146             args 
= [a
.encode(subprocess_encoding
, 'ignore') for a 
in args
] 
 148             subprocess_encoding 
= None 
 150         if self
.params
.get('verbose', False): 
 151             if subprocess_encoding
: 
 153                     a
.decode(subprocess_encoding
) if isinstance(a
, bytes) else a
 
 159                 shell_quote 
= lambda args
: ' '.join(map(pipes
.quote
, str_args
)) 
 162             self
.to_screen('[debug] rtmpdump command line: ' + shell_quote(str_args
)) 
 169         retval 
= run_rtmpdump(args
) 
 171         if retval 
== RD_NO_CONNECT
: 
 172             self
.report_error('[rtmpdump] Could not connect to RTMP server.') 
 175         while (retval 
== RD_INCOMPLETE 
or retval 
== RD_FAILED
) and not test 
and not live
: 
 176             prevsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 177             self
.to_screen('[rtmpdump] %s bytes' % prevsize
) 
 178             time
.sleep(5.0) # This seems to be needed 
 179             retval 
= run_rtmpdump(basic_args 
+ ['-e'] + [[], ['-k', '1']][retval 
== RD_FAILED
]) 
 180             cursize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 181             if prevsize 
== cursize 
and retval 
== RD_FAILED
: 
 183              # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those 
 184             if prevsize 
== cursize 
and retval 
== RD_INCOMPLETE 
and cursize 
> 1024: 
 185                 self
.to_screen('[rtmpdump] Could not download the whole video. This can happen for some advertisements.') 
 188         if retval 
== RD_SUCCESS 
or (test 
and retval 
== RD_INCOMPLETE
): 
 189             fsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 190             self
.to_screen('[rtmpdump] %s bytes' % fsize
) 
 191             self
.try_rename(tmpfilename
, filename
) 
 192             self
._hook
_progress
({ 
 193                 'downloaded_bytes': fsize
, 
 194                 'total_bytes': fsize
, 
 195                 'filename': filename
, 
 196                 'status': 'finished', 
 201             self
.report_error('rtmpdump exited with code %d' % retval
)