67 lines
2.7 KiB
TypeScript
67 lines
2.7 KiB
TypeScript
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 <T>(fn: () => Promise<T>): Promise<T> => {
|
|
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();
|