3 from .common
import InfoExtractor
4 from ..utils
import int_or_none
, traverse_obj
7 class PlaySuisseIE(InfoExtractor
):
8 _VALID_URL
= r
'https?://(?:www\.)?playsuisse\.ch/(?:watch|detail)/(?:[^#]*[?&]episodeId=)?(?P<id>[0-9]+)'
12 'url': 'https://www.playsuisse.ch/watch/763211/0',
13 'only_matching': True,
17 'url': 'https://www.playsuisse.ch/watch/763182?episodeId=763211',
18 'md5': '82df2a470b2dfa60c2d33772a8a60cf8',
23 'description': 'md5:8ea7a8076ba000cd9e8bc132fd0afdd8',
30 'thumbnail': 're:https://playsuisse-img.akamaized.net/',
34 'url': 'https://www.playsuisse.ch/watch/808675',
35 'md5': '818b94c1d2d7c4beef953f12cb8f3e75',
39 'title': 'Der Läufer',
40 'description': 'md5:9f61265c7e6dcc3e046137a792b275fd',
42 'thumbnail': 're:https://playsuisse-img.akamaized.net/',
45 # series (treated as a playlist)
46 'url': 'https://www.playsuisse.ch/detail/1115687',
48 'description': 'md5:e4a2ae29a8895823045b5c3145a02aa3',
50 'series': 'They all came out to Montreux',
51 'title': 'They all came out to Montreux',
55 'description': 'md5:f2462744834b959a31adc6292380cda2',
62 'series': 'They all came out to Montreux',
63 'thumbnail': 're:https://playsuisse-img.akamaized.net/',
69 'description': 'md5:9dfd308699fe850d3bce12dc1bad9b27',
76 'series': 'They all came out to Montreux',
77 'thumbnail': 're:https://playsuisse-img.akamaized.net/',
83 'description': 'md5:14a93a3356b2492a8f786ab2227ef602',
90 'series': 'They all came out to Montreux',
91 'thumbnail': 're:https://playsuisse-img.akamaized.net/',
100 query AssetWatch($assetId: ID!) {
101 assetV2(id: $assetId) {
108 fragment Asset on AssetV2 {
126 thumbnail16x9WithTitle {
129 thumbnail2x3WithTitle {
133 fragment ImageDetails on AssetImage {
138 def _get_media_data(self
, media_id
):
139 # NOTE In the web app, the "locale" header is used to switch between languages,
140 # However this doesn't seem to take effect when passing the header here.
141 response
= self
._download
_json
(
142 'https://4bbepzm4ef.execute-api.eu-central-1.amazonaws.com/prod/graphql',
143 media_id
, data
=json
.dumps({
144 'operationName': 'AssetWatch',
145 'query': self
._GRAPHQL
_QUERY
,
146 'variables': {'assetId': media_id}
148 headers
={'Content-Type': 'application/json', 'locale': 'de'}
)
150 return response
['data']['assetV2']
152 def _real_extract(self
, url
):
153 media_id
= self
._match
_id
(url
)
154 media_data
= self
._get
_media
_data
(media_id
)
155 info
= self
._extract
_single
(media_data
)
156 if media_data
.get('episodes'):
159 'entries': map(self
._extract
_single
, media_data
['episodes']),
163 def _extract_single(self
, media_data
):
164 thumbnails
= traverse_obj(media_data
, lambda k
, _
: k
.startswith('thumbnail'))
166 formats
, subtitles
= [], {}
167 for media
in traverse_obj(media_data
, 'medias', default
=[]):
168 if not media
.get('url') or media
.get('type') != 'HLS':
170 f
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(
171 media
['url'], media_data
['id'], 'mp4', m3u8_id
='HLS', fatal
=False)
173 self
._merge
_subtitles
(subs
, target
=subtitles
)
176 'id': media_data
['id'],
177 'title': media_data
.get('name'),
178 'description': media_data
.get('description'),
179 'thumbnails': thumbnails
,
180 'duration': int_or_none(media_data
.get('duration')),
182 'subtitles': subtitles
,
183 'series': media_data
.get('seriesName'),
184 'season_number': int_or_none(media_data
.get('seasonNumber')),
185 'episode': media_data
.get('name') if media_data
.get('episodeNumber') else None,
186 'episode_number': int_or_none(media_data
.get('episodeNumber')),