feat: rank 4K first, fall back to good 1080p
ci / web (push) Successful in 1m3s
e2e / e2e (push) Failing after 1m37s
ci / rust (push) Successful in 2m21s
ci / image (push) Successful in 2m33s

Resolution preference becomes strict tiers (step 10 000) and every
size band gains a ceiling that waives bloated releases, so a 60 GB
remux cannot win the top tier unattended. Movie grab, TV grab, RSS
and the search deck now share one ranking key from arr-core.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-11-07 18:02:12 +00:00
co-authored by Claude Opus 5.5
parent abb7e0a489
commit cd9e8a891c
13 changed files with 290 additions and 69 deletions
+21 -6
View File
@@ -17,7 +17,12 @@ export interface PolicyDoc {
hdr_rules: { dv_profile_reject: string[] };
size_bands: Record<
string,
{ floor_gib: number; target_gib: number; penalty_points_per_gib_over: number }
{
floor_gib: number;
target_gib: number;
penalty_points_per_gib_over: number;
ceiling_gib?: number;
}
>;
resolution_pref: string[];
source_weights: Record<string, number>;
@@ -501,7 +506,7 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void):
size_at_target: 1000,
source_tier: 25,
seeder_doubling: 8,
resolution_step: 300,
resolution_step: 10000,
},
};
}
@@ -557,7 +562,8 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void):
profilesInput = profiles.querySelector("input") ?? document.createElement("input");
grid.append(profiles);
// one line per resolution: floor, target, penalty — numbers stay numbers
// one line per resolution: floor, target, penalty, ceiling — numbers stay
// numbers, and an empty ceiling is no ceiling
for (const resolution of RESOLUTIONS) {
const band = current.size_bands[resolution];
const line = el("div", "band-line");
@@ -566,10 +572,16 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void):
const floor = numberControl(band?.floor_gib ?? 0);
const target = numberControl(band?.target_gib ?? 0);
const penalty = numberControl(band?.penalty_points_per_gib_over ?? 0);
const ceiling = numberControl(band?.ceiling_gib ?? 0);
if (band?.ceiling_gib === undefined) {
ceiling.value = "";
}
ceiling.placeholder = "∞";
floor.setAttribute("aria-label", `${resolution} floor GiB`);
target.setAttribute("aria-label", `${resolution} target GiB`);
penalty.setAttribute("aria-label", `${resolution} penalty points per GiB over`);
for (const input of [floor, target, penalty]) {
ceiling.setAttribute("aria-label", `${resolution} ceiling GiB, empty for none`);
for (const input of [floor, target, penalty, ceiling]) {
input.classList.add("band-num");
line.append(input);
}
@@ -630,14 +642,17 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void):
const sizeBands: PolicyDoc["size_bands"] = {};
for (const line of panel.querySelectorAll<HTMLDivElement>(".band-line")) {
const [floor, target, penalty] = [...line.querySelectorAll<HTMLInputElement>("input")];
if (!floor || !target || !penalty) {
const [floor, target, penalty, ceiling] = [
...line.querySelectorAll<HTMLInputElement>("input"),
];
if (!floor || !target || !penalty || !ceiling) {
continue;
}
sizeBands[line.dataset.resolution ?? ""] = {
floor_gib: Number(floor.value),
target_gib: Number(target.value),
penalty_points_per_gib_over: Number(penalty.value),
...(ceiling.value === "" ? {} : { ceiling_gib: Number(ceiling.value) }),
};
}
+5 -1
View File
@@ -2462,8 +2462,12 @@ select.form-input {
width: 3.5rem;
}
/* four numbers and a label must share a phone's width, so the inputs shrink
rather than push the line past the gutter */
.band-num {
width: 4.5rem;
flex: 1 1 0;
min-width: 0;
max-width: 4.5rem;
}
.subtitle-panel .field-label {