Last updated on

打造一个优雅的视频下载助手:从浏览器到本地的无缝体验

打造一个优雅的视频下载助手:从浏览器到本地的无缝体验

前言

在观看B站或YouTube视频时,你是否遇到过这些场景:

  • 想离线观看某个教程,但网络不好时反复卡顿
  • 看到优质内容想保存下来,却要打开一堆在线下载网站
  • 下载视频后想要字幕文件,又要额外操作一遍

作为一个经常需要保存视频的开发者,我决定打造一个一键下载的解决方案。于是就有了这个项目:Quick Download —— 一个优雅、高效的视频下载助手。

🎯 核心功能

1. 浏览器一键下载

安装用户脚本后,在B站或YouTube的视频页面,右侧会自动出现一个精美的下载按钮。点击即可,无需复制链接、打开网站、等待解析等繁琐步骤。

支持的平台:

  • ✅ B站(常规视频、番剧、播放列表)
  • ✅ YouTube(所有视频)

2. 智能字幕生成

下载视频的同时,系统会自动使用 AI 语音识别技术生成字幕文件(基于 Whisper 模型)。无论是中文还是英文视频,都能获得准确的字幕文本。

3. 可视化管理界面

访问 http://localhost:2999 即可打开管理界面,功能包括:

  • 📊 统计面板:实时显示视频数量和平台分布
  • 🔍 搜索功能:快速搜索视频标题和链接
  • 📺 在线预览:直接在浏览器中预览已下载的视频
  • 📝 字幕查看:查看和搜索视频字幕内容
  • 🗑️ 记录管理:删除不需要的视频记录

🏗️ 技术架构

整个项目采用现代化的技术栈,注重性能和开发体验:

后端服务

前端

  • 原生 HTML + CSS + JavaScript:无需构建工具,直接由 Bun 服务
  • 用户脚本:Tampermonkey 扩展,注入到视频页面

核心代码结构

quick-download/
├── index.ts                    # 后端服务主入口
├── database.ts                 # 数据库管理
├── download-helper.user.js     # 浏览器用户脚本
├── public/
│   └── index.html             # 管理界面
└── videos.db                  # SQLite 数据库

💡 技术亮点

1. 平台自动识别

系统会智能识别视频链接类型,自动选择对应的下载工具:

function detectLinkType(link: string): "bilibili" | "youtube" | "unknown" {
  if (link.includes("bilibili.com")) return "bilibili";
  if (link.includes("youtube.com") || link.includes("youtu.be")) return "youtube";
  return "unknown";
}

2. B站分P视频支持

B站的分P视频下载是个技术难点,我的解决方案是从URL中提取 p 参数,配合 yutto 的批处理参数实现精确下载:

const url = new URL(link);
const p = url.searchParams.get("p");
if (p) {
  // 使用 -b -p 参数组合下载特定集数
  batchParam = " -b";
  pParam = ` -p ${p}`;
}

3. 视频流式传输

管理界面支持在线预览视频,实现了 HTTP Range 请求,支持视频进度条拖动:

if (range) {
  const start = parseInt(parts[0] || "0", 10);
  const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
  const chunk = file.slice(start, end + 1);
  
  return new Response(chunk, {
    status: 206,
    headers: {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
    },
  });
}

4. 字幕文件智能匹配

字幕文件名可能有多种格式(如 video.txt, video (transcribed on 2025-10-18).txt),系统采用两级匹配策略:

// 策略1: 精确匹配
for (const ext of subtitleExts) {
  const subtitlePath = path.join(dir, baseName + ext);
  // 尝试读取
}

// 策略2: 模糊匹配
const matchingFile = files.find(file => 
  file.startsWith(baseName) && file.endsWith(ext)
);

5. 优雅的用户体验

用户脚本会根据不同平台自适应样式:

  • B站:蓝绿渐变主题色
  • YouTube:红色主题色
  • 实时状态提示(加载中、成功、失败)
  • YouTube SPA 页面切换自动适配

🎮 使用体验

安装配置(5分钟搞定)

# 1. 克隆项目
git clone https://github.com/your-username/quick-download.git

# 2. 安装依赖
bun install

# 3. 安装下载工具
brew install yt-dlp ffmpeg

# 4. 启动服务
bun run start

日常使用(3步完成)

  1. 打开视频页面:在B站或YouTube浏览想要的视频
  2. 点击下载按钮:页面右侧的下载按钮会自动出现
  3. 等待完成:视频自动下载到 ~/Downloads 文件夹,并生成字幕

整个过程无需离开视频页面,体验极其流畅。

📊 性能表现

得益于 Bun 的高性能,整个系统运行非常高效:

  • 启动速度:< 100ms
  • API 响应:< 10ms(数据库查询)
  • 内存占用:< 50MB(空闲状态)
  • 下载速度:取决于网络和视频大小,但系统本身无瓶颈

🔒 隐私与安全

  • 完全本地运行:所有数据保存在本地,不上传到任何服务器
  • 开源透明:代码完全开源,可以自行审查
  • Cookie 管理:B站 Cookie 仅用于下载,可自行配置

🚀 未来规划

这个项目还有很多可以改进的地方:

短期计划

  • 支持更多视频平台(Vimeo、Twitch等)
  • 批量下载功能
  • 下载进度实时显示
  • 字幕翻译功能

长期规划

  • 桌面客户端(Electron)
  • 云端同步(可选)
  • 视频格式转换
  • 下载队列管理

💭 开发心得

为什么选择 Bun?

在这个项目中,Bun 带来了极致的开发体验:

  1. :启动速度和运行性能都远超 Node.js
  2. 简洁:自动加载 .env,无需 dotenv
  3. 内置工具:原生支持 SQLite、TypeScript、热重载
  4. 现代化:完美支持 ES Modules 和最新 Web API

为什么选择 Hono?

相比 Express,Hono 更适合现代 JavaScript 运行时:

  • 为 Bun/Deno/Cloudflare Workers 设计
  • 路由性能极高(基于 Radix Tree)
  • TypeScript 原生支持
  • 中间件生态完善

为什么用原生前端?

这个项目的前端足够简单,不需要 React/Vue 这样的框架:

  • 零构建时间:Bun 直接服务 HTML
  • 更好的性能:无框架开销
  • 更易维护:代码简单直观

🎓 技术收获

通过这个项目,我深入学习了:

  1. Bun 生态系统:从 HTTP 服务到数据库,体验完整的 Bun 开发流程
  2. 视频下载技术:了解 yt-dlp 和 yutto 的工作原理
  3. 音频转录:Whisper 模型的实际应用
  4. 浏览器扩展开发:用户脚本的编写和调试
  5. HTTP Range 请求:视频流式传输的实现

📝 总结

Quick Download 是一个完全本地化、注重隐私、体验优雅的视频下载解决方案。它不仅解决了我的实际需求,也让我深入学习了现代 Web 技术栈。

如果你也有类似的需求,欢迎试用这个工具。项目完全开源,也欢迎贡献代码或提出建议!


附录:常见问题

Q: 为什么 B站下载需要 Docker?

A: B站的下载工具 yutto 依赖 Python 环境,使用 Docker 可以避免环境配置问题,开箱即用。

Q: 字幕生成需要多久?

A: 取决于视频长度,一般 1 分钟的视频需要 10-30 秒。使用的是 Whisper Small 模型,在速度和准确度之间取得平衡。

Q: 能下载 4K 视频吗?

A: 当前配置下载的是 480p 视频(考虑到文件大小和速度),可以在代码中修改 yt-dlp 的参数来下载更高清晰度。

Q: 支持移动端吗?

A: 目前只支持桌面浏览器。移动端浏览器的用户脚本支持不佳,建议在桌面端使用。