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