diff options
-rw-r--r-- | yt_dlp_plugins/extractor/radiko.py | 7 | ||||
-rw-r--r-- | yt_dlp_plugins/extractor/radiko_hacks.py | 56 |
2 files changed, 45 insertions, 18 deletions
diff --git a/yt_dlp_plugins/extractor/radiko.py b/yt_dlp_plugins/extractor/radiko.py index b20f9bb..e8174c6 100644 --- a/yt_dlp_plugins/extractor/radiko.py +++ b/yt_dlp_plugins/extractor/radiko.py @@ -299,13 +299,16 @@ class _RadikoBaseIE(InfoExtractor): entry_protocol = None if delivered_live and timefree and do_as_live_chunks: - chunks = hacks._generate_as_live_chunks(playlist_url, start_at, end_at) + + chunks_playlist = hacks._generate_as_live_playlist( + self, playlist_url, start_at, end_at, domain, auth_data["token"] + ) formats.append({ "url": playlist_url, # fallback to live for ffmpeg etc "format_id": join_nonempty(domain, "chunked"), "live": False, - "hls_media_playlist_data": hacks._playlist_from_chunks(self, chunks, domain, auth_data["token"]), + "hls_media_playlist_data": chunks_playlist, "preference": preference, "ext": "m4a", }) diff --git a/yt_dlp_plugins/extractor/radiko_hacks.py b/yt_dlp_plugins/extractor/radiko_hacks.py index 7b20654..eb68de1 100644 --- a/yt_dlp_plugins/extractor/radiko_hacks.py +++ b/yt_dlp_plugins/extractor/radiko_hacks.py @@ -1,4 +1,5 @@ import datetime +import re from yt_dlp.extractor.common import InfoExtractor from yt_dlp.utils import ( @@ -9,33 +10,56 @@ from yt_dlp.utils import ( # "hacks" as in great jank/schizo shit that works anyway -def _generate_as_live_chunks(playlist_url, start_at, end_at): - chunks = [] +def _generate_as_live_playlist(self, playlist_url, start_at, end_at, domain, headers={}): + playlist = "" chunk_length = 300 # max the api allows duration = int(end_at.timestamp() - start_at.timestamp()) cursor = 0 + chunk_num = 1 while cursor < duration: chunk_length = min(chunk_length, duration - cursor) + chunk_start = start_at + datetime.timedelta(seconds=cursor) chunk_url = update_url_query(playlist_url, { "seek": chunk_start.timestring(), "l": chunk_length, }) - chunks.append(chunk_url) - cursor += chunk_length - return chunks + chunk_playlist, real_chunk_length = _get_chunk_playlist(self, chunk_url, domain, chunk_num, headers) + + playlist += chunk_playlist + cursor += real_chunk_length + chunk_num += 1 -def _playlist_from_chunks(self, chunks, src_id, headers={}): - playlist = "" - for i, chunk in enumerate(chunks): - i +=1 # for more friendly cli output, it gets reset each loop so it shouldnt effect anything - chunk_id = join_nonempty(src_id, i) - base_format = self._extract_m3u8_formats( - chunk, chunk_id, fatal=False, headers=headers, - note=f"Preparing {src_id} chunk {i}" - ) - m3u8_url = traverse_obj(base_format, (..., "url",), get_all=False) - playlist += self._download_webpage(m3u8_url, chunk_id, note=f"Getting {src_id} chunk {i} fragments") return playlist + +def _get_chunk_playlist(self, chunk_url, src_id, chunk_num, headers={}): + EXTINF_duration = re.compile("^#EXTINF:([\d.]+),", flags=re.MULTILINE) + + playlist = "" + chunk_id = join_nonempty(src_id, chunk_num) + base_format = self._extract_m3u8_formats( + chunk_url, chunk_id, fatal=False, headers=headers, + note=f"Preparing {src_id} chunk {chunk_num}" + ) + m3u8_url = traverse_obj(base_format, (..., "url",), get_all=False) + playlist = self._download_webpage(m3u8_url, chunk_id, note=f"Getting {src_id} chunk {chunk_num} fragments") + + real_duration = 0 + for i in EXTINF_duration.findall(playlist): + real_duration += float(i) + real_duration = round(real_duration) + + # playlists can sometimes be longer than they should + # wowza stream does some strange things + # it goes along fine with every fragment 5s long as normal + # and then during the ad break it does one with a different length (2s here) + # i assume so they have a clean split to do ad insertion in? idk + + # but anyway now the chunks aren't always a clean 5mins long + # and we get a repeated fragment going into the next chunk + + # so to work around this, we track the real duration from the #EXTINF tags + + return playlist, real_duration |