fix(organize): stop pending sidecars when replaying failed main files
This commit is contained in:
1 parent
b306d27d08
commit
09a4c9fc1e
2 files changed
+48
-1
No files matched your search
@@ -166,7 +166,10 @@ export async function executeRecord(
|
||||
.orderBy("ordinal")
|
||||
.execute();
|
||||
const planned = new Map(frozen.organize.files.map((f) => [f.src, f]));
|
||||
const failedMains = new Set<string>();
|
||||
// 主文件的失败可能已在上次执行提交;重放也要阻止随行文件脱离主文件继续搬运。
|
||||
const failedMains = new Set(
|
||||
files.filter((f) => f.role === "main" && f.status === "failed").map((f) => f.source_path),
|
||||
);
|
||||
/** 本轮 TIMEOUT/IO 类失败、留待任务重试的文件(结果不入账本,文件结果保持 running) */
|
||||
const deferred: {
|
||||
src: string;
|
||||
|
||||
@@ -50,6 +50,8 @@ describe.skipIf(!hasPg)("整理卡死回收(真实 PG + mock qB + 临时目录)"
|
||||
"stall/Stall.Show.S01E01.1080p.WEB-DL.x264-GRP.mkv": SIZE,
|
||||
"cap/Cap.Show.S01E01.1080p.WEB-DL.x264-GRP.mkv": SIZE,
|
||||
"offline/Offline.Show.S01E01.1080p.WEB-DL.x264-GRP.mkv": SIZE,
|
||||
"sidecar/Sidecar.Show.S01E01.1080p.WEB-DL.x264-GRP.mkv": SIZE,
|
||||
"sidecar/Sidecar.Show.S01E01.1080p.WEB-DL.x264-GRP.srt": 100,
|
||||
},
|
||||
{ prefix: "umt-reccache-" },
|
||||
);
|
||||
@@ -208,6 +210,48 @@ describe.skipIf(!hasPg)("整理卡死回收(真实 PG + mock qB + 临时目录)"
|
||||
expect((await recoverStalledRecords(h.d)).failed).toBe(0);
|
||||
});
|
||||
|
||||
it.each(["copy", "move"] as const)("%s 重放已失败的主文件:未完成字幕留在源目录,不再搬运", async (action) => {
|
||||
const entityId = await seedEntity(h.db, { kind: "series", title: `字幕恢复 ${action}`, year: 2024 });
|
||||
const plan = await planImport(h.d, {
|
||||
sourcePaths: [cache.path("sidecar")],
|
||||
mediaEntityId: entityId,
|
||||
action,
|
||||
trigger: "manual",
|
||||
actorType: "user",
|
||||
});
|
||||
expect(plan.status, JSON.stringify(plan)).toBe("running");
|
||||
const recordId = plan.recordId!;
|
||||
// 主文件失败已经提交,进程在处理随行字幕前退出;恢复时主文件不会再次执行。
|
||||
await h.db
|
||||
.updateTable("organization_file_result")
|
||||
.set({ status: "failed", error: JSON.stringify({ code: "EEXIST", message: "目标冲突" }) })
|
||||
.where("organization_record_id", "=", recordId)
|
||||
.where("role", "=", "main")
|
||||
.execute();
|
||||
const sidecar = await h.db
|
||||
.selectFrom("organization_file_result")
|
||||
.selectAll()
|
||||
.where("organization_record_id", "=", recordId)
|
||||
.where("role", "=", "subtitle")
|
||||
.executeTakeFirstOrThrow();
|
||||
expect(sidecar.status).toBe("queued");
|
||||
expect(sidecar.target_path).not.toBeNull();
|
||||
|
||||
expect((await executeRecord(h.d, recordId)).status).toBe("failed");
|
||||
const result = await h.db
|
||||
.selectFrom("organization_file_result")
|
||||
.selectAll()
|
||||
.where("id", "=", sidecar.id)
|
||||
.executeTakeFirstOrThrow();
|
||||
expect(result).toMatchObject({ status: "skipped", skip_reason: "main_failed", asset_operation_id: null });
|
||||
expect(result.finished_at).not.toBeNull();
|
||||
expect(existsSync(sidecar.source_path)).toBe(true);
|
||||
expect(existsSync(join(lib.root, sidecar.target_path!))).toBe(false);
|
||||
expect(
|
||||
await h.db.selectFrom("asset_operation").select("id").where("organization_record_id", "=", recordId).execute(),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("库根离线:不抛错耗尽重试,记录等待并开待处理;恢复后巡检重投(不计上限)并完成", async () => {
|
||||
const id = await manualRecord("offline", "离线的剧");
|
||||
await rm(join(lib.root, ".uhd-root"));
|
||||
|
||||
Reference in new issue
Block a user