mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-11-04 08:35:12 +00:00 
			
		
		
		
	[f4m] Refresh fragment list periodically on live streams
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user