4 from .common
import InfoExtractor
5 from ..compat
import compat_HTTPError
, compat_str
16 class ZattooPlatformBaseIE(InfoExtractor
):
17 _power_guide_hash
= None
20 return 'https://%s' % (self
._API
_HOST
if hasattr(self
, '_API_HOST') else self
._HOST
)
22 def _real_initialize(self
):
23 if not self
._power
_guide
_hash
:
24 self
.raise_login_required('An account is needed to access this media', method
='password')
26 def _perform_login(self
, username
, password
):
28 data
= self
._download
_json
(
29 '%s/zapi/v2/account/login' % self
._host
_url
(), None, 'Logging in',
30 data
=urlencode_postdata({
35 'Referer': '%s/login' % self
._host
_url
(),
36 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
38 except ExtractorError
as e
:
39 if isinstance(e
.cause
, compat_HTTPError
) and e
.cause
.code
== 400:
41 'Unable to login: incorrect username and/or password',
45 self
._power
_guide
_hash
= data
['session']['power_guide_hash']
47 def _initialize_pre_login(self
):
48 session_token
= self
._download
_json
(
49 f
'{self._host_url()}/token.json', None, 'Downloading session token')['session_token']
51 # Will setup appropriate cookies
52 self
._request
_webpage
(
53 '%s/zapi/v3/session/hello' % self
._host
_url
(), None,
54 'Opening session', data
=urlencode_postdata({
55 'uuid': compat_str(uuid4()),
57 'app_version': '1.8.2',
59 'client_app_token': session_token
,
62 def _extract_video_id_from_recording(self
, recid
):
63 playlist
= self
._download
_json
(
64 f
'{self._host_url()}/zapi/v2/playlist', recid
, 'Downloading playlist')
67 str(item
['program_id']) for item
in playlist
['recordings']
68 if item
.get('program_id') and str(item
.get('id')) == recid
)
69 except (StopIteration, KeyError):
70 raise ExtractorError('Could not extract video id from recording')
72 def _extract_cid(self
, video_id
, channel_name
):
73 channel_groups
= self
._download
_json
(
74 '%s/zapi/v2/cached/channels/%s' % (self
._host
_url
(),
75 self
._power
_guide
_hash
),
76 video_id
, 'Downloading channel list',
77 query
={'details': False}
)['channel_groups']
79 for chgrp
in channel_groups
:
80 channel_list
.extend(chgrp
['channels'])
83 chan
['cid'] for chan
in channel_list
84 if chan
.get('cid') and (
85 chan
.get('display_alias') == channel_name
86 or chan
.get('cid') == channel_name
))
88 raise ExtractorError('Could not extract channel id')
90 def _extract_cid_and_video_info(self
, video_id
):
91 data
= self
._download
_json
(
92 '%s/zapi/v2/cached/program/power_details/%s' % (
93 self
._host
_url
(), self
._power
_guide
_hash
),
95 'Downloading video information',
97 'program_ids': video_id
,
101 p
= data
['programs'][0]
106 'title': p
.get('t') or p
['et'],
107 'description': p
.get('d'),
108 'thumbnail': p
.get('i_url'),
109 'creator': p
.get('channel_name'),
110 'episode': p
.get('et'),
111 'episode_number': int_or_none(p
.get('e_no')),
112 'season_number': int_or_none(p
.get('s_no')),
113 'release_year': int_or_none(p
.get('year')),
114 'categories': try_get(p
, lambda x
: x
['c'], list),
115 'tags': try_get(p
, lambda x
: x
['g'], list)
118 return cid
, info_dict
120 def _extract_ondemand_info(self
, ondemand_id
):
122 @returns (ondemand_token, ondemand_type, info_dict)
124 data
= self
._download
_json
(
125 '%s/zapi/vod/movies/%s' % (self
._host
_url
(), ondemand_id
),
126 ondemand_id
, 'Downloading ondemand information')
129 'title': data
.get('title'),
130 'description': data
.get('description'),
131 'duration': int_or_none(data
.get('duration')),
132 'release_year': int_or_none(data
.get('year')),
133 'episode_number': int_or_none(data
.get('episode_number')),
134 'season_number': int_or_none(data
.get('season_number')),
135 'categories': try_get(data
, lambda x
: x
['categories'], list),
137 return data
['terms_catalog'][0]['terms'][0]['token'], data
['type'], info_dict
139 def _extract_formats(self
, cid
, video_id
, record_id
=None, ondemand_id
=None, ondemand_termtoken
=None, ondemand_type
=None, is_live
=False):
141 'https_watch_urls': True,
145 postdata_common
.update({'timeshift': 10800}
)
146 url
= '%s/zapi/watch/live/%s' % (self
._host
_url
(), cid
)
148 url
= '%s/zapi/watch/recording/%s' % (self
._host
_url
(), record_id
)
150 postdata_common
.update({
151 'teasable_id': ondemand_id
,
152 'term_token': ondemand_termtoken
,
153 'teasable_type': ondemand_type
155 url
= '%s/zapi/watch/vod/video' % self
._host
_url
()
157 url
= '%s/zapi/v3/watch/replay/%s/%s' % (self
._host
_url
(), cid
, video_id
)
160 for stream_type
in ('dash', 'hls7'):
161 postdata
= postdata_common
.copy()
162 postdata
['stream_type'] = stream_type
164 data
= self
._download
_json
(
165 url
, video_id
, 'Downloading %s formats' % stream_type
.upper(),
166 data
=urlencode_postdata(postdata
), fatal
=False)
170 watch_urls
= try_get(
171 data
, lambda x
: x
['stream']['watch_urls'], list)
175 for watch
in watch_urls
:
176 if not isinstance(watch
, dict):
178 watch_url
= url_or_none(watch
.get('url'))
181 audio_channel
= watch
.get('audio_channel')
182 preference
= 1 if audio_channel
== 'A' else None
183 format_id
= join_nonempty(stream_type
, watch
.get('maxrate'), audio_channel
)
184 if stream_type
.startswith('dash'):
185 this_formats
, subs
= self
._extract
_mpd
_formats
_and
_subtitles
(
186 watch_url
, video_id
, mpd_id
=format_id
, fatal
=False)
187 self
._merge
_subtitles
(subs
, target
=subtitles
)
188 elif stream_type
.startswith('hls'):
189 this_formats
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(
190 watch_url
, video_id
, 'mp4',
191 entry_protocol
='m3u8_native', m3u8_id
=format_id
,
193 self
._merge
_subtitles
(subs
, target
=subtitles
)
194 elif stream_type
== 'hds':
195 this_formats
= self
._extract
_f
4m
_formats
(
196 watch_url
, video_id
, f4m_id
=format_id
, fatal
=False)
197 elif stream_type
== 'smooth_playready':
198 this_formats
= self
._extract
_ism
_formats
(
199 watch_url
, video_id
, ism_id
=format_id
, fatal
=False)
202 for this_format
in this_formats
:
203 this_format
['quality'] = preference
204 formats
.extend(this_formats
)
205 return formats
, subtitles
207 def _extract_video(self
, video_id
, record_id
=None):
208 cid
, info_dict
= self
._extract
_cid
_and
_video
_info
(video_id
)
209 info_dict
['formats'], info_dict
['subtitles'] = self
._extract
_formats
(cid
, video_id
, record_id
=record_id
)
212 def _extract_live(self
, channel_name
):
213 cid
= self
._extract
_cid
(channel_name
, channel_name
)
214 formats
, subtitles
= self
._extract
_formats
(cid
, cid
, is_live
=True)
217 'title': channel_name
,
220 'subtitles': subtitles
223 def _extract_record(self
, record_id
):
224 video_id
= self
._extract
_video
_id
_from
_recording
(record_id
)
225 cid
, info_dict
= self
._extract
_cid
_and
_video
_info
(video_id
)
226 info_dict
['formats'], info_dict
['subtitles'] = self
._extract
_formats
(cid
, video_id
, record_id
=record_id
)
229 def _extract_ondemand(self
, ondemand_id
):
230 ondemand_termtoken
, ondemand_type
, info_dict
= self
._extract
_ondemand
_info
(ondemand_id
)
231 info_dict
['formats'], info_dict
['subtitles'] = self
._extract
_formats
(
232 None, ondemand_id
, ondemand_id
=ondemand_id
,
233 ondemand_termtoken
=ondemand_termtoken
, ondemand_type
=ondemand_type
)
236 def _real_extract(self
, url
):
237 video_id
, record_id
= self
._match
_valid
_url
(url
).groups()
238 return getattr(self
, f
'_extract_{self._TYPE}')(video_id
or record_id
)
241 def _create_valid_url(host
, match
, qs
, base_re
=None):
242 match_base
= fr
'|{base_re}/(?P<vid1>{match})' if base_re
else '(?P<vid1>)'
243 return rf
'''(?x)https?://(?:www\.)?{re.escape(host)}/(?:
244 [^?#]+\?(?:[^#]+&)?{qs}=(?P<vid2>{match})
249 class ZattooBaseIE(ZattooPlatformBaseIE
):
250 _NETRC_MACHINE
= 'zattoo'
254 class ZattooIE(ZattooBaseIE
):
255 _VALID_URL
= _create_valid_url(ZattooBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
258 'url': 'https://zattoo.com/program/zdf/250170418',
262 'title': 'Markus Lanz',
263 'description': 'md5:e41cb1257de008ca62a73bb876ffa7fc',
264 'thumbnail': 're:http://images.zattic.com/cms/.+/format_480x360.jpg',
266 'release_year': 2022,
267 'episode': 'Folge 1655',
268 'categories': 'count:1',
271 'params': {'skip_download': 'm3u8'}
273 'url': 'https://zattoo.com/program/daserste/210177916',
274 'only_matching': True,
276 'url': 'https://zattoo.com/guide/german?channel=srf1&program=169860555',
277 'only_matching': True,
281 class ZattooLiveIE(ZattooBaseIE
):
282 _VALID_URL
= _create_valid_url(ZattooBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
285 'url': 'https://zattoo.com/channels/german?channel=srf_zwei',
286 'only_matching': True,
288 'url': 'https://zattoo.com/live/srf1',
289 'only_matching': True,
293 def suitable(cls
, url
):
294 return False if ZattooIE
.suitable(url
) else super().suitable(url
)
297 class ZattooMoviesIE(ZattooBaseIE
):
298 _VALID_URL
= _create_valid_url(ZattooBaseIE
._HOST
, r
'\w+', 'movie_id', 'vod/movies')
301 'url': 'https://zattoo.com/vod/movies/7521',
302 'only_matching': True,
304 'url': 'https://zattoo.com/ondemand?movie_id=7521&term_token=9f00f43183269484edde',
305 'only_matching': True,
309 class ZattooRecordingsIE(ZattooBaseIE
):
310 _VALID_URL
= _create_valid_url('zattoo.com', r
'\d+', 'recording')
313 'url': 'https://zattoo.com/recordings?recording=193615508',
314 'only_matching': True,
316 'url': 'https://zattoo.com/tc/ptc_recordings_all_recordings?recording=193615420',
317 'only_matching': True,
321 class NetPlusTVBaseIE(ZattooPlatformBaseIE
):
322 _NETRC_MACHINE
= 'netplus'
324 _API_HOST
= 'www.%s' % _HOST
327 class NetPlusTVIE(NetPlusTVBaseIE
):
328 _VALID_URL
= _create_valid_url(NetPlusTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
331 'url': 'https://netplus.tv/program/daserste/210177916',
332 'only_matching': True,
334 'url': 'https://netplus.tv/guide/german?channel=srf1&program=169860555',
335 'only_matching': True,
339 class NetPlusTVLiveIE(NetPlusTVBaseIE
):
340 _VALID_URL
= _create_valid_url(NetPlusTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
343 'url': 'https://netplus.tv/channels/german?channel=srf_zwei',
344 'only_matching': True,
346 'url': 'https://netplus.tv/live/srf1',
347 'only_matching': True,
351 def suitable(cls
, url
):
352 return False if NetPlusTVIE
.suitable(url
) else super().suitable(url
)
355 class NetPlusTVRecordingsIE(NetPlusTVBaseIE
):
356 _VALID_URL
= _create_valid_url(NetPlusTVBaseIE
._HOST
, r
'\d+', 'recording')
359 'url': 'https://netplus.tv/recordings?recording=193615508',
360 'only_matching': True,
362 'url': 'https://netplus.tv/tc/ptc_recordings_all_recordings?recording=193615420',
363 'only_matching': True,
367 class MNetTVBaseIE(ZattooPlatformBaseIE
):
368 _NETRC_MACHINE
= 'mnettv'
369 _HOST
= 'tvplus.m-net.de'
372 class MNetTVIE(MNetTVBaseIE
):
373 _VALID_URL
= _create_valid_url(MNetTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
376 'url': 'https://tvplus.m-net.de/program/daserste/210177916',
377 'only_matching': True,
379 'url': 'https://tvplus.m-net.de/guide/german?channel=srf1&program=169860555',
380 'only_matching': True,
384 class MNetTVLiveIE(MNetTVBaseIE
):
385 _VALID_URL
= _create_valid_url(MNetTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
388 'url': 'https://tvplus.m-net.de/channels/german?channel=srf_zwei',
389 'only_matching': True,
391 'url': 'https://tvplus.m-net.de/live/srf1',
392 'only_matching': True,
396 def suitable(cls
, url
):
397 return False if MNetTVIE
.suitable(url
) else super().suitable(url
)
400 class MNetTVRecordingsIE(MNetTVBaseIE
):
401 _VALID_URL
= _create_valid_url(MNetTVBaseIE
._HOST
, r
'\d+', 'recording')
404 'url': 'https://tvplus.m-net.de/recordings?recording=193615508',
405 'only_matching': True,
407 'url': 'https://tvplus.m-net.de/tc/ptc_recordings_all_recordings?recording=193615420',
408 'only_matching': True,
412 class WalyTVBaseIE(ZattooPlatformBaseIE
):
413 _NETRC_MACHINE
= 'walytv'
414 _HOST
= 'player.waly.tv'
417 class WalyTVIE(WalyTVBaseIE
):
418 _VALID_URL
= _create_valid_url(WalyTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
421 'url': 'https://player.waly.tv/program/daserste/210177916',
422 'only_matching': True,
424 'url': 'https://player.waly.tv/guide/german?channel=srf1&program=169860555',
425 'only_matching': True,
429 class WalyTVLiveIE(WalyTVBaseIE
):
430 _VALID_URL
= _create_valid_url(WalyTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
433 'url': 'https://player.waly.tv/channels/german?channel=srf_zwei',
434 'only_matching': True,
436 'url': 'https://player.waly.tv/live/srf1',
437 'only_matching': True,
441 def suitable(cls
, url
):
442 return False if WalyTVIE
.suitable(url
) else super().suitable(url
)
445 class WalyTVRecordingsIE(WalyTVBaseIE
):
446 _VALID_URL
= _create_valid_url(WalyTVBaseIE
._HOST
, r
'\d+', 'recording')
449 'url': 'https://player.waly.tv/recordings?recording=193615508',
450 'only_matching': True,
452 'url': 'https://player.waly.tv/tc/ptc_recordings_all_recordings?recording=193615420',
453 'only_matching': True,
457 class BBVTVBaseIE(ZattooPlatformBaseIE
):
458 _NETRC_MACHINE
= 'bbvtv'
460 _API_HOST
= 'www.%s' % _HOST
463 class BBVTVIE(BBVTVBaseIE
):
464 _VALID_URL
= _create_valid_url(BBVTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
467 'url': 'https://bbv-tv.net/program/daserste/210177916',
468 'only_matching': True,
470 'url': 'https://bbv-tv.net/guide/german?channel=srf1&program=169860555',
471 'only_matching': True,
475 class BBVTVLiveIE(BBVTVBaseIE
):
476 _VALID_URL
= _create_valid_url(BBVTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
479 'url': 'https://bbv-tv.net/channels/german?channel=srf_zwei',
480 'only_matching': True,
482 'url': 'https://bbv-tv.net/live/srf1',
483 'only_matching': True,
487 def suitable(cls
, url
):
488 return False if BBVTVIE
.suitable(url
) else super().suitable(url
)
491 class BBVTVRecordingsIE(BBVTVBaseIE
):
492 _VALID_URL
= _create_valid_url(BBVTVBaseIE
._HOST
, r
'\d+', 'recording')
495 'url': 'https://bbv-tv.net/recordings?recording=193615508',
496 'only_matching': True,
498 'url': 'https://bbv-tv.net/tc/ptc_recordings_all_recordings?recording=193615420',
499 'only_matching': True,
503 class VTXTVBaseIE(ZattooPlatformBaseIE
):
504 _NETRC_MACHINE
= 'vtxtv'
506 _API_HOST
= 'www.%s' % _HOST
509 class VTXTVIE(VTXTVBaseIE
):
510 _VALID_URL
= _create_valid_url(VTXTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
513 'url': 'https://vtxtv.ch/program/daserste/210177916',
514 'only_matching': True,
516 'url': 'https://vtxtv.ch/guide/german?channel=srf1&program=169860555',
517 'only_matching': True,
521 class VTXTVLiveIE(VTXTVBaseIE
):
522 _VALID_URL
= _create_valid_url(VTXTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
525 'url': 'https://vtxtv.ch/channels/german?channel=srf_zwei',
526 'only_matching': True,
528 'url': 'https://vtxtv.ch/live/srf1',
529 'only_matching': True,
533 def suitable(cls
, url
):
534 return False if VTXTVIE
.suitable(url
) else super().suitable(url
)
537 class VTXTVRecordingsIE(VTXTVBaseIE
):
538 _VALID_URL
= _create_valid_url(VTXTVBaseIE
._HOST
, r
'\d+', 'recording')
541 'url': 'https://vtxtv.ch/recordings?recording=193615508',
542 'only_matching': True,
544 'url': 'https://vtxtv.ch/tc/ptc_recordings_all_recordings?recording=193615420',
545 'only_matching': True,
549 class GlattvisionTVBaseIE(ZattooPlatformBaseIE
):
550 _NETRC_MACHINE
= 'glattvisiontv'
551 _HOST
= 'iptv.glattvision.ch'
554 class GlattvisionTVIE(GlattvisionTVBaseIE
):
555 _VALID_URL
= _create_valid_url(GlattvisionTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
558 'url': 'https://iptv.glattvision.ch/program/daserste/210177916',
559 'only_matching': True,
561 'url': 'https://iptv.glattvision.ch/guide/german?channel=srf1&program=169860555',
562 'only_matching': True,
566 class GlattvisionTVLiveIE(GlattvisionTVBaseIE
):
567 _VALID_URL
= _create_valid_url(GlattvisionTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
570 'url': 'https://iptv.glattvision.ch/channels/german?channel=srf_zwei',
571 'only_matching': True,
573 'url': 'https://iptv.glattvision.ch/live/srf1',
574 'only_matching': True,
578 def suitable(cls
, url
):
579 return False if GlattvisionTVIE
.suitable(url
) else super().suitable(url
)
582 class GlattvisionTVRecordingsIE(GlattvisionTVBaseIE
):
583 _VALID_URL
= _create_valid_url(GlattvisionTVBaseIE
._HOST
, r
'\d+', 'recording')
586 'url': 'https://iptv.glattvision.ch/recordings?recording=193615508',
587 'only_matching': True,
589 'url': 'https://iptv.glattvision.ch/tc/ptc_recordings_all_recordings?recording=193615420',
590 'only_matching': True,
594 class SAKTVBaseIE(ZattooPlatformBaseIE
):
595 _NETRC_MACHINE
= 'saktv'
597 _API_HOST
= 'www.%s' % _HOST
600 class SAKTVIE(SAKTVBaseIE
):
601 _VALID_URL
= _create_valid_url(SAKTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
604 'url': 'https://saktv.ch/program/daserste/210177916',
605 'only_matching': True,
607 'url': 'https://saktv.ch/guide/german?channel=srf1&program=169860555',
608 'only_matching': True,
612 class SAKTVLiveIE(SAKTVBaseIE
):
613 _VALID_URL
= _create_valid_url(SAKTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
616 'url': 'https://saktv.ch/channels/german?channel=srf_zwei',
617 'only_matching': True,
619 'url': 'https://saktv.ch/live/srf1',
620 'only_matching': True,
624 def suitable(cls
, url
):
625 return False if SAKTVIE
.suitable(url
) else super().suitable(url
)
628 class SAKTVRecordingsIE(SAKTVBaseIE
):
629 _VALID_URL
= _create_valid_url(SAKTVBaseIE
._HOST
, r
'\d+', 'recording')
632 'url': 'https://saktv.ch/recordings?recording=193615508',
633 'only_matching': True,
635 'url': 'https://saktv.ch/tc/ptc_recordings_all_recordings?recording=193615420',
636 'only_matching': True,
640 class EWETVBaseIE(ZattooPlatformBaseIE
):
641 _NETRC_MACHINE
= 'ewetv'
642 _HOST
= 'tvonline.ewe.de'
645 class EWETVIE(EWETVBaseIE
):
646 _VALID_URL
= _create_valid_url(EWETVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
649 'url': 'https://tvonline.ewe.de/program/daserste/210177916',
650 'only_matching': True,
652 'url': 'https://tvonline.ewe.de/guide/german?channel=srf1&program=169860555',
653 'only_matching': True,
657 class EWETVLiveIE(EWETVBaseIE
):
658 _VALID_URL
= _create_valid_url(EWETVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
661 'url': 'https://tvonline.ewe.de/channels/german?channel=srf_zwei',
662 'only_matching': True,
664 'url': 'https://tvonline.ewe.de/live/srf1',
665 'only_matching': True,
669 def suitable(cls
, url
):
670 return False if EWETVIE
.suitable(url
) else super().suitable(url
)
673 class EWETVRecordingsIE(EWETVBaseIE
):
674 _VALID_URL
= _create_valid_url(EWETVBaseIE
._HOST
, r
'\d+', 'recording')
677 'url': 'https://tvonline.ewe.de/recordings?recording=193615508',
678 'only_matching': True,
680 'url': 'https://tvonline.ewe.de/tc/ptc_recordings_all_recordings?recording=193615420',
681 'only_matching': True,
685 class QuantumTVBaseIE(ZattooPlatformBaseIE
):
686 _NETRC_MACHINE
= 'quantumtv'
687 _HOST
= 'quantum-tv.com'
688 _API_HOST
= 'www.%s' % _HOST
691 class QuantumTVIE(QuantumTVBaseIE
):
692 _VALID_URL
= _create_valid_url(QuantumTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
695 'url': 'https://quantum-tv.com/program/daserste/210177916',
696 'only_matching': True,
698 'url': 'https://quantum-tv.com/guide/german?channel=srf1&program=169860555',
699 'only_matching': True,
703 class QuantumTVLiveIE(QuantumTVBaseIE
):
704 _VALID_URL
= _create_valid_url(QuantumTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
707 'url': 'https://quantum-tv.com/channels/german?channel=srf_zwei',
708 'only_matching': True,
710 'url': 'https://quantum-tv.com/live/srf1',
711 'only_matching': True,
715 def suitable(cls
, url
):
716 return False if QuantumTVIE
.suitable(url
) else super().suitable(url
)
719 class QuantumTVRecordingsIE(QuantumTVBaseIE
):
720 _VALID_URL
= _create_valid_url(QuantumTVBaseIE
._HOST
, r
'\d+', 'recording')
723 'url': 'https://quantum-tv.com/recordings?recording=193615508',
724 'only_matching': True,
726 'url': 'https://quantum-tv.com/tc/ptc_recordings_all_recordings?recording=193615420',
727 'only_matching': True,
731 class OsnatelTVBaseIE(ZattooPlatformBaseIE
):
732 _NETRC_MACHINE
= 'osnateltv'
733 _HOST
= 'tvonline.osnatel.de'
736 class OsnatelTVIE(OsnatelTVBaseIE
):
737 _VALID_URL
= _create_valid_url(OsnatelTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
740 'url': 'https://tvonline.osnatel.de/program/daserste/210177916',
741 'only_matching': True,
743 'url': 'https://tvonline.osnatel.de/guide/german?channel=srf1&program=169860555',
744 'only_matching': True,
748 class OsnatelTVLiveIE(OsnatelTVBaseIE
):
749 _VALID_URL
= _create_valid_url(OsnatelTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
752 'url': 'https://tvonline.osnatel.de/channels/german?channel=srf_zwei',
753 'only_matching': True,
755 'url': 'https://tvonline.osnatel.de/live/srf1',
756 'only_matching': True,
760 def suitable(cls
, url
):
761 return False if OsnatelTVIE
.suitable(url
) else super().suitable(url
)
764 class OsnatelTVRecordingsIE(OsnatelTVBaseIE
):
765 _VALID_URL
= _create_valid_url(OsnatelTVBaseIE
._HOST
, r
'\d+', 'recording')
768 'url': 'https://tvonline.osnatel.de/recordings?recording=193615508',
769 'only_matching': True,
771 'url': 'https://tvonline.osnatel.de/tc/ptc_recordings_all_recordings?recording=193615420',
772 'only_matching': True,
776 class EinsUndEinsTVBaseIE(ZattooPlatformBaseIE
):
777 _NETRC_MACHINE
= '1und1tv'
779 _API_HOST
= 'www.%s' % _HOST
782 class EinsUndEinsTVIE(EinsUndEinsTVBaseIE
):
783 _VALID_URL
= _create_valid_url(EinsUndEinsTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
786 'url': 'https://1und1.tv/program/daserste/210177916',
787 'only_matching': True,
789 'url': 'https://1und1.tv/guide/german?channel=srf1&program=169860555',
790 'only_matching': True,
794 class EinsUndEinsTVLiveIE(EinsUndEinsTVBaseIE
):
795 _VALID_URL
= _create_valid_url(EinsUndEinsTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
798 'url': 'https://1und1.tv/channels/german?channel=srf_zwei',
799 'only_matching': True,
801 'url': 'https://1und1.tv/live/srf1',
802 'only_matching': True,
806 def suitable(cls
, url
):
807 return False if EinsUndEinsTVIE
.suitable(url
) else super().suitable(url
)
810 class EinsUndEinsTVRecordingsIE(EinsUndEinsTVBaseIE
):
811 _VALID_URL
= _create_valid_url(EinsUndEinsTVBaseIE
._HOST
, r
'\d+', 'recording')
814 'url': 'https://1und1.tv/recordings?recording=193615508',
815 'only_matching': True,
817 'url': 'https://1und1.tv/tc/ptc_recordings_all_recordings?recording=193615420',
818 'only_matching': True,
822 class SaltTVBaseIE(ZattooPlatformBaseIE
):
823 _NETRC_MACHINE
= 'salttv'
827 class SaltTVIE(SaltTVBaseIE
):
828 _VALID_URL
= _create_valid_url(SaltTVBaseIE
._HOST
, r
'\d+', 'program', '(?:program|watch)/[^/]+')
831 'url': 'https://tv.salt.ch/program/daserste/210177916',
832 'only_matching': True,
834 'url': 'https://tv.salt.ch/guide/german?channel=srf1&program=169860555',
835 'only_matching': True,
839 class SaltTVLiveIE(SaltTVBaseIE
):
840 _VALID_URL
= _create_valid_url(SaltTVBaseIE
._HOST
, r
'[^/?&#]+', 'channel', 'live')
843 'url': 'https://tv.salt.ch/channels/german?channel=srf_zwei',
844 'only_matching': True,
846 'url': 'https://tv.salt.ch/live/srf1',
847 'only_matching': True,
851 def suitable(cls
, url
):
852 return False if SaltTVIE
.suitable(url
) else super().suitable(url
)
855 class SaltTVRecordingsIE(SaltTVBaseIE
):
856 _VALID_URL
= _create_valid_url(SaltTVBaseIE
._HOST
, r
'\d+', 'recording')
859 'url': 'https://tv.salt.ch/recordings?recording=193615508',
860 'only_matching': True,
862 'url': 'https://tv.salt.ch/tc/ptc_recordings_all_recordings?recording=193615420',
863 'only_matching': True,