一个纯前端的网页版视频播放器,支持四种视频源:本地文件、URL 地址、服务器目录、百度网盘。所有数据本地存储,可离线使用,可通过 Capacitor 打包为 Android 应用。
Local Video Hub 专为家庭场景设计 —— 让平板浏览器直接观看电脑上已下载的视频,无需安装任何 App。
| Tab | 来源 | 适用场景 | 持久化 |
|---|---|---|---|
| 📁 本地 | 浏览器 IndexedDB | 手动上传 / 扫描文件夹导入的视频 | ✅ 文件 + 元数据 |
| 🔗 URL | 任意 HTTP/HTTPS 地址 | python -m http.server、NAS、公网 CDN |
✅ URL 列表(localStorage) |
| 🖥️ 服务器 | 服务器指定目录(VIDEO_ROOT) |
局域网共享电脑上的视频给平板 | ❌ 实时读取 |
| ☁️ 网盘 | 百度网盘 | 网盘中的视频文件 | ❌ 实时读取 |
- 基础:播放 / 暂停(点击视频或按钮)
- 快进/快退:±10 秒按钮 + 键盘 ←/→
- 进度条:可拖拽 seek,显示缓冲进度
- 倍速:0.5x / 0.75x / 1x / 1.25x / 1.5x / 2x / 3x
- 音量:可拖拽音量条 + 静音切换
- 全屏:原生 Fullscreen API
- 上一曲/下一曲:本地列表自动连续播放
- 断点续播:本地和 URL 视频每 5 秒持久化播放位置
- 键盘快捷键:空格/K、←/→、↑/↓、M、F、
,、.
- 服务器视频:路径穿越防护(拒绝
../攻击) - URL 代理:SSRF 防护(默认拒绝私网地址,可配置
ALLOW_PRIVATE_NETWORK=true开启) - 网盘:AppKey/Secret 仅存后端,不暴露给前端
bun install复制 .env.example 为 .env 并按需修改:
# 视频根目录(服务器视频 Tab 使用)
VIDEO_ROOT=/path/to/your/videos
# 允许局域网访问(平板访问需要)
HOST=0.0.0.0
# 允许 URL 代理访问私网地址(python http.server 场景需要)
ALLOW_PRIVATE_NETWORK=truebun run dev启动后控制台会显示:
- Local: http://localhost:3000
- Network: http://192.168.1.100:3000 ← 平板访问这个
bun run build
bun run start为了在本地快速发现问题并保持与 CI 一致,建议按以下节奏执行:
- 快速本地循环(每次改动后):
bun run check- 包含:
lint + typecheck
- 包含:
- 广义验证循环(提交或推送前):
bun run check:full- 包含:
lint + typecheck + test + build
- 包含:
- 一键验证脚本:
./scripts/verify.sh- 内部执行
bun run check:full,并返回明确的成功/失败退出码
- 内部执行
适合给孩子用平板看下载好的视频:
- 在电脑上设置
VIDEO_ROOT=/path/to/videos - 启动
bun run dev - 平板浏览器访问
http://电脑IP:3000 - 切换到「服务器」Tab,浏览视频并播放
- 把网址添加到平板主屏书签,孩子下次点图标就能用
不建议继续使用
python -m http.server:它在常见环境下会忽略Range请求,导致大文件 seek 体验差。
按你所在系统选一个命令即可:
| 操作系统 | 推荐命令(支持 Range) | 说明 |
|---|---|---|
| macOS | cd /path/to/your/videos && ruby -run -e httpd . -p 8000 |
系统自带 Ruby,零安装,最省事 |
| Linux | cd /path/to/your/videos && caddy file-server --listen :8000 --root . |
Caddy 默认支持 Range,配置简单 |
| Windows (PowerShell) | Set-Location C:\path\to\videos; caddy file-server --listen :8000 --root . |
推荐安装 Caddy 后使用 |
| 任意系统(有 Node.js) | npx http-server@14 /path/to/your/videos -p 8000 --cors |
跨平台,常用,支持 Range |
启动服务器后,在同一台机器执行:
curl -i -H "Range: bytes=0-1023" "http://电脑IP:8000/视频.mp4"期望结果:
- 状态码是
206 Partial Content - 响应头包含
Content-Range: bytes 0-1023/总长度 Content-Length约为1024
若返回 200 OK 且 Content-Length 是整文件大小,说明该服务器未正确处理 Range,请换用上表推荐命令。
在应用中切换到「URL」Tab → 点 + 添加 URL:
- 输入
http://电脑IP:8000/视频.mp4 - 默认直连播放(性能最佳)
- 若跨域导致进度条无法拖动,右键选「通过代理播放」
切换到「本地」Tab → 点「添加视频」:
- 手动选择文件:选一个或多个视频文件
- 扫描文件夹:自动发现文件夹内所有视频(需 Chrome/Edge)
视频存储在浏览器 IndexedDB,关闭服务器也能继续播放。
- 在 百度网盘开放平台 申请开发者
- 切换到「网盘」Tab → 点「未配置」按钮
- 填入 AppKey / SecretKey / Access Token
- 浏览网盘目录并播放
- 框架:Next.js 16 (App Router) + TypeScript 5
- 样式:Tailwind CSS 4 + shadcn/ui (New York)
- 图标:lucide-react
- 状态:React hooks + localStorage
- 存储:IndexedDB(本地视频)+ localStorage(URL 视频)+ 内存(网盘配置)
.
├── src/
│ ├── app/
│ │ ├── page.tsx # 主页面(4 Tab 切换)
│ │ ├── layout.tsx # 全局布局
│ │ └── api/
│ │ ├── server-file/
│ │ │ ├── list/route.ts # 服务器目录列表
│ │ │ └── stream/route.ts # 服务器视频流代理(Range 支持)
│ │ ├── http-proxy/route.ts # URL 视频代理(解决跨域 seek)
│ │ └── baidu-pan/
│ │ ├── auth/route.ts # 百度网盘 OAuth
│ │ ├── list/route.ts # 网盘文件列表
│ │ └── stream/route.ts # 网盘视频流代理
│ ├── lib/
│ │ ├── video-db.ts # IndexedDB 封装(本地视频)
│ │ ├── url-videos.ts # localStorage 封装(URL 视频)
│ │ ├── server-videos.ts # 服务器 fs 操作(服务端)
│ │ ├── server-videos-shared.ts # 共享常量(MIME、Range 解析)
│ │ ├── source-resolver.ts # 播放源标准化(HLS/DASH/渐进)
│ │ ├── capability-evaluator.ts # 浏览器能力判定(协议可播性)
│ │ ├── fallback-policy.ts # 播放失败回退策略
│ │ ├── baidu-pan.ts # 百度网盘 API 客户端
│ │ └── pan-config-store.ts # 网盘配置存储
│ └── components/
│ └── video-player/
│ ├── videojs-player.tsx # 核心播放器(流媒体能力 + 回退)
│ ├── playlist.tsx # 本地视频列表
│ ├── url-videos.tsx # URL 视频管理
│ ├── server-browser.tsx # 服务器文件浏览器
│ ├── pan-browser.tsx # 网盘文件浏览器
│ ├── add-videos.tsx # 本地视频添加(上传/扫描)
│ ├── baidu-pan-settings.tsx # 网盘配置对话框
│ └── error-boundary.tsx # 错误边界(防崩溃)
├── docs/ # 使用文档与部署说明
├── scripts/ # 开发 / 构建 / 部署辅助脚本
├── tests/ # 脚本与运行时验证用例
└── README.md
scripts/ 目录说明见 scripts/README.md。
三种视频源(服务器/URL/网盘)都通过后端流代理转发,解决:
| 问题 | 解决方案 |
|---|---|
| User-Agent 校验(网盘 >20MB) | 后端用 pan.baidu.com UA 请求 |
| CORS 跨域 | 后端补充 Access-Control-Allow-Origin: * |
| 进度条无法拖动 | 透传 Range 头,返回 206 + Content-Range |
| 内存爆炸 | fs.createReadStream 流式转发,不缓冲整个文件 |
| 流取消导致页面崩溃 | closed 标记防御 controller 已 close 后再触发事件 |
| 变量 | 默认值 | 说明 |
|---|---|---|
VIDEO_ROOT |
{project}/videos |
服务器视频根目录,逗号分隔支持多目录 |
HOST |
localhost |
监听地址,0.0.0.0 允许局域网访问 |
ALLOW_PRIVATE_NETWORK |
false |
URL 代理是否允许访问私网(localhost/192.168.x) |
NEXT_PUBLIC_ENABLE_DASH_PLAYBACK |
false |
是否启用 DASH 播放链路(需 Video.js DASH 插件可用) |
播放基于浏览器原生 <video> 元素,无需任何 CDN 运行时,离线/局域网可用。
| 格式 | 浏览器原生支持 | 备注 |
|---|---|---|
| MP4 (H.264 + AAC) | ✅ 完美 | 强烈推荐 |
| WebM (VP8/VP9) | ✅ 良好 | |
| MOV / M4V | ✅ 多数支持 | |
| MKV (H.264 + AAC) | AC3/DTS 音轨会无声 | |
| MKV (HEVC) / AVI / FLV / WMV / RMVB | ❌ 不支持 | 需 ffmpeg 转码 |
| HLS (.m3u8) | ✅ Safari/iOS 原生 | 非 Safari 浏览器需 MSE 支持,暂未引入 polyfill |
转码命令:
# 已是 H.264:只重封装为 MP4(秒级完成)
ffmpeg -i input.mkv -c:v copy -c:a aac output.mp4
# HEVC:转码为 H.264
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a aac output.mp4- 局域网共享使用指南 — 平板看电脑视频的完整配置
- 百度网盘接入方案 — 网盘视频播放技术细节
- Android 打包指南 — Capacitor 打包为 APK
- 脚本说明 —
scripts/目录中的开发、构建与部署辅助脚本 - Video.js Streaming OpenSpec — 流媒体改造需求与验收场景
- 服务器上访问
http://localhost:3000确认服务正常 - 检查防火墙放行 3000 端口(限定局域网段更安全)
- 确认平板和服务器在同一 WiFi
- 路由器绑定服务器静态 IP(避免重启后 IP 变化)
- 检查 WiFi 信号
- 服务器 CPU/磁盘 IO 是否满载
- 大文件考虑降低码率(ffmpeg 转码)
- 服务器/网盘视频:已支持 Range,正常可拖
- URL 视频:跨域且目标服务器无 CORS 头时受限,右键选「通过代理播放」
已通过 ErrorBoundary + 流代理 closed 标记防御,若仍遇到请检查浏览器控制台错误。
详见 docs/ANDROID_PACKAGING.md。核心步骤:
# 安装 Capacitor
bun add @capacitor/core @capacitor/cli @capacitor/android
# 配置 next.config.ts: output = 'export'
# 构建静态文件
bun run build
# 添加 Android 平台
bunx cap add android
bunx cap sync android
bunx cap open android私有项目,未开源。