给自己的播放器搭了个弹幕 API:danmu_api 部署教程 + 最优配置
先把地址放这儿,欢迎自取:
https://danmu.cdnapi.us.ci/59382174—— 这是我自建的弹幕 API,填到播放器(SenPlayer、Forward、FongMi 等)的弹幕 API 地址里就能直接用,免费。下面是它的搭建和配置全过程。
看剧看番的时候,弹幕基本是刚需。但弹幕散落在各家平台里:腾讯的归腾讯,B站的归B站,播放器(SenPlayer、Forward、FongMi 这类)本身不生产弹幕,得有一个中间服务替你去各家搜、去各家拉,再按弹弹play 的协议格式喂给播放器。
这个中间服务我用的是开源项目 danmu_api(作者 LogVar),一个弹幕聚合 API:给它片名,它并发去腾讯、优酷、爱奇艺、芒果、B站、咪咕、搜狐、乐视、西瓜、人人、韩剧TV、弹弹play、巴哈姆特、Animeko 等十几个源里搜;给它一集,它把弹幕拉回来、去重合并、转成播放器能吃的 JSON/XML。现在它跑在我的 danmu.cdnapi.us.ci 上,常驻成本为零。这篇把部署过程和我后来全网调研定下的一套「最优配置」一起记下来——配置那部分才是重点,默认配置其实有几个大坑。
部署路线怎么选
这个项目支持好几种跑法:Docker / 本地 Node(官方首推,最稳)、Vercel / Netlify、Cloudflare Workers、EdgeOne。我选了 Workers,理由很简单:域名和账号现成的,不占服务器资源,免费额度对个人用绰绰有余。
但要先把丑话说前面:官方 README 自己写了 Cloudflare「不稳定」、更推荐 Vercel/Netlify 或自托管。我在文末会把实测到的具体毛病列出来,对稳定性敏感的建议直接走 Docker 路线,后面的配置章节对所有路线都适用(改完记得重新部署才生效)。
Workers 部署实录
准备
- 一个 Cloudflare 账号;
- 一个托管在 Cloudflare 的域名,用来绑服务地址(我用的是
danmu.cdnapi.us.ci); - 本地装好 Node.js 和 git。
拉代码、打包
1git clone https://github.com/huangxd-/danmu_api.git
2cd danmu_api
3npm install
项目的入口是 danmu_api/worker.js,原本的流程是用 wrangler 部署。我这边因为 API 令牌的保管方式特殊,wrangler 用不上,改用 esbuild 自己打包、再调 Workers 的 API 上传——效果一样。打包这一步有三个坑,都是我实际撞出来的:
- Workers 的 nodejs_compat 里没有
node:fs。项目里有模块require('fs')读本地缓存文件,Worker 上传校验会直接报 10021 错。解法是打包时把fs、node:fs、fs/promises、node:fs/promises全部 alias 到一个空操作的替身模块——落盘缓存失效后,程序会自动退回「CDN 拉取 + 内存缓存」,这正是它在无服务器环境设计的运行方式。 process.cwd在 Workers 里不存在,而有个数据模块在加载时就调用它,不处理的话 Worker 启动即崩。解法是在打包产物头部加一行 banner 把process.cwd补成返回'/'。- 兼容性标志必须写
nodejs_compat。我一度写成nodejs_compat_v2,API 接受了、却静默不生效,结果打包校验卡在node:http上报错,排查了好一阵。兼容日期用仓库wrangler.toml里写的2025-09-13,不要乱改。
打包命令大致是这样:
1npx esbuild danmu_api/worker.js --bundle --format=esm \
2 --platform=node --target=node20 --minify \
3 --alias:fs=./stubs/fs.js --alias:node:fs=./stubs/fs.js \
4 --alias:fs/promises=./stubs/fs-promises.js \
5 --alias:node:fs/promises=./stubs/fs-promises.js \
6 --banner:js="process.cwd = () => '/';" \
7 --outfile=dist/worker.js
打包后约 3.0MB、gzip 后约 776KB,卡在 Workers 免费版「脚本 gzip 后不超过 1MB」的线内。之后用 wrangler 部署,或者像我一样把产物通过 Workers Scripts API 以 multipart 形式 PUT 上去,都可以。
绑定域名和令牌
上传时给 Worker 绑两个最基础的环境变量:
TOKEN:访问令牌,自己定一个(比如 8 位数字)。播放器和接口都靠它鉴权;ADMIN_TOKEN:管理员令牌,定一个长一点的随机串。
再在 Workers 的自定义域名里把 danmu.cdnapi.us.ci 绑到这个 Worker 上,等证书签发完,服务就上线了。
怎么用
- 播放器地址:
https://danmu.cdnapi.us.ci/<TOKEN>,填到播放器的弹幕 API 地址里就行;FongMi 还可以直接用根地址或/danmaku的简化形式。我这个实例的地址直接公开在这里,欢迎自取试用:https://danmu.cdnapi.us.ci/59382174(填到播放器的弹幕 API 地址里就能用); - 管理后台:这里有个我一开始没搞清的点——用普通 TOKEN 打开的是日常界面,里面的「系统部署」功能是锁着的,会弹窗提示你先配置 ADMIN_TOKEN。完整的后台要用管理员令牌当路径打开:
https://danmu.cdnapi.us.ci/<ADMIN_TOKEN>,配置预览、日志、接口调试、弹幕测试、环境变量管理都在里面。
最优配置(重点)
服务跑起来只是第一步。danmu_api 有几十个环境变量,我把官方文档、仓库的 .env.example、GitHub 上的讨论和社区帖子(主要是 LINUX DO 的一篇逐项点评)翻了一遍,先说结论:默认配置有三个大坑——
- 默认只开了 4 个源:
SOURCE_ORDER的默认值只有douban,360,renren,hanjutv四个聚合源,腾讯、优酷、爱奇艺、芒果、B站这些直连源默认全是关的,搜不到、匹配差都源于此; - 默认限流每分钟 3 次:
RATE_LIMIT_MAX_REQUESTS默认 3,意思是同一 IP 每分钟最多拉 3 次弹幕,播放器连着切几集就被误伤限流; - 默认缓存只有 3 分钟,对 Workers 这种「缓存只存在于单个实例内存、冷启动就没了」的环境偏短,官方给 Cloudflare 的建议区间是 1–5 分钟,应该取上限。
下面是我定下来的完整配置(后台的环境变量页面或部署变量里照着填,TOKEN 换成你自己的):
1# —— 源与匹配 ——
2SOURCE_ORDER=douban,360,tencent,youku,iqiyi,imgo,bilibili,migu,dandan,bahamut,animeko,renren,hanjutv
3PLATFORM_ORDER=bilibili1,qq,qiyi,youku,imgo,migu,dandan,bahamut,animeko,renren,hanjutv
4MERGE_SOURCE_PAIRS=dandan&animeko
5
6# —— 限流与缓存 ——
7RATE_LIMIT_MAX_REQUESTS=0
8SEARCH_CACHE_MINUTES=5
9COMMENT_CACHE_MINUTES=5
10COMMENT_CACHE_MIN_COUNT=100
11
12# —— 并发 ——
13YOUKU_CONCURRENCY=16
14TENCENT_CONCURRENCY=100
15IQIYI_CONCURRENCY=100
16
17# —— 显示效果 ——
18CONVERT_TOP_BOTTOM_TO_SCROLL=false
19CONVERT_COLOR=color
20DANMU_SIMPLIFIED_TRADITIONAL=simplified
21GROUP_MINUTE=1
22DANMU_LIMIT=8
23LIKE_SWITCH=true
24
25# —— 云端 ——
26USE_BANGUMI_DATA=false
逐项说一下理由:
源与匹配。 SOURCE_ORDER 把主流直连源全部打开,聚合源(豆瓣、360)排前面兜底——豆瓣对海外 IP 尤其重要,是 Cloudflare 出口下的主力。PLATFORM_ORDER 是自动匹配时的平台偏好:B站弹幕质量和数量通常最好排第一,腾讯、爱奇艺、优酷次之。MERGE_SOURCE_PAIRS=dandan&animeko 是社区唯一有明确推荐的合并组合:看番时把弹弹play 和 Animeko 两个源的弹幕合并返回,量明显更足;注意不要合并韩剧TV(它插广告会让弹幕时间轴错位),影视类多源合并也有同样的错位风险,我只开了动漫这一组。
限流与缓存。 限流直接设 0 关闭——实例有 TOKEN 保护,不怕陌生人刷,留着只会误伤自己。缓存两个都拉到 5 分钟(官方给 Cloudflare 的建议上限):Workers 的缓存只在单个实例内存里,多留一会儿命中率实在些。
并发。 只有优酷值得拉满:YOUKU_CONCURRENCY 默认 8、上限 16,官方说明就是用来加快优酷弹幕速度的。腾讯和爱奇艺的并发(默认 100)千万别往上调——这俩参数是 2026 年才加的「防爆上限」,本意是防止长视频整集弹幕一次性打出太多上游请求,不是提速钮;而且 Cloudflare 免费版单次请求只有 50 个子请求额度,调到 200 只会更快撞限、导致后半集没弹幕。反过来,如果你常遇到「长集后半段弹幕消失」,把这俩降到 50 让分片分批稳妥跑完,完整性更好。
显示效果。 CONVERT_COLOR=color 把白色弹幕转成随机彩色,手机和电视上醒目很多(在意原汁原味的改回 default);DANMU_SIMPLIFIED_TRADITIONAL=simplified 把繁体弹幕统一转简体,用了巴哈姆特源基本必开;DANMU_LIMIT=8 是弹幕总数上限 8000 条(单位是千),社区建议设限防播放器被弹幕洪流卡死,弱设备可以再压低;GROUP_MINUTE=1 保持默认的分钟级去重,合并多源时全靠它压重复弹幕;LIKE_SWITCH=true 保留点赞标记(只有 ≥5 赞才显示,相当于自带「前方高能」提示,不吵);CONVERT_TOP_BOTTOM_TO_SCROLL 保持 false——SenPlayer、Forward 都能正常显示顶部/底部弹幕,只有你的播放器会丢弃顶底弹幕时才开 true。
云端。 USE_BANGUMI_DATA=false 保持关闭,官方说明写得很直白:云部署时它把数据缓存在临时内存里,体验不佳。另外两个按需项:B站源建议配一个小号的 BILIBILI_COOKIE(SESSDATA),能拿到完整弹幕,但多人共用有封号风险,别用大号;豆瓣源出现搜索不稳定、频繁验证时,再补 DOUBAN_COOKIE 不迟。
最后留三档取舍,方便对号入座:想再快——从 SOURCE_ORDER 里删掉 bahamut,animeko,renren,hanjutv 并清空合并配置;想弹幕再多——DANMU_LIMIT=0、合并改成 dandan&bahamut&animeko,代价是变慢且更容易撞 Cloudflare 的子请求上限;均衡——就是上面的清单,我自己在用的。
跑在 Cloudflare 上的实测毛病
配置之外,把我上线后实测到的几个平台问题如实记下来,免得后来人(和我自己)怀疑是配置写错了:
- 搜索不算快:冷启动搜一次《火影忍者》大概 12 秒、《庆余年》15 秒上下,返回结果倒是全了(火影 27 条,弹弹源占 17 条)。这是十几个源并发查询 + 海外出口延迟的结果,热缓存命中后会快一些,但 Workers 的缓存不跨实例,别指望每次都命中。
- 腾讯源对 Cloudflare 出口 IP 间歇性限流:部署当天初测,同一集腾讯视频能拉回 2816 条弹幕;过一阵再测,连测几次都是 0 条。我特意做了对照实验——把配置全部撤回出厂默认重测,结果一样是 0,确认不是配置的问题,是腾讯对数据中心 IP 时好时坏。碰到某一集弹幕为 0,先过一会儿重试,或者在搜索结果里换优酷/爱奇艺/B站的源。
- 两步式调用偶尔会丢状态:弹弹play 协议是「先 match 拿到 episodeId、再用 ID 拉弹幕」的两步式,而 ID 和集数的对应关系只存在单个实例的内存里;两次请求被 Cloudflare 分到不同实例时,第二步就会查不到、返回 0。单请求流程(match 直接返回、按视频 URL 拉弹幕、FongMi 的
/danmaku)不受影响。官方给的根治办法是接一个 Upstash Redis 做跨实例状态(顺带还能持久化收藏),免费额度个人用够了。(2026-11-06 更新:已经接上了。这个毛病后来在实际使用里频繁发作,按集取弹幕直接 404,索性把 Upstash Redis 配好部署上去,实测「搜索 → 按集取弹幕」恢复正常,这条毛病算是根治了。中间还有个小插曲:Redis 里一份早期自动存下的旧快照自相矛盾,程序每次都拒绝加载它,清掉之后才通。) - 巴哈姆特源在 Cloudflare 上不用配代理:官方说明 vercel/netlify/cf 理应都能直连巴哈;但如果日志里看到巴哈频繁超时,它会拖慢整体搜索,那还不如直接从
SOURCE_ORDER里删掉它。
小结
整套东西的账很好算:服务本身免费跑在 Workers 上,域名是现成的,播放器里填一个地址就全网弹幕都有了。配置上真正要紧的就三件事——源要全开、限流要关掉、腾讯/爱奇艺并发别乱调高,其余都是锦上添花。
我的选择是先在 Workers 上用着,免费额度完全够个人折腾;哪天腾讯限流闹得频繁了,再把同一个项目用 Docker 搬到自己的服务器上——官方首推的跑法,域名都不用换,播放器那边无感。