remove path mappings from settings UI, fix clear-scan blocking by deleting children first
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m6s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 10:52:26 +01:00
parent 12e60c069e
commit 511a3c1ace
3 changed files with 14 additions and 58 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "netfelix-audio-fix",
"version": "2026.03.05.3",
"version": "2026.03.05.4",
"scripts": {
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
"dev:client": "vite",

View File

@@ -97,8 +97,16 @@ app.post('/path-mappings', async (c) => {
app.post('/clear-scan', (c) => {
const db = getDb();
db.prepare('DELETE FROM media_items').run();
db.prepare("UPDATE config SET value = '0' WHERE key = 'scan_running'").run();
// Delete children first to avoid slow cascade deletes
db.transaction(() => {
db.prepare('DELETE FROM stream_decisions').run();
db.prepare('DELETE FROM jobs').run();
db.prepare('DELETE FROM subtitle_files').run();
db.prepare('DELETE FROM review_plans').run();
db.prepare('DELETE FROM media_streams').run();
db.prepare('DELETE FROM media_items').run();
db.prepare("UPDATE config SET value = '0' WHERE key = 'scan_running'").run();
})();
return c.json({ ok: true });
});

View File

@@ -171,22 +171,18 @@ function ConnSection({
export function SetupPage() {
const [data, setData] = useState<SetupData | null>(null);
const [clearStatus, setClearStatus] = useState('');
const [mappings, setMappings] = useState<[string, string][]>([['', '']]);
const [mappingSaved, setMappingSaved] = useState('');
const [mappingsLoaded, setMappingsLoaded] = useState(false);
const [subLangs, setSubLangs] = useState<string[]>([]);
const [subSaved, setSubSaved] = useState('');
const [audLangs, setAudLangs] = useState<string[]>([]);
const [audSaved, setAudSaved] = useState('');
const [langsLoaded, setLangsLoaded] = useState(false);
const load = () => api.get<SetupData>('/api/setup').then((d) => {
setData(d);
if (!mappingsLoaded) {
const pm: [string, string][] = JSON.parse(d.config.path_mappings ?? '[]');
setMappings(pm.length > 0 ? pm : [['', '']]);
if (!langsLoaded) {
setSubLangs(JSON.parse(d.config.subtitle_languages ?? '["eng","deu","spa"]'));
setAudLangs(JSON.parse(d.config.audio_languages ?? '[]'));
setMappingsLoaded(true);
setLangsLoaded(true);
}
});
useEffect(() => { load(); }, []);
@@ -202,13 +198,6 @@ export function SetupPage() {
const saveSonarr = (url: string, apiKey: string) =>
api.post('/api/setup/sonarr', { url, api_key: apiKey });
const saveMappings = async () => {
const valid = mappings.filter(([f, t]) => f.trim() && t.trim());
await api.post('/api/setup/path-mappings', { mappings: valid });
setMappingSaved('Saved.');
setTimeout(() => setMappingSaved(''), 2000);
};
const saveSubtitleLangs = async () => {
await api.post('/api/setup/subtitle-languages', { langs: subLangs });
setSubSaved('Saved.');
@@ -258,47 +247,6 @@ export function SetupPage() {
onSave={saveSonarr}
/>
{/* Path mappings */}
<SectionCard
title={
<span className="flex items-center gap-2">
Path Mappings
<EnvBadge envVar="PATH_MAPPINGS" locked={locked.has('path_mappings')} />
</span>
}
subtitle="Translate Jellyfin library paths to container mount paths. E.g. Jellyfin uses /tv/ but the container mounts at /series/."
>
{mappings.map(([from, to], i) => (
<div key={i} className="flex items-center gap-2 mb-2">
<Input
value={from}
onChange={(e) => { const m = [...mappings]; m[i] = [e.target.value, m[i][1]]; setMappings(m); }}
placeholder="/tv/"
className="max-w-[12rem] text-sm"
disabled={locked.has('path_mappings')}
/>
<span className="text-gray-400"></span>
<Input
value={to}
onChange={(e) => { const m = [...mappings]; m[i] = [m[i][0], e.target.value]; setMappings(m); }}
placeholder="/series/"
className="max-w-[12rem] text-sm"
disabled={locked.has('path_mappings')}
/>
{mappings.length > 1 && (
<button type="button" onClick={() => setMappings(mappings.filter((_, j) => j !== i))} className="text-red-400 hover:text-red-600 text-sm border-0 bg-transparent cursor-pointer"></button>
)}
</div>
))}
<div className="flex items-center gap-2 mt-2">
<button type="button" onClick={() => setMappings([...mappings, ['', '']])} className="text-blue-600 text-sm border-0 bg-transparent cursor-pointer hover:underline" disabled={locked.has('path_mappings')}>+ Add mapping</button>
</div>
<div className="flex items-center gap-2 mt-3">
<Button onClick={saveMappings} disabled={locked.has('path_mappings')}>Save</Button>
{mappingSaved && <span className="text-green-700 text-sm">{mappingSaved}</span>}
</div>
</SectionCard>
{/* Audio languages */}
<SectionCard
title={