dash.py (3209B)
1 from __future__ import unicode_literals 2 3 from .fragment import FragmentFD 4 from ..compat import compat_urllib_error 5 from ..utils import ( 6 DownloadError, 7 urljoin, 8 ) 9 10 11 class DashSegmentsFD(FragmentFD): 12 """ 13 Download segments in a DASH manifest 14 """ 15 16 FD_NAME = 'dashsegments' 17 18 def real_download(self, filename, info_dict): 19 fragment_base_url = info_dict.get('fragment_base_url') 20 fragments = info_dict['fragments'][:1] if self.params.get( 21 'test', False) else info_dict['fragments'] 22 23 ctx = { 24 'filename': filename, 25 'total_frags': len(fragments), 26 } 27 28 self._prepare_and_start_frag_download(ctx) 29 30 fragment_retries = self.params.get('fragment_retries', 0) 31 skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True) 32 33 frag_index = 0 34 for i, fragment in enumerate(fragments): 35 frag_index += 1 36 if frag_index <= ctx['fragment_index']: 37 continue 38 # In DASH, the first segment contains necessary headers to 39 # generate a valid MP4 file, so always abort for the first segment 40 fatal = i == 0 or not skip_unavailable_fragments 41 count = 0 42 while count <= fragment_retries: 43 try: 44 fragment_url = fragment.get('url') 45 if not fragment_url: 46 assert fragment_base_url 47 fragment_url = urljoin(fragment_base_url, fragment['path']) 48 success, frag_content = self._download_fragment(ctx, fragment_url, info_dict) 49 if not success: 50 return False 51 self._append_fragment(ctx, frag_content) 52 break 53 except compat_urllib_error.HTTPError as err: 54 # YouTube may often return 404 HTTP error for a fragment causing the 55 # whole download to fail. However if the same fragment is immediately 56 # retried with the same request data this usually succeeds (1-2 attempts 57 # is usually enough) thus allowing to download the whole file successfully. 58 # To be future-proof we will retry all fragments that fail with any 59 # HTTP error. 60 count += 1 61 if count <= fragment_retries: 62 self.report_retry_fragment(err, frag_index, count, fragment_retries) 63 except DownloadError: 64 # Don't retry fragment if error occurred during HTTP downloading 65 # itself since it has own retry settings 66 if not fatal: 67 self.report_skip_fragment(frag_index) 68 break 69 raise 70 71 if count > fragment_retries: 72 if not fatal: 73 self.report_skip_fragment(frag_index) 74 continue 75 self.report_error('giving up after %s fragment retries' % fragment_retries) 76 return False 77 78 self._finish_frag_download(ctx) 79 80 return True