10 from .common
import InfoExtractor
13 class RadLiveIE(InfoExtractor
):
15 _VALID_URL
= r
'https?://(?:www\.)?rad\.live/content/(?P<content_type>feature|episode)/(?P<id>[a-f0-9-]+)'
17 'url': 'https://rad.live/content/feature/dc5acfbc-761b-4bec-9564-df999905116a',
18 'md5': '6219d5d31d52de87d21c9cf5b7cb27ff',
20 'id': 'dc5acfbc-761b-4bec-9564-df999905116a',
22 'title': 'Deathpact - Digital Mirage 2 [Full Set]',
24 'thumbnail': 'https://static.12core.net/cb65ae077a079c68380e38f387fbc438.png',
26 'release_timestamp': 1600185600.0,
27 'channel': 'Proximity',
28 'channel_id': '9ce6dd01-70a4-4d59-afb6-d01f807cd009',
29 'channel_url': 'https://rad.live/content/channel/9ce6dd01-70a4-4d59-afb6-d01f807cd009',
32 'url': 'https://rad.live/content/episode/bbcf66ec-0d02-4ca0-8dc0-4213eb2429bf',
33 'md5': '40b2175f347592125d93e9a344080125',
35 'id': 'bbcf66ec-0d02-4ca0-8dc0-4213eb2429bf',
37 'title': 'E01: Bad Jokes 1',
39 'thumbnail': 'https://lsp.littlstar.com/channels/WHISTLE/BAD_JOKES/SEASON_1/BAD_JOKES_101/poster.jpg',
40 'description': 'Bad Jokes - Champions, Adam Pally, Super Troopers, Team Edge and 2Hype',
41 'release_timestamp': None,
45 'episode': 'E01: Bad Jokes 1',
51 def _real_extract(self
, url
):
52 content_type
, video_id
= self
._match
_valid
_url
(url
).groups()
54 webpage
= self
._download
_webpage
(url
, video_id
)
56 content_info
= json
.loads(self
._search
_regex
(
57 r
'<script[^>]*type=([\'"])application/json\1[^>]*>(?P<json>{.+?})</script>',
58 webpage, 'video info', group='json'))['props']['pageProps']['initialContentData']
59 video_info = content_info[content_type]
62 raise ExtractorError('Unable to extract video info, make sure the URL is valid')
64 formats = self._extract_m3u8_formats(video_info['assets']['videos'][0]['url'], video_id)
66 data = video_info.get('structured_data', {})
68 release_date = unified_timestamp(traverse_obj(data, ('releasedEvent', 'startDate')))
69 channel = next(iter(content_info.get('channels', [])), {})
70 channel_id = channel.get('lrn', '').split(':')[-1] or None
74 'title': video_info['title'],
76 'language': traverse_obj(data, ('potentialAction', 'target', 'inLanguage')),
77 'thumbnail': traverse_obj(data, ('image', 'contentUrl')),
78 'description': data.get('description'),
79 'release_timestamp': release_date,
80 'channel': channel.get('name'),
81 'channel_id': channel_id,
82 'channel_url': format_field(channel_id, None, 'https://rad.live/content/channel/%s'),
85 if content_type == 'episode':
87 # TODO: Get season number when downloading single episode
88 'episode': video_info.get('title'),
89 'episode_number': video_info.get('number'),
90 'episode_id': video_info.get('id'),
96 class RadLiveSeasonIE(RadLiveIE): # XXX: Do not subclass from concrete IE
97 IE_NAME = 'radlive:season'
98 _VALID_URL = r'https?://(?:www\.)?rad\.live/content/season/(?P<id>[a-f0-9-]+)'
100 'url': 'https://rad.live/content/season/08a290f7-c9ef-4e22-9105-c255995a2e75',
101 'md5': '40b2175f347592125d93e9a344080125',
103 'id': '08a290f7-c9ef-4e22-9105-c255995a2e75',
104 'title': 'Bad Jokes - Season 1',
106 'playlist_mincount': 5,
110 def suitable(cls, url):
111 return False if RadLiveIE.suitable(url) else super(RadLiveSeasonIE, cls).suitable(url)
113 def _real_extract(self, url):
114 season_id = self._match_id(url)
115 webpage = self._download_webpage(url, season_id)
117 content_info = json.loads(self._search_regex(
118 r'<script[^>]*type=([\'"])application
/json\
1[^
>]*>(?P
<json
>{.+?}
)</script
>',
119 webpage, 'video info
', group='json
'))['props
']['pageProps
']['initialContentData
']
120 video_info = content_info['season
']
123 '_type
': 'url_transparent
',
124 'id': episode['structured_data
']['url
'].split('/')[-1],
125 'url
': episode['structured_data
']['url
'],
126 'series
': try_get(content_info, lambda x: x['series
']['title
']),
127 'season
': video_info['title
'],
128 'season_number
': video_info.get('number
'),
129 'season_id
': video_info.get('id'),
130 'ie_key
': RadLiveIE.ie_key(),
131 } for episode in video_info['episodes
']]
133 return self.playlist_result(entries, season_id, video_info.get('title
'))
136 class RadLiveChannelIE(RadLiveIE): # XXX: Do not subclass from concrete IE
137 IE_NAME = 'radlive
:channel
'
138 _VALID_URL = r'https?
://(?
:www\
.)?rad\
.live
/content
/channel
/(?P
<id>[a
-f0
-9-]+)'
140 'url
': 'https
://rad
.live
/content
/channel
/5c4d8df4
-6fa0
-413c
-81e3
-873479b49274
',
141 'md5
': '625156a08b7f2b0b849f234e664457ac
',
143 'id': '5c4d8df4
-6fa0
-413c
-81e3
-873479b49274
',
144 'title
': 'Whistle Sports
',
146 'playlist_mincount
': 7,
150 query WebChannelListing ($lrn: ID!) {
160 def suitable(cls, url):
161 return False if RadLiveIE.suitable(url) else super(RadLiveChannelIE, cls).suitable(url)
163 def _real_extract(self, url):
164 channel_id = self._match_id(url)
166 graphql = self._download_json(
167 'https
://content
.mhq
.12core
.net
/graphql
', channel_id,
168 headers={'Content-Type': 'application/json'},
170 'query
': self._QUERY,
171 'variables
': {'lrn': f'lrn:12core:media:content:channel:{channel_id}'}
174 data
= traverse_obj(graphql
, ('data', 'channel'))
176 raise ExtractorError('Unable to extract video info, make sure the URL is valid')
179 '_type': 'url_transparent',
180 'url': feature
['structured_data']['url'],
181 'ie_key': RadLiveIE
.ie_key(),
182 } for feature
in data
['features']]
184 return self
.playlist_result(entries
, channel_id
, data
.get('name'))