mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-11-04 08:35:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# coding: utf-8
 | 
						|
from __future__ import unicode_literals
 | 
						|
 | 
						|
from .common import InfoExtractor
 | 
						|
 | 
						|
from ..compat import compat_str
 | 
						|
from ..utils import (
 | 
						|
    parse_iso8601,
 | 
						|
    ExtractorError,
 | 
						|
    try_get,
 | 
						|
    mimetype2ext
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
class FancodeVodIE(InfoExtractor):
 | 
						|
    IE_NAME = 'fancode:vod'
 | 
						|
 | 
						|
    _VALID_URL = r'https?://(?:www\.)?fancode\.com/video/(?P<id>[0-9]+)\b'
 | 
						|
 | 
						|
    _TESTS = [{
 | 
						|
        'url': 'https://fancode.com/video/15043/match-preview-pbks-vs-mi',
 | 
						|
        'params': {
 | 
						|
            'skip_download': True,
 | 
						|
            'format': 'bestvideo'
 | 
						|
        },
 | 
						|
        'info_dict': {
 | 
						|
            'id': '6249806281001',
 | 
						|
            'ext': 'mp4',
 | 
						|
            'title': 'Match Preview: PBKS vs MI',
 | 
						|
            'thumbnail': r're:^https?://.*\.jpg$',
 | 
						|
            "timestamp": 1619081590,
 | 
						|
            'view_count': int,
 | 
						|
            'like_count': int,
 | 
						|
            'upload_date': '20210422',
 | 
						|
            'uploader_id': '6008340455001'
 | 
						|
        }
 | 
						|
    }, {
 | 
						|
        'url': 'https://fancode.com/video/15043',
 | 
						|
        'only_matching': True,
 | 
						|
    }]
 | 
						|
 | 
						|
    _ACCESS_TOKEN = None
 | 
						|
    _NETRC_MACHINE = 'fancode'
 | 
						|
 | 
						|
    _LOGIN_HINT = 'Use "--user refresh --password <refresh_token>" to login using a refresh token'
 | 
						|
 | 
						|
    headers = {
 | 
						|
        'content-type': 'application/json',
 | 
						|
        'origin': 'https://fancode.com',
 | 
						|
        'referer': 'https://fancode.com',
 | 
						|
    }
 | 
						|
 | 
						|
    def _login(self):
 | 
						|
        # Access tokens are shortlived, so get them using the refresh token.
 | 
						|
        username, password = self._get_login_info()
 | 
						|
        if username == 'refresh' and password is not None:
 | 
						|
            self.report_login()
 | 
						|
            data = '''{
 | 
						|
                "query":"mutation RefreshToken($refreshToken: String\\u0021) { refreshToken(refreshToken: $refreshToken) { accessToken }}",
 | 
						|
                "variables":{
 | 
						|
                    "refreshToken":"%s"
 | 
						|
                },
 | 
						|
                "operationName":"RefreshToken"
 | 
						|
            }''' % password
 | 
						|
 | 
						|
            token_json = self.download_gql('refresh token', data, "Getting the Access token")
 | 
						|
            self._ACCESS_TOKEN = try_get(token_json, lambda x: x['data']['refreshToken']['accessToken'])
 | 
						|
            if self._ACCESS_TOKEN is None:
 | 
						|
                self.report_warning('Failed to get Access token')
 | 
						|
            else:
 | 
						|
                self.headers.update({'Authorization': 'Bearer %s' % self._ACCESS_TOKEN})
 | 
						|
        elif username is not None:
 | 
						|
            self.report_warning(f'Login using username and password is not currently supported. {self._LOGIN_HINT}')
 | 
						|
 | 
						|
    def _real_initialize(self):
 | 
						|
        self._login()
 | 
						|
 | 
						|
    def _check_login_required(self, is_available, is_premium):
 | 
						|
        msg = None
 | 
						|
        if is_premium and self._ACCESS_TOKEN is None:
 | 
						|
            msg = f'This video is only available for registered users. {self._LOGIN_HINT}'
 | 
						|
        elif not is_available and self._ACCESS_TOKEN is not None:
 | 
						|
            msg = 'This video isn\'t available to the current logged in account'
 | 
						|
        if msg:
 | 
						|
            self.raise_login_required(msg, metadata_available=True, method=None)
 | 
						|
 | 
						|
    def download_gql(self, variable, data, note, fatal=False, headers=headers):
 | 
						|
        return self._download_json(
 | 
						|
            'https://www.fancode.com/graphql', variable,
 | 
						|
            data=data.encode(), note=note,
 | 
						|
            headers=headers, fatal=fatal)
 | 
						|
 | 
						|
    def _real_extract(self, url):
 | 
						|
 | 
						|
        BRIGHTCOVE_URL_TEMPLATE = 'https://players.brightcove.net/%s/default_default/index.html?videoId=%s'
 | 
						|
        video_id = self._match_id(url)
 | 
						|
 | 
						|
        brightcove_user_id = '6008340455001'
 | 
						|
        data = '''{
 | 
						|
            "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 }}",
 | 
						|
            "variables":{
 | 
						|
                "id":%s,
 | 
						|
                "filter":{
 | 
						|
                    "contentDataType":"DEFAULT"
 | 
						|
                }
 | 
						|
            },
 | 
						|
            "operationName":"Video"
 | 
						|
        }''' % video_id
 | 
						|
 | 
						|
        metadata_json = self.download_gql(video_id, data, note='Downloading metadata')
 | 
						|
 | 
						|
        media = try_get(metadata_json, lambda x: x['data']['media'], dict) or {}
 | 
						|
        brightcove_video_id = try_get(media, lambda x: x['mediaSource']['brightcove'], compat_str)
 | 
						|
 | 
						|
        if brightcove_video_id is None:
 | 
						|
            raise ExtractorError('Unable to extract brightcove Video ID')
 | 
						|
 | 
						|
        is_premium = media.get('isPremium')
 | 
						|
 | 
						|
        self._check_login_required(media.get('isUserEntitled'), is_premium)
 | 
						|
 | 
						|
        return {
 | 
						|
            '_type': 'url_transparent',
 | 
						|
            'url': BRIGHTCOVE_URL_TEMPLATE % (brightcove_user_id, brightcove_video_id),
 | 
						|
            'ie_key': 'BrightcoveNew',
 | 
						|
            'id': video_id,
 | 
						|
            'title': media['title'],
 | 
						|
            'like_count': media.get('totalUpvotes'),
 | 
						|
            'view_count': media.get('totalViews'),
 | 
						|
            'tags': media.get('tags'),
 | 
						|
            'release_timestamp': parse_iso8601(media.get('publishedTime')),
 | 
						|
            'availability': self._availability(needs_premium=is_premium),
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
class FancodeLiveIE(FancodeVodIE):
 | 
						|
    IE_NAME = 'fancode:live'
 | 
						|
 | 
						|
    _VALID_URL = r'https?://(www\.)?fancode\.com/match/(?P<id>[0-9]+).+'
 | 
						|
 | 
						|
    _TESTS = [{
 | 
						|
        'url': 'https://fancode.com/match/35328/cricket-fancode-ecs-hungary-2021-bub-vs-blb?slug=commentary',
 | 
						|
        'info_dict': {
 | 
						|
            'id': '35328',
 | 
						|
            'ext': 'mp4',
 | 
						|
            'title': 'BUB vs BLB',
 | 
						|
            "timestamp": 1624863600,
 | 
						|
            'is_live': True,
 | 
						|
            'upload_date': '20210628',
 | 
						|
        },
 | 
						|
        'skip': 'Ended'
 | 
						|
    }, {
 | 
						|
        'url': 'https://fancode.com/match/35328/',
 | 
						|
        'only_matching': True,
 | 
						|
    }, {
 | 
						|
        'url': 'https://fancode.com/match/35567?slug=scorecard',
 | 
						|
        'only_matching': True,
 | 
						|
    }]
 | 
						|
 | 
						|
    def _real_extract(self, url):
 | 
						|
 | 
						|
        id = self._match_id(url)
 | 
						|
        data = '''{
 | 
						|
            "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}",
 | 
						|
            "variables":{
 | 
						|
                "id":%s,
 | 
						|
                "isLoggedIn":true
 | 
						|
            },
 | 
						|
            "operationName":"MatchResponse"
 | 
						|
        }''' % id
 | 
						|
 | 
						|
        info_json = self.download_gql(id, data, "Info json")
 | 
						|
 | 
						|
        match_info = try_get(info_json, lambda x: x['data']['match'])
 | 
						|
 | 
						|
        if match_info.get('streamingStatus') != "STARTED":
 | 
						|
            raise ExtractorError('The stream can\'t be accessed', expected=True)
 | 
						|
        self._check_login_required(match_info.get('isUserEntitled'), True)  # all live streams are premium only
 | 
						|
 | 
						|
        return {
 | 
						|
            'id': id,
 | 
						|
            'title': match_info.get('name'),
 | 
						|
            'formats': self._extract_akamai_formats(try_get(match_info, lambda x: x['videoStreamUrl']['url']), id),
 | 
						|
            'ext': mimetype2ext(try_get(match_info, lambda x: x['videoStreamUrl']['deliveryType'])),
 | 
						|
            'is_live': True,
 | 
						|
            'release_timestamp': parse_iso8601(match_info.get('startTime'))
 | 
						|
        }
 |