xue5hen 发表于 2022-11-25 01:18

自己动手写个M3U8播放&下载工具

类似的工具论坛里有,自己编写主要是想进一步了解其中的门道。软件编写使用的是Electron+Vue。
【未加密视频的播放和下载】
这个比较简单,只在界面上设置了“视频地址输入”“下载目录设置”“播放按钮”“下载按钮”以及“播放区域”。

播放使用的是hls.js这个插件,使用方法如下:
this.hlsVideoObj = new HLS()
this.hlsVideoObj.attachMedia(this.$refs.video)
this.hlsVideoObj.on(HLS.Events.MEDIA_ATTACHED, () => {
    this.hlsVideoObj.loadSource(this.videoUrl)
})
this.$nextTick(() => {
    this.$refs.video.play()
})
下载功能是先使用fetch请求到m3u8的文件,然后使用m3u8-parser插件对文件内容进行解析,得到ts文件的列表,然后循环下载ts资源。待所有ts文件下载完成以后将它们合并并输出到本地下载目录。
downloadTs () {
    let urlArr = this.videoUrl.split('?').split('/')
    let baseUrl = urlArr.slice(0, urlArr.length - 1).join('/')
    let segments = this.parsedManifest.segments || []
    let fileName = urlArr + '.mp4'
    this.taskList = []
    this.dataList = []
    this.downloadingCount = segments.length
    segments.forEach((v, i) => {
      let tsUrl = `${baseUrl}/${v.uri}`
      this.taskList.push(
      fetch(tsUrl, {method: 'get', responseType: 'arraybuffer'}).then(res => {
            return res.arrayBuffer()
      }).then((ab) => {
            this.dataList = Buffer.from(ab)
      }).catch((err) => {
            console.log(`segment ${i}:`, err)
      }).finally(() => {
            this.downloadingCount--
      })
      )
    })
    Promise.all(this.taskList).then(() => {
      let filePath = path.join(this.toPath, fileName)
      let dataList = this.dataList.filter(v => v)
      let data = Buffer.concat(dataList)
      this.$sharedObject('jstoolsNode').writeFile(filePath, data).then(() =>{
            this.$toast('下载成功')
            this.taskList = []
            this.dataList = []
      }).catch((err) => {
            this.$toast('下载失败' + err)
      })
    })
}
【加密视频的播放和下载】
加密视频因为涉及到解密,所以会增加很多配置项,所以界面会相应变得复杂一些。

对于加密视频,首先有个套路,就是基本都会对请求源做校验,所以要先修改请求头,这里使用Electron的session模块拦截修改所有请求的referer
setOriginUrl () {
    let urlObj = {}
    try { urlObj = new URL(this.videoUrl) } catch (err) { console.log(err) }
    let origin = urlObj.origin || '*://*/*'
    let filters = {urls: [`${origin}/*`]}
    session.defaultSession.webRequest.onBeforeSendHeaders(filters, (details, callback) => {
      details.requestHeaders['Origin'] = origin
      details.requestHeaders['Referer'] = this.refererUrl
      callback({cancel: false, requestHeaders: details.requestHeaders})
    })
    this.$toast('请求源设置已生效')
}
接着,在m3u8中找到key对应的地址获取到key,这里接口返回的往往是经过加密的key,这时就需要解密一次

得到真实key以后,就可以用它对下载下来的ts数据进行解密了。
decrypt (buffer, key, iv) {
    key = this.key || key || ''
    iv = this.iv || iv || ''
    // 处理key和iv的格式
    let keyWA = key
    if (typeof key === 'string') {
      // 密钥转字节数组(16位)
      let keyBy = stringToBytes(key)
      // 字节数组转Uint8Array
      let keyBv = new Uint8Array(keyBy)
      // Uint8Array转WordArray
      keyWA = CryptoJS.enc.u8array.parse(keyBv)
    }
    let ivWA = iv
    if (typeof iv === 'string') {
      let ivBy = hexToBytes(iv)
      let ivBv = new Uint8Array(ivBy)
      ivWA = CryptoJS.enc.u8array.parse(ivBv)
    }
    let view = new Uint8Array(buffer)
    // 将Uint8Array 转成 WordArray
    let contentWA = CryptoJS.enc.u8array.parse(view)
    // base64字符串
    let dcBase64String = contentWA.toString(CryptoJS.enc.Base64)
    // 解密
    let decryptedData = CryptoJS.AES.decrypt(dcBase64String, keyWA, {
      iv: ivWA,
      mode: CryptoJS.mode,
      padding: CryptoJS.pad
    })
    // 把解密后的对象再转为base64编码,这步是关键,跟解密文字不同
    let d64 = decryptedData.toString(CryptoJS.enc.Base64)
    return base642arraybuffer(d64)
}
待所有数据都解密完成后,就可以将他们合并然后下载到本地了。


更多详情和代码解释可以看视频展示:https://www.bilibili.com/video/BV14M411r7QX/


gdbop 发表于 2022-11-25 08:03

期待楼主提供个成品能试用试用,看能不能下载某书网课的资源。

flylg999 发表于 2022-11-25 17:14

实战经验:好多收费课程key 的原始字符串有时候字符会是乱码,不如直接用32位的hex字符串
iv 一般都在m3u8里直接有定义,可以下载时直接获取

FYL11162022 发表于 2022-11-25 03:04

感谢分享{:1_893:}

xixicoco 发表于 2022-11-25 05:07

都是高手,厉害

owenlrj 发表于 2022-11-25 06:10

不错不错

dx163 发表于 2022-11-25 06:33

没有成品啊

生命的插曲 发表于 2022-11-25 06:34

有定时功能就好了

hflyday 发表于 2022-11-25 06:53

不太明白,认真学习

ITtongxue 发表于 2022-11-25 07:09

想找能下加密m3u8的程序好久了!谢谢分享!

aonima 发表于 2022-11-25 07:12

感谢分享

zrz444 发表于 2022-11-25 07:24

希望出更多作品并分享。
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 自己动手写个M3U8播放&下载工具