]>
jfr.im git - yt-dlp.git/blob - yt_dlp/extractor/plutotv.py
5 from . common
import InfoExtractor
15 class PlutoTVIE ( InfoExtractor
):
18 https?://(?:www\.)?pluto\.tv(?:/[^/]+)?/on-demand
19 /(?P<video_type>movies|series)
20 /(?P<series_or_movie_slug>[^/]+)
22 (?:/seasons?/(?P<season_no>\d+))?
23 (?:/episode/(?P<episode_slug>[^/]+))?
27 _INFO_URL
= 'https://service-vod.clusters.pluto.tv/v3/vod/slugs/'
28 _INFO_QUERY_PARAMS
= {
31 'clientID' : str ( uuid
. uuid1 ()),
32 'clientModelNumber' : 'na' ,
33 'serverSideAds' : 'false' ,
34 'deviceMake' : 'unknown' ,
37 'deviceVersion' : 'unknown' ,
38 'sid' : str ( uuid
. uuid1 ()),
42 'url' : 'https://pluto.tv/on-demand/series/i-love-money/season/2/episode/its-in-the-cards-2009-2-3' ,
43 'md5' : 'ebcdd8ed89aaace9df37924f722fd9bd' ,
45 'id' : '5de6c598e9379ae4912df0a8' ,
47 'title' : 'It \' s In The Cards' ,
48 'episode' : 'It \' s In The Cards' ,
49 'description' : 'The teams face off against each other in a 3-on-2 soccer showdown. Strategy comes into play, though, as each team gets to select their opposing teams’ two defenders.' ,
50 'series' : 'I Love Money' ,
56 'url' : 'https://pluto.tv/on-demand/series/i-love-money/season/1/' ,
59 'id' : '5de6c582e9379ae4912dedbd' ,
60 'title' : 'I Love Money - Season 1' ,
63 'url' : 'https://pluto.tv/on-demand/series/i-love-money/' ,
66 'id' : '5de6c582e9379ae4912dedbd' ,
67 'title' : 'I Love Money' ,
70 'url' : 'https://pluto.tv/on-demand/movies/arrival-2015-1-1' ,
71 'md5' : '3cead001d317a018bf856a896dee1762' ,
73 'id' : '5e83ac701fa6a9001bb9df24' ,
76 'description' : 'When mysterious spacecraft touch down across the globe, an elite team - led by expert translator Louise Banks (Academy Award® nominee Amy Adams) – races against time to decipher their intent.' ,
80 'url' : 'https://pluto.tv/en/on-demand/series/manhunters-fugitive-task-force/seasons/1/episode/third-times-the-charm-1-1' ,
81 'only_matching' : True ,
83 'url' : 'https://pluto.tv/it/on-demand/series/csi-vegas/episode/legacy-2021-1-1' ,
84 'only_matching' : True ,
87 'url' : 'https://pluto.tv/en/on-demand/movies/attack-of-the-killer-tomatoes-1977-1-1-ptv1' ,
88 'md5' : '7db56369c0da626a32d505ec6eb3f89f' ,
90 'id' : '5b190c7bb0875c36c90c29c4' ,
92 'title' : 'Attack of the Killer Tomatoes' ,
93 'description' : 'A group of scientists band together to save the world from mutated tomatoes that KILL! (1978)' ,
99 def _to_ad_free_formats ( self
, video_id
, formats
, subtitles
):
100 ad_free_formats
, ad_free_subtitles
, m3u8_urls
= [], {}, set ()
102 res
= self
._ download
_ webpage
(
103 fmt
. get ( 'url' ), video_id
, note
= 'Downloading m3u8 playlist' ,
107 first_segment_url
= re
. search (
108 r
'^(https?://.*/)0\-(end|[0-9]+)/[^/]+\.ts$' , res
,
110 if first_segment_url
:
112 urllib
. parse
. urljoin ( first_segment_url
. group ( 1 ), '0-end/master.m3u8' ))
114 first_segment_url
= re
. search (
115 r
'^(https?://.*/).+\-0+[0-1]0\.ts$' , res
,
117 if first_segment_url
:
119 urllib
. parse
. urljoin ( first_segment_url
. group ( 1 ), 'master.m3u8' ))
122 for m3u8_url
in m3u8_urls
:
123 fmts
, subs
= self
._ extract
_ m
3u8_ formats
_ and
_ subtitles
(
124 m3u8_url
, video_id
, 'mp4' , 'm3u8_native' , m3u8_id
= 'hls' , fatal
= False )
125 ad_free_formats
. extend ( fmts
)
126 ad_free_subtitles
= self
._ merge
_ subtitles
( ad_free_subtitles
, subs
)
128 formats
, subtitles
= ad_free_formats
, ad_free_subtitles
130 self
. report_warning ( 'Unable to find ad-free formats' )
131 return formats
, subtitles
133 def _get_video_info ( self
, video_json
, slug
, series_name
= None ):
134 video_id
= video_json
. get ( '_id' , slug
)
135 formats
, subtitles
= [], {}
136 for video_url
in try_get ( video_json
, lambda x
: x
[ 'stitched' ][ 'urls' ], list ) or []:
137 if video_url
. get ( 'type' ) != 'hls' :
139 url
= url_or_none ( video_url
. get ( 'url' ))
141 fmts
, subs
= self
._ extract
_ m
3u8_ formats
_ and
_ subtitles
(
142 url
, video_id
, 'mp4' , 'm3u8_native' , m3u8_id
= 'hls' , fatal
= False )
144 subtitles
= self
._ merge
_ subtitles
( subtitles
, subs
)
146 formats
, subtitles
= self
._ to
_ ad
_ free
_ formats
( video_id
, formats
, subtitles
)
151 'subtitles' : subtitles
,
152 'title' : video_json
. get ( 'name' ),
153 'description' : video_json
. get ( 'description' ),
154 'duration' : float_or_none ( video_json
. get ( 'duration' ), scale
= 1000 ),
158 'series' : series_name
,
159 'episode' : video_json
. get ( 'name' ),
160 'season_number' : int_or_none ( video_json
. get ( 'season' )),
161 'episode_number' : int_or_none ( video_json
. get ( 'number' )),
165 def _real_extract ( self
, url
):
166 mobj
= self
._ match
_ valid
_u rl
( url
). groupdict ()
167 info_slug
= mobj
[ 'series_or_movie_slug' ]
168 video_json
= self
._ download
_ json
( self
._ INFO
_U RL
+ info_slug
, info_slug
, query
= self
._ INFO
_ QUERY
_ PARAMS
)
170 if mobj
[ 'video_type' ] == 'series' :
171 series_name
= video_json
. get ( 'name' , info_slug
)
172 season_number
, episode_slug
= mobj
. get ( 'season_number' ), mobj
. get ( 'episode_slug' )
175 for season
in video_json
[ 'seasons' ]:
176 if season_number
is not None and season_number
!= int_or_none ( season
. get ( 'number' )):
178 for episode
in season
[ 'episodes' ]:
179 if episode_slug
is not None and episode_slug
!= episode
. get ( 'slug' ):
181 videos
. append ( self
._ get
_ video
_ info
( episode
, episode_slug
, series_name
))
183 raise ExtractorError ( 'Failed to find any videos to extract' )
184 if episode_slug
is not None and len ( videos
) == 1 :
186 playlist_title
= series_name
187 if season_number
is not None :
188 playlist_title
+= ' - Season %d ' % season_number
189 return self
. playlist_result ( videos
,
190 playlist_id
= video_json
. get ( '_id' , info_slug
),
191 playlist_title
= playlist_title
)
192 return self
._ get
_ video
_ info
( video_json
, info_slug
)