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