这套东西由三个仓库组成,解决的是同一个问题:给 Beat Saber 的自定义歌曲配背景视频,并且能一口气连着打下去,不用每首之间回菜单。
0. 先说结论
能实现的是「自动跳结算的连播」,不是真正的零间隔无缝。歌与歌之间仍会退出关卡、重新加载下一首,通常有 1~数秒的黑屏或加载时间。原因见第 6 节。
1. 三个仓库分别管什么
- beat-saber-video-tool(网页版)—— 主控台。浏览器里管理全部歌曲的视频状态、补 YouTube 链接、批量下载、试看调偏移、写 Cinema / Theater 配置,顺带生成连打挑战的 JSON。
- yt-pot-helper(可选)—— 专治 YouTube 的「Sign in to confirm you’re not a bot」。起一个本地 PoToken 服务;另外带 Cookie Sync,能把浏览器里的 YouTube 登录态一键推到网页版。
- SetlistChallenge(游戏 mod)—— 真正在游戏里生效的部分:按预设顺序连打、自动跳过结算、NoFail 式继续。
数据流大概是这样:
YouTube ──yt-dlp──▶ 本地视频 ──ffmpeg──▶ 歌曲文件夹
│
网页版写配置 ────┴──▶ cinema-video.json / theater-video.json
(Cinema / Theater mod 读取)
网页版「挑战」页 ──▶ UserData/SetlistChallenge/*.json ──▶ SetlistChallenge mod
2. 准备工作
- Beat Saber 1.44.1,已装 BSIPA
- 视频播放 mod:Cinema 全系,或 Theater v1.17(1.27 之后 Cinema 停更,后继是 Theater)
- Node.js 18+(网页版)
- yt-dlp.exe 和 ffmpeg.exe(下载与转码;网页版设置页里填路径)
- Windows 为主(工具本身也支持 macOS / Linux,只是启动脚本是 .bat)
3. 第一步:跑起网页版
下载 release 包(或直接克隆仓库),进入目录后:
- 双击
启动网页版.bat - 浏览器会自动打开
http://127.0.0.1:8770;如果 8770 被占用,会自动换成 8771、8772……以黑色窗口里显示的地址为准 - 关掉那个黑色窗口 = 停止服务
服务只监听本机 127.0.0.1,不对局域网或外网开放,这点可以放心。
首次打开要填的三样东西
- yt-dlp 路径 和 ffmpeg 路径:不填的话下载会直接失败
- 代理:默认直连;YouTube 连不上时再填本机或局域网代理(例如
http://127.0.0.1:7890) - Cookies:启动时会自动探测本机浏览器,也可以在设置页手动导入

歌曲总览
打开就是全部自定义歌曲的清单,按状态分成「已下载 / 待下载 / 需补链接」,每首显示时长、YouTube 链接和偏移值。

常用按钮:
- 🎬 试看:有视频时打开试看弹窗,顺便调偏移
- 🎧 试听:没视频时只听歌曲音频,用来确认是哪首歌
- 📁 本地:直接用本地已有的视频
- 下载 / 重下:单个下载或强制覆盖
- 📂:打开这首歌的文件夹
试看弹窗:调偏移的地方
点「🎬 试看」打开。左边是视频、右边是 3D 谱面预览,底部是两条频谱(视频声 vs 谱面声)。

- 偏移:
-1000到+1000毫秒的粗调,配合 ms 微调按钮 - 🪄 自动对齐:解码两条音轨自动算建议偏移,并立即应用到试看(不会自动保存)
- 💾 保存偏移:满意后写入配置
- 🎧 左耳 / 右耳 / 立体声:左耳只有视频声、右耳只有谱面声,方便听感对拍
- 🔇 删除音频 / 🗑 删除视频:清掉不需要的音轨或视频
偏移调好之后点保存,游戏里选中这首歌就能看到视频了。
4. YouTube 下载:Cookies 与 PoToken
批量下载报这个错,说明 YouTube 要你证明自己是人:
Sign in to confirm you’re not a bot. Use --cookies-from-browser or --cookies for the authentication.
两种解法,按情况选:
方案 A:给 cookies(大多数情况够用)
浏览器里已经登录 YouTube 的话,在网页版「设置 → Cookies」里:
- 点「检测」看当前生效的来源(浏览器 / cookies.txt / 手动)
- 或者直接粘贴 Netscape 格式的
cookies.txt内容导入 - 也可以填一个 URL 让工具去拉
方案 B:yt-pot-helper(cookies 也不管用时)
它在本机起一个 PoToken 服务给 yt-dlp 用:
powershell -ExecutionPolicy Bypass -File .\setup-pot.ps1
按提示走完,保持窗口开着,再回网页版重新下载。
Cookie Sync:抓完自动推给网页版
以前要在浏览器导出 cookies.txt、再手动粘贴到网页版,现在一条命令:
# 默认从 Edge 导出,推送到 http://127.0.0.1:8770
powershell -ExecutionPolicy Bypass -File .\sync-cookies.ps1
# 换浏览器 / 换工具地址
.\sync-cookies.ps1 -Browser firefox -ToolUrl http://127.0.0.1:8770
# 已经有 cookies.txt,直接上传
.\sync-cookies.ps1 -CookiesFile .\cookies.txt
也可以直接双击 sync-cookies.bat。脚本会:
- 用 yt-dlp 从浏览器导出 YouTube cookies
- POST 到网页版的
/api/cookies/import(就是设置页「导入」用的同一个接口) - 自动调用
/api/cookies/detect做一次有效性检测并打印结果 - 临时文件默认用完即删
参数一览:
-Browser:firefox / edge / chrome / brave / opera / vivaldi / chromium,默认edge-ToolUrl:网页版地址,默认http://127.0.0.1:8770-CookiesFile:直接上传已有文件,跳过导出-Ytdlp:指定 yt-dlp 路径,默认自动找.\bin\yt-dlp.exe-KeepFile:保留导出的临时文件
安全:cookies 等价于登录态。脚本只会 POST 到你本机运行的网页版,不会发给任何第三方。
5. 装 SetlistChallenge
依赖
BeatSaberMarkupLanguage ^1.12.0SiraUtil ^3.0.0SongCore ^3.14.0
把 SetlistChallenge.dll 放进:
<Beat Saber>\Plugins\SetlistChallenge.dll
游戏内使用
- 进游戏 → 设置 → Mods 设置 → 无缝挑战
- 选一个挑战,设定「失败即结束 / 跳过结算 / 进度 HUD」
- 点「▶ 开始挑战」
挑战配置格式
配置读自 UserData/SetlistChallenge/*.json:
{
"title": "周末挑战",
"mode": "setlist",
"failEndsRun": false,
"skipResults": true,
"shuffle": false,
"entries": [
{ "folder": "13b80 (Emir - Hitchcock)", "characteristic": "Standard", "difficulty": "ExpertPlus" }
]
}
mode:setlist按 entries 顺序;random按筛选条件随机抽failEndsRun:true失败即结束;false开 NoFail 继续打skipResults:结算界面出现后自动按掉shuffle:把 entries 顺序打乱entries[].folder:曲目文件夹名,必须和游戏里的目录名一致
6. 用网页版生成挑战配置
手写 JSON 容易把文件夹名拼错,网页版的「🎬 无缝挑战」页就是干这个的:
- 新建挑战,或者从现有播放列表一键导入
- 设置「失败即结束 / 跳过结算」
- 保存时直接写进
UserData/SetlistChallenge/*.json,SetlistChallenge 读的就是这个格式

7. 它到底怎么「连播」(重要)
标题里一直写「无缝挑战」,但这里必须说清楚:它不是真正的无缝换谱。
最初的目标是 Shaffuru 那种「关卡内直接换谱」:上一首快结束时溶解音符、改写活的谱面数据、接着播下一首,全程不退出关卡。这个在 1.44 上没做成——谱面数据能插进去,但游戏不会据此生成新的音符和物块,结果只是音乐换了、谱面还是旧的。
所以现在的实现换了一条稳的路:
- 一首正常打完 → 游戏进入 Results(结算)界面
SkipResultsPatch在界面激活约 2 帧后自动触发「继续」,人几乎看不到结算StartStandardLevel的回调拿到本首结果,立即开始下一首- 下一首重新加载整个关卡(正常读谱、载入场景、重新起播)
实际体验:
- ✅ 不用回菜单、不用手点,歌与歌之间不需要任何人工操作
- ❌ 不是零间隔:每首之间仍有关卡加载,会有可见的停顿或黑屏,通常 1~数秒
- ❌ 不是场景内换谱:音乐和谱面都是重新加载的
代码里场景内换谱那条路径仍然保留着,但 SeamlessSwapEnabled = false,默认关闭。等 1.44 的换谱问题解决了才会打开。
8. 常见问题
游戏里看不到视频
- 确认 Cinema 或 Theater 已装、且版本匹配游戏 1.44.1
- 确认写的是对应的配置文件:Cinema 用
cinema-video.json,Theater 用theater-video.json - 改完配置要重启游戏才生效
- 歌曲文件夹名变了的话,配置里的路径会失效,重新跑一次下载/本地关联
试看没有声音
浏览器要求先有用户操作才允许出声,点一下「▶ 播放」就好。
视频和谱面对不上
在试看弹窗里调偏移:先用「🪄 自动对齐」拿建议值,再用左右耳分离听感微调,满意后点「💾 保存偏移」。偏移单位是毫秒,和游戏内 Cinema 的 offset 一致。
下载报 bot 检查 / 403
见第 4 节:先补 cookies,还不行就上 yt-pot-helper 的 PoToken。
连打时每首之间黑屏几秒
这是预期行为,属于关卡重新加载,不是 bug。想彻底消除只能等场景内换谱方案成熟。
连打里的成绩算不算
当前每首都走普通的 StartStandardLevel,成绩按游戏默认规则提交。但连续高强度打容易失误,想刷分还是单曲打更稳。
mod 版本不匹配崩溃
SetlistChallenge 的 manifest.json 固定 gameVersion 1.44.1。游戏升版本后要等 mod 更新,别硬装。
9. 文件位置速查
- 视频配置:
<Beat Saber>\UserData\cinema-video.json/theater-video.json - 挑战配置:
<Beat Saber>\UserData\SetlistChallenge\*.json - mod 本体:
<Beat Saber>\Plugins\SetlistChallenge.dll - 歌曲:
<Beat Saber>\Beat Saber_Data\CustomLevels\<文件夹>(视频一般放在歌曲文件夹里)
10. 仓库地址
- github.com/EuphonXD/beat-saber-video-tool
- github.com/EuphonXD/SetlistChallenge
- github.com/EuphonXD/yt-pot-helper
三个都是 MIT 协议,随便改随便用。遇到问题优先看各仓库的 README,里面写得更细。