您当前的位置:首页 > 计算机 > 精彩资源

用二进制流的形式下载网络上的视频-只要视频可以播放就可以下载下来!

时间:11-02来源:作者:点击数:

1.引言

  • 荔枝微课上面有很多很好的资源,不过上面的视频在手机APP或者网页上播放不太方便,此外很多付费购买的课程是存在一定的期限的,所以我们要么抓紧时间看,要么把它下载下来本地播放,看起来就非常nice了。但是现在他上面的m3u8文件是加密的,如果你没有key的话,是无法进行下载的。我之前是使用一些解析网站(例如豆宝)来下载的,但是那个费用太贵了,一个视频需要0.4元,二三十个视视频就得十几块,不至于付不起,但很抗拒下载这种自己已经付费买过的课,就是不愿意二次付费。
  • 此外,我还尝试过去淘宝上找代下载的,以为万能的淘宝上会便宜一点,只是没想到那个更贵,下载36个视频,30元起步,下载一个视频要一块钱,这和明抢有啥区别?
  • 之前之所以想找解析网站和淘宝待下载,是因为自己的重心在学习励志微课的教程上,而不是想要折腾下载方式,但是没想到这竟然成了自己学习的拦路虎,那自己必须得把它干掉才行。
  • 无奈基于此,遂下午花了两个小时时间来研究具体的下载方式。我想既然视频可以播放出来,那么它一定通过一个过道来传输这个画面。那么我们只要在这个过道的部位设置一个hook,将其保存下来,那岂不是任何可以播放的视频,我们都可以下载下来呢?本教程的解决下载问题的思路就是这样的:我们这里采用二进制流的方法来下载上面的课程,只要这个视频可以在我们的浏览器上正常播放,我们就可以把它下载下来。
  • 但必须承认,这种方式其实也是存在一定的缺点的,一是浏览器必须缓存完视频之后才可以下载,不过这个问题不大,我们可以开倍速播放,最高可以支持到16倍速。
  • 二是下载下来的视频文件和音频文件是分离的,需要之后再手动将视频文件和音频文件合并,这个也问题不大,我们可以写一个脚本来批量完成这个工作。
  • 免责声明:本博客只做技术交流,请勿将代码用于非法用途,更不可用于商业用途。使用代码所造成的一切后果由使用者自行承担。

2.操作

2.1 安装油猴插件无限制下载器

代码

// ==UserScript==
// @name         Unlimited_downloader
// @name:zh-CN   无限制下载器
// @namespace    ooooooooo.io
// @version      0.1.9
// @description  Get video and audio binary streams directly, breaking all download limitations. (As long as you can play, then you can download!)
// @description:zh-Cn  直接获取视频和音频二进制流,打破所有下载限制。(只要你可以播放,你就可以下载!)
// @author       dabaisuv
// @match        *://*/*
// @exclude      https://mail.qq.com/*
// @exclude      https://wx.mail.qq.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @run-atdocument-start
// ==/UserScript==

(function () {
   'use strict';
   console.log(`Unlimited_downloader: begin......${location.href}`);

   //Setting it to 1 will automatically download the video after it finishes playing.
   window.autoDownload = 1;
   window.isComplete = 0;
   window.audio = [];
   window.video = [];
   window.downloadAll = 0;
   window.quickPlay = 1.0;

   const _endOfStream = window.MediaSource.prototype.endOfStream
   window.MediaSource.prototype.endOfStream = function () {
      window.isComplete = 1;
      return _endOfStream.apply(this, arguments)
   }
   window.MediaSource.prototype.endOfStream.toString = function() {
       console.log('endOfStream hook is detecting!');
      return _endOfStream.toString();
   }

   const _addSourceBuffer = window.MediaSource.prototype.addSourceBuffer
   window.MediaSource.prototype.addSourceBuffer = function (mime) {
      console.log("MediaSource.addSourceBuffer ", mime)
      if (mime.toString().indexOf('audio') !== -1) {
         window.audio = [];
         console.log('audio array cleared.');
      } else if (mime.toString().indexOf('video') !== -1) {
         window.video = [];
         console.log('video array cleared.');
      }
      let sourceBuffer = _addSourceBuffer.call(this, mime)
      const _append = sourceBuffer.appendBuffer
      sourceBuffer.appendBuffer = function (buffer) {
         console.log(mime, buffer);
         if (mime.toString().indexOf('audio') !== -1) {
            window.audio.push(buffer);
         } else if (mime.toString().indexOf('video') !== -1) {
            window.video.push(buffer)
         }
         _append.call(this, buffer)
      }

      sourceBuffer.appendBuffer.toString = function () {
         console.log('appendSourceBuffer hook is detecting!');
         return _append.toString();
      }
      return sourceBuffer
   }

   window.MediaSource.prototype.addSourceBuffer.toString = function () {
      console.log('addSourceBuffer hook is detecting!');
      return _addSourceBuffer.toString();
   }

   function download() {
      let a = document.createElement('a');
      a.href = window.URL.createObjectURL(new Blob(window.audio));
      a.download = 'audio_' + document.title + '.mp4';
      a.click();
      a.href = window.URL.createObjectURL(new Blob(window.video));
      a.download = 'video_' + document.title + '.mp4';
      a.click();
      window.downloadAll = 0;
      window.isComplete = 0;

      // window.open(window.URL.createObjectURL(new Blob(window.audio)));
      // window.open(window.URL.createObjectURL(new Blob(window.video)));
      // window.downloadAll = 0

      // GM_download(window.URL.createObjectURL(new Blob(window.audio)));
      // GM_download(window.URL.createObjectURL(new Blob(window.video)));
      // window.isComplete = 0;

      // const { createFFmpeg } = FFmpeg;
      // const ffmpeg = createFFmpeg({ log: true });
      // (async () => {
      //     const { audioName } = new File([new Blob(window.audio)], 'audio');
      //     const { videoName } = new File([new Blob(window.video)], 'video')
      //     await ffmpeg.load();
      //     //ffmpeg -i audioLess.mp4 -i sampleAudio.mp3 -c copy output.mp4
      //     await ffmpeg.run('-i', audioName, '-i', videoName, '-c', 'copy', 'output.mp4');
      //     const data = ffmpeg.FS('readFile', 'output.mp4');
      //     let a = document.createElement('a');
      //     let blobUrl = new Blob([data.buffer], { type: 'video/mp4' })
      //     console.log(blobUrl);
      //     a.href = URL.createObjectURL(blobUrl);
      //     a.download = 'output.mp4';
      //     a.click();
      // })()
      // window.downloadAll = 0;
   }

   setInterval(() => {
      if (window.downloadAll === 1) {
         download();
      }
   }, 2000);

   //    setInterval(() => {
   //        if(window.quickPlay !==1.0){
   //              document.querySelector('video').playbackRate = window.quickPlay;
   // }
   //
   //   }, 2000);

   if (window.autoDownload === 1) {
      let autoDownInterval = setInterval(() => {
         //document.querySelector('video').playbackRate = 16.0;
         if (window.isComplete === 1) {
            download();
         }
      }, 2000);
   }

   (function (that) {
      let removeSandboxInterval = setInterval(() => {
         if (that.document.querySelectorAll('iframe')[0] !== undefined) {
            that.document.querySelectorAll('iframe').forEach((v, i, a) => {
               let ifr = v;
               // ifr.sandbox.add('allow-popups');
               ifr.removeAttribute('sandbox');
               const parentElem = that.document.querySelectorAll('iframe')[i].parentElement;
               a[i].remove();
               parentElem.appendChild(ifr);
            });
            clearInterval(removeSandboxInterval);
         }
      }, 1000);
   })(window);

   // Your code here...
})();
  • 点击油猴脚本管理器,选择新建一个脚本
  • 删除新建脚本中已有的内容,然后把上面的代码粘贴进去,点击保存
  • 之后再打开或者刷新荔枝微课视频所在的网页,会发现这个脚本已经自动启动了

2.2下载单个视频

  • 下载单个视频很简单,只需要慢慢地把这个视频播放完,就会自动下载下来视频和音频了
  • 缓存完之后,这里会显示插件想要下载多个文件,我们点击允许
  • 可以看到我下载下载了一个视频和一个音频

2.3合并视频和音频

  • 合并视频、音频,我们借助ffmpeg来完成,这个工具很出名,而且不是本教程的介绍重点,因此具体ffmpeg的安装方法请自行百度吧
  • 我们这里使用的操作语法为ffmpeg -i video.mp4 -i audio.mp4 -c:v copy -c:a aac -strict experimental output.mp4
  • video.mp4为我们下载的视频文件,audio.mp4为我们下载的音频文件,output.mp4为我们想要保存的输出文件
  • 结合我的具体文件名,我这里的命名为
  • ffmpeg -i "video_Zotero IF Pro Max 插件安装.mp4" -i "audio_Zotero IF Pro Max 插件安装.mp4" -c:v copy -c:a aac -strict experimental output.mp4

正在合并

合并完成

- 点击之后是可以正常播放的

2.4加速下载

  • 如果我们是想一边看视频一边下载,那你慢慢看无可厚非。但如果你是想先把网页上的视频下载下来之后在本地观看,那么我们可以让网页倍速播放,这样很短的时间就可以把视频播放完,也就下载完
  • 安装这个油猴脚本[HTML5视频播放器增强脚本] ,之后按C是加速播放,按X是减速,按Z是还原HTML5视频播放器增强脚本HTML5视频播放器增强脚本
  • 速度的极限是16倍速,可以大大节省我们缓存视频的时间
  • 此外,脚本与脚本之间是不影响工作的。我们可以新建几个页面,上面播放不同级的视频,从而进行多线程缓存。这样只要你的电脑性能够强,网速够快,就可以在非常短的时间内把所有的视频都下载完

而且这上面的视频如果不太长的话,建议就使用一倍速或者二倍速播放就行了,这样稳当点,不会中间出错,毕竟短视频总共播放完也不用多长时间

2.5下载一系列视频

  • 下载一系列视频和下载单个视频的操作是一样的,只需要缓存完一个视频,再缓存一个视频就好了。好在荔枝微课上面播放以外一个视频之后,是可以自动播放另一个的。所以我们只需要让浏览器在上面以16倍速挂着,不用多长时间就可以把这个视频缓存完了,也就下载完了
  • 把所有视频都缓存完毕之后,会下载下来两类文件,一个是音频文件,另一个是视频文件。如下图文件夹所示

2.6批量合并

  • 我们按照上面的方法是可以单个对视频进行合并的,但是那样操作太繁琐。特别是当我们有十几个甚至几十个视频的时候,那简直要死人。因此我这里写成了一个脚本,可以直接从下载的视频中提取出来文件名,然后调用命令对其进行合并
  • 这里可以直接用我写的Python脚本来进行合并 只需要把视频和音频文件混合放在一个文件夹中,然后将脚本放在当前目录下运行脚本,之后就会自动将这些个视频和音频文件合并到一起,并放到merge文件夹下面

正在进行多线程合并

全部合并完毕,36个视频一共耗时138秒,也就是2分钟,其实还挺快的

  • C:\Users\Lenovo\AppData\Local\Packages\45479liulios.17062D84F7C46_p7pnf6hceqser\LocalState\history\temp\Snipaste_2023-10-12_18-36-58.png
  • 脚本如下
import os
import glob
import subprocess
from concurrent import futures
import time

def make_dir(path):
    import os
    dir = os.path.exists(path)
    if not dir:
        os.makedirs(path)

def process_file(file):
    if (file_prefix in file):
        print('--------------------')
        video_name = os.path.basename(file)
        audio_name = video_name.replace('video_', 'audio_')
        base_name = video_name.replace('video_', '')
        print('base_name:', base_name)
        print('video_name:', video_name)
        print('audio_name:', audio_name)

        video_path = os.path.join(folder_path, video_name)
        audio_path = os.path.join(folder_path, audio_name)
        output_path = os.path.join(output_folder, base_name)

        print('output_path:', output_path)
        ffmpeg_command = f'ffmpeg -i "{video_path}" -i "{audio_path}" -c:v copy -c:a aac -strict experimental "{output_path}"'

        # 使用subprocess运行命令
        print('ffmpeg_command:', ffmpeg_command)
        subprocess.run(ffmpeg_command, shell=True)

folder_path = ''
file_prefix = 'video_'
file_extension = '*.mp4'  # 假设你只关注扩展名为.mp4的文件
output_folder = 'merge'
make_dir(output_folder)

files = glob.glob(os.path.join(folder_path, file_extension))

start_time = time.time()

with futures.ThreadPoolExecutor() as executor:
    executor.map(process_file, files)

end_time = time.time()
total_time = end_time - start_time

print(f"处理完成,总共使用的时间为:{total_time}秒")
  • 效果如下
  • 可以看到,音频和视频已经合并成一个独立的文件了

3.结语

  • 到这里,这个荔枝微课就全部下载下来了,这个方法永远使用,只要这个视频可以在你电脑上正常播放,就可以把它下载下来。而且不止可以下载荔枝微课的视频,其他任何的网页视频都可以下载下来。Just Enjoy It!

4.报错

  • 为什么下载下来的音频上面有变声的现象?
  • 和网站有关系,测试B站上的视频不存在这个问题
  • 换其他浏览器试一下 经过试验发现,火狐浏览器是可以正常下载下来这个荔枝微课上面的音频的,没有任何问题
  • 为什么使用倍速播放的时候播放不出来视频?
  • 一个视频不要上来,就16倍速,这样可能会直接把网页给干蒙,应该先用原速度把视频播放出来之后再换16倍速
  • 或者刷新一下就好了
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门