3 from .turner
import TurnerBaseIE
16 class AdultSwimIE(TurnerBaseIE
):
17 _VALID_URL
= r
'https?://(?:www\.)?adultswim\.com/videos/(?P<show_path>[^/?#]+)(?:/(?P<episode_path>[^/?#]+))?'
20 'url': 'http://adultswim.com/videos/rick-and-morty/pilot',
22 'id': 'rQxZvXQ4ROaSOqq-or2Mow',
24 'title': 'Rick and Morty - Pilot',
25 'description': 'Rick moves in with his daughter\'s family and establishes himself as a bad influence on his grandson, Morty.',
26 'timestamp': 1543294800,
27 'upload_date': '20181127',
31 'skip_download': True,
33 'expected_warnings': ['Unable to download f4m manifest'],
35 'url': 'http://www.adultswim.com/videos/tim-and-eric-awesome-show-great-job/dr-steve-brule-for-your-wine/',
37 'id': 'sY3cMUR_TbuE4YmdjzbIcQ',
39 'title': 'Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine',
40 'description': 'Dr. Brule reports live from Wine Country with a special report on wines. \nWatch Tim and Eric Awesome Show Great Job! episode #20, "Embarrassed" on Adult Swim.',
41 'upload_date': '20080124',
42 'timestamp': 1201150800,
46 'skip_download': True,
48 'skip': '404 Not Found',
50 'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
52 'id': 'I0LQFQkaSUaFp8PnAWHhoQ',
54 'title': 'Decker - Inside Decker: A New Hero',
55 'description': 'The guys recap the conclusion of the season. They announce a new hero, take a peek into the Victorville Film Archive and welcome back the talented James Dean.',
56 'timestamp': 1469480460,
57 'upload_date': '20160725',
61 'skip_download': True,
63 'expected_warnings': ['Unable to download f4m manifest'],
65 'url': 'http://www.adultswim.com/videos/attack-on-titan',
67 'id': 'attack-on-titan',
68 'title': 'Attack on Titan',
69 'description': 'md5:41caa9416906d90711e31dc00cb7db7e',
71 'playlist_mincount': 12,
73 'url': 'http://www.adultswim.com/videos/streams/williams-stream',
75 'id': 'd8DEBj7QRfetLsRgFnGEyg',
77 'title': r
're:^Williams Stream \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
78 'description': 'original programming',
82 'skip_download': True,
84 'skip': '404 Not Found',
87 def _real_extract(self
, url
):
88 show_path
, episode_path
= self
._match
_valid
_url
(url
).groups()
89 display_id
= episode_path
or show_path
91 getShowBySlug(slug:"%s") {
94 }''' % show_path
# noqa: UP031
96 query
= query
% '''title
97 getVideoBySlug(slug:"%s") {
111 query
= query
% '''metaDescription
113 videos(first:1000,sort:["episode_number"]) {
121 show_data
= self
._download
_json
(
122 'https://www.adultswim.com/api/search', display_id
,
123 data
=json
.dumps({'query': query
}).encode(),
124 headers
={'Content-Type': 'application/json'})['data']['getShowBySlug']
126 video_data
= show_data
['getVideoBySlug']
127 video_id
= video_data
['_id']
128 episode_title
= title
= video_data
['title']
129 series
= show_data
.get('title')
131 title
= f
'{series} - {title}'
135 'description': strip_or_none(video_data
.get('description')),
136 'duration': float_or_none(video_data
.get('duration')),
139 'age_limit': parse_age_limit(video_data
.get('tvRating')),
140 'thumbnail': video_data
.get('poster'),
141 'timestamp': parse_iso8601(video_data
.get('launchDate')),
143 'season_number': int_or_none(video_data
.get('seasonNumber')),
144 'episode': episode_title
,
145 'episode_number': int_or_none(video_data
.get('episodeNumber')),
148 auth
= video_data
.get('auth')
149 media_id
= video_data
.get('mediaID')
151 info
.update(self
._extract
_ngtv
_info
(media_id
, {
152 # CDN_TOKEN_APP_ID from:
153 # https://d2gg02c3xr550i.cloudfront.net/assets/asvp.e9c8bef24322d060ef87.bundle.js
154 'appId': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6ImFzLXR2ZS1kZXNrdG9wLXB0enQ2bSIsInByb2R1Y3QiOiJ0dmUiLCJuZXR3b3JrIjoiYXMiLCJwbGF0Zm9ybSI6ImRlc2t0b3AiLCJpYXQiOjE1MzI3MDIyNzl9.BzSCk-WYOZ2GMCIaeVb8zWnzhlgnXuJTCu0jGp_VaZE',
157 'site_name': 'AdultSwim',
158 'auth_required': auth
,
162 extract_data
= self
._download
_json
(
163 'https://www.adultswim.com/api/shows/v1/videos/' + video_id
,
164 video_id
, query
={'fields': 'stream'}, fatal
=False) or {}
165 assets
= try_get(extract_data
, lambda x
: x
['data']['video']['stream']['assets'], list) or []
167 asset_url
= asset
.get('url')
170 ext
= determine_ext(asset_url
, mimetype2ext(asset
.get('mime_type')))
172 fmts
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(
173 asset_url
, video_id
, 'mp4', m3u8_id
='hls', fatal
=False)
174 info
['formats'].extend(fmts
)
175 self
._merge
_subtitles
(subs
, target
=info
['subtitles'])
178 # info['formats'].extend(self._extract_f4m_formats(
179 # asset_url, video_id, f4m_id='hds', fatal=False))
180 elif ext
in ('scc', 'ttml', 'vtt'):
181 info
['subtitles'].setdefault('en', []).append({
188 for edge
in show_data
.get('videos', {}).get('edges', []):
189 video
= edge
.get('node') or {}
190 slug
= video
.get('slug')
193 entries
.append(self
.url_result(
194 f
'http://adultswim.com/videos/{show_path}/{slug}',
195 'AdultSwim', video
.get('_id')))
196 return self
.playlist_result(
197 entries
, show_path
, show_data
.get('title'),
198 strip_or_none(show_data
.get('metaDescription')))