]>
Commit | Line | Data |
---|---|---|
6800d337 | 1 | from __future__ import unicode_literals |
6800d337 | 2 | |
5219cb3e | 3 | from ..downloader import _get_real_downloader |
c43fe026 | 4 | from .fragment import FragmentFD |
5219cb3e | 5 | |
e33baba0 | 6 | from ..compat import compat_urllib_error |
e06632e3 S |
7 | from ..utils import ( |
8 | DownloadError, | |
9 | urljoin, | |
10 | ) | |
453a1617 | 11 | |
6800d337 | 12 | |
c43fe026 | 13 | class DashSegmentsFD(FragmentFD): |
6800d337 YCH |
14 | """ |
15 | Download segments in a DASH manifest | |
16 | """ | |
6800d337 | 17 | |
c43fe026 | 18 | FD_NAME = 'dashsegments' |
5bf3276e | 19 | |
c43fe026 | 20 | def real_download(self, filename, info_dict): |
1141e910 S |
21 | fragment_base_url = info_dict.get('fragment_base_url') |
22 | fragments = info_dict['fragments'][:1] if self.params.get( | |
86f4d14f | 23 | 'test', False) else info_dict['fragments'] |
5bf3276e | 24 | |
5219cb3e | 25 | real_downloader = _get_real_downloader(info_dict, 'frag_urls', self.params, None) |
26 | ||
c43fe026 | 27 | ctx = { |
28 | 'filename': filename, | |
1141e910 | 29 | 'total_frags': len(fragments), |
c43fe026 | 30 | } |
5bf3276e | 31 | |
5219cb3e | 32 | if real_downloader: |
33 | self._prepare_external_frag_download(ctx) | |
34 | else: | |
35 | self._prepare_and_start_frag_download(ctx) | |
6800d337 | 36 | |
e33baba0 | 37 | fragment_retries = self.params.get('fragment_retries', 0) |
25afc2a7 | 38 | skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True) |
e33baba0 | 39 | |
5219cb3e | 40 | fragment_urls = [] |
75a24854 | 41 | frag_index = 0 |
1141e910 | 42 | for i, fragment in enumerate(fragments): |
75a24854 | 43 | frag_index += 1 |
3e0304fe | 44 | if frag_index <= ctx['fragment_index']: |
75a24854 | 45 | continue |
5219cb3e | 46 | fragment_url = fragment.get('url') |
47 | if not fragment_url: | |
48 | assert fragment_base_url | |
49 | fragment_url = urljoin(fragment_base_url, fragment['path']) | |
50 | ||
51 | if real_downloader: | |
52 | fragment_urls.append(fragment_url) | |
53 | continue | |
54 | ||
86f4d14f S |
55 | # In DASH, the first segment contains necessary headers to |
56 | # generate a valid MP4 file, so always abort for the first segment | |
75a24854 | 57 | fatal = i == 0 or not skip_unavailable_fragments |
e33baba0 S |
58 | count = 0 |
59 | while count <= fragment_retries: | |
60 | try: | |
1141e910 | 61 | success, frag_content = self._download_fragment(ctx, fragment_url, info_dict) |
e33baba0 S |
62 | if not success: |
63 | return False | |
75a24854 | 64 | self._append_fragment(ctx, frag_content) |
e33baba0 | 65 | break |
2e99cd30 | 66 | except compat_urllib_error.HTTPError as err: |
e33baba0 S |
67 | # YouTube may often return 404 HTTP error for a fragment causing the |
68 | # whole download to fail. However if the same fragment is immediately | |
843ad179 | 69 | # retried with the same request data this usually succeeds (1-2 attempts |
e33baba0 | 70 | # is usually enough) thus allowing to download the whole file successfully. |
25afc2a7 S |
71 | # To be future-proof we will retry all fragments that fail with any |
72 | # HTTP error. | |
e33baba0 S |
73 | count += 1 |
74 | if count <= fragment_retries: | |
75a24854 | 75 | self.report_retry_fragment(err, frag_index, count, fragment_retries) |
e06632e3 S |
76 | except DownloadError: |
77 | # Don't retry fragment if error occurred during HTTP downloading | |
78 | # itself since it has own retry settings | |
79 | if not fatal: | |
80 | self.report_skip_fragment(frag_index) | |
81 | break | |
82 | raise | |
83 | ||
e33baba0 | 84 | if count > fragment_retries: |
919cf1a6 | 85 | if not fatal: |
75a24854 RA |
86 | self.report_skip_fragment(frag_index) |
87 | continue | |
e33baba0 | 88 | self.report_error('giving up after %s fragment retries' % fragment_retries) |
c43fe026 | 89 | return False |
c43fe026 | 90 | |
5219cb3e | 91 | if real_downloader: |
92 | info_copy = info_dict.copy() | |
93 | info_copy['url_list'] = fragment_urls | |
94 | fd = real_downloader(self.ydl, self.params) | |
95 | # TODO: Make progress updates work without hooking twice | |
96 | # for ph in self._progress_hooks: | |
97 | # fd.add_progress_hook(ph) | |
98 | success = fd.real_download(filename, info_copy) | |
99 | if not success: | |
100 | return False | |
101 | else: | |
102 | self._finish_frag_download(ctx) | |
6800d337 | 103 | return True |