feat(web): simplify subscription and manual download flows and sync UHD styles

This commit is contained in:
root committed 2026-10-13 14:37:03 +08:00
1 parent 27c0f27434
commit c3c4456913
33 files changed
+1083 -633

No files matched your search

@@ -15,7 +15,6 @@ import {
seasonLabel,
shiftMonth,
slotBadge,
upgradeOf,
} from "./model.ts";
const slot = (x: Partial<SubscriptionSlotSummary>): SubscriptionSlotSummary => ({
@@ -86,15 +85,44 @@ describe("覆盖矩阵格", () => {
expect(moveItem(["a", "b", "c"], 2, 0)).toEqual(["c", "a", "b"]);
expect(moveItem(["a", "b"], 0, 5)).toEqual(["a", "b"]);
});
it("升级策略取自收录版本", () => {
expect(upgradeOf([{ upgradeThreshold: {} }], "series")).toBe("off");
expect(upgradeOf([{ upgradeThreshold: { enabled: true, mode: "full_pack" } }], "series")).toBe("full_pack");
expect(upgradeOf([{ upgradeThreshold: { enabled: true } }], "movie")).toBe("movie");
});
});
describe("表单 ↔ params", () => {
it("保留未知键,空值不写,只在改动时带 best_version / total_episode", () => {
it("普通编辑不重建补全配置;只改补全开关也保留预算精度,明确修改才更新", () => {
const params = {
autoFill: "on",
fillBudget: { unitsPerDay: 2, bytesPerDay: Math.round(1.25 * 1024 ** 3), future: true },
};
const sub = {
mediaKind: "movie",
seasonScope: "current_season",
mode: "once",
priority: "tracking",
seasonUnitId: null,
policyRevisionId: null,
params,
} as unknown as Subscription;
const d = draftFromSubscription(sub);
const ordinary = draftToParams({ ...d, searchIntervalHours: "6" }, params, {
totalChanged: false,
autoFillChanged: false,
});
expect(ordinary.params).toEqual({ ...params, searchIntervalHours: 6 });
const modeOnly = draftToParams({ ...d, autoFill: "off" }, params, { totalChanged: false, autoFillChanged: true });
expect(modeOnly.params).toEqual({ ...params, autoFill: "off" });
const changed = draftToParams({ ...d, gbPerDay: "2", unitsPerDay: "" }, params, {
totalChanged: false,
autoFillChanged: true,
});
expect(changed.params.fillBudget).toEqual({ bytesPerDay: 2 * 1024 ** 3, future: true });
const cleared = draftToParams(
{ ...d, gbPerDay: "", unitsPerDay: "" },
{ ...params, fillBudget: { unitsPerDay: 2, bytesPerDay: params.fillBudget.bytesPerDay } },
{ totalChanged: false, autoFillChanged: true },
);
expect(cleared.params.fillBudget).toBeUndefined();
});
it("保留未知键,空值不写,只在改动时带 total_episode", () => {
const d = {
...emptyDraft("series"),
siteIds: ["x"],
@@ -104,12 +132,11 @@ describe("表单 ↔ params", () => {
unitsPerDay: "2",
gbPerDay: "1.5",
total: "12",
upgrade: "full_pack" as const,
};
const { params, errors } = draftToParams(
d,
{ custom: 1, keywords: ["旧"] },
{ upgradeChanged: true, totalChanged: true },
{ totalChanged: true, autoFillChanged: true },
);
expect(errors).toEqual({});
expect(params).toEqual({
@@ -121,10 +148,8 @@ describe("表单 ↔ params", () => {
manual_total_episode: false,
autoFill: "off",
fillBudget: { unitsPerDay: 2, bytesPerDay: Math.round(1.5 * 1024 ** 3) },
best_version: true,
best_version_full: true,
});
const same = draftToParams(d, {}, { upgradeChanged: false, totalChanged: false }).params;
const same = draftToParams(d, {}, { totalChanged: false, autoFillChanged: true }).params;
expect(same.best_version).toBeUndefined();
expect(same.total_episode).toBeUndefined();
});
@@ -132,7 +157,7 @@ describe("表单 ↔ params", () => {
const { errors } = draftToParams(
{ ...emptyDraft("series"), episodeStart: "5", episodeEnd: "2" },
{},
{ upgradeChanged: false, totalChanged: false },
{ totalChanged: false, autoFillChanged: true },
);
expect(errors.episodeEnd).toBeTruthy();
});
@@ -151,40 +176,45 @@ describe("表单 ↔ params", () => {
keywords: ["a", "b"],
},
} as unknown as Subscription;
const d = draftFromSubscription(sub, "per_unit");
const d = draftFromSubscription(sub);
expect(d.episodeStart).toBe("2");
expect(d.autoFill).toBe("on");
expect(d.gbPerDay).toBe("2");
expect(d.keywords).toBe("a\nb");
expect(d.upgrade).toBe("per_unit");
});
it("质量预设:草稿 ↔ params;未知值回填为空", () => {
const d = { ...emptyDraft("movie"), resolution: "4k" as const, source: "remux" as const, effect: "dv" as const };
const { params } = draftToParams(
d,
{ resolution: "720p", effect: "sdr" },
{ upgradeChanged: false, totalChanged: false },
);
expect(params).toEqual({ resolution: "4k", source: "remux", effect: "dv" });
const cleared = draftToParams(
emptyDraft("movie"),
{ resolution: "720p" },
{ upgradeChanged: false, totalChanged: false },
);
expect(cleared.params.resolution).toBeUndefined();
const back = draftFromSubscription(
{
mediaKind: "movie",
seasonScope: "current_season",
mode: "once",
priority: "tracking",
seasonUnitId: null,
policyRevisionId: null,
params: { resolution: "1080p", source: "bogus", effect: "hdr" },
} as unknown as Subscription,
"off",
);
expect([back.resolution, back.source, back.effect]).toEqual(["1080p", "", "hdr"]);
it("编辑跟踪与下载信息保留旧收录规则,不再生成统一洗版指令", () => {
const legacy = {
resolution: "4k",
source: "uhd",
effect: "atmos",
ruleGroupIds: ["quality"],
future: { keep: true },
};
const sub = {
mediaKind: "movie",
seasonScope: "current_season",
mode: "once",
priority: "tracking",
seasonUnitId: null,
policyRevisionId: null,
params: legacy,
} as unknown as Subscription;
const draft = draftFromSubscription(sub);
const { params, errors } = draftToParams({ ...draft, siteIds: ["site"], searchIntervalHours: "6" }, legacy, {
totalChanged: false,
autoFillChanged: true,
});
expect(errors).toEqual({});
expect(params).toEqual({ ...legacy, siteIds: ["site"], searchIntervalHours: 6 });
expect(params.best_version).toBeUndefined();
expect(params.best_version_full).toBeUndefined();
const cleared = draftToParams(draft, legacy, {
totalChanged: false,
autoFillChanged: false,
clearQualityPreset: true,
});
expect(cleared.params).toEqual({ ruleGroupIds: ["quality"], future: { keep: true } });
expect(draftToParams(emptyDraft("movie"), {}, { totalChanged: false, autoFillChanged: true }).params).toEqual({});
});
});