]>
Commit | Line | Data |
---|---|---|
3ca3f77f U |
1 | # coding: utf-8 |
2 | from __future__ import unicode_literals | |
3 | ||
4 | import json | |
5 | ||
6 | from .common import InfoExtractor | |
7 | ||
8 | from ..utils import ( | |
9 | ExtractorError, | |
10 | unified_timestamp, | |
11 | ) | |
12 | ||
13 | ||
14 | class TennisTVIE(InfoExtractor): | |
15 | _VALID_URL = r'https?://(?:www\.)?tennistv\.com/videos/(?P<id>[-a-z0-9]+)' | |
16 | _TEST = { | |
17 | 'url': 'https://www.tennistv.com/videos/indian-wells-2018-verdasco-fritz', | |
18 | 'info_dict': { | |
19 | 'id': 'indian-wells-2018-verdasco-fritz', | |
20 | 'ext': 'mp4', | |
21 | 'title': 'Fernando Verdasco v Taylor Fritz', | |
22 | 'description': 're:^After his stunning victory.{174}$', | |
23 | 'thumbnail': 'https://atp-prod.akamaized.net/api/images/v1/images/112831/landscape/1242/0', | |
24 | 'timestamp': 1521017381, | |
25 | 'upload_date': '20180314', | |
26 | }, | |
27 | 'params': { | |
28 | 'skip_download': True, | |
29 | }, | |
30 | 'skip': 'Requires email and password of a subscribed account', | |
31 | } | |
32 | _NETRC_MACHINE = 'tennistv' | |
33 | ||
34 | def _login(self): | |
35 | username, password = self._get_login_info() | |
36 | if not username or not password: | |
37 | raise ExtractorError('No login info available, needed for using %s.' % self.IE_NAME, expected=True) | |
38 | ||
39 | login_form = { | |
40 | 'Email': username, | |
41 | 'Password': password, | |
42 | } | |
43 | login_json = json.dumps(login_form).encode('utf-8') | |
44 | headers = { | |
45 | 'content-type': 'application/json', | |
46 | 'Referer': 'https://www.tennistv.com/login', | |
47 | 'Origin': 'https://www.tennistv.com', | |
48 | } | |
49 | ||
50 | login_result = self._download_json( | |
51 | 'https://www.tennistv.com/api/users/v1/login', None, | |
52 | note='Logging in', | |
53 | errnote='Login failed (wrong password?)', | |
54 | headers=headers, | |
55 | data=login_json) | |
56 | ||
57 | if login_result['error']['errorCode']: | |
58 | raise ExtractorError('Login failed, %s said: %r' % (self.IE_NAME, login_result['error']['errorMessage'])) | |
59 | ||
60 | if login_result['entitlement'] != 'SUBSCRIBED': | |
61 | self.report_warning('%s may not be subscribed to %s.' % (username, self.IE_NAME)) | |
62 | ||
63 | self._session_token = login_result['sessionToken'] | |
64 | ||
65 | def _real_initialize(self): | |
66 | self._login() | |
67 | ||
68 | def _real_extract(self, url): | |
69 | video_id = self._match_id(url) | |
70 | webpage = self._download_webpage(url, video_id) | |
71 | ||
72 | internal_id = self._search_regex(r'video=([0-9]+)', webpage, 'internal video id') | |
73 | ||
74 | headers = { | |
75 | 'Origin': 'https://www.tennistv.com', | |
76 | 'authorization': 'ATP %s' % self._session_token, | |
77 | 'content-type': 'application/json', | |
78 | 'Referer': url, | |
79 | } | |
80 | check_data = { | |
81 | 'videoID': internal_id, | |
82 | 'VideoUrlType': 'HLSV3', | |
83 | } | |
84 | check_json = json.dumps(check_data).encode('utf-8') | |
85 | check_result = self._download_json( | |
86 | 'https://www.tennistv.com/api/users/v1/entitlementchecknondiva', | |
87 | video_id, note='Checking video authorization', headers=headers, data=check_json) | |
88 | formats = self._extract_m3u8_formats(check_result['contentUrl'], video_id, ext='mp4') | |
89 | ||
90 | vdata_url = 'https://www.tennistv.com/api/channels/v1/de/none/video/%s' % video_id | |
91 | vdata = self._download_json(vdata_url, video_id) | |
92 | ||
93 | timestamp = unified_timestamp(vdata['timestamp']) | |
94 | thumbnail = vdata['video']['thumbnailUrl'] | |
95 | description = vdata['displayText']['description'] | |
96 | title = vdata['video']['title'] | |
97 | ||
98 | series = vdata['tour'] | |
99 | venue = vdata['displayText']['venue'] | |
100 | round_str = vdata['seo']['round'] | |
101 | ||
102 | return { | |
103 | 'id': video_id, | |
104 | 'title': title, | |
105 | 'description': description, | |
106 | 'formats': formats, | |
107 | 'thumbnail': thumbnail, | |
108 | 'timestamp': timestamp, | |
109 | 'series': series, | |
110 | 'season': venue, | |
111 | 'episode': round_str, | |
112 | } |