aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--yt_dlp_plugins/extractor/radiko_mobile.py84
1 files changed, 84 insertions, 0 deletions
diff --git a/yt_dlp_plugins/extractor/radiko_mobile.py b/yt_dlp_plugins/extractor/radiko_mobile.py
new file mode 100644
index 0000000..d3793d0
--- /dev/null
+++ b/yt_dlp_plugins/extractor/radiko_mobile.py
@@ -0,0 +1,84 @@
+import re
+
+from yt_dlp.utils import (
+ clean_html,
+ join_nonempty,
+ traverse_obj,
+ update_url_query
+)
+import yt_dlp_plugins.extractor.radiko_time as rtime
+from yt_dlp_plugins.extractor.radiko import _RadikoBaseIE, RadikoTimeFreeIE
+
+
+class _RadikoMobileBaseIE(_RadikoBaseIE):
+ _MOBILE_USER_AGENT = 'Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Mobile Safari/537.36'
+
+ def _get_nextjs_data(self, url, video_id):
+ html = self._download_webpage(url, video_id, headers={"User-Agent": self._MOBILE_USER_AGENT})
+ data = self._search_nextjs_data(html, video_id)
+ return traverse_obj(data, ("props", "pageProps")), data
+
+ def _get_station_region(self, station):
+ api_url = update_url_query("https://radiko.jp/api/stations/batchGetStations", {"stationId": station})
+ data = self._download_json(api_url, station, note="Getting station regions")
+ return traverse_obj(data, ("stationList", lambda _, v: v["id"] == station, "prefecturesList"), get_all=False)
+
+ _extract_chapters = RadikoTimeFreeIE._extract_chapters
+
+
+class RadikoMobileEventIE(_RadikoMobileBaseIE):
+ _VALID_URL = r"https?://(?:www\.)?radiko\.jp/mobile/events/(?P<id>[0-9-]+$)"
+
+ def _get_programme_meta(self, program, actors=None):
+ if actors is not None:
+ cast = []
+ for actor_id in program.get("actorIdsList"):
+ cast.append(traverse_obj(actors, ("actorsList", lambda _, v: v["key"] == actor_id, "name"), get_all=None))
+ else:
+ cast = traverse_obj(program, (
+ 'performer', {lambda x: re.split(r'[//、 ,,]', x)}, ..., {str.strip})) or None
+
+ start = traverse_obj(program, ("startAt", "seconds"))
+ end = traverse_obj(program, ("endAt", "seconds"))
+
+ return {
+ **traverse_obj(program, {
+ "id": "id",
+ "title": "title",
+ "series": "rSeasonName",
+ "tags": "tagsList",
+ "thumbnail": "imageUrl",
+
+ "channel": "stationName",
+ "channel_id": "stationId",
+ "uploader": "stationName",
+ "uploader_id": "stationId",
+ }),
+ "description": clean_html(join_nonempty("summary", "description", from_dict=program, delim="\n")),
+ "cast": cast,
+ "timestamp": start,
+ "release_timestamp": end,
+ "duration": end - start,
+ }
+
+ def _real_extract(self, url):
+ event_id = self._match_id(url)
+ pageProps, _ = self._get_nextjs_data(url, event_id)
+
+ meta = self._get_programme_meta(pageProps.get("program"), pageProps.get("actors"))
+ station = meta.get("channel_id")
+ start = rtime.RadikoTime.fromtimestamp(meta.get("timestamp"), tz=rtime.JST)
+ end = rtime.RadikoTime.fromtimestamp(meta.get("release_timestamp"), tz=rtime.JST)
+
+ chapters = self._extract_chapters(station, start, end, video_id=meta["id"])
+ area = self._get_station_region(station)[0]
+ auth_data = self._auth(area)
+ formats = self._get_station_formats(station, True, auth_data, start_at=start, end_at=end)
+
+ return {
+ **meta,
+ "chapters": chapters,
+ "formats": formats,
+ "live_status": "was_live",
+ "container": "m4a_dash", # force fixup, AAC-only HLS
+ }