feat(organize): expose recovery facts and retain original request on retries

This commit is contained in:
root committed 2026-10-13 20:24:22 +08:00
1 parent 80151077a9
commit b7883eaf4f
2 files changed
+47 -1

No files matched your search

+6 -1
View File
@@ -3,7 +3,7 @@ import { Id, JsonObject, NonNegInt, Timestamp } from "./common.ts";
import { MediaKind } from "./content.ts";
import { DownloadStatus } from "./execution.ts";
import { InstanceHealth, LibraryMediaKind } from "./instances.ts";
import { OrganizationStatus, PublicOrganizationRecord } from "./organization.ts";
import { OrganizationRecovery, OrganizationStatus, PublicOrganizationRecord } from "./organization.ts";
import { SlotStatus } from "./policy.ts";
import { ScrapeMode, ScrapeRecord } from "./scrape.ts";
@@ -400,6 +400,8 @@ export const FilesOrganizeEpisodeStructure = z.object({
export type FilesOrganizeEpisodeStructure = z.infer<typeof FilesOrganizeEpisodeStructure>;
export const FilesOrganizeRequest = z.object({
/** 重新整理沿用此记录的已知业务选项,表单明确选择覆盖它们。 */
recoveryRecordId: Id.optional(),
sourceConfigId: Id.optional(),
idempotencyKey: IdempotencyKey,
items: z.array(FilesOrganizeItem).min(1).max(500),
@@ -970,6 +972,9 @@ export type FilesTreeResult = z.infer<typeof FilesTreeResult>;
export const FilesRecordSourcesQuery = z.object({ recordId: Id });
export type FilesRecordSourcesQuery = z.infer<typeof FilesRecordSourcesQuery>;
export const FilesRecordSourcesResult = z.object({
recovery: OrganizationRecovery.optional(),
/** 已核对登记库根和目录边界的原目标,前端无需从本机绝对路径推算。 */
targetDir: FileRef.optional(),
/** 原整理计划保存的来源策略;不存在时由当前入口重新匹配。 */
sourceConfigId: Id.optional(),
recordId: Id,
+41
View File
@@ -239,7 +239,48 @@ export const RecordAttention = z.object({
});
export type RecordAttention = z.infer<typeof RecordAttention>;
/** 历史整理可以沿用的事实与需补齐的设置;只在详情和重新整理入口返回。 */
export const OrganizationRecoveryRequest = z.object({
downloadTaskId: Id.optional(),
sourcePaths: z.array(z.string()).optional(),
mediaEntityId: Id.optional(),
contentUnitId: Id.optional(),
action: z.enum(["copy", "move", "in_place"]).optional(),
targetLibraryRootId: Id.optional(),
targetDir: z.string().optional(),
sourceConfigId: Id.optional(),
slotId: Id.optional(),
onExisting: z.enum(["skip", "conflict", "replace_with_verify"]).optional(),
namingMode: z.enum(["template", "keep"]).optional(),
minSizeBytes: NonNegInt.optional(),
scrapeAfterImport: z.boolean().optional(),
skipSuccessfulRecords: z.boolean().optional(),
keepBoth: z.boolean().optional(),
importMode: z.enum(["normal", "replace"]).optional(),
replacementAssetIds: z.array(Id).optional(),
fileOverrides: z
.record(
z.string(),
z.object({
season: NonNegInt.optional(),
episodes: z.object({ start: NonNegInt, end: NonNegInt }).optional(),
contentUnitIds: z.array(Id).optional(),
}),
)
.optional(),
episodeStructure: z.object({ groupId: z.string().nullable(), appliedToEntity: z.boolean().optional() }).optional(),
});
export type OrganizationRecoveryRequest = z.infer<typeof OrganizationRecoveryRequest>;
export const OrganizationRecovery = z.object({
ready: z.boolean(),
source: z.enum(["request", "task", "plan", "record"]),
missing: z.array(z.enum(["sourcePaths", "options", "episodeStructure"])),
request: OrganizationRecoveryRequest,
});
export type OrganizationRecovery = z.infer<typeof OrganizationRecovery>;
export const PublicOrganizationRecord = z.object({
recovery: OrganizationRecovery.optional(),
/** 稳定记录 ID */
id: Id,
taskId: Id.nullable(),