]>
Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/FileDownloader.py 
b43acd19b42a1b7ad8b4c8329657864b35b365b3
   2  # -*- coding: utf-8 -*-    4  from  __future__ 
import  absolute_import
  21  class  FileDownloader ( object ):   22      """File Downloader class.   24      File downloader objects are the ones responsible of downloading the   25      actual video file and writing it to disk if the user has requested   26      it, among some other tasks. In most cases there should be one per   27      program. As, given a video URL, the downloader doesn't know how to   28      extract all the needed information, task that InfoExtractors do, it   29      has to pass the URL to one of them.   31      For this, file downloader objects have a method that allows   32      InfoExtractors to be registered in a given order. When it is passed   33      a URL, the file downloader handles it to the first InfoExtractor it   34      finds that reports being able to handle it. The InfoExtractor extracts   35      all the information about the video or videos the URL refers to, and   36      asks the FileDownloader to process the video information, possibly   37      downloading the video.   39      File downloaders accept a lot of parameters. In order not to saturate   40      the object constructor with arguments, it receives a dictionary of   41      options instead. These options are available through the params   42      attribute for the InfoExtractors to use. The FileDownloader also   43      registers itself as the downloader in charge for the InfoExtractors   44      that are added to it, so this is a "mutual registration".   48      username:          Username for authentication purposes.   49      password:          Password for authentication purposes.   50      usenetrc:          Use netrc for authentication instead.   51      quiet:             Do not print messages to stdout.   52      forceurl:          Force printing final URL.   53      forcetitle:        Force printing title.   54      forcethumbnail:    Force printing thumbnail URL.   55      forcedescription:  Force printing description.   56      forcefilename:     Force printing final filename.   57      simulate:          Do not download the video files.   58      format:            Video format code.   59      format_limit:      Highest quality format to try.   60      outtmpl:           Template for output names.   61      restrictfilenames: Do not allow "&" and spaces in file names   62      ignoreerrors:      Do not stop on download errors.   63      ratelimit:         Download speed limit, in bytes/sec.   64      nooverwrites:      Prevent overwriting files.   65      retries:           Number of times to retry for HTTP error 5xx   66      buffersize:        Size of download buffer in bytes.   67      noresizebuffer:    Do not automatically resize the download buffer.   68      continuedl:        Try to continue downloads if possible.   69      noprogress:        Do not print the progress bar.   70      playliststart:     Playlist item to start at.   71      playlistend:       Playlist item to end at.   72      matchtitle:        Download only matching titles.   73      rejecttitle:       Reject downloads for matching titles.   74      logtostderr:       Log messages to stderr instead of stdout.   75      consoletitle:      Display progress in console window's titlebar.   76      nopart:            Do not use temporary .part files.   77      updatetime:        Use the Last-modified header to set output file timestamps.   78      writedescription:  Write the video description to a .description file   79      writeinfojson:     Write the video description to a .info.json file   80      writesubtitles:    Write the video subtitles to a .srt file   81      subtitleslang:     Language of the subtitles to download   82      test:              Download only first bytes to test the downloader.   88      _download_retcode 
=  None   92      def  __init__ ( self
,  params
):   93          """Create a FileDownloader object with the given options."""   96          self
._ download
_ retcode 
=  0   97          self
._ num
_ downloads 
=  0   98          self
._ screen
_ file 
= [ sys
. stdout
,  sys
. stderr
][ params
. get ( 'logtostderr' ,  False )]  101          if  ' %(stitle)s '  in  self
. params
[ 'outtmpl' ]:  102              self
. to_stderr ( u
'WARNING:  %(stitle)s  is deprecated. Use the  %(title)s  and the --restrict-filenames flag(which also secures  %(uploader)s  et al) instead.' )  105      def  format_bytes ( bytes ):  108          if  type ( bytes )  is  str :  113              exponent 
=  int ( math
. log ( bytes ,  1024.0 ))  114          suffix 
=  'bkMGTPEZY' [ exponent
]  115          converted 
=  float ( bytes ) /  float ( 1024  **  exponent
)  116          return  '%.2f %s '  % ( converted
,  suffix
)  119      def  calc_percent ( byte_counter
,  data_len
):  122          return  '%6s'  % ( '%3.1f %% '  % ( float ( byte_counter
) /  float ( data_len
) *  100.0 ))  125      def  calc_eta ( start
,  now
,  total
,  current
):  129          if  current 
==  0  or  dif 
<  0.001 :  # One millisecond  131          rate 
=  float ( current
) /  dif
 132          eta 
=  int (( float ( total
) -  float ( current
)) /  rate
)  133          ( eta_mins
,  eta_secs
) =  divmod ( eta
,  60 )  136          return  ' %0 2d: %0 2d'  % ( eta_mins
,  eta_secs
)  139      def  calc_speed ( start
,  now
,  bytes ):  141          if  bytes  ==  0  or  dif 
<  0.001 :  # One millisecond  142              return  '%10s'  %  '---b/s'  143          return  '%10s'  % ( ' %s /s'  %  FileDownloader
. format_bytes ( float ( bytes ) /  dif
))  146      def  best_block_size ( elapsed_time
,  bytes ):  147          new_min 
=  max ( bytes  /  2.0 ,  1.0 )  148          new_max 
=  min ( max ( bytes  *  2.0 ,  1.0 ),  4194304 )  # Do not surpass 4 MB  149          if  elapsed_time 
<  0.001 :  151          rate 
=  bytes  /  elapsed_time
 159      def  parse_bytes ( bytestr
):  160          """Parse a string indicating a byte quantity into an integer."""  161          matchobj 
=  re
. match ( r
'(?i)^(\d+(?:\.\d+)?)([kMGTPEZY]?)$' ,  bytestr
)  164          number 
=  float ( matchobj
. group ( 1 ))  165          multiplier 
=  1024.0  **  'bkmgtpezy' . index ( matchobj
. group ( 2 ). lower ())  166          return  int ( round ( number 
*  multiplier
))  168      def  add_info_extractor ( self
,  ie
):  169          """Add an InfoExtractor object to the end of the list."""  171          ie
. set_downloader ( self
)  173      def  add_post_processor ( self
,  pp
):  174          """Add a PostProcessor object to the end of the chain."""  176          pp
. set_downloader ( self
)  178      def  to_screen ( self
,  message
,  skip_eol
= False ):  179          """Print message to stdout if not in quiet mode."""  180          assert  type ( message
) ==  type ( u
'' )  181          if not  self
. params
. get ( 'quiet' ,  False ):  182              terminator 
= [ u
' \n ' ,  u
'' ][ skip_eol
]  183              output 
=  message 
+  terminator
 184              if  'b'  in  getattr ( self
._ screen
_ file
,  'mode' ,  '' )  or  sys
. version_info
[ 0 ] <  3 :  # Python 2 lies about the mode of sys.stdout/sys.stderr  185                  output 
=  output
. encode ( preferredencoding (),  'ignore' )  186              self
._ screen
_ file
. write ( output
)  187              self
._ screen
_ file
. flush ()  189      def  to_stderr ( self
,  message
):  190          """Print message to stderr."""  191          assert  type ( message
) ==  type ( u
'' )  192          output 
=  message 
+  u
' \n '  193          if  'b'  in  getattr ( self
._ screen
_ file
,  'mode' ,  '' )  or  sys
. version_info
[ 0 ] <  3 :  # Python 2 lies about the mode of sys.stdout/sys.stderr  194              output 
=  output
. encode ( preferredencoding ())  195          sys
. stderr
. write ( output
)  197      def  to_cons_title ( self
,  message
):  198          """Set console/terminal window title to message."""  199          if not  self
. params
. get ( 'consoletitle' ,  False ):  201          if  os
. name 
==  'nt'  and  ctypes
. windll
. kernel32
. GetConsoleWindow ():  202              # c_wchar_p() might not be necessary if `message` is  203              # already of type unicode()  204              ctypes
. windll
. kernel32
. SetConsoleTitleW ( ctypes
. c_wchar_p ( message
))  205          elif  'TERM'  in  os
. environ
:  206              sys
. stderr
. write ( ' \033 ]0; %s \007 '  %  message
. encode ( preferredencoding ()))  208      def  fixed_template ( self
):  209          """Checks if the output template is fixed."""  210          return  ( re
. search ( u
'(?u)% \\ (.+? \\ )s' ,  self
. params
[ 'outtmpl' ])  is None )  212      def  trouble ( self
,  message
= None ):  213          """Determine action to take when a download problem appears.  215          Depending on if the downloader has been configured to ignore  216          download errors or not, this method may throw an exception or  217          not when errors are found, after printing the message.  219          if  message 
is not None :  220              self
. to_stderr ( message
)  221          if  self
. params
. get ( 'verbose' ):  222              self
. to_stderr ( u
'' . join ( traceback
. format_list ( traceback
. extract_stack ())))  223          if not  self
. params
. get ( 'ignoreerrors' ,  False ):  224              raise  DownloadError ( message
)  225          self
._ download
_ retcode 
=  1  227      def  slow_down ( self
,  start_time
,  byte_counter
):  228          """Sleep if the download speed is over the rate limit."""  229          rate_limit 
=  self
. params
. get ( 'ratelimit' ,  None )  230          if  rate_limit 
is None or  byte_counter 
==  0 :  233          elapsed 
=  now 
-  start_time
 236          speed 
=  float ( byte_counter
) /  elapsed
 237          if  speed 
>  rate_limit
:  238              time
. sleep (( byte_counter 
-  rate_limit 
* ( now 
-  start_time
)) /  rate_limit
)  240      def  temp_name ( self
,  filename
):  241          """Returns a temporary filename for the given filename."""  242          if  self
. params
. get ( 'nopart' ,  False )  or  filename 
==  u
'-'  or  \
 243                  ( os
. path
. exists ( encodeFilename ( filename
))  and not  os
. path
. isfile ( encodeFilename ( filename
))):  245          return  filename 
+  u
'.part'  247      def  undo_temp_name ( self
,  filename
):  248          if  filename
. endswith ( u
'.part' ):  249              return  filename
[:- len ( u
'.part' )]  252      def  try_rename ( self
,  old_filename
,  new_filename
):  254              if  old_filename 
==  new_filename
:  256              os
. rename ( encodeFilename ( old_filename
),  encodeFilename ( new_filename
))  257          except  ( IOError ,  OSError )  as  err
:  258              self
. trouble ( u
'ERROR: unable to rename file' )  260      def  try_utime ( self
,  filename
,  last_modified_hdr
):  261          """Try to set the last-modified time of the given file."""  262          if  last_modified_hdr 
is None :  264          if not  os
. path
. isfile ( encodeFilename ( filename
)):  266          timestr 
=  last_modified_hdr
 269          filetime 
=  timeconvert ( timestr
)  273              os
. utime ( filename
, ( time
. time (),  filetime
))  278      def  report_writedescription ( self
,  descfn
):  279          """ Report that the description file is being written """  280          self
. to_screen ( u
'[info] Writing video description to: '  +  descfn
)  282      def  report_writesubtitles ( self
,  srtfn
):  283          """ Report that the subtitles file is being written """  284          self
. to_screen ( u
'[info] Writing video subtitles to: '  +  srtfn
)  286      def  report_writeinfojson ( self
,  infofn
):  287          """ Report that the metadata file has been written """  288          self
. to_screen ( u
'[info] Video description metadata as JSON to: '  +  infofn
)  290      def  report_destination ( self
,  filename
):  291          """Report destination filename."""  292          self
. to_screen ( u
'[download] Destination: '  +  filename
)  294      def  report_progress ( self
,  percent_str
,  data_len_str
,  speed_str
,  eta_str
):  295          """Report download progress."""  296          if  self
. params
. get ( 'noprogress' ,  False ):  298          self
. to_screen ( u
' \r [download]  %s  of  %s  at  %s  ETA  %s '  %  299                  ( percent_str
,  data_len_str
,  speed_str
,  eta_str
),  skip_eol
= True )  300          self
. to_cons_title ( u
'youtube-dl -  %s  of  %s  at  %s  ETA  %s '  %  301                  ( percent_str
. strip (),  data_len_str
. strip (),  speed_str
. strip (),  eta_str
. strip ()))  303      def  report_resuming_byte ( self
,  resume_len
):  304          """Report attempt to resume at given byte."""  305          self
. to_screen ( u
'[download] Resuming download at byte  %s '  %  resume_len
)  307      def  report_retry ( self
,  count
,  retries
):  308          """Report retry in case of HTTP error 5xx"""  309          self
. to_screen ( u
'[download] Got server HTTP error. Retrying (attempt  %d  of  %d )...'  % ( count
,  retries
))  311      def  report_file_already_downloaded ( self
,  file_name
):  312          """Report file has already been fully downloaded."""  314              self
. to_screen ( u
'[download]  %s  has already been downloaded'  %  file_name
)  315          except  ( UnicodeEncodeError )  as  err
:  316              self
. to_screen ( u
'[download] The file has already been downloaded' )  318      def  report_unable_to_resume ( self
):  319          """Report it was impossible to resume download."""  320          self
. to_screen ( u
'[download] Unable to resume' )  322      def  report_finish ( self
):  323          """Report download finished."""  324          if  self
. params
. get ( 'noprogress' ,  False ):  325              self
. to_screen ( u
'[download] Download completed' )  329      def  increment_downloads ( self
):  330          """Increment the ordinal that assigns a number to each file."""  331          self
._ num
_ downloads 
+=  1  333      def  prepare_filename ( self
,  info_dict
):  334          """Generate the output filename."""  336              template_dict 
=  dict ( info_dict
)  338              template_dict
[ 'epoch' ] =  int ( time
. time ())  339              template_dict
[ 'autonumber' ] =  u
' %0 5d'  %  self
._ num
_ downloads
 341              sanitize 
=  lambda  k
, v
:  sanitize_filename (  342                  u
'NA'  if  v 
is None else  compat_str ( v
),  343                  restricted
= self
. params
. get ( 'restrictfilenames' ),  345              template_dict 
=  dict (( k
,  sanitize ( k
,  v
))  for  k
, v 
in  template_dict
. items ())  347              filename 
=  self
. params
[ 'outtmpl' ] %  template_dict
 349          except  ( ValueError ,  KeyError )  as  err
:  350              self
. trouble ( u
'ERROR: invalid system charset or erroneous output template' )  353      def  _match_entry ( self
,  info_dict
):  354          """ Returns None iff the file should be downloaded """  356          title 
=  info_dict
[ 'title' ]  357          matchtitle 
=  self
. params
. get ( 'matchtitle' ,  False )  359              matchtitle 
=  matchtitle
. decode ( 'utf8' )  360              if not  re
. search ( matchtitle
,  title
,  re
. IGNORECASE
):  361                  return  u
'[download] "'  +  title 
+  '" title did not match pattern "'  +  matchtitle 
+  '"'  362          rejecttitle 
=  self
. params
. get ( 'rejecttitle' ,  False )  364              rejecttitle 
=  rejecttitle
. decode ( 'utf8' )  365              if  re
. search ( rejecttitle
,  title
,  re
. IGNORECASE
):  366                  return  u
'"'  +  title 
+  '" title matched reject pattern "'  +  rejecttitle 
+  '"'  369      def  process_info ( self
,  info_dict
):  370          """Process a single dictionary returned by an InfoExtractor."""  372          # Keep for backwards compatibility  373          info_dict
[ 'stitle' ] =  info_dict
[ 'title' ]  375          if not  'format'  in  info_dict
:  376              info_dict
[ 'format' ] =  info_dict
[ 'ext' ]  378          reason 
=  self
._ match
_ entry
( info_dict
)  379          if  reason 
is not None :  380              self
. to_screen ( u
'[download] '  +  reason
)  383          max_downloads 
=  self
. params
. get ( 'max_downloads' )  384          if  max_downloads 
is not None :  385              if  self
._ num
_ downloads 
>  int ( max_downloads
):  386                  raise  MaxDownloadsReached ()  388          filename 
=  self
. prepare_filename ( info_dict
)  391          if  self
. params
. get ( 'forcetitle' ,  False ):  392              compat_print ( info_dict
[ 'title' ])  393          if  self
. params
. get ( 'forceurl' ,  False ):  394              compat_print ( info_dict
[ 'url' ])  395          if  self
. params
. get ( 'forcethumbnail' ,  False )  and  'thumbnail'  in  info_dict
:  396              compat_print ( info_dict
[ 'thumbnail' ])  397          if  self
. params
. get ( 'forcedescription' ,  False )  and  'description'  in  info_dict
:  398              compat_print ( info_dict
[ 'description' ])  399          if  self
. params
. get ( 'forcefilename' ,  False )  and  filename 
is not None :  400              compat_print ( filename
)  401          if  self
. params
. get ( 'forceformat' ,  False ):  402              compat_print ( info_dict
[ 'format' ])  404          # Do nothing else if in simulate mode  405          if  self
. params
. get ( 'simulate' ,  False ):  412              dn 
=  os
. path
. dirname ( encodeFilename ( filename
))  413              if  dn 
!=  ''  and not  os
. path
. exists ( dn
):  # dn is already encoded  415          except  ( OSError ,  IOError )  as  err
:  416              self
. trouble ( u
'ERROR: unable to create directory '  +  compat_str ( err
))  419          if  self
. params
. get ( 'writedescription' ,  False ):  421                  descfn 
=  filename 
+  u
'.description'  422                  self
. report_writedescription ( descfn
)  423                  descfile 
=  open ( encodeFilename ( descfn
),  'wb' )  425                      descfile
. write ( info_dict
[ 'description' ]. encode ( 'utf-8' ))  428              except  ( OSError ,  IOError ):  429                  self
. trouble ( u
'ERROR: Cannot write description file '  +  descfn
)  432          if  self
. params
. get ( 'writesubtitles' ,  False )  and  'subtitles'  in  info_dict 
and  info_dict
[ 'subtitles' ]:  433              # subtitles download errors are already managed as troubles in relevant IE  434              # that way it will silently go on when used with unsupporting IE  436                  srtfn 
=  filename
. rsplit ( '.' ,  1 )[ 0 ] +  u
'.srt'  437                  self
. report_writesubtitles ( srtfn
)  438                  srtfile 
=  open ( encodeFilename ( srtfn
),  'wb' )  440                      srtfile
. write ( info_dict
[ 'subtitles' ]. encode ( 'utf-8' ))  443              except  ( OSError ,  IOError ):  444                  self
. trouble ( u
'ERROR: Cannot write subtitles file '  +  descfn
)  447          if  self
. params
. get ( 'writeinfojson' ,  False ):  448              infofn 
=  filename 
+  u
'.info.json'  449              self
. report_writeinfojson ( infofn
)  452              except  ( NameError , AttributeError ):  453                  self
. trouble ( u
'ERROR: No JSON encoder found. Update to Python 2.6+, setup a json module, or leave out --write-info-json.' )  456                  infof 
=  open ( encodeFilename ( infofn
),  'wb' )  458                      json_info_dict 
=  dict (( k
, v
)  for  k
, v 
in  info_dict
. iteritems ()  if not  k 
in  ( 'urlhandle' ,))  459                      json
. dump ( json_info_dict
,  infof
)  462              except  ( OSError ,  IOError ):  463                  self
. trouble ( u
'ERROR: Cannot write metadata to JSON file '  +  infofn
)  466          if not  self
. params
. get ( 'skip_download' ,  False ):  467              if  self
. params
. get ( 'nooverwrites' ,  False )  and  os
. path
. exists ( encodeFilename ( filename
)):  471                      success 
=  self
._ do
_ download
( filename
,  info_dict
)  472                  except  ( OSError ,  IOError )  as  err
:  473                      raise  UnavailableVideoError ()  474                  except  ( compat_urllib_error
. URLError
,  compat_http_client
. HTTPException
,  socket
. error
)  as  err
:  475                      self
. trouble ( u
'ERROR: unable to download video data:  %s '  %  str ( err
))  477                  except  ( ContentTooShortError
, )  as  err
:  478                      self
. trouble ( u
'ERROR: content too short (expected  %s  bytes and served  %s )'  % ( err
. expected
,  err
. downloaded
))  483                      self
. post_process ( filename
,  info_dict
)  484                  except  ( PostProcessingError
)  as  err
:  485                      self
. trouble ( u
'ERROR: postprocessing:  %s '  %  str ( err
))  488      def  download ( self
,  url_list
):  489          """Download a given list of URLs."""  490          if  len ( url_list
) >  1  and  self
. fixed_template ():  491              raise  SameFileError ( self
. params
[ 'outtmpl' ])  494              suitable_found 
=  False  496                  # Go to next InfoExtractor if not suitable  497                  if not  ie
. suitable ( url
):  500                  # Warn if the _WORKING attribute is False  502                      self
. trouble ( u
'WARNING: the program functionality for this site has been marked as broken, '  503                                   u
'and will probably not work. If you want to go on, use the -i option.' )  505                  # Suitable InfoExtractor found  506                  suitable_found 
=  True  508                  # Extract information from URL and process it  509                  videos 
=  ie
. extract ( url
)  510                  for  video 
in  videos 
or  []:  511                      video
[ 'extractor' ] =  ie
. IE_NAME
 513                          self
. increment_downloads ()  514                          self
. process_info ( video
)  515                      except  UnavailableVideoError
:  516                          self
. trouble ( u
' \n ERROR: unable to download video' )  518                  # Suitable InfoExtractor had been found; go to next URL  521              if not  suitable_found
:  522                  self
. trouble ( u
'ERROR: no suitable InfoExtractor:  %s '  %  url
)  524          return  self
._ download
_ retcode
 526      def  post_process ( self
,  filename
,  ie_info
):  527          """Run the postprocessing chain on the given file."""  529          info
[ 'filepath' ] =  filename
 535      def  _download_with_rtmpdump ( self
,  filename
,  url
,  player_url
):  536          self
. report_destination ( filename
)  537          tmpfilename 
=  self
. temp_name ( filename
)  539          # Check for rtmpdump first  541              subprocess
. call ([ 'rtmpdump' ,  '-h' ],  stdout
=( file ( os
. path
. devnull
,  'w' )),  stderr
= subprocess
. STDOUT
)  542          except  ( OSError ,  IOError ):  543              self
. trouble ( u
'ERROR: RTMP download detected but "rtmpdump" could not be run' )  546          # Download using rtmpdump. rtmpdump returns exit code 2 when  547          # the connection was interrumpted and resuming appears to be  548          # possible. This is part of rtmpdump's normal usage, AFAIK.  549          basic_args 
= [ 'rtmpdump' ,  '-q' ] + [[], [ '-W' ,  player_url
]][ player_url 
is not None ] + [ '-r' ,  url
,  '-o' ,  tmpfilename
]  550          args 
=  basic_args 
+ [[], [ '-e' ,  '-k' ,  '1' ]][ self
. params
. get ( 'continuedl' ,  False )]  551          if  self
. params
. get ( 'verbose' ,  False ):  554                  shell_quote 
=  lambda  args
:  ' ' . join ( map ( pipes
. quote
,  args
))  557              self
. to_screen ( u
'[debug] rtmpdump command line: '  +  shell_quote ( args
))  558          retval 
=  subprocess
. call ( args
)  559          while  retval 
==  2  or  retval 
==  1 :  560              prevsize 
=  os
. path
. getsize ( encodeFilename ( tmpfilename
))  561              self
. to_screen ( u
' \r [rtmpdump]  %s  bytes'  %  prevsize
,  skip_eol
= True )  562              time
. sleep ( 5.0 )  # This seems to be needed  563              retval 
=  subprocess
. call ( basic_args 
+ [ '-e' ] + [[], [ '-k' ,  '1' ]][ retval 
==  1 ])  564              cursize 
=  os
. path
. getsize ( encodeFilename ( tmpfilename
))  565              if  prevsize 
==  cursize 
and  retval 
==  1 :  567               # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those  568              if  prevsize 
==  cursize 
and  retval 
==  2  and  cursize 
>  1024 :  569                  self
. to_screen ( u
' \r [rtmpdump] Could not download the whole video. This can happen for some advertisements.' )  573              self
. to_screen ( u
' \r [rtmpdump]  %s  bytes'  %  os
. path
. getsize ( encodeFilename ( tmpfilename
)))  574              self
. try_rename ( tmpfilename
,  filename
)  577              self
. trouble ( u
' \n ERROR: rtmpdump exited with code  %d '  %  retval
)  580      def  _do_download ( self
,  filename
,  info_dict
):  581          url 
=  info_dict
[ 'url' ]  582          player_url 
=  info_dict
. get ( 'player_url' ,  None )  584          # Check file already present  585          if  self
. params
. get ( 'continuedl' ,  False )  and  os
. path
. isfile ( encodeFilename ( filename
))  and not  self
. params
. get ( 'nopart' ,  False ):  586              self
. report_file_already_downloaded ( filename
)  589          # Attempt to download using rtmpdump  590          if  url
. startswith ( 'rtmp' ):  591              return  self
._ download
_ with
_ rtmpdump
( filename
,  url
,  player_url
)  593          tmpfilename 
=  self
. temp_name ( filename
)  596          # Do not include the Accept-Encoding header  597          headers 
= { 'Youtubedl-no-compression' :  'True' }  598          basic_request 
=  compat_urllib_request
. Request ( url
,  None ,  headers
)  599          request 
=  compat_urllib_request
. Request ( url
,  None ,  headers
)  601          if  self
. params
. get ( 'test' ,  False ):  602              request
. add_header ( 'Range' , 'bytes=0-10240' )  604          # Establish possible resume length  605          if  os
. path
. isfile ( encodeFilename ( tmpfilename
)):  606              resume_len 
=  os
. path
. getsize ( encodeFilename ( tmpfilename
))  612              if  self
. params
. get ( 'continuedl' ,  False ):  613                  self
. report_resuming_byte ( resume_len
)  614                  request
. add_header ( 'Range' , 'bytes= %d- '  %  resume_len
)  620          retries 
=  self
. params
. get ( 'retries' ,  0 )  621          while  count 
<=  retries
:  622              # Establish connection  624                  if  count 
==  0  and  'urlhandle'  in  info_dict
:  625                      data 
=  info_dict
[ 'urlhandle' ]  626                  data 
=  compat_urllib_request
. urlopen ( request
)  628              except  ( compat_urllib_error
. HTTPError
, )  as  err
:  629                  if  ( err
. code 
<  500  or  err
. code 
>=  600 )  and  err
. code 
!=  416 :  630                      # Unexpected HTTP error  632                  elif  err
. code 
==  416 :  633                      # Unable to resume (requested range not satisfiable)  635                          # Open the connection again without the range header  636                          data 
=  compat_urllib_request
. urlopen ( basic_request
)  637                          content_length 
=  data
. info ()[ 'Content-Length' ]  638                      except  ( compat_urllib_error
. HTTPError
, )  as  err
:  639                          if  err
. code 
<  500  or  err
. code 
>=  600 :  642                          # Examine the reported length  643                          if  ( content_length 
is not None and  644                                  ( resume_len 
-  100  <  int ( content_length
) <  resume_len 
+  100 )):  645                              # The file had already been fully downloaded.  646                              # Explanation to the above condition: in issue #175 it was revealed that  647                              # YouTube sometimes adds or removes a few bytes from the end of the file,  648                              # changing the file size slightly and causing problems for some users. So  649                              # I decided to implement a suggested change and consider the file  650                              # completely downloaded if the file size differs less than 100 bytes from  651                              # the one in the hard drive.  652                              self
. report_file_already_downloaded ( filename
)  653                              self
. try_rename ( tmpfilename
,  filename
)  656                              # The length does not match, we start the download over  657                              self
. report_unable_to_resume ()  663                  self
. report_retry ( count
,  retries
)  666              self
. trouble ( u
'ERROR: giving up after  %s  retries'  %  retries
)  669          data_len 
=  data
. info (). get ( 'Content-length' ,  None )  670          if  data_len 
is not None :  671              data_len 
=  int ( data_len
) +  resume_len
 672          data_len_str 
=  self
. format_bytes ( data_len
)  673          byte_counter 
=  0  +  resume_len
 674          block_size 
=  self
. params
. get ( 'buffersize' ,  1024 )  679              data_block 
=  data
. read ( block_size
)  681              if  len ( data_block
) ==  0 :  683              byte_counter 
+=  len ( data_block
)  685              # Open file just in time  688                      ( stream
,  tmpfilename
) =  sanitize_open ( tmpfilename
,  open_mode
)  689                      assert  stream 
is not None  690                      filename 
=  self
. undo_temp_name ( tmpfilename
)  691                      self
. report_destination ( filename
)  692                  except  ( OSError ,  IOError )  as  err
:  693                      self
. trouble ( u
'ERROR: unable to open for writing:  %s '  %  str ( err
))  696                  stream
. write ( data_block
)  697              except  ( IOError ,  OSError )  as  err
:  698                  self
. trouble ( u
' \n ERROR: unable to write data:  %s '  %  str ( err
))  700              if not  self
. params
. get ( 'noresizebuffer' ,  False ):  701                  block_size 
=  self
. best_block_size ( after 
-  before
,  len ( data_block
))  704              speed_str 
=  self
. calc_speed ( start
,  time
. time (),  byte_counter 
-  resume_len
)  706                  self
. report_progress ( 'Unknown %' ,  data_len_str
,  speed_str
,  'Unknown ETA' )  708                  percent_str 
=  self
. calc_percent ( byte_counter
,  data_len
)  709                  eta_str 
=  self
. calc_eta ( start
,  time
. time (),  data_len 
-  resume_len
,  byte_counter 
-  resume_len
)  710                  self
. report_progress ( percent_str
,  data_len_str
,  speed_str
,  eta_str
)  713              self
. slow_down ( start
,  byte_counter 
-  resume_len
)  716              self
. trouble ( u
' \n ERROR: Did not get any data blocks' )  720          if  data_len 
is not None and  byte_counter 
!=  data_len
:  721              raise  ContentTooShortError ( byte_counter
,  int ( data_len
))  722          self
. try_rename ( tmpfilename
,  filename
)  724          # Update file modification time  725          if  self
. params
. get ( 'updatetime' ,  True ):  726              info_dict
[ 'filetime' ] =  self
. try_utime ( filename
,  data
. info (). get ( 'last-modified' ,  None ))