diff --git a/.env.example b/.env.example
index 52946f3..faec802 100644
--- a/.env.example
+++ b/.env.example
@@ -17,7 +17,7 @@ MEDIA_SECRET_KEY=
PASSWORD_RESET_WEBHOOK_URL=
# 弹弹play 开放弹幕网络(https://doc.dandanplay.com/open)
-# 未配置 AppId/AppSecret 时,弹幕接口返回 source=none「未配置开放网络」
+# AppId/AppSecret 推荐在「账户设置」页配置(存库、即时生效,仅管理员);此处为可选的服务端兜底
OPEN_DANMAKU_APP_ID=
OPEN_DANMAKU_APP_SECRET=
# 可选:自定义开放弹幕 API 地址(默认 https://api.dandanplay.net)
diff --git a/app/web/src/components/pages/DanmakuSettingsBody.tsx b/app/web/src/components/pages/DanmakuSettingsBody.tsx
index c1ba8c1..a544ce4 100644
--- a/app/web/src/components/pages/DanmakuSettingsBody.tsx
+++ b/app/web/src/components/pages/DanmakuSettingsBody.tsx
@@ -150,7 +150,10 @@ function Inner(props: { locale: Locale }) {
{t(locale, "danmaku.title")}
{openNetworkMissing && (
)}
diff --git a/packages/i18n/src/messages/en.ts b/packages/i18n/src/messages/en.ts
index 8d0019c..f3a8f20 100644
--- a/packages/i18n/src/messages/en.ts
+++ b/packages/i18n/src/messages/en.ts
@@ -251,7 +251,7 @@ export const en: Record = {
"danmaku.blockTypeTop": "Top",
"danmaku.blockTypeBottom": "Bottom",
"danmaku.openNetworkNotConfigured":
- "Open danmaku network credentials are not configured; online danmaku may be empty.",
+ "Open danmaku network credentials are not configured; online danmaku may be empty. Admins can set them in Settings:",
"danmaku.serverPrefsHint":
"Preferences are stored on this account; watch-page filters and display follow this config.",
diff --git a/packages/i18n/src/messages/zh-CN.ts b/packages/i18n/src/messages/zh-CN.ts
index 794ec18..fdca021 100644
--- a/packages/i18n/src/messages/zh-CN.ts
+++ b/packages/i18n/src/messages/zh-CN.ts
@@ -244,7 +244,8 @@ export const zhCN = {
"danmaku.blockTypeScroll": "滚动",
"danmaku.blockTypeTop": "顶部",
"danmaku.blockTypeBottom": "底部",
- "danmaku.openNetworkNotConfigured": "未配置开放弹幕网络服务端凭据,在线弹幕可能为空。",
+ "danmaku.openNetworkNotConfigured":
+ "未配置开放弹幕网络凭据,在线弹幕可能为空。管理员可前往设置:",
"danmaku.serverPrefsHint": "偏好保存在当前账号下,播放页过滤与显示沿用此配置。",
"auth.login.title": "登录",
diff --git a/packages/trpc/src/services/danmaku.service.test.ts b/packages/trpc/src/services/danmaku.service.test.ts
index 3d96616..4039a5d 100644
--- a/packages/trpc/src/services/danmaku.service.test.ts
+++ b/packages/trpc/src/services/danmaku.service.test.ts
@@ -239,7 +239,7 @@ describe("danmakuService.fetch", () => {
vi.mocked(hasOpenCredentials).mockReturnValue(false);
const r = await danmakuService.fetch("u1", "m1");
expect(r.source).toBe("none");
- expect(r.message).toContain("OPEN_DANMAKU_APP_ID");
+ expect(r.message).toContain("账户设置");
expect(openMatch).not.toHaveBeenCalled();
});
@@ -642,7 +642,7 @@ describe("danmakuService.fetch 同名侧车", () => {
vi.mocked(hasOpenCredentials).mockReturnValue(false);
const r = await danmakuService.fetch("u1", "m1");
expect(r.source).toBe("none");
- expect(r.message).toContain("OPEN_DANMAKU_APP_ID");
+ expect(r.message).toContain("账户设置");
});
it("已有 local-xml 时不扫描侧车", async () => {
diff --git a/packages/trpc/src/services/danmaku.service.ts b/packages/trpc/src/services/danmaku.service.ts
index 4f695c6..f932537 100644
--- a/packages/trpc/src/services/danmaku.service.ts
+++ b/packages/trpc/src/services/danmaku.service.ts
@@ -160,8 +160,7 @@ export const danmakuService = {
ok: true,
source: "none",
xml: "",
- message:
- "未配置开放弹幕网络:服务端需设置 OPEN_DANMAKU_APP_ID / OPEN_DANMAKU_APP_SECRET",
+ message: "未配置开放弹幕网络:管理员请在「账户设置」页填写 AppId / AppSecret",
};
}
@@ -365,6 +364,8 @@ export const danmakuService = {
},
async getPrefs(userId: string): Promise {
+ // 全新启动且凭据只存在库内时,先把库内凭据注入运行时,状态显示才准确
+ await ensureOpenRuntime();
const row = await danmakuPrefsDao.getByUser(userId);
return {
enabled: row?.enabled ?? true,
diff --git a/packages/trpc/src/services/scrape.service.ts b/packages/trpc/src/services/scrape.service.ts
index 7d15a07..d0dd730 100644
--- a/packages/trpc/src/services/scrape.service.ts
+++ b/packages/trpc/src/services/scrape.service.ts
@@ -7,6 +7,7 @@ import {
openMatch,
rankCandidates,
} from "@app/danmaku";
+import { ensureOpenRuntime } from "./settings.service.js";
import { parseEpisodeFromFilename, type BatchResult, type DanmakuMatchOutput } from "@app/types";
import { remoteFileHash } from "./danmaku.service.js";
@@ -192,14 +193,14 @@ export const scrapeService = {
* 未中且有 Bangumi 绑定时走 bgmtv 桥按集数兜底。写 dandanplayEpisodeId + 元数据。
*/
async matchDandanplay(userId: string, mediaItemId: string): Promise {
+ await ensureOpenRuntime();
const row = await mediaItemDao.getByIdForUser(mediaItemId, userId);
if (!row) throw new TRPCError({ code: "NOT_FOUND", message: "媒体不存在" });
if (!hasOpenCredentials()) {
return {
ok: false,
matched: false,
- message:
- "未配置开放弹幕网络:服务端需设置 OPEN_DANMAKU_APP_ID / OPEN_DANMAKU_APP_SECRET",
+ message: "未配置开放弹幕网络:管理员请在「账户设置」页填写 AppId / AppSecret",
candidates: [],
highConfidence: false,
};