1 from .common
import InfoExtractor
2 from ..utils
import ExtractorError
, mimetype2ext
, parse_iso8601
, try_get
5 class FancodeVodIE(InfoExtractor
):
7 IE_NAME
= 'fancode:vod'
9 _VALID_URL
= r
'https?://(?:www\.)?fancode\.com/video/(?P<id>[0-9]+)\b'
12 'url': 'https://fancode.com/video/15043/match-preview-pbks-vs-mi',
14 'skip_download': True,
17 'id': '6249806281001',
19 'title': 'Match Preview: PBKS vs MI',
20 'thumbnail': r
're:^https?://.*\.jpg$',
21 'timestamp': 1619081590,
24 'upload_date': '20210422',
25 'uploader_id': '6008340455001',
28 'url': 'https://fancode.com/video/15043',
29 'only_matching': True,
33 _NETRC_MACHINE
= 'fancode'
35 _LOGIN_HINT
= 'Use "--username refresh --password <refresh_token>" to login using a refresh token'
38 'content-type': 'application/json',
39 'origin': 'https://fancode.com',
40 'referer': 'https://fancode.com',
43 def _perform_login(self
, username
, password
):
44 # Access tokens are shortlived, so get them using the refresh token.
45 if username
!= 'refresh':
46 self
.report_warning(f
'Login using username and password is not currently supported. {self._LOGIN_HINT}')
50 "query":"mutation RefreshToken($refreshToken: String\\u0021) { refreshToken(refreshToken: $refreshToken) { accessToken }}",
54 "operationName":"RefreshToken"
55 }''' % password
# noqa: UP031
57 token_json
= self
.download_gql('refresh token', data
, 'Getting the Access token')
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')
62 self
.headers
.update({'Authorization': f'Bearer {self._ACCESS_TOKEN}
'})
64 def _check_login_required(self, is_available, is_premium):
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
'
71 self.raise_login_required(msg, metadata_available=True, method=None)
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)
79 def _real_extract(self, url):
81 BRIGHTCOVE_URL_TEMPLATE = 'https
://players
.brightcove
.net
/%s/default_default
/index
.html?videoId
=%s'
82 video_id = self._match_id(url)
84 brightcove_user_id = '6008340455001'
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 }}",
90 "contentDataType":"DEFAULT"
93 "operationName":"Video"
94 }''' % video_id # noqa: UP031
96 metadata_json = self.download_gql(video_id, data, note='Downloading metadata
')
98 media = try_get(metadata_json, lambda x: x['data
']['media
'], dict) or {}
99 brightcove_video_id = try_get(media, lambda x: x['mediaSource
']['brightcove
'], str)
101 if brightcove_video_id is None:
102 raise ExtractorError('Unable to extract brightcove Video ID
')
104 is_premium = media.get('isPremium
')
106 self._check_login_required(media.get('isUserEntitled
'), is_premium)
109 '_type
': 'url_transparent
',
110 'url
': BRIGHTCOVE_URL_TEMPLATE % (brightcove_user_id, brightcove_video_id),
111 'ie_key
': 'BrightcoveNew
',
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),
122 class FancodeLiveIE(FancodeVodIE): # XXX: Do not subclass from concrete IE
124 IE_NAME = 'fancode
:live
'
126 _VALID_URL = r'https?
://(www\
.)?fancode\
.com
/match
/(?P
<id>[0-9]+).+'
129 'url
': 'https
://fancode
.com
/match
/35328/cricket
-fancode
-ecs
-hungary
-2021-bub
-vs
-blb?slug
=commentary
',
133 'title
': 'BUB vs BLB
',
134 'timestamp
': 1624863600,
136 'upload_date
': '20210628',
140 'url
': 'https
://fancode
.com
/match
/35328/',
141 'only_matching
': True,
143 'url
': 'https
://fancode
.com
/match
/35567?slug
=scorecard
',
144 'only_matching
': True,
147 def _real_extract(self, url):
149 video_id = self._match_id(url)
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}",
156 "operationName":"MatchResponse"
157 }''' % video_id # noqa: UP031
159 info_json = self.download_gql(video_id, data, 'Info json
')
161 match_info = try_get(info_json, lambda x: x['data
']['match
'])
163 if match_info.get('streamingStatus
') != 'STARTED
':
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
169 'title
': match_info.get('name
'),
170 'formats
': self._extract_akamai_formats(try_get(match_info, lambda x: x['videoStreamUrl
']['url
']), video_id),
171 'ext
': mimetype2ext(try_get(match_info, lambda x: x['videoStreamUrl
']['deliveryType
'])),
173 'release_timestamp
': parse_iso8601(match_info.get('startTime
')),