diff --git a/app/web/src/components/pages/LibraryBody.tsx b/app/web/src/components/pages/LibraryBody.tsx index c253bdb..73f2ecf 100644 --- a/app/web/src/components/pages/LibraryBody.tsx +++ b/app/web/src/components/pages/LibraryBody.tsx @@ -1008,6 +1008,21 @@ function LibraryInner(props: { locale: Locale }) { void utils.media.librarySeries.invalidate(); }, }); + const dandanMatch = trpc.media.scrapeDandanMatch.useMutation({ + onSuccess: (res) => { + if (res.matched) { + const best = res.candidates[0]; + const title = best?.animeTitle ?? best?.episodeTitle ?? ""; + toast.success(t(locale, "library.dandanMatchOk", { title })); + refreshAll(); + } else { + toast.error(res.message ?? t(locale, "library.dandanMatchNone")); + } + }, + onError: () => { + toast.error(t(locale, "library.dandanMatchFail", { message: "" })); + }, + }); const bindWithEp = (bangumiId: number) => { if (!matchId) return; @@ -1190,7 +1205,19 @@ function LibraryInner(props: { locale: Locale }) { {matchId && (
-

{t(locale, "library.manualTitle")}

+
+

{t(locale, "library.manualTitle")}

+ +
= { "library.reScrape": "Re-scrape", "library.manualTitle": "Manual match", "library.candidates": "Candidates (click to bind)", + "library.dandanMatch": "Identify via dandanplay", + "library.dandanMatchOk": "Matched: {title}", + "library.dandanMatchNone": "dandanplay could not identify this file", + "library.dandanMatchFail": "Match failed: {message}", "browse.batchScrape": "Batch scrape folder", "browse.batchBind": "Bind folder as title", "browse.batchDone": "{message}", @@ -220,7 +224,8 @@ export const en: Record = { "watch.clickToSeek": "Click to seek", "watch.fileMatch": "Match by file", "watch.manualPick": "Pick danmaku source", - "watch.manualPickHint": "Steps: search title → pick episode → fetch that episode (DanDanPlay open network)", + "watch.manualPickHint": + "Steps: search title → pick episode → fetch that episode (DanDanPlay open network)", "watch.sourceSearch": "Search", "watch.sourceSearchPlaceholder": "Search by title keyword", "watch.sourceBackToAnime": "Back to titles", @@ -322,7 +327,8 @@ export const en: Record = { "settings.adminSection": "Open danmaku network", "settings.adminOnly": "Admin only", - "settings.openNetworkHint": "Configure DanDanPlay open network AppId / AppSecret for online danmaku matching and fetch. Admin only.", + "settings.openNetworkHint": + "Configure DanDanPlay open network AppId / AppSecret for online danmaku matching and fetch. Admin only.", "settings.openAppId": "AppId", "settings.openAppSecret": "AppSecret", "settings.secretPlainHint": "Secret is stored in plaintext and visible to admins only.", @@ -332,7 +338,9 @@ export const en: Record = { "settings.openSaveFailed": "Save failed", "settings.save": "Save", "settings.obtainCreds": "Get credentials", - "settings.obtainCredsHint": "AppId/AppSecret are issued by the DanDanPlay DevCenter (create an app and pass review). They cannot be obtained by signing in with a normal account.", + "settings.obtainCredsHint": + "AppId/AppSecret are issued by the DanDanPlay DevCenter (create an app and pass review). They cannot be obtained by signing in with a normal account.", "settings.openDevCenter": "Open DevCenter", - "settings.obtainSteps": "Steps: sign in / register at DevCenter → create an app → wait for review → copy AppId and AppSecret from the app detail", + "settings.obtainSteps": + "Steps: sign in / register at DevCenter → create an app → wait for review → copy AppId and AppSecret from the app detail", }; diff --git a/packages/i18n/src/messages/zh-CN.ts b/packages/i18n/src/messages/zh-CN.ts index eb18a2a..794ec18 100644 --- a/packages/i18n/src/messages/zh-CN.ts +++ b/packages/i18n/src/messages/zh-CN.ts @@ -66,6 +66,10 @@ export const zhCN = { "library.reScrape": "重新刮削", "library.manualTitle": "手动指定", "library.candidates": "候选作品(点选绑定)", + "library.dandanMatch": "弹弹play 文件识别", + "library.dandanMatchOk": "识别成功:{title}", + "library.dandanMatchNone": "弹弹play 未识别到该文件", + "library.dandanMatchFail": "识别失败:{message}", "browse.batchScrape": "批量刮削此夹", "browse.batchBind": "指定此夹为作品", "browse.batchDone": "{message}", @@ -312,7 +316,8 @@ export const zhCN = { "error.internal.desc": "服务器处理请求时发生意外。请稍后重试;若持续出现请反馈路径与时间。", "settings.adminSection": "开放弹幕网络", "settings.adminOnly": "仅管理员", - "settings.openNetworkHint": "配置弹弹play 开放网络 AppId / AppSecret,用于在线弹幕匹配与拉取。仅管理员可见可改。", + "settings.openNetworkHint": + "配置弹弹play 开放网络 AppId / AppSecret,用于在线弹幕匹配与拉取。仅管理员可见可改。", "settings.openAppId": "AppId", "settings.openAppSecret": "AppSecret", "settings.secretPlainHint": "Secret 以明文保存,仅管理员可查看与修改。", @@ -322,9 +327,11 @@ export const zhCN = { "settings.openSaveFailed": "保存失败", "settings.save": "保存", "settings.obtainCreds": "获取凭证", - "settings.obtainCredsHint": "AppId/AppSecret 由弹弹play 开发者中心发放(创建应用并通过审核),无法用普通账号登录自动获取。", + "settings.obtainCredsHint": + "AppId/AppSecret 由弹弹play 开发者中心发放(创建应用并通过审核),无法用普通账号登录自动获取。", "settings.openDevCenter": "打开开发者中心", - "settings.obtainSteps": "步骤:登录/注册开发者中心 → 创建应用 → 等待审核 → 在应用详情复制 AppId 与 AppSecret", + "settings.obtainSteps": + "步骤:登录/注册开发者中心 → 创建应用 → 等待审核 → 在应用详情复制 AppId 与 AppSecret", } as const; export type MessageKey = keyof typeof zhCN;