Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Starlight Player

现代简洁的本地视频播放器。优先适配 Windows,架构上按跨平台设计,可直接向 macOS / Linux 移植。

支持 MP4 / MKV / AVI / MOV / FLV / WMV / WEBM / TS / M3U8 等主流格式,具备播放列表管理、倍速播放、完整快捷键体系,并针对播放流畅性与资源占用做了专门优化。


一、为什么不是"套壳浏览器"

Chromium 只能解码 MP4/MOV/WEBM 等少数容器,MKV、AVI、FLV、WMV、RMVB、TS 直接丢给 <video> 是播不了的。很多同类项目在这里妥协,只支持浏览器原生格式。

Starlight 的做法是在 Electron 与播放器之间加了一层 媒体管线(Media Pipeline):用本地 ffmpeg 把不兼容的容器实时转成 HLS 分片,交给 hls.js 播放。三种代价递增的策略,按需选用、失败自动降级:

策略 适用场景 开销 首帧延迟
direct 原生直连 MP4/MOV/WEBM,视频 H.264/VP8/VP9/AV1,音频 AAC/MP3/Opus/Vorbis/FLAC/PCM(无音轨同样直连) 零额外 CPU 即时
hls-copy 无损重封装 MKV/AVI/FLV/TS,视频 H.264 + 音频 AAC/MP3 仅换封装,速度 50~200× 1~2 秒
hls-remux 视频复制 + 音频转码 视频 H.264,音频为 AC3/DTS/FLAC 等 中等 2~5 秒
hls-transcode 全量转码 H.265 / VP9 / MPEG-2 等不兼容编码 较高,优先硬件加速 视片长

关键点:只有真正需要时才付出转码代价。绝大多数 MKV(H.264 + AAC 压制)走 hls-copy,几百毫秒就能开始播放,画质零损失。

播放过程中若某个策略失败(编码嗅探不准、文件损坏、显卡驱动异常),引擎会沿降级阶梯自动换下一种方式,并给出提示,无需用户干预。


二、快速开始

环境要求

  • Node.js ≥ 18
  • ffmpeg / ffprobe(播放 MKV/AVI/FLV 等格式必需)
# Windows
winget install Gyan.FFmpeg
# 或 choco install ffmpeg

# macOS
brew install ffmpeg

# Linux
sudo apt install ffmpeg

没装 ffmpeg 也能用——MP4/MOV/WEBM 依旧正常播放,只是 MKV/AVI/FLV 会提示需要解码环境。 打包发布时可以把 ffmpeg 二进制放进 resources/bin/<platform>-<arch>/,应用会优先使用它,用户无需自行安装。

运行

npm install     # 会自动把 hls.js 拷贝到渲染层 vendor 目录
npm start       # 启动应用
npm run dev     # 带开发者工具

界面预览(无需安装 Electron)

npm run demo

生成 demo/starlight-player-demo.html,双击即可在浏览器中查看界面与交互。 (该 Demo 走纯浏览器路径,只能播放浏览器原生格式与网络 HLS;完整格式支持需运行桌面端。)

自检与静态检查

npm test        # 管线自检:策略决策 / HLS 产出 / Range 服务 / 字幕转换(不依赖 Electron)
npm run verify  # 播放自检:真实启动应用,验证画面与字幕确实渲染出来
npm run lint    # ESLint(核心规则 no-undef,专门防「调用了未定义的标识符」)
npm run capture # Electron 离屏截图验证 UI 渲染,产物在 docs/preview/
npm run check:pack  # 打包就绪度:require 解析 / 资源引用 / files 覆盖 / 图标齐全
npm run check:arch  # 架构约定:平台判断收口 / 渲染层隔离 / 媒体管线独立性
npm run check:theme # 主题令牌:变量名对齐 + 两套主题的 WCAG 对比度
npm run measure     # 实测资源占用:空闲 / 直连播放 / 重封装播放三场景对比
npm run measure:speed # 实测高倍速:1×/2×/4× 逐档采样推进速率 / 丢帧 / 缓冲
npm run verify:install  # 安装包端到端:静默安装 → 快捷方式 → 卸载登记 → 启动播放 → 静默卸载

七个检查各有分工,别只跑一个:

  • npm test 断言「管线产出合法」——用真实 ffmpeg 生成 7 种编码组合的样本, 检查策略决策、HLS 清单与分片、Range 请求、非法 token 与目录穿越拦截、 字幕发现与 WebVTT 转换、音轨与章节解析、按平台的 ffmpeg 安装指引, 以及设置/续播记录的越界值清洗、目录截断提示与自检临时目录清理,共 126 项。不依赖 Electron。

  • npm run verify 断言「画面真的出来了」——启动真实应用(无头模式, 窗口不显示、不抢焦点),让 6 种场景各走一遍完整的 IPC → 媒体服务 → ffmpeg → hls.js → <video> 链路,回读解码状态并截图。

    判定标准是 videoWidth/videoHeight 非零 且 时间轴推进 且 无 mediaError。 只看 currentTime 是不够的——有声音无画面时时间轴照样走,这正是最容易漏掉的失败模式。

  • npm run check:pack 断言「打包后能启动」——打包最常见的翻车不是构建失败, 而是构建成功却一启动就白屏。该脚本静态走查所有相对 require、index.html 的资源引用、build.files 覆盖度与图标齐全性。

  • npm run verify:install 断言「用户拿到安装包后能装上、能启动、能卸干净」—— 分发前最后一道闸。它会静默安装到临时目录,校验开始菜单与桌面快捷方式、 「程序和功能」里的卸载登记项,然后启动已安装的程序跑一遍真实播放, 最后静默卸载并确认安装目录、快捷方式、注册表都清理干净。

    这一步在开发机上永远暴露不出来——开发机一直是 npm start 跑源码。 脚本会先清理上一轮的历史安装,可反复运行。

  • npm run check:theme 断言「浅色主题没有漏样式」——两件事都靠算而不是靠看:

    一是变量名对齐:浅色主题必须覆盖深色主题的全部配色变量。少覆盖一个, 那个组件就会在浅色下沿用深色值,表现为局部没换过来,而这正是主题切换最容易漏的坑。 二是对比度:按 WCAG 计算文字/背景配对,正文要求 ≥ 4.5:1、控件态 ≥ 3:1, 半透明令牌先与背景合成再计算(直接拿 rgba 算会得到错误结果)。

    该检查顺带修掉了深色主题原有的两处不达标:三级灰在白底/深底上的对比度不足, 以及白字压在强调色填充按钮上只有 3.5:1。

  • npm run check:arch 断言「架构约定没有被绕过」——把只写在文档里、 靠人记着的规则变成可执行的检查。约定一旦只存在于文档,就会慢慢被绕过, 而这类漂移往往到跨平台构建时才暴露。五项:

    1. 平台判断收口:process.platform 只允许出现在已登记的文件中 (窗口装饰、菜单、ffmpeg 定位、进程终止),每项都写明理由。 业务逻辑里出现它,就说明平台差异泄漏了。
    2. 渲染层隔离:src/renderer/ 不得出现 require / module.exports / process.* / __dirname —— 一切能力经 preload.js 白名单暴露。
    3. 媒体管线独立性:media-server / hls-jobs / media-probe 不得 require('electron'),它们要能在 CLI 与单测中独立运行。
    4. 配置注入:ffmpeg-locate 的查找优先级里写了「用户配置」最优先, 但它不依赖 store,必须由主进程显式注入 —— 断言这一步没有被漏掉。 (曾经漏过:文档写了优先级、设置界面也有输入框,实际却没人注入, 用户填的自定义路径静默失效。)
    5. 渲染层代次约定:teardown() 只释放资源、不得改动 loadToken; 作废在途加载要用 cancelPending();playIndex 必须先认领代次再 teardown。 (曾经在 teardown 里自增,逼得降级重试把代次「写回去」, 并发时覆盖更新的加载 → 快速切换文件弹出「无法播放该文件:已取消」的误报。)

    清单里登记了但实际已不再使用的文件也会报错,避免清单本身腐化。

  • npm run measure:speed 断言「高倍速播放确实流畅」—— 1×/2×/4× 逐档采样, 核心指标是实际推进速率(媒体时间增量 ÷ 墙钟时间,应等于设定倍速; 明显偏低说明媒体时钟被音频渲染或缓冲饿死拖慢),外加丢帧率、waiting 次数与缓冲余量。 每档倍速都走应用自身的 setSpeed 路径,因此测的是真实播放链路而非裸 <video>。

无 GPU 的环境(CI / 容器 / 远程会话)里 Electron 的 GPU 进程会直接 FATAL 退出, npm run verify 会识别出这个特征并自动改用 --no-sandbox --disable-gpu --in-process-gpu 重试。 若需强制指定其他开关,仍可通过环境变量覆盖: STARLIGHT_EXTRA_FLAGS="--no-sandbox --disable-gpu --in-process-gpu" npm run verify

打包

npm run clean          # 清理构建产物与缓存(release/ 等),保证全新构建
npm run dist:win       # Windows → NSIS 安装包 + 便携版
npm run dist:win:fresh # 清理 + 打包,一条命令完成
npm run dist:portable  # 仅 Windows 便携版,不依赖 electron-builder 的 PowerShell 子流程
npm run dist:linux     # Linux → AppImage + deb(需要联网下载 appimage / fpm 工具链)
npm run dist:linux:portable  # Linux → tar.gz,不依赖额外工具链
npm run dist:mac       # macOS → DMG(x64 + arm64,只能在 macOS 上构建)
npm run icons          # 重新生成三平台图标(产物需入库以保证打包可复现)

dist:portable 是 Windows 的退路:它只用 Node API + @electron/asar + yazl 产出便携目录与 ZIP,完全不经过 electron-builder。受限环境(企业策略 / CI 容器 禁止 PowerShell 子进程)里 dist:win 跑不通时用它。 代价是不产 NSIS 安装器、不注册文件关联。

dist:linux:portable 的定位不同:Linux 侧的障碍不是 PowerShell,而是 AppImage / deb 需要额外下载 appimage / fpm 工具链。它只取 dir 目标再打成 tar.gz, 解压即用。两者别混为一谈。

交叉构建:Linux 可以,macOS 不行(实测)

宿主 → 目标 能否构建 说明
Windows → Windows ✅ 主战场
Windows → Linux ✅ node scripts/package-win.mjs --linux dir --out <目录> 实测可产出完整目录包
Windows → macOS ❌ electron-builder 直接拒绝:Build for macOS is supported only on macOS
Linux/macOS → Windows 未实测 理论上可行,但 Windows 目标通常仍需在 Windows 上产出安装包

因此 macOS 的打包配置只能在 macOS 上验证。在 Windows 上误跑 npm run dist:mac 只会得到上面那句报错——不是配置问题,是工具链的硬限制。

scripts/package-win.mjs 是通用包装脚本,名字里的 "win" 只是默认平台: --linux / --mac 都会原样透传给 electron-builder。

Linux 包为什么不含 ffmpeg

Windows 包随附 ffmpeg,Linux 包不随附,这是刻意的:

  • 本项目的 ffmpeg 定位优先级是 用户配置 > 环境变量 > 随包 > 系统 PATH, Linux 上几乎总能从系统 PATH 找到;
  • 发行版仓库里的 ffmpeg 通常比任何随包方案都新,随包反而容易过时;
  • 随包一个旧版本会造成「Windows 能播、Linux 不能」这类极难排查的支持负担 —— 应用的策略链与探测逻辑是针对较新 ffmpeg 调优的。

缺少 ffmpeg 时应用会明确提示,并给出当前平台的安装命令 (见 src/shared/constants.js 的 ffmpegInstallHint)。要随附的话, 把二进制放进 resources/bin/linux-<arch>/ 即可,构建时会自动带上并通过平台/架构校验。

chrome-sandbox 需要 setuid root。 Chromium 沙箱依赖该权限位,而用户以普通身份 解压 tar.gz 时它不会生效(只有 root 解压才能还原)。此时应用会报沙箱错误, 需要手动执行一次 sudo chown root:root chrome-sandbox && sudo chmod 4755 chrome-sandbox, 或改用 AppImage / deb 安装包(安装时自动处理)。 打包脚本会把这段说明写进归档内的 README-LINUX.txt。

打包产物不含 node_modules。 渲染层通过 <script src="vendor/hls.min.js"> 加载 hls.js, src/ 中没有任何 require() 第三方包的代码,因此生产依赖为空,包体只包含应用代码与 随包分发的 ffmpeg(若放置于 resources/bin/<platform>-<arch>/)。

图标由 scripts/make-icons.mjs 纯 Node 生成(手写 PNG 编码 + ICO/ICNS 打包, 不依赖任何图像库),一次产出 Windows / macOS / Linux 三平台所需的全部格式。

dist:win 为什么要套一层包装脚本

scripts/package-win.mjs 做两件事,都源于实机踩到的坑:

  1. 压制 npm 的 warning。electron-builder 收集依赖树时会执行 npm list --production --json;npm 10 对该写法先输出一行 npm warn config production Use --omit=dev instead.,把 JSON 污染掉, 于是打包报 No JSON content found in output 并中断。 脚本用 NPM_CONFIG_LOGLEVEL=error 消除这一行。
  2. 跨平台地设置环境变量。Windows 上 npm script 由 cmd.exe 执行, 不支持 VAR=value cmd 这种 POSIX 内联写法,所以必须用 Node 派发。

另外还有一个无法用脚本解决的环境前提,见下节。

Windows 构建环境前提:PowerShell 执行策略

electron-builder 在 Windows 上收集依赖树时,会把 npm 调用包进 powershell.exe -EncodedCommand(规避 CVE-2024-27980 的命令注入)。 而本机若执行策略是 Restricted,npm 会被解析到 Node 自带的 npm.ps1, 直接报「因为在此系统上禁止运行脚本」,依赖树收集拿到空输出, 最终同样表现为 No JSON content found in output。

排查结论(都已实测):

  • npm 在 PowerShell 里优先解析 .ps1,即使 PATH 里排在前面的目录只有 npm.cmd 也会被 .ps1 抢走
  • NPM_CONFIG_LOGLEVEL、PSExecutionPolicyPreference 环境变量、补全 PATHEXT 都无效

因此需要放开当前用户的执行策略:

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
# 仅当前用户、无需管理员;想还原:Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Undefined

判断是不是这个问题:powershell.exe -NoProfile -Command "npm list --omit=dev --json", 若报「禁止运行脚本」即是。注意该报错藏在 stderr 的 CLIXML 里, electron-builder 只会报出 No JSON content found in output,容易被误判成 npm 配置问题。

Windows 安装版(NSIS)

一条命令产出可双击运行的安装程序:

npm run dist:win                 # 构建 NSIS 安装包 + 便携版
npm run verify:install           # 验证:安装 → 快捷方式 → 卸载登记 → 启动播放 → 卸载

依赖:npm install 时已装好(electron + electron-builder,均为 devDependencies)。 首次构建需联网拉取 electron 与 NSIS 工具链,缓存到 %LOCALAPPDATA%\electron-builder\Cache; 离线环境可用 ELECTRON_BUILDER_CACHE 预置。机器上不需要装 WiX、Visual Studio 或任何签名工具。

产物(默认输出到 release/;若该目录被杀软占用删不掉,可加 --out dist 换一个目录,验证脚本会同时搜索这两个位置):

文件 说明
Starlight-Player-Setup-1.0.0.exe NSIS 安装程序(约 158 MB),双击运行
Starlight-Player-Portable-1.0.0.exe 便携版单文件(免安装,直接运行)
*.exe.blockmap 增量更新用的块映射,分发时可不带

安装后:默认装到 %LOCALAPPDATA%\Programs\Starlight Player(每用户安装,不需要管理员权限), 自动创建开始菜单与桌面快捷方式,并在「程序和功能」中登记卸载项(含版本、发布者、图标)。

运行时无需用户配置任何环境——ffmpeg / ffprobe 已随包内置于 <安装目录>\resources\bin\,Node 与 Chromium 由 Electron 自带, src/ 中也没有任何第三方 require(),因此打包后不含 node_modules。

应用名 / 版本 / 图标等信息全部集中在 package.json,不需要改构建脚本:

想改什么 改哪里
应用显示名(窗口标题、快捷方式、卸载列表) productName
版本号(文件名、卸载列表、安装包属性) version(遵循 semver)
卸载项唯一标识(升级时判断是否同一应用) build.appId
图标 build.win.icon → resources/icons/icon.ico(用 npm run icons 重新生成)
快捷方式名称、是否建桌面快捷方式、装完是否启动 build.nsis.shortcutName / createDesktopShortcut / runAfterFinish
安装范围(每用户 / 每台机器) build.nsis.perMachine
随包二进制(ffmpeg 等) build.extraResources,源目录 resources/bin/<platform>-<arch>/
文件关联(双击视频用本应用打开) build.fileAssociations

换版本号后旧的 release/ 产物不会自动清理,分发前建议确认目录里没有同名的旧安装包。

打包常见问题

现象 原因与解决
No JSON content found in output 有两种来源:① npm 的更新提示 / --production 告警污染了 npm list 的 JSON,已由 scripts/package-win.mjs 通过 NPM_CONFIG_LOGLEVEL=error 解决;② 执行策略为 Restricted,见上文「Windows 构建环境前提」
打包报 EBUSY / 旧产物删不掉 release/ 里的 app.asar 常被杀软实时监控占用,进程列表里查不到持有者。npm run clean 会先改名探测再删:改不动立即报告并跳过(不会卡住),改得动才进入删除。仍删不掉时用 node scripts/package-win.mjs --win --out dist-<时间戳> 换一个输出目录。注意 --out 若不以 dist- 开头会被告警——.gitignore 只忽略 release/ 与 dist-*/,别的名字有误提交风险
configuration.linux.desktop should be null electron-builder 26 起 linux.desktop 改为 { entry, desktopActions } 结构,MIME 类型改用 linux.mimeTypes
交叉构建的包里混入了其他平台的二进制 extraResources 里的 ${platform} 宏展开的是宿主平台而非目标平台(见 app-builder-lib/out/util/macroExpander.js,case "platform": return process.platform;目标平台对应 ${os})。因此必须按平台分别写死路径。package-win.mjs 会按二进制文件头校验平台与架构(scripts/lib/binary-format.mjs)并拦下这种包
Build for macOS is supported only on macOS electron-builder 的硬限制,macOS 目标无法交叉构建。Linux 目标可以(--linux dir 实测通过)
Windows 打包报 PowerShell 相关错误 electron-builder 在 Windows 上会把 npm 调用包进 powershell.exe -EncodedCommand(规避 CVE-2024-27980),受限环境下需确保可派生 PowerShell
首次打包卡在下载 需要联网拉取 electron 与 NSIS 工具链;离线环境可预置 ELECTRON_BUILDER_CACHE
下载 Electron 报 Response code 502 网络/代理抖动。可改用镜像:ELECTRON_MIRROR=https://registry.npmmirror.com/-/binary/electron/ npm run dist:linux:portable。已缓存且校验和匹配时不会重新下载
Linux 包解压后无法运行 大概率是丢了可执行位。本项目的 tar 写入器(scripts/lib/tar.mjs)会显式写入权限位,并用系统 tar 独立回读校验;若自行用 tar 打包,Windows 上产出的归档会把所有文件记成 0644

三、功能

播放

  • 播放 / 暂停、上一集 / 下一集、拖拽定位
  • 进度条:缓冲区间可视化、悬停时间预览、拖拽预览
  • 音量滑块 + 静音,音量随鼠标悬停展开
  • 倍速 0.25× ~ 4×(10 档预设),高倍速全程流畅不丢帧(见下)
  • 画面比例:适应 / 铺满 / 拉伸 / 原始
  • 全屏、画中画、迷你模式(置顶小窗)、截图保存
  • 续播记忆:同一文件下次打开自动从上次位置继续(距结尾 10 秒内视为看完,不再续播)
  • 任务栏 / Dock 进度条同步

高倍速为什么不卡

高倍速卡顿的根源有三个,分别处理:

  1. 音频时间拉伸(WSOLA)跟不上。preservesPitch 的变调保持在 2× 以上 开销陡增且质量劣化,音频渲染一旦落后还会反过来拖慢媒体时钟 —— 表现就是画面一顿一顿。 超过 2× 后自动改用简单重采样(音调变高但开销小),1×/2× 听感不变。
  2. 音频渲染本身成为瓶颈。达到 4× 自动静音,把音频从关键路径上彻底摘掉; 回落到 4× 以下恢复原本偏好,期间按 M 手动取消静音也会被尊重(OSD 有提示)。
  3. HLS 缓冲被「播放列表快照」锁死。hls.js 对 EVENT 播放列表的重载极其保守, 它能看到的分片被首帧时抓到的快照限死,缓冲顶到快照末尾就饿死 —— 与 maxBufferLength 设多大无关。两处修复: 媒体服务对清单响应发送 no-store(分片仍长缓存,分片写出后不可变); ffmpeg 作业退出时(清单已完整)强制 loadSource 刷新一次,hls.js 转为 VOD 语义后立即把缓冲填满。此外缓冲目标按当前倍速等比放大 —— 40s 媒体缓冲在 4× 下墙钟只剩 10s,不放大就会周期性 waiting。

验证方式:npm run measure:speed 在 1×/2×/4× 逐档采样实际推进速率(应等于 设定倍速)、丢帧率、waiting 次数与缓冲余量,走应用自身的 setSpeed 路径而非裸改 playbackRate。优化前后对比见下表(沙箱无 GPU、软件渲染,绝对值偏保守):

场景 档位 优化前实际速率 优化后实际速率 优化前丢帧率 优化后丢帧率
MP4 直连 4× 3.98 3.99 42.8% 2.8%
MKV 重封装 2× 0.92(已饿死) 1.99 1.4% 1.0%
MKV 重封装 4× 0.00(完全停滞) 3.97 - 2.7%

转码(hls-transcode)路径的吞吐受 ffmpeg 编码速度限制,本身达不到 4× 实时, 高倍速下仍会周期性等待 —— 这是转码策略的固有约束,不是回放层问题。

外观主题

  • 深色 / 浅色两套主题,一键切换:标题栏右侧图标循环「跟随系统 → 浅色 → 深色」,或走菜单「视图 → 主题」
  • 跟随系统偏好:prefers-color-scheme 变化时即时响应,无需重启
  • 选择持久化:模式写入设置文件,重启后按模式恢复,首帧就是正确主题
  • 窗口底色、Windows 标题栏叠加层、原生对话框与滚动条一并跟随,不出现「内容切了外壳没切」

实现要点,两处最容易踩的坑:

  1. 首帧不能闪。IPC 是异步的,等主进程回话时首帧已经画完——深色用户会看到一帧白屏。 因此主进程把持久化的模式放进 URL 查询串,theme-init.js 在 <head> 里同步读取并 打上 data-theme,窗口底色与标题栏也按同一主题创建。注意 CSP 是 script-src 'self', 这个脚本必须是外部文件,内联会被拦掉。

  2. 视频叠层不跟随主题。控制栏、字幕、OSD 压在实际画面上,画面内容不可控, 浅色主题下它们必须保持「浅字深底」。因此令牌分三组: 尺寸与动效、视频叠层(--stage-*,两套主题共用同一取值)、配色(唯一被覆盖的一组)。 切换主题只改配色,叠层不受影响。

多音轨与章节

  • 音轨切换:MKV 双语版(原声 + 配音)可自由选择音频流,切换时保持播放位置与播放状态
  • 章节:解析容器章节元数据,在进度条上渲染分隔标记,悬停显示章节名,支持快速跳转

实现要点:Chromium 的 <video> 只能播放容器里的默认音轨,无法选择其他音频流。 因此切换音轨必须由 ffmpeg 重新映射(-map 0:a:<n>)并换封装,播放策略会自动从 direct 升级到 HLS 路径。文件只有单条音轨时不会做这个降级。

字幕

  • 外挂字幕自动识别:与视频同目录、同主文件名的 .srt / .ass / .ssa / .vtt 自动加载
  • 多语言后缀:movie.zh-CN.srt / movie.en.srt 会作为多个轨道列出,语言标记按 BCP-47 归一化
  • 内嵌字幕轨:MKV 等容器内的字幕流可直接选择(抽取为 WebVTT 后渲染)
  • 时间轴偏移:±0.1s / ±1s 微调,解决字幕与片源不同步——这是实际观影中最常遇到的问题
  • 字号四档:小 / 中 / 大 / 特大,随窗口尺寸自适应
  • 自绘渲染层:多重描边 + 投影,保证在亮暗画面上都清晰可读;字幕随控制栏显隐自动升降

播放列表

  • 打开文件、打开文件夹(递归扫描,最多 4 层 / 3000 个文件)
  • 拖拽文件或文件夹到窗口任意位置导入
  • 拖拽条目调整顺序
  • 关键词筛选
  • 循环模式(关 / 列表循环 / 单个循环)、随机播放
  • 列表持久化,下次启动自动恢复
  • 时长懒加载(后台串行 ffprobe,不阻塞界面)

快捷键

按键 功能 按键 功能
Space / K 播放 / 暂停 F 全屏
← → 快退 / 快进 5s Esc 退出全屏 / 关闭弹层
Shift+← → 快退 / 快进 30s [ ] 减速 / 加速
J / L 后退 / 前进 10s Shift+[ ] 恢复 1.0×
↑ ↓ 音量 ±5% C 截图
M 静音 A 画面比例
V 显示 / 隐藏字幕 G 切换到下一条字幕轨
Z / X 字幕延迟 ∓0.1s Shift+Z X 字幕延迟 ∓1s
B 音轨切换菜单 , . 上一章 / 下一章
N / P 下一个 / 上一个 Ctrl+O 打开文件
0–9 跳转到 0%~90% Ctrl+Shift+O 打开文件夹
Home / End 片头 / 片尾 Ctrl+U 打开网络地址
R 循环模式 Ctrl+B 显示 / 隐藏播放列表
H 随机播放 Ctrl+Shift+V 字幕开关
Tab 显示 / 隐藏播放列表 Ctrl+Shift+L 打开字幕文件
? 快捷键一览 Ctrl+← → 上一章 / 下一章

四、资源占用优化

播放器长时间挂机很容易吃满 CPU 和内存,这里做了几件事:

优化 说明
策略优选 能直连就直连,能重封装就不转码。避免为了一致性把一切都转码。
边转边播 HLS 播放列表使用 event 类型,ffmpeg 产出首个分片即可开播,首帧延迟与转码总时长解耦。
硬件加速 + 自动回退 转码默认尝试 -hwaccel auto(D3D11VA / VideoToolbox / VAAPI),失败自动去掉重试,兼顾性能与兼容性。
限制回退缓存 hls.js backBufferLength: 30、maxBufferSize: 60MB,长片播放内存占用保持平稳。
后台暂停 窗口最小化 / 隐藏时暂停播放,避免无意义的解码开销。
进程清理 切换文件、关闭窗口、应用退出时立即 kill ffmpeg 进程(Windows 用 taskkill /T 连同子进程),并清理临时分片。
懒探测 播放列表时长串行后台探测,一次只跑一个 ffprobe。
按需重绘 进度更新走 requestAnimationFrame;列表时长刷新只改文本节点,不重绘整个列表。
原子持久化 状态写入使用「临时文件 + rename」,避免异常退出损坏配置。

五、项目结构

Starlight/
├─ src/
│  ├─ main/                     # 主进程(Node 侧)
│  │  ├─ main.js                #   生命周期、窗口、IPC 路由
│  │  ├─ menu.js                #   应用菜单与带修饰键的快捷键
│  │  ├─ preload.js             #   contextBridge 白名单桥
│  │  ├─ media-server.js        #   本地回环 HTTP 流服务(Range / HLS / 目录映射)
│  │  ├─ media-probe.js         #   ffprobe 探测(策略决策在 shared/constants.js)
│  │  ├─ hls-jobs.js            #   ffmpeg 后台作业管理
│  │  ├─ subtitles.js           #   字幕发现与 WebVTT 抽取
│  │  ├─ scan-dir.js            #   目录递归扫描(黑名单 / 层级上限 / 截断)
│  │  ├─ proc.js                #   结束进程树(各平台杀法不同)
│  │  ├─ ffmpeg-locate.js       #   ffmpeg/ffprobe 跨平台定位
│  │  └─ store.js               #   设置 / 播放列表 / 续播持久化
│  ├─ renderer/                 # 渲染进程(浏览器侧)
│  │  ├─ index.html             #   界面结构
│  │  ├─ theme-init.js          #   首帧前同步应用主题(防闪烁)
│  │  ├─ styles.css             #   视觉系统(三组 CSS 变量驱动,双主题)
│  │  ├─ app.js                 #   播放引擎 + UI 逻辑
│  │  └─ vendor/hls.min.js      #   hls.js(由 postinstall 拷贝)
│  └─ shared/
│     └─ constants.js           # 主进程与渲染层共用的格式能力表 / 策略决策
├─ scripts/
│  ├─ vendor.mjs                # 拷贝 hls.js
│  ├─ build-demo.mjs            # 生成单文件 Demo
│  └─ check-theme.mjs           # 主题令牌对齐与对比度校验
├─ docs/ARCHITECTURE.md         # 架构与跨平台移植说明
└─ resources/bin/               # 随包分发的 ffmpeg(可选)

六、跨平台移植

架构上已经把平台差异收敛到三个边界,移植时不需要改动业务代码:

  1. ffmpeg-locate.js — 二进制定位。新增平台只需补充「查找路径表」。
  2. main.js 的窗口配置 — 标题栏样式、交通灯位置、菜单角色按 process.platform 分支。
  3. preload.js — 渲染层唯一的外部依赖,保证渲染层零 Node API。

详细说明、待办项(macOS 公证、Linux MPRIS、libmpv 后端)见 docs/ARCHITECTURE.md。


七、已知限制

  • HLS 重封装的分片会写入系统临时目录,长片(>2 小时)转码会占用相应磁盘空间,退出应用时自动清理。
  • 转码模式下的拖动 受限于已转换完成的区间;无损重封装模式下,ffmpeg 通常数秒内完成,可全片随意拖动。
  • 图形字幕(PGS / VobSub)不支持。这类字幕是图片而非文本,无法转成 WebVTT;需要 OCR 或图形渲染后端(见 libmpv 后端规划)。
  • 浏览器 Demo 版本无法播放 MKV/AVI/FLV,也不支持字幕转换 —— 这两者都依赖 Node 侧的 ffmpeg,是架构决定的,不是实现缺陷。

八、许可

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages