2 from __future__
import unicode_literals
6 from .common
import InfoExtractor
9 compat_urllib_parse_urlparse
,
21 class SVTBaseIE(InfoExtractor
):
22 _GEO_COUNTRIES
= ['SE']
24 def _extract_video(self
, video_info
, video_id
):
25 is_live
= dict_get(video_info
, ('live', 'simulcast'), default
=False)
26 m3u8_protocol
= 'm3u8' if is_live
else 'm3u8_native'
28 for vr
in video_info
['videoReferences']:
29 player_type
= vr
.get('playerType') or vr
.get('format')
31 ext
= determine_ext(vurl
)
33 formats
.extend(self
._extract
_m
3u8_formats
(
35 ext
='mp4', entry_protocol
=m3u8_protocol
,
36 m3u8_id
=player_type
, fatal
=False))
38 formats
.extend(self
._extract
_f
4m
_formats
(
39 vurl
+ '?hdcore=3.3.0', video_id
,
40 f4m_id
=player_type
, fatal
=False))
42 if player_type
== 'dashhbbtv':
43 formats
.extend(self
._extract
_mpd
_formats
(
44 vurl
, video_id
, mpd_id
=player_type
, fatal
=False))
47 'format_id': player_type
,
50 if not formats
and video_info
.get('rights', {}).get('geoBlockedSweden'):
51 self
.raise_geo_restricted(
52 'This video is only available in Sweden',
53 countries
=self
._GEO
_COUNTRIES
)
54 self
._sort
_formats
(formats
)
57 subtitle_references
= dict_get(video_info
, ('subtitles', 'subtitleReferences'))
58 if isinstance(subtitle_references
, list):
59 for sr
in subtitle_references
:
60 subtitle_url
= sr
.get('url')
61 subtitle_lang
= sr
.get('language', 'sv')
63 if determine_ext(subtitle_url
) == 'm3u8':
64 # TODO(yan12125): handle WebVTT in m3u8 manifests
67 subtitles
.setdefault(subtitle_lang
, []).append({'url': subtitle_url
})
69 title
= video_info
.get('title')
71 series
= video_info
.get('programTitle')
72 season_number
= int_or_none(video_info
.get('season'))
73 episode
= video_info
.get('episodeTitle')
74 episode_number
= int_or_none(video_info
.get('episodeNumber'))
76 duration
= int_or_none(dict_get(video_info
, ('materialLength', 'contentDuration')))
79 video_info
, ('inappropriateForChildren', 'blockedForChildren'),
80 skip_false_values
=False)
82 age_limit
= 18 if adult
else 0
88 'subtitles': subtitles
,
90 'age_limit': age_limit
,
92 'season_number': season_number
,
94 'episode_number': episode_number
,
99 class SVTIE(SVTBaseIE
):
100 _VALID_URL
= r
'https?://(?:www\.)?svt\.se/wd\?(?:.*?&)?widgetId=(?P<widget_id>\d+)&.*?\barticleId=(?P<id>\d+)'
102 'url': 'http://www.svt.se/wd?widgetId=23991§ionId=541&articleId=2900353&type=embed&contextSectionId=123&autostart=false',
103 'md5': '33e9a5d8f646523ce0868ecfb0eed77d',
107 'title': 'Stjärnorna skojar till det - under SVT-intervjun',
114 def _extract_url(webpage
):
116 r
'(?:<iframe src|href)="(?P<url>%s[^"]*)"' % SVTIE
._VALID
_URL
, webpage
)
118 return mobj
.group('url')
120 def _real_extract(self
, url
):
121 mobj
= re
.match(self
._VALID
_URL
, url
)
122 widget_id
= mobj
.group('widget_id')
123 article_id
= mobj
.group('id')
125 info
= self
._download
_json
(
126 'http://www.svt.se/wd?widgetId=%s&articleId=%s&format=json&type=embed&output=json' % (widget_id
, article_id
),
129 info_dict
= self
._extract
_video
(info
['video'], article_id
)
130 info_dict
['title'] = info
['context']['title']
134 class SVTPlayBaseIE(SVTBaseIE
):
135 _SVTPLAY_RE
= r
'root\s*\[\s*(["\'])_
*svtplay\
1\s
*\
]\s
*=\s
*(?P
<json
>{.+?
})\s
*;\s
*\n'
138 class SVTPlayIE(SVTPlayBaseIE):
139 IE_DESC = 'SVT Play
and Öppet arkiv
'
140 _VALID_URL = r'https?
://(?
:www\
.)?
(?
:svtplay|oppetarkiv
)\
.se
/(?
:video|klipp|kanaler
)/(?P
<id>[^
/?
#&]+)'
142 'url': 'http://www.svtplay.se/video/5996901/flygplan-till-haile-selassie/flygplan-till-haile-selassie-2',
143 'md5': '2b6704fe4a28801e1a098bbf3c5ac611',
147 'title': 'Flygplan till Haile Selassie',
149 'thumbnail': r
're:^https?://.*[\.-]jpg$',
158 # geo restricted to Sweden
159 'url': 'http://www.oppetarkiv.se/video/5219710/trollflojten',
160 'only_matching': True,
162 'url': 'http://www.svtplay.se/klipp/9023742/stopptid-om-bjorn-borg',
163 'only_matching': True,
165 'url': 'https://www.svtplay.se/kanaler/svt1',
166 'only_matching': True,
169 def _real_extract(self
, url
):
170 video_id
= self
._match
_id
(url
)
172 webpage
= self
._download
_webpage
(url
, video_id
)
174 data
= self
._parse
_json
(
176 self
._SVTPLAY
_RE
, webpage
, 'embedded data', default
='{}',
178 video_id
, fatal
=False)
180 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
182 def adjust_title(info
):
184 info
['title'] = self
._live
_title
(info
['title'])
187 video_info
= try_get(
188 data
, lambda x
: x
['context']['dispatcher']['stores']['VideoTitlePageStore']['data']['video'],
191 info_dict
= self
._extract
_video
(video_info
, video_id
)
193 'title': data
['context']['dispatcher']['stores']['MetaStore']['title'],
194 'thumbnail': thumbnail
,
196 adjust_title(info_dict
)
199 video_id
= self
._search
_regex
(
200 r
'<video[^>]+data-video-id=["\']([\da
-zA
-Z
-]+)',
201 webpage, 'video
id', default=None)
204 data = self._download_json(
205 'https
://api
.svt
.se
/videoplayer
-api
/video
/%s' % video_id,
206 video_id, headers=self.geo_verification_headers())
207 info_dict = self._extract_video(data, video_id)
208 if not info_dict.get('title
'):
209 info_dict['title
'] = re.sub(
211 info_dict.get('episode
') or self._og_search_title(webpage))
212 adjust_title(info_dict)
216 class SVTSeriesIE(SVTPlayBaseIE):
217 _VALID_URL = r'https?
://(?
:www\
.)?svtplay\
.se
/(?P
<id>[^
/?
&#]+)'
219 'url': 'https://www.svtplay.se/rederiet',
223 'description': 'md5:505d491a58f4fcf6eb418ecab947e69e',
225 'playlist_mincount': 318,
227 'url': 'https://www.svtplay.se/rederiet?tab=sasong2',
229 'id': 'rederiet-sasong2',
230 'title': 'Rederiet - Säsong 2',
231 'description': 'md5:505d491a58f4fcf6eb418ecab947e69e',
233 'playlist_count': 12,
237 def suitable(cls
, url
):
238 return False if SVTIE
.suitable(url
) or SVTPlayIE
.suitable(url
) else super(SVTSeriesIE
, cls
).suitable(url
)
240 def _real_extract(self
, url
):
241 series_id
= self
._match
_id
(url
)
243 qs
= compat_parse_qs(compat_urllib_parse_urlparse(url
).query
)
244 season_slug
= qs
.get('tab', [None])[0]
247 series_id
+= '-%s' % season_slug
249 webpage
= self
._download
_webpage
(
250 url
, series_id
, 'Downloading series page')
252 root
= self
._parse
_json
(
254 self
._SVTPLAY
_RE
, webpage
, 'content', group
='json'),
260 for season
in root
['relatedVideoContent']['relatedVideosAccordion']:
261 if not isinstance(season
, dict):
264 if season
.get('slug') != season_slug
:
266 season_name
= season
.get('name')
267 videos
= season
.get('videos')
268 if not isinstance(videos
, list):
271 content_url
= video
.get('contentUrl')
272 if not content_url
or not isinstance(content_url
, compat_str
):
276 urljoin(url
, content_url
),
277 ie
=SVTPlayIE
.ie_key(),
278 video_title
=video
.get('title')
281 metadata
= root
.get('metaData')
282 if not isinstance(metadata
, dict):
285 title
= metadata
.get('title')
286 season_name
= season_name
or season_slug
288 if title
and season_name
:
289 title
= '%s - %s' % (title
, season_name
)
293 return self
.playlist_result(
294 entries
, series_id
, title
, metadata
.get('description'))