1 from __future__ 
import unicode_literals
 
   9 from .common 
import FileDownloader
 
  19 def rtmpdump_version(): 
  20     return get_exe_version( 
  21         'rtmpdump', ['--help'], r
'(?i)RTMPDump\s*v?([0-9a-zA-Z._-]+)') 
  24 class RtmpFD(FileDownloader
): 
  25     def real_download(self
, filename
, info_dict
): 
  26         def run_rtmpdump(args
): 
  29             resume_downloaded_data_len 
= None 
  30             proc 
= subprocess
.Popen(args
, stderr
=subprocess
.PIPE
) 
  31             cursor_in_new_line 
= True 
  32             proc_stderr_closed 
= False 
  33             while not proc_stderr_closed
: 
  34                 # read line from stderr 
  37                     char 
= proc
.stderr
.read(1) 
  39                         proc_stderr_closed 
= True 
  41                     if char 
in [b
'\r', b
'\n']: 
  43                     line 
+= char
.decode('ascii', 'replace') 
  45                     # proc_stderr_closed is True 
  47                 mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec \(([0-9]{1,2}\.[0-9])%\)', line
) 
  49                     downloaded_data_len 
= int(float(mobj
.group(1)) * 1024) 
  50                     percent 
= float(mobj
.group(2)) 
  51                     if not resume_percent
: 
  52                         resume_percent 
= percent
 
  53                         resume_downloaded_data_len 
= downloaded_data_len
 
  54                     eta 
= self
.calc_eta(start
, time
.time(), 100 - resume_percent
, percent 
- resume_percent
) 
  55                     speed 
= self
.calc_speed(start
, time
.time(), downloaded_data_len 
- resume_downloaded_data_len
) 
  58                         data_len 
= int(downloaded_data_len 
* 100 / percent
) 
  59                     data_len_str 
= '~' + format_bytes(data_len
) 
  60                     self
.report_progress(percent
, data_len_str
, speed
, eta
) 
  61                     cursor_in_new_line 
= False 
  63                         'downloaded_bytes': downloaded_data_len
, 
  64                         'total_bytes': data_len
, 
  65                         'tmpfilename': tmpfilename
, 
  67                         'status': 'downloading', 
  72                     # no percent for live streams 
  73                     mobj 
= re
.search(r
'([0-9]+\.[0-9]{3}) kB / [0-9]+\.[0-9]{2} sec', line
) 
  75                         downloaded_data_len 
= int(float(mobj
.group(1)) * 1024) 
  76                         time_now 
= time
.time() 
  77                         speed 
= self
.calc_speed(start
, time_now
, downloaded_data_len
) 
  78                         self
.report_progress_live_stream(downloaded_data_len
, speed
, time_now 
- start
) 
  79                         cursor_in_new_line 
= False 
  81                             'downloaded_bytes': downloaded_data_len
, 
  82                             'tmpfilename': tmpfilename
, 
  84                             'status': 'downloading', 
  87                     elif self
.params
.get('verbose', False): 
  88                         if not cursor_in_new_line
: 
  90                         cursor_in_new_line 
= True 
  91                         self
.to_screen('[rtmpdump] ' + line
) 
  93             if not cursor_in_new_line
: 
  95             return proc
.returncode
 
  97         url 
= info_dict
['url'] 
  98         player_url 
= info_dict
.get('player_url', None) 
  99         page_url 
= info_dict
.get('page_url', None) 
 100         app 
= info_dict
.get('app', None) 
 101         play_path 
= info_dict
.get('play_path', None) 
 102         tc_url 
= info_dict
.get('tc_url', None) 
 103         flash_version 
= info_dict
.get('flash_version', None) 
 104         live 
= info_dict
.get('rtmp_live', False) 
 105         conn 
= info_dict
.get('rtmp_conn', None) 
 106         protocol 
= info_dict
.get('rtmp_protocol', None) 
 108         self
.report_destination(filename
) 
 109         tmpfilename 
= self
.temp_name(filename
) 
 110         test 
= self
.params
.get('test', False) 
 112         # Check for rtmpdump first 
 113         if not check_executable('rtmpdump', ['-h']): 
 114             self
.report_error('RTMP download detected but "rtmpdump" could not be run. Please install it.') 
 117         # Download using rtmpdump. rtmpdump returns exit code 2 when 
 118         # the connection was interrumpted and resuming appears to be 
 119         # possible. This is part of rtmpdump's normal usage, AFAIK. 
 120         basic_args 
= ['rtmpdump', '--verbose', '-r', url
, '-o', tmpfilename
] 
 121         if player_url 
is not None: 
 122             basic_args 
+= ['--swfVfy', player_url
] 
 123         if page_url 
is not None: 
 124             basic_args 
+= ['--pageUrl', page_url
] 
 126             basic_args 
+= ['--app', app
] 
 127         if play_path 
is not None: 
 128             basic_args 
+= ['--playpath', play_path
] 
 129         if tc_url 
is not None: 
 130             basic_args 
+= ['--tcUrl', url
] 
 132             basic_args 
+= ['--stop', '1'] 
 133         if flash_version 
is not None: 
 134             basic_args 
+= ['--flashVer', flash_version
] 
 136             basic_args 
+= ['--live'] 
 137         if isinstance(conn
, list): 
 139                 basic_args 
+= ['--conn', entry
] 
 140         elif isinstance(conn
, compat_str
): 
 141             basic_args 
+= ['--conn', conn
] 
 142         if protocol 
is not None: 
 143             basic_args 
+= ['--protocol', protocol
] 
 144         args 
= basic_args 
+ [[], ['--resume', '--skip', '1']][not live 
and self
.params
.get('continuedl', False)] 
 146         if sys
.platform 
== 'win32' and sys
.version_info 
< (3, 0): 
 147             # Windows subprocess module does not actually support Unicode 
 149             # See http://stackoverflow.com/a/9951851/35070 
 150             subprocess_encoding 
= sys
.getfilesystemencoding() 
 151             args 
= [a
.encode(subprocess_encoding
, 'ignore') for a 
in args
] 
 153             subprocess_encoding 
= None 
 155         if self
.params
.get('verbose', False): 
 156             if subprocess_encoding
: 
 158                     a
.decode(subprocess_encoding
) if isinstance(a
, bytes) else a
 
 164                 shell_quote 
= lambda args
: ' '.join(map(pipes
.quote
, str_args
)) 
 167             self
.to_screen('[debug] rtmpdump command line: ' + shell_quote(str_args
)) 
 174         retval 
= run_rtmpdump(args
) 
 176         if retval 
== RD_NO_CONNECT
: 
 177             self
.report_error('[rtmpdump] Could not connect to RTMP server.') 
 180         while (retval 
== RD_INCOMPLETE 
or retval 
== RD_FAILED
) and not test 
and not live
: 
 181             prevsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 182             self
.to_screen('[rtmpdump] %s bytes' % prevsize
) 
 183             time
.sleep(5.0)  # This seems to be needed 
 184             retval 
= run_rtmpdump(basic_args 
+ ['-e'] + [[], ['-k', '1']][retval 
== RD_FAILED
]) 
 185             cursize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 186             if prevsize 
== cursize 
and retval 
== RD_FAILED
: 
 188              # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those 
 189             if prevsize 
== cursize 
and retval 
== RD_INCOMPLETE 
and cursize 
> 1024: 
 190                 self
.to_screen('[rtmpdump] Could not download the whole video. This can happen for some advertisements.') 
 193         if retval 
== RD_SUCCESS 
or (test 
and retval 
== RD_INCOMPLETE
): 
 194             fsize 
= os
.path
.getsize(encodeFilename(tmpfilename
)) 
 195             self
.to_screen('[rtmpdump] %s bytes' % fsize
) 
 196             self
.try_rename(tmpfilename
, filename
) 
 197             self
._hook
_progress
({ 
 198                 'downloaded_bytes': fsize
, 
 199                 'total_bytes': fsize
, 
 200                 'filename': filename
, 
 201                 'status': 'finished', 
 206             self
.report_error('rtmpdump exited with code %d' % retval
)