fix inbox sort during scan, move dropdown to button row, per-item Process button
Build and Push Docker Image / build (push) Successful in 48s
Build and Push Docker Image / build (push) Successful in 48s
- sort state lifted to PipelinePage so loadGroups includes the sort param on every reload (scan SSE events no longer reset the sort) - sort dropdown moved from subtitle to ColumnShell middle slot (left of Process Inbox button) - ColumnShell.skip renamed to middle, accepts ReactNode or ColumnAction - per-item "Process →" button on inbox movie cards and series cards: POST /:id/process resolves language + reanalyzes + sorts a single item - dashboard stat pills refresh during scan (every 25 items) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1123,6 +1123,65 @@ app.post("/process-inbox/stop", (c) => {
|
||||
return c.json({ ok: true, message: "not running" });
|
||||
});
|
||||
|
||||
// ─── Process single item ────────────────────────────────────────────────────
|
||||
// Runs language resolution + reanalysis + sort for one inbox item.
|
||||
app.post("/:id/process", async (c) => {
|
||||
const db = getDb();
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (id == null) return c.json({ error: "invalid id" }, 400);
|
||||
const plan = db
|
||||
.prepare("SELECT id FROM review_plans WHERE item_id = ? AND status = 'pending' AND sorted = 0")
|
||||
.get(id) as { id: number } | undefined;
|
||||
if (!plan) return c.json({ error: "item not in inbox" }, 404);
|
||||
|
||||
// Build language resolver (same as processInbox)
|
||||
const cfg = getAllConfig();
|
||||
const radarrCfg = { url: cfg.radarr_url, apiKey: cfg.radarr_api_key };
|
||||
const sonarrCfg = { url: cfg.sonarr_url, apiKey: cfg.sonarr_api_key };
|
||||
const radarrEnabled = cfg.radarr_enabled === "1" && radarrUsable(radarrCfg);
|
||||
const sonarrEnabled = cfg.sonarr_enabled === "1" && sonarrUsable(sonarrCfg);
|
||||
const [radarrLibrary, sonarrLibrary] = await Promise.all([
|
||||
radarrEnabled ? loadRadarrLibrary(radarrCfg) : Promise.resolve(null),
|
||||
sonarrEnabled ? loadSonarrLibrary(sonarrCfg) : Promise.resolve(null),
|
||||
]);
|
||||
const resolverCfg: LanguageResolverConfig = {
|
||||
radarr: radarrEnabled ? radarrCfg : null,
|
||||
sonarr: sonarrEnabled ? sonarrCfg : null,
|
||||
radarrLibrary,
|
||||
sonarrLibrary,
|
||||
};
|
||||
|
||||
// Resolve language
|
||||
const langResult = await resolveLanguage(db, id, resolverCfg);
|
||||
if (langResult.externalRaw != null) {
|
||||
db
|
||||
.prepare("UPDATE media_items SET original_language = ?, orig_lang_source = ?, needs_review = ? WHERE id = ?")
|
||||
.run(langResult.origLang, langResult.origLangSource, langResult.needsReview, id);
|
||||
}
|
||||
|
||||
// Reanalyze + sort
|
||||
const audioLanguages = getAudioLanguages();
|
||||
reanalyze(db, id, audioLanguages);
|
||||
const updated = db.prepare("SELECT auto_class, is_noop FROM review_plans WHERE item_id = ?").get(id) as
|
||||
| { auto_class: string | null; is_noop: number }
|
||||
| undefined;
|
||||
|
||||
if (updated && !updated.is_noop) {
|
||||
if (updated.auto_class === "auto") {
|
||||
db
|
||||
.prepare("UPDATE review_plans SET status = 'approved', reviewed_at = datetime('now'), sorted = 1 WHERE id = ?")
|
||||
.run(plan.id);
|
||||
const { item, streams, decisions } = loadItemDetail(db, id);
|
||||
if (item) enqueueAudioJob(db, id, buildCommand(item, streams, decisions));
|
||||
} else {
|
||||
db.prepare("UPDATE review_plans SET sorted = 1 WHERE id = ?").run(plan.id);
|
||||
}
|
||||
}
|
||||
|
||||
emitPipelineChanged();
|
||||
return c.json({ ok: true, destination: updated?.auto_class === "auto" ? "queue" : "review" });
|
||||
});
|
||||
|
||||
// ─── Approve all ready ───────────────────────────────────────────────────────
|
||||
// Bulk-approves every auto_heuristic-classified plan currently in Review.
|
||||
app.post("/approve-ready", (c) => {
|
||||
|
||||
Reference in New Issue
Block a user