前言

越能体现人性尊严的快乐,越是一种最大的快乐,因为他跟人的尊严是相关的。有很多快乐,是降低了人性的尊严。而越能体现人性尊严的快乐,越是一种最高级的快乐,我们之所以读书行路,其实就是希望我们能够不断的。享受什么的快乐呢?高级的快乐。—- 罗翔老师经典语录

image.png

开始

今天看到一个开源库,用Node.js下载Youtube 视频,我眼前一亮,马上打开页面,看了一下介绍,好家伙,这么容易使用,那以后打工人上下班看离线视频那不是方便多了。我们先看一下API https://github.com/fent/node-ytdl-core

image.png

这API可真是简洁啊,我们把包在本地安装,然后下载不就好了。事情肯定没有这么简单,网络不通,哈哈。没关系~我有云,我有Serverless,我不用,就是玩。不不不,确实要用。

这里我的思路就是通过API网关生成一个URL,调用之后转到云函数,云函数下载视频到临时文件夹,云函数使用运行角色密钥上传视频文件至COS,我们再通过COS客户端在手机上面看视频。把函数部署到香港地域就没有网络的限制了,说着说着我都激动了一把。

image.png

大概思路就是图上这样。那我们开干吧。
这里我选择腾讯云作为云提供商,因为腾讯云一直有免费的额度可以用,我们这一点点运算量,基本上不要米。技术上我们用Node.js,当然要上TypeScript这样才有逼格。
我们通过Serverless这个框架创建一个SCF函数,然后基于它编写代码。

$ npm i serverless -g
$ serverless init scf-demo

这样一个项目就创建好了 我们进入src/index.js下编写文件,先把后缀改为.ts,然后接着配置

$ npm i ytdl-core cos-nodejs-sdk-v5 -S
$ npm i typescript ts-node prettier -D

这里ytdl-core是下载视频的包,cos-nodejs-sdk-v5是上传视频到COS的Node SDK

然后我们这里还需要一个强悍的打包工具,把所有代码打包到一起,毕竟云函数是不支持运行TS的。
这里我推荐一个很好用的打包工具ncc,零配置,内置TS,按需加载,支持所有node包

$ ncc build input.js -o dist

可以看到,打包方法非常简单,到时候我们把这个脚本配置到npm build命令里那不是美滋滋。我们全局安装一下

$ npm i -g @vercel/ncc

编码

接下来我们编写代码:

const fs = require('fs')
const path = require('path')
const ytdl = require('ytdl-core')
const COS = require('cos-nodejs-sdk-v5')

exports.main_handler = (event, context, callback) => {
  ytdl('https://www.youtube.com/watch?v=aqz-KE-bpKQ', {
    // 这里为了演示,只下载一小段,免得浪费大家流量
    range: {
      start: 0,
      end: 5355705,
    },
  })
    .pipe(fs.createWriteStream(path.join('/tmp', 'video.mp4')))
    .on('finish', () => {
      // 这里有个坑,临时密钥一定要填这个SESSION TOKEN 不然怎么都上传不了
      // 通过运行角色获取临时密钥
      const environment = JSON.parse(context.environment)
      const cos = new COS({
        SecretId: environment.TENCENTCLOUD_SECRETID,
        SecretKey: environment.TENCENTCLOUD_SECRETKEY,
        SecurityToken: environment.TENCENTCLOUD_SESSIONTOKEN,
      })

      cos.putObject(
        {
          Bucket: 'youtube-1253555942' /* 我们创建的COS Bucket */,
          Region: 'ap-hongkong' /* 地域 */,
          Key: `video-${Date.now()}.mp4` /* 文件名 */,
          StorageClass: 'STANDARD' /* 默认就好了 */,
          Body: fs.createReadStream(path.join('/tmp', 'video.mp4')), // 上传文件对象
          onProgress: function (progressData) {
            console.log(JSON.stringify(progressData))
          },
        },
        function (err, data) {
          console.log(err || data)
          callback(null, 'ok')
        },
      )
    })
    .on('error', (e) => {
      console.log(e)
    })
}

这段代码的逻辑:引入包,进入主函数main_handler,编写下载逻辑,存入临时文件,创建COS对象,上传COS文件,结束。一气呵成,非常顺滑。TS的类型就交给你们补充了。云函数支持两种异步方法,一种async,一种callback,这里我们用到了流所以callback的方式会简单一些,调用callback告诉引擎函数执行完毕。
我们再配置一下项目就可以运行起来了。首先是serverless.yml这个是上传至云函数的配置,这里用到香港地域,还有代码路径填我们打包后的目录,这样可以少上传些文件,让部署速度更快,其它配置基本不变。

#应用信息
app: scf-demo-3723d4bb

#组件信息
component: scf # (必填) 引用 component 的名称,当前用到的是 tencent-scf 组件
name: youtube # (必填) 创建的实例名称,请修改成您的实例名称

#组件参数
inputs:
  name: ${name}-${stage} #函数名称
  src: ./dist  #代码路径
  handler: index.main_handler #入口
  runtime: Nodejs10.15 # 云函数运行时的环境
  region: ap-hongkong # 云函数所在区域
  events: # 触发器
    - apigw: # 网关触发器
        parameters:
          endpoints:
            - path: /
              method: GET

再配置一下我们的deploy脚本,这样我们运行npm run deploy时,会先执行打包再部署。

"scripts": {
    "start": "ts-node src/local.ts",
    "build": "ncc build src/index.ts",
    "deploy": "npm run build && sls deploy"
  },

一切准备就绪,运行npm run deploy,可以看到已经成功上传到云函数了。并且还帮我们创建了一个网关触发器,可以通过URL直接触发访问函数

image.png

image.png

我们登上函数控制台页面,在函数配置这里把运行角色给加上,后续上传到COS,就可以直接从函数运行上下文中取到SecretKey和SecretID了,不用写死在代码里面,很方便。

image.png

我们手动测试一下函数

image.png

不错,成功了

image.png

image.png

可以看到COS里已经有我们想下载的视频文件了,xdm是不是很方便。
这里我偷个懒,通过网关URL参数获取视频地址,这个功能就交给你们开发了。
我把代码提交一下,大家可以直接Fork到自己仓库下开发。

serverless-youtube-download

image.png

再见

感谢大家支持,我们下期再见。

image.png