fix(MediaManager, DeviceDetailOverlay): improve file existence checks and handle missing files in swap lists
This commit is contained in:
parent
39c72386fa
commit
4c07f8c4a1
|
|
@ -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 { X, ChevronLeft, ChevronRight, Printer, HardDrive, Network, Box, FolderOpen, MoreVertical, Play, Pause, SkipForward, SkipBack, RotateCcw } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'motion/react';
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getFileContents, joinPath } from '../webdav';
|
import { fileExists, getFileContents, joinPath } from '../webdav';
|
||||||
import MediaBrowser from './MediaBrowser';
|
import MediaBrowser from './MediaBrowser';
|
||||||
import MediaSet from './MediaSet';
|
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.
|
// 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) {
|
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();
|
if (!deviceData.url) { setMediaSetFiles(null); return; }
|
||||||
return detected ? detected.files : null;
|
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 switchMedia = (file: string) => {
|
||||||
const path = getDevicePath();
|
const path = getDevicePath();
|
||||||
|
|
@ -166,11 +154,17 @@ export default function DeviceDetailOverlay({
|
||||||
try {
|
try {
|
||||||
const text = await (await getFileContents(selectedPath)).text();
|
const text = await (await getFileContents(selectedPath)).text();
|
||||||
const dir = selectedPath.split('/').slice(0, -1).join('/') || '/';
|
const dir = selectedPath.split('/').slice(0, -1).join('/') || '/';
|
||||||
const files = text.split('\n')
|
const candidates = text.split('\n')
|
||||||
.map(l => l.trim())
|
.map(l => l.trim())
|
||||||
.filter(l => l.length > 0 && !l.startsWith('#'))
|
.filter(l => l.length > 0 && !l.startsWith('#'))
|
||||||
.map(l => l.startsWith('/') ? l : joinPath(dir, l));
|
.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));
|
const newConfig = JSON.parse(JSON.stringify(config));
|
||||||
let dev = newConfig;
|
let dev = newConfig;
|
||||||
for (const k of devicePath) dev = dev[k];
|
for (const k of devicePath) dev = dev[k];
|
||||||
|
|
|
||||||
|
|
@ -760,14 +760,23 @@ export default function MediaManager({ initialPath = '/', rootPath, title, confi
|
||||||
try {
|
try {
|
||||||
const text = await (await getFileContents(mountEntry.path)).text();
|
const text = await (await getFileContents(mountEntry.path)).text();
|
||||||
const dir = splitPath(mountEntry.path).parent;
|
const dir = splitPath(mountEntry.path).parent;
|
||||||
const files = text.split('\n')
|
const candidates = text.split('\n')
|
||||||
.map(l => l.trim())
|
.map(l => l.trim())
|
||||||
.filter(l => l.length > 0 && !l.startsWith('#'))
|
.filter(l => l.length > 0 && !l.startsWith('#'))
|
||||||
.map(l => l.startsWith('/') ? l : joinPath(dir, l));
|
.map(l => l.startsWith('/') ? l : joinPath(dir, l));
|
||||||
if (files.length === 0) {
|
if (candidates.length === 0) {
|
||||||
toast.error(`${mountEntry.name}: swap list is empty`);
|
toast.error(`${mountEntry.name}: swap list is empty`);
|
||||||
return;
|
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.url = files[0];
|
||||||
dev.media_set = files;
|
dev.media_set = files;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user