fix(acquisition): 同一种子被别的计划占用而取消的计划不让位,避免 B/C 两线合并后取消 → 重评 → 再取消的空转
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
15d7f2ec2f
commit
5b023ad682
2 files changed
+22
-2
No files matched your search
@@ -1103,12 +1103,18 @@ export function subscriptionSavePath(t: Pick<InstanceTopology, "inst" | "mapping
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 旧计划是否让位给新计划(T1-3c):失败 / 取消 / 过期可以重建(站点 Cookie 修好、下载器恢复、集数映射改号后重评),
|
* 旧计划是否让位给新计划(T1-3c):失败 / 取消 / 过期可以重建(站点 Cookie 修好、下载器恢复、集数映射改号后重评),
|
||||||
* 但用户明确不要的(在下载器里移除、手动取消)和整理阶段失败的(文件已下好,重下同一种子无意义,交给整理重试)仍挡住。
|
* 但用户明确不要的(在下载器里移除、手动取消)和整理阶段失败的(文件已下好,重下同一种子无意义,交给整理重试)仍挡住;
|
||||||
|
* 同一种子已被别的计划占用而取消的(torrent_in_use_by_plan)也挡住,否则取消即重评、重评又撞上同一任务,来回空转。
|
||||||
*/
|
*/
|
||||||
function planRetryable(p: { status: string; status_reason: string | null }): boolean {
|
function planRetryable(p: { status: string; status_reason: string | null }): boolean {
|
||||||
if (!["failed", "cancelled", "expired"].includes(p.status)) return false;
|
if (!["failed", "cancelled", "expired"].includes(p.status)) return false;
|
||||||
const r = p.status_reason ?? "";
|
const r = p.status_reason ?? "";
|
||||||
return r !== "download_removed" && r !== "cancelled_by_user" && !r.startsWith("organize:");
|
return (
|
||||||
|
r !== "download_removed" &&
|
||||||
|
r !== "cancelled_by_user" &&
|
||||||
|
!r.startsWith("organize:") &&
|
||||||
|
!r.startsWith("torrent_in_use_by_plan")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reserve(
|
async function reserve(
|
||||||
|
|||||||
@@ -302,6 +302,20 @@ describe.skipIf(!hasPg)("被挡住与失败后的重评", () => {
|
|||||||
expect((await evaluateAcquisition(h.d, { sourceReleaseId: rel })).plans).toHaveLength(0);
|
expect((await evaluateAcquisition(h.d, { sourceReleaseId: rel })).plans).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("同一种子已被别的计划占用而取消的不让位(避免取消 → 重评 → 再撞上同一任务的空转)", async () => {
|
||||||
|
const { entityId, slotId } = await movieSlot("InUse Movie");
|
||||||
|
const rel = await release(entityId, "InUse.Movie.2024.1080p.WEB-DL.x264-GRP");
|
||||||
|
const ev1 = await evaluateAcquisition(h.d, { sourceReleaseId: rel });
|
||||||
|
expect(ev1.plans).toHaveLength(1);
|
||||||
|
await db()
|
||||||
|
.updateTable("acquisition_plan")
|
||||||
|
.set({ status: "cancelled", status_reason: "torrent_in_use_by_plan:00000000-0000-0000-0000-000000000000" })
|
||||||
|
.where("id", "=", ev1.plans[0]!)
|
||||||
|
.execute();
|
||||||
|
await releaseSlot(slotId);
|
||||||
|
expect((await evaluateAcquisition(h.d, { sourceReleaseId: rel })).plans).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it("在途计划(已提交)仍挡住同一资源", async () => {
|
it("在途计划(已提交)仍挡住同一资源", async () => {
|
||||||
const { entityId } = await seriesSlot("Inflight Series");
|
const { entityId } = await seriesSlot("Inflight Series");
|
||||||
const rel = await release(entityId, "Inflight.Series.S01E01.1080p.WEB-DL.x264-GRP");
|
const rel = await release(entityId, "Inflight.Series.S01E01.1080p.WEB-DL.x264-GRP");
|
||||||
|
|||||||
Reference in new issue
Block a user