diff --git a/youtube_dl/extractor/extractors.py b/youtube_dl/extractor/extractors.py index 2eed706f9..6d6e1bc93 100644 --- a/youtube_dl/extractor/extractors.py +++ b/youtube_dl/extractor/extractors.py @@ -1363,5 +1363,11 @@ from .youtube import ( ) from .zapiks import ZapiksIE from .zaq1 import Zaq1IE +from .zattoo import ( + QuicklineIE, + QuicklineLiveIE, + ZattooIE, + ZattooLiveIE, +) from .zdf import ZDFIE, ZDFChannelIE from .zingmp3 import ZingMp3IE diff --git a/youtube_dl/extractor/zattoo.py b/youtube_dl/extractor/zattoo.py new file mode 100644 index 000000000..d1f63a36f --- /dev/null +++ b/youtube_dl/extractor/zattoo.py @@ -0,0 +1,261 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import uuid +import re + +from .common import InfoExtractor +from ..utils import ( + compat_str, + ExtractorError, + sanitized_Request, + urlencode_postdata, + urljoin, +) + + +class ZattooBaseIE(InfoExtractor): + + _NETRC_MACHINE = 'zattoo' + _HOST_URL = 'https://zattoo.com/' + + def _login(self, uuid, session_id, video_id): + (username, password) = self._get_login_info() + if not username or not password: + raise ExtractorError( + 'A valid %s account is needed to access this media.' % self._NETRC_MACHINE, + expected=True) + login_form = { + 'login': username, + 'password': password, + 'remember': True, + } + request = sanitized_Request( + urljoin(self._HOST_URL, '/zapi/v2/account/login'), + urlencode_postdata(login_form)) + request.add_header( + 'Referer', urljoin(self._HOST_URL, '/login')) + request.add_header( + 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') + request.add_header( + 'Cookie', self._generate_cookie(uuid, session_id)) + response = self._request_webpage( + request, video_id, 'Logging in as %s' % login_form['login']) + cookie = response.headers.get('Set-Cookie') + pzuid = self._search_regex(r'pzuid\s*=\s*(.+?);', cookie, 'pzuid') + data = self._parse_json( + response.read(), video_id) + + return { + 'ppid': data['session']['ppid'], + 'powerhash': data['session']['power_guide_hash'], + 'pzuid': pzuid, + 'uuid': uuid, + 'session_id': session_id + } + + def _get_app_token_and_version(self, video_id): + host_webpage = self._download_webpage( + self._HOST_URL, video_id) + app_token = self._html_search_regex( + r'[^/]+)/(?P[0-9]+)' + + def _real_extract(self, url): + channel_name, video_id = re.match(self._VALID_URL, url).groups() + return self._extract_video(channel_name, video_id) + + +class QuicklineLiveIE(QuicklineBaseIE): + _VALID_URL = r'https?://(?:www\.)?mobiltv\.quickline\.com/watch/(?P[^/]+)' + + def _real_extract(self, url): + channel_name = video_id = self._match_id(url) + return self._extract_video(channel_name, video_id, is_live=True) + + +class ZattooIE(ZattooBaseIE): + _VALID_URL = r'https?://(?:www\.)?zattoo\.com/watch/(?P[^/]+)/(?P[0-9]+)' + + # Since videos are only available for 7 days, we cannot have detailed tests. + _TEST = { + 'url': 'https://zattoo.com/watch/prosieben/130671867-maze-runner-die-auserwaehlten-in-der-brandwueste', + 'only_matching': True, + } + + def _real_extract(self, url): + channel_name, video_id = re.match(self._VALID_URL, url).groups() + return self._extract_video(channel_name, video_id) + + +class ZattooLiveIE(ZattooBaseIE): + _VALID_URL = r'https?://(?:www\.)?zattoo\.com/watch/(?P[^/]+)' + + _TEST = { + 'url': 'https://zattoo.com/watch/srf1', + 'only_matching': True, + } + + def _real_extract(self, url): + channel_name = video_id = self._match_id(url) + return self._extract_video(channel_name, video_id, is_live=True)