feat(scheduler): each row shows its last three runs, an interrupted run as such, and the ad-hoc runs beside the jobs

A row's one dot said how the LAST run went, and an interrupted run fell through to "idle" — a grey dot
that read as "no run recorded". The row now leads with its last three runs, newest first, each named
for a screen reader and in its tooltip (how it went, when, who started it); an interrupted run is a
ring, set apart from a skipped run and a failure, and its line says the server restarted under it.

A channel's full-history backfill gets its own "Ad-hoc runs" block: its runs, its channel and what
it fetched, no interval and no run button — it needs its target. AD_HOC_JOBS names every such job;
the backend-id contract holds their names in both languages, and run_ad_hoc refuses any other id.
This commit is contained in:
2026-10-18 01:20:53 +02:00
parent 27c2fcbd2e
commit 1b1e3cdcf2
9 changed files with 404 additions and 89 deletions
+19 -7
View File
@@ -56,10 +56,11 @@
},
"dot": {
"running": "running now",
"ok": "last run OK",
"idle": "idle (no run recorded yet)",
"error": "last run failed",
"skipped": "skipped (sync paused)"
"ok": "OK",
"idle": "no run recorded yet",
"error": "failed",
"skipped": "skipped (sync paused)",
"interrupted": "interrupted (the server restarted)"
},
"jobDesc": {
"rss_poll": "Checks each channel's RSS feed for new uploads (free, no quota). If it stops, new videos only appear later when a backfill pass catches them, so your Feed lags behind YouTube.",
@@ -78,7 +79,8 @@
"audit_gc": "Prunes admin audit-log entries and YouTube quota history older than their retention windows (Configuration → Audit log, Quota). If it stops, both just keep growing — harmless but unbounded.",
"log_gc": "Deletes stored log events older than the retention window (Configuration → Diagnostics). Runs hourly so nothing outlives the promised limit by more than an hour.",
"tmdb_enrich": "Enriches the mirrored Plex catalogue from TMDB (keywords, crew, budget/revenue, TMDb score, recommendations, artwork), matched by each title's canonical id and served from the app's own database. Optional — a no-op until a TMDB API key is set. If it stops, new Plex titles just miss their TMDB extras until it runs again.",
"people_enrich": "Makes the people in the Plex library local: fetches each person's biography from TMDB and caches their portrait, resized through Plex, the most-credited people first. A person page and a cast strip then open from the app's own data. The TMDB half does nothing without a TMDB API key, the portrait half nothing without Plex. If it stops, a person page fetches its own bio when opened, a moment later."
"people_enrich": "Makes the people in the Plex library local: fetches each person's biography from TMDB and caches their portrait, resized through Plex, the most-credited people first. A person page and a cast strip then open from the app's own data. The TMDB half does nothing without a TMDB API key, the portrait half nothing without Plex. If it stops, a person page fetches its own bio when opened, a moment later.",
"channel_backfill": "Turning on full history for a channel fetches its whole back-catalogue at once, as its own run. The Backfill job finishes what this one could not (a very large channel, low quota, or two of these already running)."
},
"jobs": {
"rss_poll": "RSS poll (new uploads)",
@@ -97,7 +99,8 @@
"tmdb_enrich": "TMDB enrichment (Plex)",
"audit_gc": "Audit-log and quota-history cleanup",
"log_gc": "Log cleanup",
"people_enrich": "People details and portraits (Plex)"
"people_enrich": "People details and portraits (Plex)",
"channel_backfill": "A channel's full history"
},
"queue": {
"recentPending": "Channels to sync",
@@ -199,5 +202,14 @@
"transport": "unreachable",
"apply": "could not be saved",
"error": "unexpected error"
}
},
"dotRun": "{{state}} · {{time}} · {{trigger}}",
"trigger": {
"scheduled": "on schedule",
"manual": "started by an admin",
"ad_hoc": "started by a user"
},
"interrupted": "interrupted: the server restarted during the run",
"adHocTitle": "Ad-hoc runs",
"adHocStarted": "started by a user"
}
+19 -7
View File
@@ -56,10 +56,11 @@
},
"dot": {
"running": "most fut",
"ok": "utolsó futás OK",
"idle": "tétlen (még nincs rögzített futás)",
"error": "utolsó futás hibázott",
"skipped": "kihagyva (szinkron szüneteltetve)"
"ok": "rendben",
"idle": "még nincs rögzített futás",
"error": "hibával ért véget",
"skipped": "kihagyva (szinkron szüneteltetve)",
"interrupted": "megszakadt (újraindult a szerver)"
},
"jobDesc": {
"rss_poll": "Az egyes csatornák RSS-feedjét nézi új feltöltésekért (ingyenes, nem fogyaszt kvótát). Ha leáll, az új videók csak később jelennek meg, amikor a videók behúzása eléri őket — így a Feed lemarad a YouTube mögött.",
@@ -78,7 +79,8 @@
"audit_gc": "Törli a megőrzési időnél régebbi admin-naplóbejegyzéseket és YouTube-kvótaelőzményeket (Konfiguráció → Eseménynapló, Kvóta). Ha leáll, mindkettő csak nő — ártalmatlan, de korlátlanul.",
"log_gc": "Törli a megőrzési időnél (Konfiguráció → Diagnosztika) régebbi tárolt naplóeseményeket. Óránként fut, így semmi sem marad meg egy óránál tovább az ígért határidőn túl.",
"tmdb_enrich": "A tükrözött Plex-katalógust dúsítja a TMDB-ből (kulcsszavak, stáb, költségvetés/bevétel, TMDb-pontszám, ajánlások, artwork), a címek kanonikus azonosítója alapján párosítva, és az app saját adatbázisából szolgálja ki. Opcionális — TMDB API-kulcs nélkül nem csinál semmit. Ha leáll, az új Plex-címek csak a következő futásig maradnak TMDB-extrák nélkül.",
"people_enrich": "Helyivé teszi a Plex-könyvtár szereplőit: a TMDB-ből letölti az életrajzukat, a portréjukat pedig a Plexszel kicsinyítve elmenti, a legtöbbet szereplőkkel kezdve. A személyoldal és a szereplősáv így az app saját adataiból nyílik meg. A TMDB-rész TMDB API-kulcs nélkül nem csinál semmit, a portrérész Plex nélkül. Ha leáll, a személyoldal megnyitáskor maga kéri le az életrajzot, egy pillanattal később."
"people_enrich": "Helyivé teszi a Plex-könyvtár szereplőit: a TMDB-ből letölti az életrajzukat, a portréjukat pedig a Plexszel kicsinyítve elmenti, a legtöbbet szereplőkkel kezdve. A személyoldal és a szereplősáv így az app saját adataiból nyílik meg. A TMDB-rész TMDB API-kulcs nélkül nem csinál semmit, a portrérész Plex nélkül. Ha leáll, a személyoldal megnyitáskor maga kéri le az életrajzot, egy pillanattal később.",
"channel_backfill": "Ha egy csatornánál bekapcsolod a teljes előzményt, a teljes korábbi kínálata egyszerre, saját futásként töltődik le. Amit ez nem tud befejezni (egy nagyon nagy csatorna, kevés kvóta, vagy ha már két ilyen fut), azt a „Videók behúzása” feladat fejezi be."
},
"jobs": {
"rss_poll": "RSS-lekérdezés (új feltöltések)",
@@ -97,7 +99,8 @@
"tmdb_enrich": "TMDB-dúsítás (Plex)",
"audit_gc": "Eseménynapló- és kvótaelőzmény-takarítás",
"log_gc": "Naplótakarítás",
"people_enrich": "Személyadatok és portrék (Plex)"
"people_enrich": "Személyadatok és portrék (Plex)",
"channel_backfill": "Egy csatorna teljes előzménye"
},
"queue": {
"recentPending": "Szinkronra váró csatorna",
@@ -199,5 +202,14 @@
"transport": "nem érhető el",
"apply": "nem sikerült menteni",
"error": "váratlan hiba"
}
},
"dotRun": "{{state}} · {{time}} · {{trigger}}",
"trigger": {
"scheduled": "ütemezve",
"manual": "admin indította",
"ad_hoc": "felhasználó indította"
},
"interrupted": "megszakadt: a futás közben újraindult a szerver",
"adHocTitle": "Alkalmi futások",
"adHocStarted": "felhasználó indítja"
}