fix: scanner catalog empty + left column blank
Scanner: new instance with existing sf_scan_state.json did not populate in-memory _catalog. Now re-inspects unchanged files to fill catalog on first scan. Frontend: left column reads instrumentSelectorData. soundfonts instead of sfPresets (null when no presets). Click SF in left column triggers on-demand fetch.
This commit is contained in:
@@ -50,6 +50,22 @@ class SoundFontAutoScanner:
|
|||||||
out.append((fname, os.path.join(directory, fname)))
|
out.append((fname, os.path.join(directory, fname)))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def _inspect_single(self, fname: str, full: str, inspector) -> dict:
|
||||||
|
if fname.lower().endswith(".sf2"):
|
||||||
|
sf_info = inspector.inspect_sf2_file(full) or {}
|
||||||
|
else:
|
||||||
|
sf_info = {}
|
||||||
|
if not sf_info.get("soundfont_id"):
|
||||||
|
sf_id = os.path.splitext(fname)[0].lower()
|
||||||
|
sf_info = {
|
||||||
|
"soundfont_id": sf_id,
|
||||||
|
"filename": fname,
|
||||||
|
"total_instruments": 0,
|
||||||
|
"instruments": [],
|
||||||
|
"_sf3": True
|
||||||
|
}
|
||||||
|
return sf_info
|
||||||
|
|
||||||
def scan_once(self) -> bool:
|
def scan_once(self) -> bool:
|
||||||
from app.core.soundfont_inspector import SoundFontInspector
|
from app.core.soundfont_inspector import SoundFontInspector
|
||||||
inspector = SoundFontInspector(self.system_sf_dir, self.upload_sf_dir)
|
inspector = SoundFontInspector(self.system_sf_dir, self.upload_sf_dir)
|
||||||
@@ -64,23 +80,19 @@ class SoundFontAutoScanner:
|
|||||||
key = f"{source}:{sf_id}"
|
key = f"{source}:{sf_id}"
|
||||||
sig = _file_sig(full)
|
sig = _file_sig(full)
|
||||||
prev = self._state.get(key)
|
prev = self._state.get(key)
|
||||||
if prev and prev["size"] == sig[0] and prev["mtime"] == sig[1]:
|
unchanged = prev and prev["size"] == sig[0] and prev["mtime"] == sig[1]
|
||||||
|
if unchanged and sf_id in self._catalog:
|
||||||
|
continue
|
||||||
|
if unchanged:
|
||||||
|
with self._lock:
|
||||||
if sf_id in self._catalog:
|
if sf_id in self._catalog:
|
||||||
continue
|
continue
|
||||||
|
sf_info = self._inspect_single(fname, full, inspector)
|
||||||
|
self._catalog[sf_id] = sf_info
|
||||||
|
continue
|
||||||
found_new = True
|
found_new = True
|
||||||
logger.info("New/changed SF detected: %s", fname)
|
logger.info("New/changed SF detected: %s", fname)
|
||||||
if fname.lower().endswith(".sf2"):
|
sf_info = self._inspect_single(fname, full, inspector)
|
||||||
sf_info = inspector.inspect_sf2_file(full) or {}
|
|
||||||
else:
|
|
||||||
sf_info = {}
|
|
||||||
if not sf_info.get("soundfont_id"):
|
|
||||||
sf_info = {
|
|
||||||
"soundfont_id": sf_id,
|
|
||||||
"filename": fname,
|
|
||||||
"total_instruments": 0,
|
|
||||||
"instruments": [],
|
|
||||||
"_sf3": True
|
|
||||||
}
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._catalog[sf_id] = sf_info
|
self._catalog[sf_id] = sf_info
|
||||||
self._state[key] = {"size": sig[0], "mtime": sig[1], "file": fname}
|
self._state[key] = {"size": sig[0], "mtime": sig[1], "file": fname}
|
||||||
|
|||||||
+23
-9
@@ -17704,15 +17704,29 @@ const App = () => {
|
|||||||
onClick: () => { setSelectedSoundFontId(null); setSynthCategory(null); },
|
onClick: () => { setSelectedSoundFontId(null); setSynthCategory(null); },
|
||||||
className: "w-full text-left px-3 py-2 text-sm rounded " + (!selectedSoundFontId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
|
className: "w-full text-left px-3 py-2 text-sm rounded " + (!selectedSoundFontId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
|
||||||
}, "All Instruments"),
|
}, "All Instruments"),
|
||||||
sfPresets === null ? (
|
(instrumentSelectorData?.soundfonts || []).map(sf => {
|
||||||
/*#__PURE__*/React.createElement("p", { className: "text-sm text-zinc-500 py-2" }, "Loading...")
|
const sfId = sf.id.startsWith('sf_') ? sf.id : 'sf_' + sf.id;
|
||||||
) : (
|
const sfName = sf.display || sf.name || sf.id;
|
||||||
[...new Map(sfPresets.map(p => [p._sfId, p])).values()].map(sf => /*#__PURE__*/React.createElement("button", {
|
return /*#__PURE__*/React.createElement("button", {
|
||||||
key: sf._sfId,
|
key: sfId,
|
||||||
onClick: () => setSelectedSoundFontId(sf._sfId),
|
onClick: () => {
|
||||||
className: "w-full text-left px-3 py-2 text-sm rounded " + (selectedSoundFontId === sf._sfId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
|
setSelectedSoundFontId(sfId);
|
||||||
}, sf._sfName))
|
// Fetch instruments on demand if not yet loaded
|
||||||
)
|
if (!sf.presets && window.SonicAPI) {
|
||||||
|
const baseId = sfId.replace('sf_', '');
|
||||||
|
window.SonicAPI.listSoundfontInstruments(baseId).then(data => {
|
||||||
|
if (data && data.presets) {
|
||||||
|
const mapping = data.presets.map(p => ({ ...p, _sfId: sfId, _sfName: sfName, _sfDisplay: sfName.substring(0, 30) }));
|
||||||
|
setSfPresets(prev => prev ? [...prev, ...mapping] : mapping);
|
||||||
|
// Also update instrumentSelectorData cache
|
||||||
|
setInstrumentSelectorData(prev => prev ? { ...prev, soundfonts: (prev.soundfonts || []).map(s => s.id === sf.id ? { ...s, presets: data.presets } : s) } : prev);
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
className: "w-full text-left px-3 py-2 text-sm rounded " + (selectedSoundFontId === sfId ? "bg-amber-700 text-white" : "bg-zinc-800 hover:bg-zinc-700 text-zinc-300")
|
||||||
|
}, sfName);
|
||||||
|
})
|
||||||
),
|
),
|
||||||
/*#__PURE__*/React.createElement("div", { className: "flex-1 overflow-y-auto space-y-0.5" },
|
/*#__PURE__*/React.createElement("div", { className: "flex-1 overflow-y-auto space-y-0.5" },
|
||||||
/*#__PURE__*/React.createElement("button", {
|
/*#__PURE__*/React.createElement("button", {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user