Show per-indexer grab share and rebuild the Indexers card as a list
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:
Barely Removable 2026-08-26 22:28:39 -07:00
parent 2cc62979a2
commit 909a0b568c
4 changed files with 147 additions and 16 deletions

View file

@ -332,7 +332,7 @@
<!-- ══ Requests + Indexers ══ -->
<section class="section" aria-labelledby="h-ops">
<h2 id="h-ops">Requests &amp; indexers</h2>
<div class="row cols-2">
<div class="row cols-2 align-start">
<!-- Ombi -->
<div class="card">
@ -377,16 +377,38 @@
{% if prowlarr.status == 'ok' %}
<span class="card-meta">
<span id="prowlarr-up-count">{{ prowlarr.up_count }}</span>/<span id="prowlarr-total">{{ prowlarr.total }}</span> online
{%- if prowlarr.has_stats %} · share of {{ prowlarr.share_metric }}, {{ prowlarr.stats_days }}d{% endif %}
</span>
{% endif %}
</div>
<div class="card-body" id="prowlarr-body">
{% if prowlarr.status == 'ok' %}
{# Bars are scaled against the busiest indexer, not against 100%,
so a spread of single-digit shares is still readable. The label
beside each bar stays the true percentage. #}
{% set top_share = prowlarr.indexers[0].share if prowlarr.indexers else 0 %}
<div class="indexer-grid" id="indexer-grid">
{% for idx in prowlarr.indexers %}
{% set share = idx.share %}
<div class="indexer-item {{ 'up' if idx.up else 'down' }}" id="idx-{{ idx.id }}">
<span class="status-light {{ 'up' if idx.up else 'down' }}" id="idx-light-{{ idx.id }}"></span>
<span class="indexer-name" title="{{ idx.name }}">{{ idx.name }}</span>
<div class="indexer-info">
<div class="indexer-name" title="{{ idx.name }}">{{ idx.name }}</div>
<div class="indexer-sub">
<span class="proto">{{ idx.protocol }}</span>{% if prowlarr.has_stats %} · {{ '{:,}'.format(idx.grabs) }} grabs · {{ '{:,}'.format(idx.queries) }} queries{% endif %}
</div>
{% if prowlarr.has_stats %}
<div class="share-bar" role="img"
aria-label="{{ share }}% of {{ prowlarr.share_metric }} in the last {{ prowlarr.stats_days }} days">
<span style="width: {{ (share / top_share * 100) | round(1) if top_share else 0 }}%"></span>
</div>
{% endif %}
</div>
{% if prowlarr.has_stats %}
<span class="share-pct">{{ share }}%</span>
{% endif %}
<span class="badge {{ 'online' if idx.up else 'offline' }}"
id="idx-badge-{{ idx.id }}">{{ 'Online' if idx.up else 'Offline' }}</span>
</div>
{% endfor %}
</div>