add version badge in nav, apply path mappings at execution time, clear done/error jobs
All checks were successful
Build and Push Docker Image / build (push) Successful in 58s

- show version (from package.json) in nav bar, warn on frontend/server mismatch
- apply path_mappings to file access check and command string at execution time
  so existing scans with old jellyfin paths work without re-scanning
- add clear done/errors button on execute page
- bump version to 2026.03.04

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 17:22:14 +01:00
parent 37fae33bbc
commit 818b0d1396
7 changed files with 58 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
import { Hono } from 'hono';
import { stream } from 'hono/streaming';
import { getDb } from '../db/index';
import { getDb, applyPathMappings, applyPathMappingsToCommand } from '../db/index';
import { execStream } from '../services/ssh';
import type { Job, Node, MediaItem, MediaStream } from '../types';
import { predictExtractedFiles } from '../services/ffmpeg';
@@ -129,6 +129,12 @@ app.post('/clear', (c) => {
return c.json({ ok: true, cleared: result.changes });
});
app.post('/clear-completed', (c) => {
const db = getDb();
const result = db.prepare("DELETE FROM jobs WHERE status IN ('done', 'error')").run();
return c.json({ ok: true, cleared: result.changes });
});
// ─── SSE ──────────────────────────────────────────────────────────────────────
app.get('/events', (c) => {
@@ -167,8 +173,9 @@ async function runJob(job: Job): Promise<void> {
if (!job.node_id) {
const itemRow = db.prepare('SELECT file_path FROM media_items WHERE id = ?').get(job.item_id) as { file_path: string } | undefined;
if (itemRow?.file_path) {
try { accessSync(itemRow.file_path, constants.R_OK | constants.W_OK); } catch (fsErr) {
const msg = `File not accessible: ${itemRow.file_path}\n${(fsErr as Error).message}`;
const mappedPath = applyPathMappings(itemRow.file_path);
try { accessSync(mappedPath, constants.R_OK | constants.W_OK); } catch (fsErr) {
const msg = `File not accessible: ${mappedPath}\n${(fsErr as Error).message}`;
db.prepare("UPDATE jobs SET status = 'error', output = ?, exit_code = 1, completed_at = datetime('now') WHERE id = ?").run(msg, job.id);
emitJobUpdate(job.id, 'error', msg);
db.prepare("UPDATE review_plans SET status = 'error' WHERE item_id = ?").run(job.item_id);
@@ -197,7 +204,8 @@ async function runJob(job: Job): Promise<void> {
if (node.series_path) cmd = cmd.replaceAll('/series/', node.series_path.replace(/\/$/, '') + '/');
for await (const line of execStream(node, cmd)) { outputLines.push(line); flush(); }
} else {
const proc = Bun.spawn(['sh', '-c', job.command], { stdout: 'pipe', stderr: 'pipe' });
const mappedCmd = applyPathMappingsToCommand(job.command);
const proc = Bun.spawn(['sh', '-c', mappedCmd], { stdout: 'pipe', stderr: 'pipe' });
const readStream = async (readable: ReadableStream<Uint8Array>, prefix = '') => {
const reader = readable.getReader();
const decoder = new TextDecoder();