import { Api, type SubtitleApplyRequest, type SubtitleFetchRequest, type SubtitleSource } from "@umt/contracts"; import { ApiException } from "../../http/errors.ts"; import { route } from "../agent/http.ts"; import { sourcesIdentityModule } from "../sources-identity/index.ts"; import type { ServerModule } from "../types.ts"; import { downloadOnline, SubtitleSiteError, searchOnline, subtitleSearchQuery } from "./online/index.ts"; import { applySubtitles, parseSubtitles, subtitleTargets, subtitleWrites, undoSubtitleWrite } from "./service.ts"; /** 补字幕(docs/plan/subtitles.md):上传 / 在线下载的字幕解析、配对、写入、历史与撤销 */ export function createSubtitlesModule(): ServerModule { return { name: "subtitles", registerRoutes(app, ctx) { const tag = { tag: "subtitles" }; const rt = () => sourcesIdentityModule.runtime(ctx); const siteErr = async (fn: () => Promise): Promise => { try { return await fn(); } catch (e) { if (e instanceof SubtitleSiteError) throw new ApiException("upstream_unavailable", e.message); throw e; } }; route(app, Api.subtitles.targets, ({ query }) => subtitleTargets(ctx, query.entityId), tag); route(app, Api.subtitles.parse, ({ body }) => parseSubtitles(ctx, body.files, body.entityId), tag); route( app, Api.subtitles.search, ({ body }) => siteErr(async () => searchOnline(ctx, rt(), await subtitleSearchQuery(ctx, body.entityId, body))), tag, ); route( app, Api.subtitles.fetch, ({ body }) => siteErr(async () => { const b = body as SubtitleFetchRequest; const f = await downloadOnline(ctx, rt(), b.siteId, b.ref); const parsed = await parseSubtitles(ctx, [{ name: f.name, base64: f.base64 }], b.entityId); // 在线来源:每项带上字幕站与条目地址,写入时记进历史 return { ...parsed, items: parsed.items.map((it) => ({ ...it, source: f.siteKey as SubtitleSource, sourceRef: f.pageUrl })), }; }), tag, ); route( app, Api.subtitles.apply, ({ body, actorId }) => applySubtitles(ctx, body as SubtitleApplyRequest, actorId), tag, ); route( app, Api.subtitles.writes, async ({ query }) => ({ items: await subtitleWrites(ctx, query.entityId) }), tag, ); route(app, Api.subtitles.undo, ({ params }) => undoSubtitleWrite(ctx, params.id), tag); }, registerTasks() {}, }; } export const subtitlesModule = createSubtitlesModule();