Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
XianxinMao
Yt Dlp
提交
30d569d2
Y
Yt Dlp
项目概览
XianxinMao
/
Yt Dlp
12 个月 前同步成功
通知
27
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
Yt Dlp
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
30d569d2
编写于
7月 07, 2021
作者:
Z
zenerdi0de
提交者:
GitHub
7月 07, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[fancode] Fix extraction, support live and allow login with refresh token (#471)
Authored-by: zenerdi0de
上级
08625e41
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
117 addition
and
17 deletion
+117
-17
yt_dlp/extractor/extractors.py
yt_dlp/extractor/extractors.py
+5
-1
yt_dlp/extractor/fancode.py
yt_dlp/extractor/fancode.py
+112
-16
未找到文件。
yt_dlp/extractor/extractors.py
浏览文件 @
30d569d2
...
...
@@ -398,7 +398,11 @@
FacebookIE
,
FacebookPluginsVideoIE
,
)
from
.fancode
import
FancodeVodIE
from
.fancode
import
(
FancodeVodIE
,
FancodeLiveIE
)
from
.faz
import
FazIE
from
.fc2
import
(
FC2IE
,
...
...
yt_dlp/extractor/fancode.py
浏览文件 @
30d569d2
...
...
@@ -7,7 +7,8 @@
from
..utils
import
(
parse_iso8601
,
ExtractorError
,
try_get
try_get
,
mimetype2ext
)
...
...
@@ -38,16 +39,63 @@ class FancodeVodIE(InfoExtractor):
'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
)
webpage
=
self
.
_download_webpage
(
url
,
video_id
)
brightcove_user_id
=
self
.
_html_search_regex
(
r
'(?:https?://)?players\.brightcove\.net/(\d+)/default_default/index(?:\.min)?\.js'
,
webpage
,
'user id'
)
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":{
...
...
@@ -57,15 +105,9 @@ def _real_extract(self, url):
}
},
"operationName":"Video"
}'''
%
video_id
}'''
%
video_id
metadata_json
=
self
.
_download_json
(
'https://www.fancode.com/graphql'
,
video_id
,
data
=
data
.
encode
(),
note
=
'Downloading metadata'
,
headers
=
{
'content-type'
:
'application/json'
,
'origin'
:
'https://fancode.com'
,
'referer'
:
url
,
})
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
)
...
...
@@ -74,8 +116,8 @@ def _real_extract(self, url):
raise
ExtractorError
(
'Unable to extract brightcove Video ID'
)
is_premium
=
media
.
get
(
'isPremium'
)
if
is_premium
:
self
.
report_warning
(
'this video requires a premium account'
,
video_id
)
self
.
_check_login_required
(
media
.
get
(
'isUserEntitled'
),
is_premium
)
return
{
'_type'
:
'url_transparent'
,
...
...
@@ -89,3 +131,57 @@ def _real_extract(self, url):
'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
(
'status'
)
!=
"LIVE"
:
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'
))
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录