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)
65 self._sort_formats(formats)
67 data = video_info.get('structured_data', {})
69 release_date = unified_timestamp(traverse_obj(data, ('releasedEvent', 'startDate')))
70 channel = next(iter(content_info.get('channels', [])), {})
71 channel_id = channel.get('lrn', '').split(':')[-1] or None
75 'title': video_info['title'],
77 'language': traverse_obj(data, ('potentialAction', 'target', 'inLanguage')),
78 'thumbnail': traverse_obj(data, ('image', 'contentUrl')),
79 'description': data.get('description'),
80 'release_timestamp': release_date,
81 'channel': channel.get('name'),
82 'channel_id': channel_id,
83 'channel_url': format_field(channel_id, template='https://rad.live/content/channel/%s'),
86 if content_type == 'episode':
88 # TODO: Get season number when downloading single episode
89 'episode': video_info.get('title'),
90 'episode_number': video_info.get('number'),
91 'episode_id': video_info.get('id'),
97 class RadLiveSeasonIE(RadLiveIE):
98 IE_NAME = 'radlive:season'
99 _VALID_URL = r'https?://(?:www\.)?rad\.live/content/season/(?P<id>[a-f0-9-]+)'
101 'url': 'https://rad.live/content/season/08a290f7-c9ef-4e22-9105-c255995a2e75',
102 'md5': '40b2175f347592125d93e9a344080125',
104 'id': '08a290f7-c9ef-4e22-9105-c255995a2e75',
105 'title': 'Bad Jokes - Season 1',
107 'playlist_mincount': 5,
111 def suitable(cls, url):
112 return False if RadLiveIE.suitable(url) else super(RadLiveSeasonIE, cls).suitable(url)
114 def _real_extract(self, url):
115 season_id = self._match_id(url)
116 webpage = self._download_webpage(url, season_id)
118 content_info = json.loads(self._search_regex(
119 r'<script[^>]*type=([\'"])application
/json\
1[^
>]*>(?P
<json
>{.+?}
)</script
>',
120 webpage, 'video info
', group='json
'))['props
']['pageProps
']['initialContentData
']
121 video_info = content_info['season
']
124 '_type
': 'url_transparent
',
125 'id': episode['structured_data
']['url
'].split('/')[-1],
126 'url
': episode['structured_data
']['url
'],
127 'series
': try_get(content_info, lambda x: x['series
']['title
']),
128 'season
': video_info['title
'],
129 'season_number
': video_info.get('number
'),
130 'season_id
': video_info.get('id'),
131 'ie_key
': RadLiveIE.ie_key(),
132 } for episode in video_info['episodes
']]
134 return self.playlist_result(entries, season_id, video_info.get('title
'))
137 class RadLiveChannelIE(RadLiveIE):
138 IE_NAME = 'radlive
:channel
'
139 _VALID_URL = r'https?
://(?
:www\
.)?rad\
.live
/content
/channel
/(?P
<id>[a
-f0
-9-]+)'
141 'url
': 'https
://rad
.live
/content
/channel
/5c4d8df4
-6fa0
-413c
-81e3
-873479b49274
',
142 'md5
': '625156a08b7f2b0b849f234e664457ac
',
144 'id': '5c4d8df4
-6fa0
-413c
-81e3
-873479b49274
',
145 'title
': 'Whistle Sports
',
147 'playlist_mincount
': 7,
151 query WebChannelListing ($lrn: ID!) {
161 def suitable(cls, url):
162 return False if RadLiveIE.suitable(url) else super(RadLiveChannelIE, cls).suitable(url)
164 def _real_extract(self, url):
165 channel_id = self._match_id(url)
167 graphql = self._download_json(
168 'https
://content
.mhq
.12core
.net
/graphql
', channel_id,
169 headers={'Content-Type': 'application/json'},
171 'query
': self._QUERY,
172 'variables
': {'lrn': f'lrn:12core:media:content:channel:{channel_id}'}
175 data
= traverse_obj(graphql
, ('data', 'channel'))
177 raise ExtractorError('Unable to extract video info, make sure the URL is valid')
180 '_type': 'url_transparent',
181 'url': feature
['structured_data']['url'],
182 'ie_key': RadLiveIE
.ie_key(),
183 } for feature
in data
['features']]
185 return self
.playlist_result(entries
, channel_id
, data
.get('name'))