本文将讨论如何使用cloudflare推出的workers边缘计算功能来代理B站视频。
从B站iframe视频嵌套说开去
笔者的几个网站经常需要引入一些B站视频来丰富网站内容。虽然B站默认为我们提供了iframe嵌入代码来方便将视频插入自己的页面中,但是缺点是iframe页面所需要加载的js文件多而庞大,而且视频的小小的封面图片居然没有做任何的压缩处理,光是加载完框架就要用去了2MB流量,渲染的内容也特别多,在网络环境不佳的情况下会严重拖慢我们页面整体加载速度。那么,如果我们能解析出B站视频地址直接加载不就好了吗?
B站视频解析API
网上搜寻了一圈,发现目前网络上现成的第三方解析接口大多不支持B站视频。最终,我还是决定从官方的API下手。B站的视频有两个ID,一个是AID,也就是视频URL里AV后面一串数字。以下面视频为例,AID即为19390801。
https://www.bilibili.com/video/av19390801
另外一个为CID,CID是B站视频的唯一编号,也就是说一个视频可能有多个分P,它们共用一个AID,但是每个分P都有独有的CID。获取CID的方法也很简单,我们可以从视频的嵌入代码中找到。例如下面的嵌入代码,它的CID即为31621681。
<iframe src="//player.bilibili.com/player.html?aid=19390801&cid=31621681&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
获取到AVID和CID后,我们便可以通过B站官方的API来获取视频的解析地址了。解析地址如下,其中将{$aid}和{$cid}分别替换为你的视频的aid和cid即可。
https://api.bilibili.com/x/player/playurl?avid={$aid}&cid={$cid}&qn=64&type=&otype=json&fnver=0&fnval=1
API会返回一个json结果,解析出来的地址在data下的durl数组中。我们尝试用浏览器直接访问这个地址,可以正常播放。但是如果我们把它插入页面中访问,则返回403报错,这是因为B站开启了防盗链,非B站的referer都会直接403拒绝访问。而且这个地址是有时间限制的,超过了一定时间,这个地址就自动失效了。
解决办法也有,我们可以写一个自动化脚本,解析出视频地址,再反向代理这个视频即可。
cloudflare workers的引入
cloudflare是全球最大的CDN运营商之一,在2017年推出了Worker边缘计算,将Service worker搬到了云端,可以用于实现处理简单的运算,代理,图像处理等等。目前个人每天有10万次的免费计算额度,并且不计算消耗流量。利用Workers,我们可以在云端搭建一个代理服务器,帮我们自动解析B站视频地址,并返回视频内容。
cloudflare与workers的注册本文就不多赘述了,需要相关帮助可以到百度谷歌上搜索一下,有详细教程。本文将重点介绍worker的创建与使用。
使用worker代理视频
- 注册好你的专属worker域名后从后台管理页面进入workers管理。
- 选择创建一个新的worker。
-
首先可以给你的worker起一个固定的名字,例如本文的video。然后将下面的代码复制到代码区内,点击下方的保存。
/**
* Bilibili Proxy
* @Author: Tyler
* @Version:1.1
*/
//防盗链白名单
const ALLOWEDREFERERS = [
"www.example.com",
"servicewechat.com",
"tmaservice.developer.toutiao.com"
]
addEventListener("fetch", event => {
event.respondWith(getVideo(event));
})
async function getVideo(event) {
//防盗链判断 如果出现视频无法加载,请将这段代码注释
//Begin
let referer = event.request.headers.get("referer");
//如果referer为空或者不在白名单内,则返回403错误
let verified = false;
if (referer == null) {
return new Response('Sorry, you do not have permission to access.',
{ status: 403, statusText: 'Forbidden' })
}
for(let i = 0 ; i<ALLOWEDREFERERS.length;i++){
if (referer.includes(ALLOWEDREFERERS[i])){
verified= true;
break;
}
}
if(!verified){
return new Response('Sorry, you do not have permission to access.',
{ status: 403, statusText: 'Forbidden' })
}
//End
let url = new URL(event.request.url);
//截取/与.mp4之间的参数
let videoIDString = url.pathname.substring(1, url.pathname.length - 4);
//以“/”做间隔,拆分成数组,分别对应AVID,CID
let videoID = videoIDString.split('/');
let avid = videoID[0];
let cid = videoID[1];
console.log("AVID: " + avid, " CID: " + cid);
let APIURL = "https://api.bilibili.com/x/player/playurl?avid=" + avid + "&cid=" + cid + "&qn=64&otype=json&fnver=0&fnval=1";
let videoURL = await fetch(APIURL)
.then(function (response) {
return response.json();
})
.then(function ({ data }) {
let url = data.durl[0].url;
console.log(url);
return url;
})
let request = new Request(videoURL, event.request)
return fetch(request, {
cf: {
cacheEverything: true,
cacheTtl: 300
}
});
}
- 简单说明一下代码。我设定的URL采用了伪静态的设计模式,以方便CDN缓存我们的视频。例如我们之前的视频,就通过如下URL访问。
https://video.example.workers.dev/19390801/31621681.mp4
其中19390801表示的是视频的AID,31621681表示的是视频的CID。当然你也可以根据你的喜好修改代码换成参数形式的URL等等。
我的代码默认只有白名单数组中的网站可以访问到视频,其他网站或者直接访问都会直接403报错。在调试阶段建议先将防盗链相关代码注释。当然可以根据你的实际情况做代码上的调整。
- 最后我们还可以将使用自己的域名来访问workers,并且笔者也推荐这么做,毕竟worker.dev这个域名是共享的,指不定哪天就因为被滥用而遭DNS污染。如图在域名管理中找到worker选项卡,添加一个新的路由,将你想自定义的域名填入,worker下拉框里选择你刚刚创建好的worker脚本,点击保存。
回到DNS管理中,添加一条新的CNAME记录,Name为你自定义的域名,Target填入刚刚你的cloudflare提供给你的workers.dev域名地址。添加完成后就可使用你自己的域名来访问啦!
温馨提示
- 即便加了防盗链,请求失败也会消耗一天中的worker是使用次数,因此建议不要将视频直接用video标签“裸奔”,而是动态使用Dplayer、CKplayer等播放器加载,以避免被采集走而造成的workers使用次数浪费。
- 本文只用于技术交流,使用B站UP主视频前应获取原作者同意。
遗留问题
目前网上关于cloudflare workers的相关内容少之甚少,笔者也是爬了大量帖子才一点一点拼凑出这段代码。目前存在的问题是cloudflare无法缓存代理的视频,笔者尝试了各种方法,包括添加自定义页面规则,代码中添加缓存头,返回头中缓存状态依然为MISS。希望能有大神能够找到解决方案。