fix(MediaManager, DeviceDetailOverlay): improve file existence checks and handle missing files in swap lists

This commit is contained in:
Jaime Idolpx 2026-06-08 20:42:21 -04:00
parent 39c72386fa
commit 4c07f8c4a1
2 changed files with 37 additions and 34 deletions

View File

@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { X, ChevronLeft, ChevronRight, Printer, HardDrive, Network, Box, FolderOpen, MoreVertical, Play, Pause, SkipForward, SkipBack, RotateCcw } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { toast } from 'sonner';
import { getFileContents, joinPath } from '../webdav';
import { fileExists, getFileContents, joinPath } from '../webdav';
import MediaBrowser from './MediaBrowser';
import MediaSet from './MediaSet';
@ -122,38 +122,26 @@ export default function DeviceDetailOverlay({
}
};
// Detect if URL is part of a media set (e.g., disk1.d64, disk2.d64)
const detectMediaSet = () => {
if (!deviceData.url) return null;
const match = deviceData.url.match(/^(.+?)(\d+)(\.[^.]+)$/);
if (!match) return null;
const [, prefix, num, ext] = match;
const currentNum = parseInt(num);
// Generate potential media set
const mediaSet = [];
for (let i = 1; i <= 10; i++) {
mediaSet.push(`${prefix}${i}${ext}`);
}
return {
prefix,
extension: ext,
currentIndex: currentNum - 1,
files: mediaSet
};
};
// Prefer an explicit .lst-derived mediaSet stored in config; fall back to pattern detection.
const mediaSetFiles: string[] | null = (() => {
const [mediaSetFiles, setMediaSetFiles] = useState<string[] | null>(null);
useEffect(() => {
if (Array.isArray(deviceData.media_set) && deviceData.media_set.length > 0) {
return deviceData.media_set as string[];
setMediaSetFiles(deviceData.media_set as string[]);
return;
}
const detected = detectMediaSet();
return detected ? detected.files : null;
})();
if (!deviceData.url) { setMediaSetFiles(null); return; }
const match = (deviceData.url as string).match(/^(.+?)(\d+)(\.[^.]+)$/);
if (!match) { setMediaSetFiles(null); return; }
const [, prefix, , ext] = match;
const candidates: string[] = [];
for (let i = 1; i <= 10; i++) candidates.push(`${prefix}${i}${ext}`);
let cancelled = false;
Promise.all(candidates.map(f => fileExists(f).catch(() => false))).then(flags => {
if (!cancelled) setMediaSetFiles(candidates.filter((_, i) => flags[i]));
});
return () => { cancelled = true; };
}, [deviceData.url, deviceData.media_set]);
const switchMedia = (file: string) => {
const path = getDevicePath();
@ -166,11 +154,17 @@ export default function DeviceDetailOverlay({
try {
const text = await (await getFileContents(selectedPath)).text();
const dir = selectedPath.split('/').slice(0, -1).join('/') || '/';
const files = text.split('\n')
const candidates = text.split('\n')
.map(l => l.trim())
.filter(l => l.length > 0 && !l.startsWith('#'))
.map(l => l.startsWith('/') ? l : joinPath(dir, l));
if (files.length === 0) { toast.error('Swap list is empty'); return; }
if (candidates.length === 0) { toast.error('Swap list is empty'); return; }
const existsArr = await Promise.all(candidates.map(f => fileExists(f).catch(() => false)));
const files = candidates.filter((_, i) => existsArr[i]);
if (files.length === 0) { toast.error('No files in swap list exist on device'); return; }
if (files.length < candidates.length) {
toast.warning(`${candidates.length - files.length} missing file(s) skipped from swap list`);
}
const newConfig = JSON.parse(JSON.stringify(config));
let dev = newConfig;
for (const k of devicePath) dev = dev[k];

View File

@ -760,14 +760,23 @@ export default function MediaManager({ initialPath = '/', rootPath, title, confi
try {
const text = await (await getFileContents(mountEntry.path)).text();
const dir = splitPath(mountEntry.path).parent;
const files = text.split('\n')
const candidates = text.split('\n')
.map(l => l.trim())
.filter(l => l.length > 0 && !l.startsWith('#'))
.map(l => l.startsWith('/') ? l : joinPath(dir, l));
if (files.length === 0) {
if (candidates.length === 0) {
toast.error(`${mountEntry.name}: swap list is empty`);
return;
}
const exists = await Promise.all(candidates.map(f => fileExists(f).catch(() => false)));
const files = candidates.filter((_, i) => exists[i]);
if (files.length === 0) {
toast.error(`${mountEntry.name}: no files in swap list exist on device`);
return;
}
if (files.length < candidates.length) {
toast.warning(`${candidates.length - files.length} missing file(s) skipped from swap list`);
}
dev.url = files[0];
dev.media_set = files;
} catch (e: any) {