巧用 cloudflare workers 代理B站视频
服务器运维
2020-02-23
4492
0

本文将讨论如何使用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代理视频

  1. 注册好你的专属worker域名后从后台管理页面进入workers管理。

  1. 选择创建一个新的worker。

  2. 首先可以给你的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
    }
  });
}

  1. 简单说明一下代码。我设定的URL采用了伪静态的设计模式,以方便CDN缓存我们的视频。例如我们之前的视频,就通过如下URL访问。

https://video.example.workers.dev/19390801/31621681.mp4

其中19390801表示的是视频的AID,31621681表示的是视频的CID。当然你也可以根据你的喜好修改代码换成参数形式的URL等等。

我的代码默认只有白名单数组中的网站可以访问到视频,其他网站或者直接访问都会直接403报错。在调试阶段建议先将防盗链相关代码注释。当然可以根据你的实际情况做代码上的调整。

  1. 最后我们还可以将使用自己的域名来访问workers,并且笔者也推荐这么做,毕竟worker.dev这个域名是共享的,指不定哪天就因为被滥用而遭DNS污染。如图在域名管理中找到worker选项卡,添加一个新的路由,将你想自定义的域名填入,worker下拉框里选择你刚刚创建好的worker脚本,点击保存。

回到DNS管理中,添加一条新的CNAME记录,Name为你自定义的域名,Target填入刚刚你的cloudflare提供给你的workers.dev域名地址。添加完成后就可使用你自己的域名来访问啦!

温馨提示

  1. 即便加了防盗链,请求失败也会消耗一天中的worker是使用次数,因此建议不要将视频直接用video标签“裸奔”,而是动态使用Dplayer、CKplayer等播放器加载,以避免被采集走而造成的workers使用次数浪费。
  2. 本文只用于技术交流,使用B站UP主视频前应获取原作者同意。

遗留问题

目前网上关于cloudflare workers的相关内容少之甚少,笔者也是爬了大量帖子才一点一点拼凑出这段代码。目前存在的问题是cloudflare无法缓存代理的视频,笔者尝试了各种方法,包括添加自定义页面规则,代码中添加缓存头,返回头中缓存状态依然为MISS。希望能有大神能够找到解决方案。

参考文档

CloudflareWeb Worker