1 from __future__ 
import unicode_literals
 
   9 from .common 
import FileDownloader
 
  18 class RtmpFD(FileDownloader
): 
  19     def real_download(self
, filename
, info_dict
): 
  20         def run_rtmpdump(args
): 
  23             resume_downloaded_data_len 
= None 
  24             proc 
= subprocess
.Popen(args
, stderr
=subprocess
.PIPE
) 
  25             cursor_in_new_line 
= True 
  26             proc_stderr_closed 
= False 
  27             while not proc_stderr_closed
: 
  28                 # read line from stderr 
  31                     char 
= proc
.stderr
.read(1) 
  33                         proc_stderr_closed 
= True 
  35                     if char 
in [b
'\r', b
'\n']: 
  37                     line 
+= char
.decode('ascii', 'replace') 
  39                     # proc_stderr_closed is True 
  41                 mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec \(([0-9]{1,2}\.[0-9])%\)', line
) 
  43                     downloaded_data_len 
= int(float(mobj
.group(1))*1024) 
  44                     percent 
= float(mobj
.group(2)) 
  45                     if not resume_percent
: 
  46                         resume_percent 
= percent
 
  47                         resume_downloaded_data_len 
= downloaded_data_len
 
  48                     eta 
= self
.calc_eta(start
, time
.time(), 100-resume_percent
, percent
-resume_percent
) 
  49                     speed 
= self
.calc_speed(start
, time
.time(), downloaded_data_len
-resume_downloaded_data_len
) 
  52                         data_len 
= int(downloaded_data_len 
* 100 / percent
) 
  53                     data_len_str 
= '~' + format_bytes(data_len
) 
  54                     self
.report_progress(percent
, data_len_str
, speed
, eta
) 
  55                     cursor_in_new_line 
= False 
  57                         'downloaded_bytes': downloaded_data_len
, 
  58                         'total_bytes': data_len
, 
  59                         'tmpfilename': tmpfilename
, 
  61                         'status': 'downloading', 
  66                     # no percent for live streams 
  67                     mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec', line
) 
  69                         downloaded_data_len 
= int(float(mobj
.group(1))*1024) 
  70                         time_now 
= time
.time() 
  71                         speed 
= self
.calc_speed(start
, time_now
, downloaded_data_len
) 
  72                         self
.report_progress_live_stream(downloaded_data_len
, speed
, time_now 
- start
) 
  73                         cursor_in_new_line 
= False 
  75                             'downloaded_bytes': downloaded_data_len
, 
  76                             'tmpfilename': tmpfilename
, 
  78                             'status': 'downloading', 
  81                     elif self
.params
.get('verbose', False): 
  82                         if not cursor_in_new_line
: 
  84                         cursor_in_new_line 
= True 
  85                         self
.to_screen('[rtmpdump] '+line
) 
  87             if not cursor_in_new_line
: 
  89             return proc
.returncode
 
  91         url 
= info_dict
['url'] 
  92         player_url 
= info_dict
.get('player_url', None) 
  93         page_url 
= info_dict
.get('page_url', None) 
  94         app 
= info_dict
.get('app', None) 
  95         play_path 
= info_dict
.get('play_path', None) 
  96         tc_url 
= info_dict
.get('tc_url', None) 
  97         flash_version 
= info_dict
.get('flash_version', None) 
  98         live 
= info_dict
.get('rtmp_live', False) 
  99         conn 
= info_dict
.get('rtmp_conn', None) 
 100         protocol 
= info_dict
.get('rtmp_protocol', None) 
 102         self
.report_destination(filename
) 
 103         tmpfilename 
= self
.temp_name(filename
) 
 104         test 
= self
.params
.get('test', False) 
 106         # Check for rtmpdump first 
 107         if not check_executable('rtmpdump', ['-h']): 
 108             self
.report_error('RTMP download detected but "rtmpdump" could not be run. Please install it.') 
 111         # Download using rtmpdump. rtmpdump returns exit code 2 when 
 112         # the connection was interrumpted and resuming appears to be 
 113         # possible. This is part of rtmpdump's normal usage, AFAIK. 
 114         basic_args 
= ['rtmpdump', '--verbose', '-r', url
, '-o', tmpfilename
] 
 115         if player_url 
is not None: 
 116             basic_args 
+= ['--swfVfy', player_url
] 
 117         if page_url 
is not None: 
 118             basic_args 
+= ['--pageUrl', page_url
] 
 120             basic_args 
+= ['--app', app
] 
 121         if play_path 
is not None: 
 122             basic_args 
+= ['--playpath', play_path
] 
 123         if tc_url 
is not None: 
 124             basic_args 
+= ['--tcUrl', url
] 
 126             basic_args 
+= ['--stop', '1'] 
 127         if flash_version 
is not None: 
 128             basic_args 
+= ['--flashVer', flash_version
] 
 130             basic_args 
+= ['--live'] 
 131         if isinstance(conn
, list): 
 133                 basic_args 
+= ['--conn', entry
] 
 134         elif isinstance(conn
, compat_str
): 
 135             basic_args 
+= ['--conn', conn
] 
 136         if protocol 
is not None: 
 137             basic_args 
+= ['--protocol', protocol
] 
 138         args 
= basic_args 
+ [[], ['--resume', '--skip', '1']][not live 
and self
.params
.get('continuedl', False)] 
 140         if sys
.platform 
== 'win32' and sys
.version_info 
< (3, 0): 
 141             # Windows subprocess module does not actually support Unicode 
 143             # See http://stackoverflow.com/a/9951851/35070 
 144             subprocess_encoding 
= sys
.getfilesystemencoding() 
 145             args 
= [a
.encode(subprocess_encoding
, 'ignore') for a 
in args
] 
 147             subprocess_encoding 
= None 
 149         if self
.params
.get('verbose', False): 
 150             if subprocess_encoding
: 
 152                     a
.decode(subprocess_encoding
) if isinstance(a
, bytes) else a
 
 158                 shell_quote 
= lambda args
: ' '.join(map(pipes
.quote
, str_args
)) 
 161             self
.to_screen('[debug] rtmpdump command line: ' + shell_quote(str_args
)) 
 168         retval 
= run_rtmpdump(args
) 
 170         if retval 
== RD_NO_CONNECT
: 
 171             self
.report_error('[rtmpdump] Could not connect to RTMP server.') 
 174         while (retval 
== RD_INCOMPLETE 
or retval 
== RD_FAILED
) and not test 
and not live
: 
 175             prevsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 176             self
.to_screen('[rtmpdump] %s bytes' % prevsize
) 
 177             time
.sleep(5.0) # This seems to be needed 
 178             retval 
= run_rtmpdump(basic_args 
+ ['-e'] + [[], ['-k', '1']][retval 
== RD_FAILED
]) 
 179             cursize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 180             if prevsize 
== cursize 
and retval 
== RD_FAILED
: 
 182              # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those 
 183             if prevsize 
== cursize 
and retval 
== RD_INCOMPLETE 
and cursize 
> 1024: 
 184                 self
.to_screen('[rtmpdump] Could not download the whole video. This can happen for some advertisements.') 
 187         if retval 
== RD_SUCCESS 
or (test 
and retval 
== RD_INCOMPLETE
): 
 188             fsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 189             self
.to_screen('[rtmpdump] %s bytes' % fsize
) 
 190             self
.try_rename(tmpfilename
, filename
) 
 191             self
._hook
_progress
({ 
 192                 'downloaded_bytes': fsize
, 
 193                 'total_bytes': fsize
, 
 194                 'filename': filename
, 
 195                 'status': 'finished', 
 200             self
.report_error('rtmpdump exited with code %d' % retval
)