feat(subtitles): 字幕站(SubHD、字幕库)在线搜索与下载;rar / 7z 解压;验证码与页面解析在沙箱里

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 02:28:29 +08:00
1 parent 92d456d123
commit 59b3680f35
32 files changed
+1704 -166

No files matched your search

+55
View File
@@ -69,6 +69,9 @@ export const SubtitleParsedItem = z.object({
content: z.string(),
/** 建议的视频位置 */
targetLocationId: Id.nullable(),
/** 在线下载的:字幕站与条目地址(写入时记进历史) */
source: SubtitleSource.optional(),
sourceRef: z.string().optional(),
});
export type SubtitleParsedItem = z.infer<typeof SubtitleParsedItem>;
@@ -91,6 +94,8 @@ export const SubtitleApplyRequest = z.object({
targetLocationId: Id,
/** 在线来源的条目地址 */
sourceRef: z.string().max(1000).optional(),
/** 这一项的来源(不给用整批的 source) */
source: SubtitleSource.optional(),
}),
)
.min(1)
@@ -140,3 +145,53 @@ export const SubtitleWriteRow = z.object({
export const SubtitleWritesQuery = z.object({ entityId: Id });
export const SubtitleWritesResponse = z.object({ items: z.array(SubtitleWriteView) });
// ── 在线(字幕站:SubHD、字幕库) ──
export const SubtitleSearchRequest = z.object({
entityId: Id,
/** 剧集:只要这一季(集)的字幕;不给 = 全部 */
season: z.number().int().min(0).optional(),
episode: z.number().int().min(0).optional(),
/** 额外的搜索词(作品名搜不到时手动填) */
keyword: z.string().trim().max(100).optional(),
});
export type SubtitleSearchRequest = z.infer<typeof SubtitleSearchRequest>;
export const SubtitleSearchResult = z.object({
siteId: Id,
siteKey: z.string(),
siteName: z.string(),
/** 站内引用(下载时原样提交) */
ref: z.string(),
title: z.string(),
pageUrl: z.string(),
languages: z.array(z.string()),
format: z.string().nullable(),
downloads: z.number().int().nullable(),
uploader: z.string().nullable(),
publishedAt: z.string().nullable(),
season: z.number().int().nullable(),
episode: z.number().int().nullable(),
score: z.number(),
});
export type SubtitleSearchResult = z.infer<typeof SubtitleSearchResult>;
export const SubtitleSearchResponse = z.object({
results: z.array(SubtitleSearchResult),
/** 用到的搜索词 */
keywords: z.array(z.string()),
/** 各站出错 / 需要人工处理 */
errors: z.array(z.object({ siteName: z.string(), reason: z.string() })),
/** 没有启用的字幕站 */
noSites: z.boolean(),
});
export type SubtitleSearchResponse = z.infer<typeof SubtitleSearchResponse>;
export const SubtitleFetchRequest = z.object({
entityId: Id,
siteId: Id,
ref: z.string().min(1).max(500),
title: z.string().max(500).default(""),
});
export type SubtitleFetchRequest = z.input<typeof SubtitleFetchRequest>;