feat(DeviceDetailOverlay): improve media set detection logic and optimize state management

This commit is contained in:
Jaime Idolpx 2026-06-12 05:02:18 -04:00
parent de303e9327
commit 5c28a69055

View File

@ -125,33 +125,40 @@ export default function DeviceDetailOverlay({
}
};
// Prefer an explicit .lst-derived mediaSet stored in config; fall back to pattern detection.
const [mediaSetFiles, setMediaSetFiles] = useState<MediaSetEntry[] | null>(null);
const prevDeviceNumberRef = useRef(device.number);
const detectTokenRef = useRef(0);
// Cancel any in-flight detection and reset when navigating to a different device.
useEffect(() => {
const deviceChanged = prevDeviceNumberRef.current !== device.number;
prevDeviceNumberRef.current = device.number;
++detectTokenRef.current;
setMediaSetFiles(null);
}, [device.number]);
// Sync config-backed media_set to display state whenever it is set explicitly
// (e.g. after a playlist is mounted).
useEffect(() => {
if (Array.isArray(deviceData.media_set) && deviceData.media_set.length > 1) {
setMediaSetFiles(deviceData.media_set as MediaSetEntry[]);
return;
}
// Don't run pattern detection when switching devices — only when the URL
// is actively being changed within the same device.
if (deviceChanged || !deviceData.url) { setMediaSetFiles(null); return; }
const match = (deviceData.url as string).match(/^(.+?)(\d+)(\.[^.]+)$/);
if (!match) { setMediaSetFiles(null); return; }
}, [deviceData.media_set]);
// Pattern-detect sibling numbered files for the given URL. Called only when
// the user actively sets a URL — never on overlay load or device switch.
const detectMediaSet = async (url: string) => {
const token = ++detectTokenRef.current;
setMediaSetFiles(null);
const match = url.match(/^(.+?)(\d+)(\.[^.]+)$/);
if (!match) 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 => stat(f).then(r => r !== null).catch(() => false))).then(flags => {
const flags = await Promise.all(
candidates.map(f => stat(f).then(r => r !== null).catch(() => false))
);
if (detectTokenRef.current !== token) return;
const found = candidates.filter((_, i) => flags[i]);
if (!cancelled) setMediaSetFiles(found.length > 1 ? found : null);
});
return () => { cancelled = true; };
}, [device.number, deviceData.url, deviceData.media_set]);
setMediaSetFiles(found.length > 1 ? found : null);
};
const switchMedia = (file: string) => {
const path = getDevicePath();