2019-04-05 04:45:49 -06:00
import json
2014-05-19 05:25:58 -06:00
2016-08-28 09:43:15 -06:00
from . turner import TurnerBaseIE
2016-08-29 09:40:35 -06:00
from . . utils import (
2019-04-05 04:45:49 -06:00
determine_ext ,
float_or_none ,
2016-08-29 09:40:35 -06:00
int_or_none ,
2019-04-05 04:45:49 -06:00
mimetype2ext ,
parse_age_limit ,
parse_iso8601 ,
2017-05-08 08:01:10 -06:00
strip_or_none ,
2019-04-05 04:45:49 -06:00
try_get ,
2016-08-29 09:40:35 -06:00
)
2014-05-19 05:25:58 -06:00
2014-11-23 12:41:03 -07:00
2016-08-28 09:43:15 -06:00
class AdultSwimIE ( TurnerBaseIE ) :
2017-05-08 08:01:10 -06:00
_VALID_URL = r ' https?://(?:www \ .)?adultswim \ .com/videos/(?P<show_path>[^/?#]+)(?:/(?P<episode_path>[^/?#]+))? '
2014-12-05 22:49:41 -07:00
_TESTS = [ {
' url ' : ' http://adultswim.com/videos/rick-and-morty/pilot ' ,
' info_dict ' : {
2015-02-17 16:49:10 -07:00
' id ' : ' rQxZvXQ4ROaSOqq-or2Mow ' ,
2017-05-08 08:01:10 -06:00
' ext ' : ' mp4 ' ,
2014-12-05 22:49:41 -07:00
' title ' : ' Rick and Morty - Pilot ' ,
2017-05-08 08:01:10 -06:00
' description ' : ' Rick moves in with his daughter \' s family and establishes himself as a bad influence on his grandson, Morty. ' ,
2019-04-05 04:45:49 -06:00
' timestamp ' : 1543294800 ,
' upload_date ' : ' 20181127 ' ,
2015-10-10 05:28:12 -06:00
} ,
2017-05-08 08:01:10 -06:00
' params ' : {
# m3u8 download
' skip_download ' : True ,
2014-12-05 22:49:41 -07:00
} ,
2017-05-08 08:01:10 -06:00
' expected_warnings ' : [ ' Unable to download f4m manifest ' ] ,
2015-03-08 13:32:42 -06:00
} , {
' url ' : ' http://www.adultswim.com/videos/tim-and-eric-awesome-show-great-job/dr-steve-brule-for-your-wine/ ' ,
' info_dict ' : {
' id ' : ' sY3cMUR_TbuE4YmdjzbIcQ ' ,
2017-05-08 08:01:10 -06:00
' ext ' : ' mp4 ' ,
2015-03-08 13:32:42 -06:00
' title ' : ' Tim and Eric Awesome Show Great Job! - Dr. Steve Brule, For Your Wine ' ,
2017-05-08 08:01:10 -06:00
' description ' : ' Dr. Brule reports live from Wine Country with a special report on wines. \n Watch Tim and Eric Awesome Show Great Job! episode #20, " Embarrassed " on Adult Swim. ' ,
' upload_date ' : ' 20080124 ' ,
' timestamp ' : 1201150800 ,
2015-03-08 13:32:42 -06:00
} ,
2015-12-21 09:07:19 -07:00
' params ' : {
# m3u8 download
' skip_download ' : True ,
2017-05-08 08:01:10 -06:00
} ,
2019-04-05 04:45:49 -06:00
' skip ' : ' 404 Not Found ' ,
2016-08-05 11:00:05 -06:00
} , {
' url ' : ' http://www.adultswim.com/videos/decker/inside-decker-a-new-hero/ ' ,
' info_dict ' : {
' id ' : ' I0LQFQkaSUaFp8PnAWHhoQ ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Decker - Inside Decker: A New Hero ' ,
2017-05-08 08:01:10 -06:00
' 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. ' ,
' timestamp ' : 1469480460 ,
' upload_date ' : ' 20160725 ' ,
2016-08-05 11:00:05 -06:00
} ,
' params ' : {
# m3u8 download
' skip_download ' : True ,
2016-08-28 09:43:15 -06:00
} ,
' expected_warnings ' : [ ' Unable to download f4m manifest ' ] ,
2016-10-26 12:16:48 -06:00
} , {
2017-05-08 08:01:10 -06:00
' url ' : ' http://www.adultswim.com/videos/attack-on-titan ' ,
' info_dict ' : {
2019-04-05 04:45:49 -06:00
' id ' : ' attack-on-titan ' ,
2017-05-08 08:01:10 -06:00
' title ' : ' Attack on Titan ' ,
2019-04-05 04:45:49 -06:00
' description ' : ' md5:41caa9416906d90711e31dc00cb7db7e ' ,
2017-05-08 08:01:10 -06:00
} ,
' playlist_mincount ' : 12 ,
} , {
' url ' : ' http://www.adultswim.com/videos/streams/williams-stream ' ,
2016-10-26 12:16:48 -06:00
' info_dict ' : {
2017-05-08 08:01:10 -06:00
' id ' : ' d8DEBj7QRfetLsRgFnGEyg ' ,
' ext ' : ' mp4 ' ,
' title ' : r ' re:^Williams Stream \ d {4} - \ d {2} - \ d {2} \ d {2} : \ d {2} $ ' ,
' description ' : ' original programming ' ,
2016-10-26 12:16:48 -06:00
} ,
' params ' : {
# m3u8 download
' skip_download ' : True ,
} ,
2019-04-05 04:45:49 -06:00
' skip ' : ' 404 Not Found ' ,
2014-12-05 22:49:41 -07:00
} ]
2014-05-19 05:25:58 -06:00
def _real_extract ( self , url ) :
2021-08-18 19:41:24 -06:00
show_path , episode_path = self . _match_valid_url ( url ) . groups ( )
2017-05-08 08:01:10 -06:00
display_id = episode_path or show_path
2019-04-05 04:45:49 -06:00
query = ''' query {
getShowBySlug ( slug : " %s " ) {
% % s
}
} ''' % s how_path
if episode_path :
query = query % ''' title
getVideoBySlug ( slug : " %s " ) {
_id
auth
description
duration
episodeNumber
launchDate
mediaID
seasonNumber
poster
title
tvRating
} ''' % e pisode_path
[ ' getVideoBySlug ' ]
else :
query = query % ''' metaDescription
title
videos ( first : 1000 , sort : [ " episode_number " ] ) {
edges {
node {
_id
slug
}
}
} '''
show_data = self . _download_json (
' https://www.adultswim.com/api/search ' , display_id ,
data = json . dumps ( { ' query ' : query } ) . encode ( ) ,
headers = { ' Content-Type ' : ' application/json ' } ) [ ' data ' ] [ ' getShowBySlug ' ]
if episode_path :
video_data = show_data [ ' getVideoBySlug ' ]
video_id = video_data [ ' _id ' ]
episode_title = title = video_data [ ' title ' ]
series = show_data . get ( ' title ' )
if series :
title = ' %s - %s ' % ( series , title )
info = {
' id ' : video_id ,
' title ' : title ,
' description ' : strip_or_none ( video_data . get ( ' description ' ) ) ,
' duration ' : float_or_none ( video_data . get ( ' duration ' ) ) ,
' formats ' : [ ] ,
' subtitles ' : { } ,
' age_limit ' : parse_age_limit ( video_data . get ( ' tvRating ' ) ) ,
' thumbnail ' : video_data . get ( ' poster ' ) ,
' timestamp ' : parse_iso8601 ( video_data . get ( ' launchDate ' ) ) ,
' series ' : series ,
' season_number ' : int_or_none ( video_data . get ( ' seasonNumber ' ) ) ,
' episode ' : episode_title ,
' episode_number ' : int_or_none ( video_data . get ( ' episodeNumber ' ) ) ,
}
2017-05-08 08:01:10 -06:00
2019-04-05 04:45:49 -06:00
auth = video_data . get ( ' auth ' )
media_id = video_data . get ( ' mediaID ' )
if media_id :
info . update ( self . _extract_ngtv_info ( media_id , {
# CDN_TOKEN_APP_ID from:
# https://d2gg02c3xr550i.cloudfront.net/assets/asvp.e9c8bef24322d060ef87.bundle.js
' appId ' : ' eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6ImFzLXR2ZS1kZXNrdG9wLXB0enQ2bSIsInByb2R1Y3QiOiJ0dmUiLCJuZXR3b3JrIjoiYXMiLCJwbGF0Zm9ybSI6ImRlc2t0b3AiLCJpYXQiOjE1MzI3MDIyNzl9.BzSCk-WYOZ2GMCIaeVb8zWnzhlgnXuJTCu0jGp_VaZE ' ,
} , {
' url ' : url ,
' site_name ' : ' AdultSwim ' ,
' auth_required ' : auth ,
} ) )
2017-05-08 08:01:10 -06:00
2019-04-05 04:45:49 -06:00
if not auth :
extract_data = self . _download_json (
' https://www.adultswim.com/api/shows/v1/videos/ ' + video_id ,
video_id , query = { ' fields ' : ' stream ' } , fatal = False ) or { }
assets = try_get ( extract_data , lambda x : x [ ' data ' ] [ ' video ' ] [ ' stream ' ] [ ' assets ' ] , list ) or [ ]
for asset in assets :
asset_url = asset . get ( ' url ' )
if not asset_url :
2017-05-08 08:01:10 -06:00
continue
2019-04-05 04:45:49 -06:00
ext = determine_ext ( asset_url , mimetype2ext ( asset . get ( ' mime_type ' ) ) )
if ext == ' m3u8 ' :
2023-06-25 14:22:38 -06:00
fmts , subs = self . _extract_m3u8_formats_and_subtitles (
asset_url , video_id , ' mp4 ' , m3u8_id = ' hls ' , fatal = False )
info [ ' formats ' ] . extend ( fmts )
self . _merge_subtitles ( subs , target = info [ ' subtitles ' ] )
2019-04-05 04:45:49 -06:00
elif ext == ' f4m ' :
2017-05-08 08:01:10 -06:00
continue
2019-04-05 04:45:49 -06:00
# info['formats'].extend(self._extract_f4m_formats(
# asset_url, video_id, f4m_id='hds', fatal=False))
elif ext in ( ' scc ' , ' ttml ' , ' vtt ' ) :
info [ ' subtitles ' ] . setdefault ( ' en ' , [ ] ) . append ( {
' url ' : asset_url ,
} )
2017-05-08 08:01:10 -06:00
2019-04-05 04:45:49 -06:00
return info
else :
entries = [ ]
for edge in show_data . get ( ' videos ' , { } ) . get ( ' edges ' , [ ] ) :
video = edge . get ( ' node ' ) or { }
slug = video . get ( ' slug ' )
if not slug :
continue
entries . append ( self . url_result (
' http://adultswim.com/videos/ %s / %s ' % ( show_path , slug ) ,
' AdultSwim ' , video . get ( ' _id ' ) ) )
return self . playlist_result (
entries , show_path , show_data . get ( ' title ' ) ,
strip_or_none ( show_data . get ( ' metaDescription ' ) ) )