feat(web): simplify subscription and manual download flows and sync UHD styles
This commit is contained in:
1 parent
27c0f27434
commit
c3c4456913
33 files changed
+1083
-633
No files matched your search
@@ -0,0 +1,217 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { draftFromJson, draftToJson, type SlotRulesDraft, type SlotRulesJson } from "./slot-rules.tsx";
|
||||
|
||||
const existingRules = (): SlotRulesJson => ({
|
||||
hardConditions: {
|
||||
resolutions: ["1080p", "2160p"],
|
||||
sources: ["WEB-DL", "BluRay"],
|
||||
requireEncode: true,
|
||||
minSizeBytes: 1024 ** 3,
|
||||
maxSizeBytes: 10 * 1024 ** 3,
|
||||
requiredSubtitles: ["zh", "en"],
|
||||
cuts: ["default", "IMAX"],
|
||||
excludeGroups: ["LOW", "BAD"],
|
||||
contentTypes: ["main"],
|
||||
codecs: ["H.265"],
|
||||
hdr: { anyOf: ["HDR10"], noneOf: ["DV"] },
|
||||
},
|
||||
softPreferences: {
|
||||
hdr: ["DV", "HDR10"],
|
||||
groups: ["CHD", "ADE"],
|
||||
sources: ["BluRay", "WEB-DL"],
|
||||
codecs: ["H.265", "H.264"],
|
||||
preferredSizeBytes: { min: 2 * 1024 ** 3, max: 8 * 1024 ** 3 },
|
||||
tiers: [
|
||||
{ name: "优先蓝光", sources: ["BluRay"], codecs: ["H.265"] },
|
||||
{ name: "追更", sources: ["WEB-DL"], resolutions: ["1080p"] },
|
||||
],
|
||||
tiersRequired: true,
|
||||
},
|
||||
fallback: {
|
||||
unknownPolicy: "hold",
|
||||
retryLimit: 3,
|
||||
investigation: { enabled: true, fields: ["codec", "hdr"] },
|
||||
},
|
||||
upgradeThreshold: {
|
||||
enabled: true,
|
||||
ruleGroupIds: ["quality", "release-group"],
|
||||
consistency: "season_release",
|
||||
minTierSteps: 2,
|
||||
allowResolutionUpgrade: true,
|
||||
encodeGroupAllowlist: ["CHD", "ADE"],
|
||||
ceilingTier: 4,
|
||||
mode: "full_pack",
|
||||
basis: "priority",
|
||||
targetPri: 98,
|
||||
sourceTiers: { BluRay: 3, "WEB-DL": 2 },
|
||||
},
|
||||
completion: { kind: "movie", cuts: ["default", "IMAX"] },
|
||||
});
|
||||
|
||||
function save(draft: SlotRulesDraft): SlotRulesJson {
|
||||
const result = draftToJson(draft);
|
||||
if (!result.ok) throw new Error(JSON.stringify(result.errors));
|
||||
return result.value;
|
||||
}
|
||||
|
||||
describe("收录规则编辑回写", () => {
|
||||
it("逐版本修改洗版方式,关闭再开启保留模式,明确选默认才清除模式", () => {
|
||||
const original = existingRules();
|
||||
const disabled = save({ ...draftFromJson(original), upgradeEnabled: false });
|
||||
expect(disabled.upgradeThreshold).toEqual({ ...original.upgradeThreshold, enabled: false });
|
||||
const reopened = save({ ...draftFromJson(disabled), upgradeEnabled: true });
|
||||
expect(reopened).toEqual(original);
|
||||
const perUnit = save({ ...draftFromJson(original), upgradeMode: "per_unit" });
|
||||
expect(perUnit.upgradeThreshold).toEqual({ ...original.upgradeThreshold, mode: "per_unit" });
|
||||
const { mode: _mode, ...rest } = original.upgradeThreshold;
|
||||
expect(save({ ...draftFromJson(original), upgradeMode: "" }).upgradeThreshold).toEqual(rest);
|
||||
expect(save(draftFromJson({})).upgradeThreshold).toEqual({ enabled: false });
|
||||
});
|
||||
it("未修改保存时保留表单没有展示的各层 JSON 字段", () => {
|
||||
const original = existingRules();
|
||||
|
||||
expect(save(draftFromJson(original))).toEqual(original);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "分辨率",
|
||||
edit: { resolutions: ["2160p"] },
|
||||
expected: (original: SlotRulesJson) => ({
|
||||
...original,
|
||||
hardConditions: { ...original.hardConditions, resolutions: ["2160p"] },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "编码偏好",
|
||||
edit: { softCodecs: ["AV1", "H.265"] },
|
||||
expected: (original: SlotRulesJson) => ({
|
||||
...original,
|
||||
softPreferences: { ...original.softPreferences, codecs: ["AV1", "H.265"] },
|
||||
}),
|
||||
},
|
||||
])("编辑$name只改写对应字段,不改硬编码限制或其他规则", ({ edit, expected }) => {
|
||||
const original = existingRules();
|
||||
const snapshot = structuredClone(original);
|
||||
|
||||
expect(save({ ...draftFromJson(original), ...edit })).toEqual(expected(original));
|
||||
expect(original).toEqual(snapshot);
|
||||
});
|
||||
|
||||
it("同时更新分辨率和编码偏好仍保留阶梯、洗版口径、完成版本及回退扩展", () => {
|
||||
const original = existingRules();
|
||||
const value = save({
|
||||
...draftFromJson(original),
|
||||
resolutions: ["720p", "1080p"],
|
||||
softCodecs: ["AV1"],
|
||||
});
|
||||
|
||||
expect(value).toEqual({
|
||||
...original,
|
||||
hardConditions: { ...original.hardConditions, resolutions: ["720p", "1080p"] },
|
||||
softPreferences: { ...original.softPreferences, codecs: ["AV1"] },
|
||||
});
|
||||
});
|
||||
|
||||
it.each([false, true])("清空洗版规则和阈值不会从原 JSON 恢复,升级开关为 %s", (upgradeEnabled) => {
|
||||
const original = existingRules();
|
||||
const {
|
||||
ruleGroupIds: _groups,
|
||||
consistency: _consistency,
|
||||
minTierSteps: _steps,
|
||||
...remaining
|
||||
} = original.upgradeThreshold;
|
||||
const value = save({
|
||||
...draftFromJson(original),
|
||||
upgradeEnabled,
|
||||
upgradeGroupIds: [],
|
||||
seasonConsistency: false,
|
||||
minTierSteps: "",
|
||||
});
|
||||
|
||||
expect(value).toEqual({
|
||||
...original,
|
||||
upgradeThreshold: { ...remaining, enabled: upgradeEnabled },
|
||||
});
|
||||
expect(value.upgradeThreshold).not.toHaveProperty("ruleGroupIds");
|
||||
expect(value.upgradeThreshold).not.toHaveProperty("consistency");
|
||||
expect(value.upgradeThreshold).not.toHaveProperty("minTierSteps");
|
||||
});
|
||||
|
||||
it("关闭升级并清空配置后,再加载并打开升级也不会复活旧规则", () => {
|
||||
const disabled = save({
|
||||
...draftFromJson(existingRules()),
|
||||
upgradeEnabled: false,
|
||||
upgradeGroupIds: [],
|
||||
seasonConsistency: false,
|
||||
minTierSteps: "",
|
||||
});
|
||||
const reopened = draftFromJson(disabled);
|
||||
|
||||
expect(reopened.upgradeGroupIds).toEqual([]);
|
||||
expect(reopened.seasonConsistency).toBe(false);
|
||||
expect(reopened.minTierSteps).toBe("");
|
||||
const enabled = save({ ...reopened, upgradeEnabled: true });
|
||||
expect(enabled.upgradeThreshold).toEqual({ ...disabled.upgradeThreshold, enabled: true });
|
||||
expect(enabled.upgradeThreshold).not.toHaveProperty("ruleGroupIds");
|
||||
expect(enabled.upgradeThreshold).not.toHaveProperty("consistency");
|
||||
expect(enabled.upgradeThreshold).not.toHaveProperty("minTierSteps");
|
||||
});
|
||||
});
|
||||
|
||||
describe("按字段配置的未知值回退", () => {
|
||||
const fieldFallback = () => ({
|
||||
...existingRules(),
|
||||
fallback: {
|
||||
unknownPolicy: { codec: "hold", resolution: "reject", hdr: "accept" },
|
||||
investigation: { enabled: true, fields: ["codec"] },
|
||||
},
|
||||
});
|
||||
|
||||
it("按字段对象回填为保留状态,未修改保存仍保留原配置", () => {
|
||||
const original = fieldFallback();
|
||||
const draft = draftFromJson(original);
|
||||
|
||||
expect(draft.unknownPolicy).toBe("fields");
|
||||
expect(save(draft)).toEqual(original);
|
||||
});
|
||||
|
||||
it("仅修改其他规则时也不会清除按字段回退对象及其扩展字段", () => {
|
||||
const original = fieldFallback();
|
||||
|
||||
expect(save({ ...draftFromJson(original), resolutions: ["2160p"] })).toEqual({
|
||||
...original,
|
||||
hardConditions: { ...original.hardConditions, resolutions: ["2160p"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("明确选择全局回退后替换原按字段对象,保留其他回退字段", () => {
|
||||
const original = fieldFallback();
|
||||
|
||||
expect(save({ ...draftFromJson(original), unknownPolicy: "reject" })).toEqual({
|
||||
...original,
|
||||
fallback: { ...original.fallback, unknownPolicy: "reject" },
|
||||
});
|
||||
});
|
||||
|
||||
it("原按字段配置先改为全局拒绝,再明确选系统默认时清空回退,不恢复原对象", () => {
|
||||
const original = fieldFallback();
|
||||
const draft = draftFromJson(original);
|
||||
const selectedGlobal = { ...draft, unknownPolicy: "reject" as const };
|
||||
const cleared = { ...selectedGlobal, unknownPolicy: "" as const };
|
||||
const { unknownPolicy: _policy, ...remaining } = original.fallback;
|
||||
const value = save(cleared);
|
||||
|
||||
expect(value).toEqual({ ...original, fallback: remaining });
|
||||
expect(value.fallback).not.toHaveProperty("unknownPolicy");
|
||||
});
|
||||
|
||||
it("已有全局回退可以清空,保存后不会恢复原选择", () => {
|
||||
const original = existingRules();
|
||||
const { unknownPolicy: _policy, ...remaining } = original.fallback;
|
||||
const value = save({ ...draftFromJson(original), unknownPolicy: "" });
|
||||
|
||||
expect(value).toEqual({ ...original, fallback: remaining });
|
||||
expect(value.fallback).not.toHaveProperty("unknownPolicy");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user