]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/adultswim.py
[youtube] Detect DRM better
[yt-dlp.git] / yt_dlp / extractor / adultswim.py
CommitLineData
48fbb100
AMW
1# coding: utf-8
2from __future__ import unicode_literals
3
afb74964 4import json
48fbb100 5
29825140 6from .turner import TurnerBaseIE
3fb2a230 7from ..utils import (
afb74964
RA
8 determine_ext,
9 float_or_none,
3fb2a230 10 int_or_none,
afb74964
RA
11 mimetype2ext,
12 parse_age_limit,
13 parse_iso8601,
3b859145 14 strip_or_none,
afb74964 15 try_get,
3fb2a230 16)
48fbb100 17
5f6a1245 18
29825140 19class AdultSwimIE(TurnerBaseIE):
3b859145 20 _VALID_URL = r'https?://(?:www\.)?adultswim\.com/videos/(?P<show_path>[^/?#]+)(?:/(?P<episode_path>[^/?#]+))?'
71155991 21
22 _TESTS = [{
23 'url': 'http://adultswim.com/videos/rick-and-morty/pilot',
71155991 24 'info_dict': {
11e611a7 25 'id': 'rQxZvXQ4ROaSOqq-or2Mow',
3b859145 26 'ext': 'mp4',
71155991 27 'title': 'Rick and Morty - Pilot',
3b859145 28 'description': 'Rick moves in with his daughter\'s family and establishes himself as a bad influence on his grandson, Morty.',
afb74964
RA
29 'timestamp': 1543294800,
30 'upload_date': '20181127',
58cd7e17 31 },
3b859145
RA
32 'params': {
33 # m3u8 download
34 'skip_download': True,
71155991 35 },
3b859145 36 'expected_warnings': ['Unable to download f4m manifest'],
cc08b11d
NJ
37 }, {
38 'url': 'http://www.adultswim.com/videos/tim-and-eric-awesome-show-great-job/dr-steve-brule-for-your-wine/',
cc08b11d
NJ
39 'info_dict': {
40 'id': 'sY3cMUR_TbuE4YmdjzbIcQ',
3b859145 41 'ext': 'mp4',
cc08b11d 42 'title': 'Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine',
3b859145
RA
43 '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.',
44 'upload_date': '20080124',
45 'timestamp': 1201150800,
cc08b11d 46 },
0f15ad7b 47 'params': {
48 # m3u8 download
49 'skip_download': True,
3b859145 50 },
afb74964 51 'skip': '404 Not Found',
038a5e1a 52 }, {
038a5e1a 53 'url': 'http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/',
038a5e1a
S
54 'info_dict': {
55 'id': 'I0LQFQkaSUaFp8PnAWHhoQ',
56 'ext': 'mp4',
57 'title': 'Decker - Inside Decker: A New Hero',
3b859145
RA
58 '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.',
59 'timestamp': 1469480460,
60 'upload_date': '20160725',
038a5e1a
S
61 },
62 'params': {
63 # m3u8 download
64 'skip_download': True,
29825140
RA
65 },
66 'expected_warnings': ['Unable to download f4m manifest'],
a5a8877f 67 }, {
3b859145
RA
68 'url': 'http://www.adultswim.com/videos/attack-on-titan',
69 'info_dict': {
afb74964 70 'id': 'attack-on-titan',
3b859145 71 'title': 'Attack on Titan',
afb74964 72 'description': 'md5:41caa9416906d90711e31dc00cb7db7e',
3b859145
RA
73 },
74 'playlist_mincount': 12,
75 }, {
76 'url': 'http://www.adultswim.com/videos/streams/williams-stream',
a5a8877f 77 'info_dict': {
3b859145
RA
78 'id': 'd8DEBj7QRfetLsRgFnGEyg',
79 'ext': 'mp4',
80 'title': r're:^Williams Stream \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
81 'description': 'original programming',
a5a8877f 82 },
a5a8877f
YCH
83 'params': {
84 # m3u8 download
85 'skip_download': True,
86 },
afb74964 87 'skip': '404 Not Found',
71155991 88 }]
89
48fbb100 90 def _real_extract(self, url):
5ad28e7f 91 show_path, episode_path = self._match_valid_url(url).groups()
3b859145 92 display_id = episode_path or show_path
afb74964
RA
93 query = '''query {
94 getShowBySlug(slug:"%s") {
95 %%s
96 }
97}''' % show_path
98 if episode_path:
99 query = query % '''title
100 getVideoBySlug(slug:"%s") {
101 _id
102 auth
103 description
104 duration
105 episodeNumber
106 launchDate
107 mediaID
108 seasonNumber
109 poster
110 title
111 tvRating
112 }''' % episode_path
113 ['getVideoBySlug']
114 else:
115 query = query % '''metaDescription
116 title
117 videos(first:1000,sort:["episode_number"]) {
118 edges {
119 node {
120 _id
121 slug
122 }
123 }
124 }'''
125 show_data = self._download_json(
126 'https://www.adultswim.com/api/search', display_id,
127 data=json.dumps({'query': query}).encode(),
128 headers={'Content-Type': 'application/json'})['data']['getShowBySlug']
129 if episode_path:
130 video_data = show_data['getVideoBySlug']
131 video_id = video_data['_id']
132 episode_title = title = video_data['title']
133 series = show_data.get('title')
134 if series:
135 title = '%s - %s' % (series, title)
136 info = {
137 'id': video_id,
138 'title': title,
139 'description': strip_or_none(video_data.get('description')),
140 'duration': float_or_none(video_data.get('duration')),
141 'formats': [],
142 'subtitles': {},
143 'age_limit': parse_age_limit(video_data.get('tvRating')),
144 'thumbnail': video_data.get('poster'),
145 'timestamp': parse_iso8601(video_data.get('launchDate')),
146 'series': series,
147 'season_number': int_or_none(video_data.get('seasonNumber')),
148 'episode': episode_title,
149 'episode_number': int_or_none(video_data.get('episodeNumber')),
150 }
3b859145 151
afb74964
RA
152 auth = video_data.get('auth')
153 media_id = video_data.get('mediaID')
154 if media_id:
155 info.update(self._extract_ngtv_info(media_id, {
156 # CDN_TOKEN_APP_ID from:
157 # https://d2gg02c3xr550i.cloudfront.net/assets/asvp.e9c8bef24322d060ef87.bundle.js
158 'appId': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6ImFzLXR2ZS1kZXNrdG9wLXB0enQ2bSIsInByb2R1Y3QiOiJ0dmUiLCJuZXR3b3JrIjoiYXMiLCJwbGF0Zm9ybSI6ImRlc2t0b3AiLCJpYXQiOjE1MzI3MDIyNzl9.BzSCk-WYOZ2GMCIaeVb8zWnzhlgnXuJTCu0jGp_VaZE',
159 }, {
160 'url': url,
161 'site_name': 'AdultSwim',
162 'auth_required': auth,
163 }))
3b859145 164
afb74964
RA
165 if not auth:
166 extract_data = self._download_json(
167 'https://www.adultswim.com/api/shows/v1/videos/' + video_id,
168 video_id, query={'fields': 'stream'}, fatal=False) or {}
169 assets = try_get(extract_data, lambda x: x['data']['video']['stream']['assets'], list) or []
170 for asset in assets:
171 asset_url = asset.get('url')
172 if not asset_url:
3b859145 173 continue
afb74964
RA
174 ext = determine_ext(asset_url, mimetype2ext(asset.get('mime_type')))
175 if ext == 'm3u8':
176 info['formats'].extend(self._extract_m3u8_formats(
177 asset_url, video_id, 'mp4', m3u8_id='hls', fatal=False))
178 elif ext == 'f4m':
3b859145 179 continue
afb74964
RA
180 # info['formats'].extend(self._extract_f4m_formats(
181 # asset_url, video_id, f4m_id='hds', fatal=False))
182 elif ext in ('scc', 'ttml', 'vtt'):
183 info['subtitles'].setdefault('en', []).append({
184 'url': asset_url,
185 })
186 self._sort_formats(info['formats'])
3b859145 187
afb74964
RA
188 return info
189 else:
190 entries = []
191 for edge in show_data.get('videos', {}).get('edges', []):
192 video = edge.get('node') or {}
193 slug = video.get('slug')
194 if not slug:
195 continue
196 entries.append(self.url_result(
197 'http://adultswim.com/videos/%s/%s' % (show_path, slug),
198 'AdultSwim', video.get('_id')))
199 return self.playlist_result(
200 entries, show_path, show_data.get('title'),
201 strip_or_none(show_data.get('metaDescription')))