Show per-indexer grab share and rebuild the Indexers card as a list
All checks were successful
Deploy / deploy (push) Successful in 26s
All checks were successful
Deploy / deploy (push) Successful in 26s
The Indexers card sat beside Requests as a stretched, vertically centred grid of pills — mostly dead space once the Ombi list was expanded, and it said nothing about which indexer was actually doing the work. - Fetch Prowlarr's indexerstats over a rolling 30-day window and merge per-indexer queries/grabs into the summary. Cached 10 min on success and never on failure, so a stats outage can't blank the column after it recovers or take the up/down list down with it. - Rank indexers busiest-first by grab share (query share as fallback); queries mostly count enabled indexers, grabs count who delivered. - One indexer per line, matched to the Ombi request rows: same padding, gap, radius and two-line body, plus a share bar scaled to the busiest indexer and an Online/Offline badge the 30s poll keeps current. - Let that row size cards to their own content instead of stretching. Bump APP_VERSION to 1.2.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2cc62979a2
commit
909a0b568c
4 changed files with 147 additions and 16 deletions
|
|
@ -163,7 +163,10 @@ main {
|
|||
}
|
||||
.card-body { padding: 1.1rem; flex: 1; }
|
||||
.card-body > :first-child { margin-top: 0; }
|
||||
#prowlarr-body { display: flex; flex-direction: column; justify-content: center; }
|
||||
/* Requests can grow tall when expanded; the Indexers card beside it should
|
||||
keep its natural height and sit at the top rather than stretching into a
|
||||
mostly-empty box with its content floating in the middle. */
|
||||
.row.align-start { align-items: start; }
|
||||
|
||||
/* ── "Right now" hero strip ─────────────────────────── */
|
||||
.hero .card { border-color: var(--border-lt); }
|
||||
|
|
@ -431,26 +434,52 @@ dd.is-accent { color: var(--accent); }
|
|||
}
|
||||
|
||||
/* ── Indexers ───────────────────────────────────────── */
|
||||
.indexer-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||
gap: 0.65rem;
|
||||
}
|
||||
/* One indexer per line, built to the same rhythm as the Ombi request rows
|
||||
beside it — same padding, gap, radius and two-line body, so the two cards
|
||||
read as one pair rather than two unrelated layouts. */
|
||||
.indexer-grid { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.indexer-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.65rem;
|
||||
min-height: var(--tap);
|
||||
padding: 0.75rem 0.9rem;
|
||||
gap: 0.7rem;
|
||||
padding: 0.6rem 0.7rem;
|
||||
background: var(--surface-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--fs-base);
|
||||
min-width: 0;
|
||||
}
|
||||
.indexer-item.down { border-color: var(--danger-border); }
|
||||
.indexer-item .status-light { width: 9px; height: 9px; }
|
||||
.indexer-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-2); }
|
||||
.indexer-item .status-light { width: 9px; height: 9px; flex-shrink: 0; }
|
||||
.indexer-info { min-width: 0; flex: 1; }
|
||||
.indexer-name {
|
||||
font-size: var(--fs-sm); font-weight: 600;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.indexer-sub { font-size: var(--fs-xs); color: var(--text-3); }
|
||||
.indexer-sub .proto { text-transform: capitalize; }
|
||||
.indexer-sub:empty::before { content: "Indexer"; }
|
||||
/* Share of grabs, scaled to the busiest indexer (see dashboard.html) */
|
||||
.share-bar {
|
||||
margin-top: 0.35rem;
|
||||
height: 4px;
|
||||
border-radius: 100px;
|
||||
background: var(--surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
.share-bar span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: 100px;
|
||||
background: var(--accent);
|
||||
}
|
||||
.indexer-item.down .share-bar span { background: var(--text-3); }
|
||||
.share-pct {
|
||||
font-size: var(--fs-sm);
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── Requests (Ombi) ────────────────────────────────── */
|
||||
.req-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
|
|
@ -479,6 +508,8 @@ dd.is-accent { color: var(--accent); }
|
|||
.badge.denied { background: var(--danger-dim); color: var(--danger); }
|
||||
.badge.idle { background: var(--surface); color: var(--text-3); }
|
||||
.badge.downloading { background: var(--accent-dim); color: var(--accent); }
|
||||
.badge.online { background: var(--ok-dim); color: var(--ok); }
|
||||
.badge.offline { background: var(--danger-dim); color: var(--danger); }
|
||||
.badge.paused { background: var(--warn-dim); color: var(--warn); }
|
||||
.req-info { min-width: 0; flex: 1; }
|
||||
.req-title {
|
||||
|
|
|
|||
|
|
@ -187,6 +187,12 @@ async function pollProwlarr() {
|
|||
const light = document.getElementById('idx-light-' + idx.id);
|
||||
if (row) { row.classList.toggle('up', idx.up); row.classList.toggle('down', !idx.up); }
|
||||
if (light) { light.classList.toggle('up', idx.up); light.classList.toggle('down', !idx.up); }
|
||||
const badge = document.getElementById('idx-badge-' + idx.id);
|
||||
if (badge) {
|
||||
badge.classList.toggle('online', idx.up);
|
||||
badge.classList.toggle('offline', !idx.up);
|
||||
badge.textContent = idx.up ? 'Online' : 'Offline';
|
||||
}
|
||||
});
|
||||
updateServicePill('prowlarr', d.up_count === d.total && d.total > 0);
|
||||
const stat = document.getElementById('svc-stat-prowlarr');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue