]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/fancode.py
[test/download] Fallback test to `bv`
[yt-dlp.git] / yt_dlp / extractor / fancode.py
CommitLineData
b0089e89 1# coding: utf-8
2from __future__ import unicode_literals
3
4from .common import InfoExtractor
5
6from ..compat import compat_str
7from ..utils import (
8 parse_iso8601,
9 ExtractorError,
30d569d2 10 try_get,
11 mimetype2ext
b0089e89 12)
13
14
15class FancodeVodIE(InfoExtractor):
16 IE_NAME = 'fancode:vod'
17
18 _VALID_URL = r'https?://(?:www\.)?fancode\.com/video/(?P<id>[0-9]+)\b'
19
20 _TESTS = [{
21 'url': 'https://fancode.com/video/15043/match-preview-pbks-vs-mi',
22 'params': {
23 'skip_download': True,
b0089e89 24 },
25 'info_dict': {
26 'id': '6249806281001',
27 'ext': 'mp4',
28 'title': 'Match Preview: PBKS vs MI',
29 'thumbnail': r're:^https?://.*\.jpg$',
30 "timestamp": 1619081590,
31 'view_count': int,
32 'like_count': int,
33 'upload_date': '20210422',
34 'uploader_id': '6008340455001'
35 }
36 }, {
37 'url': 'https://fancode.com/video/15043',
38 'only_matching': True,
39 }]
40
30d569d2 41 _ACCESS_TOKEN = None
42 _NETRC_MACHINE = 'fancode'
43
44 _LOGIN_HINT = 'Use "--user refresh --password <refresh_token>" to login using a refresh token'
45
46 headers = {
47 'content-type': 'application/json',
48 'origin': 'https://fancode.com',
49 'referer': 'https://fancode.com',
50 }
51
52 def _login(self):
53 # Access tokens are shortlived, so get them using the refresh token.
54 username, password = self._get_login_info()
55 if username == 'refresh' and password is not None:
56 self.report_login()
57 data = '''{
58 "query":"mutation RefreshToken($refreshToken: String\\u0021) { refreshToken(refreshToken: $refreshToken) { accessToken }}",
59 "variables":{
60 "refreshToken":"%s"
61 },
62 "operationName":"RefreshToken"
63 }''' % password
64
65 token_json = self.download_gql('refresh token', data, "Getting the Access token")
66 self._ACCESS_TOKEN = try_get(token_json, lambda x: x['data']['refreshToken']['accessToken'])
67 if self._ACCESS_TOKEN is None:
68 self.report_warning('Failed to get Access token')
69 else:
70 self.headers.update({'Authorization': 'Bearer %s' % self._ACCESS_TOKEN})
71 elif username is not None:
72 self.report_warning(f'Login using username and password is not currently supported. {self._LOGIN_HINT}')
73
74 def _real_initialize(self):
75 self._login()
76
77 def _check_login_required(self, is_available, is_premium):
78 msg = None
79 if is_premium and self._ACCESS_TOKEN is None:
80 msg = f'This video is only available for registered users. {self._LOGIN_HINT}'
81 elif not is_available and self._ACCESS_TOKEN is not None:
82 msg = 'This video isn\'t available to the current logged in account'
83 if msg:
84 self.raise_login_required(msg, metadata_available=True, method=None)
85
86 def download_gql(self, variable, data, note, fatal=False, headers=headers):
87 return self._download_json(
88 'https://www.fancode.com/graphql', variable,
89 data=data.encode(), note=note,
90 headers=headers, fatal=fatal)
91
b0089e89 92 def _real_extract(self, url):
93
94 BRIGHTCOVE_URL_TEMPLATE = 'https://players.brightcove.net/%s/default_default/index.html?videoId=%s'
b0089e89 95 video_id = self._match_id(url)
b0089e89 96
30d569d2 97 brightcove_user_id = '6008340455001'
b0089e89 98 data = '''{
99 "query":"query Video($id: Int\\u0021, $filter: SegmentFilter) { media(id: $id, filter: $filter) { id contentId title contentId publishedTime totalViews totalUpvotes provider thumbnail { src } mediaSource {brightcove } duration isPremium isUserEntitled tags duration }}",
100 "variables":{
101 "id":%s,
102 "filter":{
103 "contentDataType":"DEFAULT"
104 }
105 },
106 "operationName":"Video"
30d569d2 107 }''' % video_id
b0089e89 108
30d569d2 109 metadata_json = self.download_gql(video_id, data, note='Downloading metadata')
b0089e89 110
111 media = try_get(metadata_json, lambda x: x['data']['media'], dict) or {}
112 brightcove_video_id = try_get(media, lambda x: x['mediaSource']['brightcove'], compat_str)
113
114 if brightcove_video_id is None:
115 raise ExtractorError('Unable to extract brightcove Video ID')
116
117 is_premium = media.get('isPremium')
30d569d2 118
119 self._check_login_required(media.get('isUserEntitled'), is_premium)
b0089e89 120
121 return {
122 '_type': 'url_transparent',
123 'url': BRIGHTCOVE_URL_TEMPLATE % (brightcove_user_id, brightcove_video_id),
124 'ie_key': 'BrightcoveNew',
125 'id': video_id,
126 'title': media['title'],
127 'like_count': media.get('totalUpvotes'),
128 'view_count': media.get('totalViews'),
129 'tags': media.get('tags'),
130 'release_timestamp': parse_iso8601(media.get('publishedTime')),
131 'availability': self._availability(needs_premium=is_premium),
132 }
30d569d2 133
134
135class FancodeLiveIE(FancodeVodIE):
136 IE_NAME = 'fancode:live'
137
138 _VALID_URL = r'https?://(www\.)?fancode\.com/match/(?P<id>[0-9]+).+'
139
140 _TESTS = [{
141 'url': 'https://fancode.com/match/35328/cricket-fancode-ecs-hungary-2021-bub-vs-blb?slug=commentary',
142 'info_dict': {
143 'id': '35328',
144 'ext': 'mp4',
145 'title': 'BUB vs BLB',
146 "timestamp": 1624863600,
147 'is_live': True,
148 'upload_date': '20210628',
149 },
150 'skip': 'Ended'
151 }, {
152 'url': 'https://fancode.com/match/35328/',
153 'only_matching': True,
154 }, {
155 'url': 'https://fancode.com/match/35567?slug=scorecard',
156 'only_matching': True,
157 }]
158
159 def _real_extract(self, url):
160
161 id = self._match_id(url)
162 data = '''{
163 "query":"query MatchResponse($id: Int\\u0021, $isLoggedIn: Boolean\\u0021) { match: matchWithScores(id: $id) { id matchDesc mediaId videoStreamId videoStreamUrl { ...VideoSource } liveStreams { videoStreamId videoStreamUrl { ...VideoSource } contentId } name startTime streamingStatus isPremium isUserEntitled @include(if: $isLoggedIn) status metaTags bgImage { src } sport { name slug } tour { id name } squads { name shortName } liveStreams { contentId } mediaId }}fragment VideoSource on VideoSource { title description posterUrl url deliveryType playerType}",
164 "variables":{
165 "id":%s,
166 "isLoggedIn":true
167 },
168 "operationName":"MatchResponse"
169 }''' % id
170
171 info_json = self.download_gql(id, data, "Info json")
172
173 match_info = try_get(info_json, lambda x: x['data']['match'])
174
9c95ac67 175 if match_info.get('streamingStatus') != "STARTED":
30d569d2 176 raise ExtractorError('The stream can\'t be accessed', expected=True)
177 self._check_login_required(match_info.get('isUserEntitled'), True) # all live streams are premium only
178
179 return {
180 'id': id,
181 'title': match_info.get('name'),
182 'formats': self._extract_akamai_formats(try_get(match_info, lambda x: x['videoStreamUrl']['url']), id),
183 'ext': mimetype2ext(try_get(match_info, lambda x: x['videoStreamUrl']['deliveryType'])),
184 'is_live': True,
185 'release_timestamp': parse_iso8601(match_info.get('startTime'))
186 }