diff options
| author | Antti Ajanki <antti.ajanki@iki.fi> | 2015-02-22 21:03:49 +0200 | 
|---|---|---|
| committer | Antti Ajanki <antti.ajanki@iki.fi> | 2015-02-24 21:22:59 +0200 | 
| commit | c4f8c453ae2f735fc2320856e15e66510d74fd72 (patch) | |
| tree | 77ca1e29cca641311dadf94be1c7615e2c8c2da3 | |
| parent | 6f4ba54079893a09c6aa78fe3420523fb96df858 (diff) | |
[f4m] Refresh fragment list periodically on live streams
| -rw-r--r-- | youtube_dl/downloader/f4m.py | 59 | 
1 files changed, 49 insertions, 10 deletions
| diff --git a/youtube_dl/downloader/f4m.py b/youtube_dl/downloader/f4m.py index 7b8fe8cf5..1df9ebe5b 100644 --- a/youtube_dl/downloader/f4m.py +++ b/youtube_dl/downloader/f4m.py @@ -121,7 +121,8 @@ class FlvReader(io.BytesIO):          self.read_unsigned_int()  # BootstrapinfoVersion          # Profile,Live,Update,Reserved -        self.read(1) +        flags = self.read_unsigned_char() +        live = flags & 0x20 != 0          # time scale          self.read_unsigned_int()          # CurrentMediaTime @@ -160,6 +161,7 @@ class FlvReader(io.BytesIO):          return {              'segments': segments,              'fragments': fragments, +            'live': live,          }      def read_bootstrap_info(self): @@ -182,6 +184,10 @@ def build_fragments_list(boot_info):      for segment, fragments_count in segment_run_table['segment_run']:          for _ in range(fragments_count):              res.append((segment, next(fragments_counter))) + +    if boot_info['live']: +        res = res[-2:] +      return res @@ -246,6 +252,38 @@ class F4mFD(FileDownloader):              self.report_error('Unsupported DRM')          return media +    def _get_bootstrap_from_url(self, bootstrap_url): +        bootstrap = self.ydl.urlopen(bootstrap_url).read() +        return read_bootstrap_info(bootstrap) + +    def _update_live_fragments(self, bootstrap_url, latest_fragment): +        fragments_list = [] +        retries = 30 +        while (not fragments_list) and (retries > 0): +            boot_info = self._get_bootstrap_from_url(bootstrap_url) +            fragments_list = build_fragments_list(boot_info) +            fragments_list = [f for f in fragments_list if f[1] > latest_fragment] +            if not fragments_list: +                # Retry after a while +                time.sleep(5.0) +                retries -= 1 + +        if not fragments_list: +            self.report_error('Failed to update fragments') + +        return fragments_list + +    def _parse_bootstrap_node(self, node, base_url): +        if node.text is None: +            bootstrap_url = compat_urlparse.urljoin( +                base_url, node.attrib['url']) +            boot_info = self._get_bootstrap_from_url(bootstrap_url) +        else: +            bootstrap_url = None +            bootstrap = base64.b64decode(node.text) +            boot_info = read_bootstrap_info(bootstrap) +        return (boot_info, bootstrap_url) +      def real_download(self, filename, info_dict):          man_url = info_dict['url']          requested_bitrate = info_dict.get('tbr') @@ -265,18 +303,13 @@ class F4mFD(FileDownloader):          base_url = compat_urlparse.urljoin(man_url, media.attrib['url'])          bootstrap_node = doc.find(_add_ns('bootstrapInfo')) -        if bootstrap_node.text is None: -            bootstrap_url = compat_urlparse.urljoin( -                base_url, bootstrap_node.attrib['url']) -            bootstrap = self.ydl.urlopen(bootstrap_url).read() -        else: -            bootstrap = base64.b64decode(bootstrap_node.text) +        boot_info, bootstrap_url = self._parse_bootstrap_node(bootstrap_node, base_url) +        live = boot_info['live']          metadata_node = media.find(_add_ns('metadata'))          if metadata_node is not None:              metadata = base64.b64decode(metadata_node.text)          else:              metadata = None -        boot_info = read_bootstrap_info(bootstrap)          fragments_list = build_fragments_list(boot_info)          if self.params.get('test', False): @@ -301,7 +334,8 @@ class F4mFD(FileDownloader):          (dest_stream, tmpfilename) = sanitize_open(tmpfilename, 'wb')          write_flv_header(dest_stream) -        write_metadata_tag(dest_stream, metadata) +        if not live: +            write_metadata_tag(dest_stream, metadata)          # This dict stores the download progress, it's updated by the progress          # hook @@ -348,7 +382,8 @@ class F4mFD(FileDownloader):          http_dl.add_progress_hook(frag_progress_hook)          frags_filenames = [] -        for (seg_i, frag_i) in fragments_list: +        while fragments_list: +            seg_i, frag_i = fragments_list.pop(0)              name = 'Seg%d-Frag%d' % (seg_i, frag_i)              url = base_url + name              if akamai_pv: @@ -367,6 +402,10 @@ class F4mFD(FileDownloader):                          break              frags_filenames.append(frag_filename) +            if not fragments_list and live and bootstrap_url: +                fragments_list = self._update_live_fragments(bootstrap_url, frag_i) +                self.to_screen('Updated available fragments: %d' % len(fragments_list)) +          dest_stream.close()          elapsed = time.time() - start | 
