]>
Commit | Line | Data |
---|---|---|
1 | import time | |
2 | import urllib.parse | |
3 | ||
4 | from . import get_suitable_downloader | |
5 | from .fragment import FragmentFD | |
6 | from ..utils import update_url_query, urljoin | |
7 | ||
8 | ||
9 | class DashSegmentsFD(FragmentFD): | |
10 | """ | |
11 | Download segments in a DASH manifest. External downloaders can take over | |
12 | the fragment downloads by supporting the 'dash_frag_urls' protocol | |
13 | """ | |
14 | ||
15 | FD_NAME = 'dashsegments' | |
16 | ||
17 | def real_download(self, filename, info_dict): | |
18 | if 'http_dash_segments_generator' in info_dict['protocol'].split('+'): | |
19 | real_downloader = None # No external FD can support --live-from-start | |
20 | else: | |
21 | if info_dict.get('is_live'): | |
22 | self.report_error('Live DASH videos are not supported') | |
23 | real_downloader = get_suitable_downloader( | |
24 | info_dict, self.params, None, protocol='dash_frag_urls', to_stdout=(filename == '-')) | |
25 | ||
26 | real_start = time.time() | |
27 | ||
28 | requested_formats = [{**info_dict, **fmt} for fmt in info_dict.get('requested_formats', [])] | |
29 | args = [] | |
30 | for fmt in requested_formats or [info_dict]: | |
31 | try: | |
32 | fragment_count = 1 if self.params.get('test') else len(fmt['fragments']) | |
33 | except TypeError: | |
34 | fragment_count = None | |
35 | ctx = { | |
36 | 'filename': fmt.get('filepath') or filename, | |
37 | 'live': 'is_from_start' if fmt.get('is_from_start') else fmt.get('is_live'), | |
38 | 'total_frags': fragment_count, | |
39 | } | |
40 | ||
41 | if real_downloader: | |
42 | self._prepare_external_frag_download(ctx) | |
43 | else: | |
44 | self._prepare_and_start_frag_download(ctx, fmt) | |
45 | ctx['start'] = real_start | |
46 | ||
47 | extra_query = None | |
48 | extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url') | |
49 | if extra_param_to_segment_url: | |
50 | extra_query = urllib.parse.parse_qs(extra_param_to_segment_url) | |
51 | ||
52 | fragments_to_download = self._get_fragments(fmt, ctx, extra_query) | |
53 | ||
54 | if real_downloader: | |
55 | self.to_screen( | |
56 | f'[{self.FD_NAME}] Fragment downloads will be delegated to {real_downloader.get_basename()}') | |
57 | info_dict['fragments'] = list(fragments_to_download) | |
58 | fd = real_downloader(self.ydl, self.params) | |
59 | return fd.real_download(filename, info_dict) | |
60 | ||
61 | args.append([ctx, fragments_to_download, fmt]) | |
62 | ||
63 | return self.download_and_append_fragments_multiple(*args, is_fatal=lambda idx: idx == 0) | |
64 | ||
65 | def _resolve_fragments(self, fragments, ctx): | |
66 | fragments = fragments(ctx) if callable(fragments) else fragments | |
67 | return [next(iter(fragments))] if self.params.get('test') else fragments | |
68 | ||
69 | def _get_fragments(self, fmt, ctx, extra_query): | |
70 | fragment_base_url = fmt.get('fragment_base_url') | |
71 | fragments = self._resolve_fragments(fmt['fragments'], ctx) | |
72 | ||
73 | frag_index = 0 | |
74 | for i, fragment in enumerate(fragments): | |
75 | frag_index += 1 | |
76 | if frag_index <= ctx['fragment_index']: | |
77 | continue | |
78 | fragment_url = fragment.get('url') | |
79 | if not fragment_url: | |
80 | assert fragment_base_url | |
81 | fragment_url = urljoin(fragment_base_url, fragment['path']) | |
82 | if extra_query: | |
83 | fragment_url = update_url_query(fragment_url, extra_query) | |
84 | ||
85 | yield { | |
86 | 'frag_index': frag_index, | |
87 | 'fragment_count': fragment.get('fragment_count'), | |
88 | 'index': i, | |
89 | 'url': fragment_url, | |
90 | } |