1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-D0Bmh-UX.js"; 3import { s as synth, p as parseMidiInstruments } from "./synth-BE1TSTD4.js"; 4import { b4 as FolderOpen, b5 as Trash2, cj as Layers, e9 as CloudUpload, bi as Sparkles, e7 as CircleX, cL as RefreshCw, bf as Search, fg as SquareCheckBig, dg as Square, bZ as Pause, bU as Play, cY as ChevronLeft, bE as ChevronRight, b9 as Settings, ba as Info, aY as TriangleAlert, eu as Terminal, ez as WandSparkles, bg as Music, df as SlidersVertical, cG as Tag, dX as Server } from "./vendor-dVNaLJx_.js"; 5import "./vendor-i18n-R-bMoYp_.js"; 6import "./vendor-react-three-MDLA5jog.js"; 7import "./wasm-synth-adapter-BrqgBX1y.js"; 8import "./vendor-spessasynth-DH8n6o_V.js"; 9import "./vendor-audio-DH3w0BzW.js"; 10function ArchiveImporter() { 11 const [activeTab, setActiveTab] = reactExports.useState("queue"); 12 const [stagedFiles, setStagedFiles] = reactExports.useState([]); 13 const [totalStagedCount, setTotalStagedCount] = reactExports.useState(0); 14 const [stagingPath, setStagingPath] = reactExports.useState(""); 15 const [isLoading, setIsLoading] = reactExports.useState(false); 16 const [overwriteExisting, setOverwriteExisting] = reactExports.useState(false); 17 const [searchTerm, setSearchTerm] = reactExports.useState(""); 18 const [currentPage, setCurrentPage] = reactExports.useState(1); 19 const [pageSize, setPageSize] = reactExports.useState(20); 20 const [selectedPaths, setSelectedPaths] = reactExports.useState(/* @__PURE__ */ new Set()); 21 const [lakhStatus, setLakhStatus] = reactExports.useState({ folderExists: false, totalFiles: 0, lakhPath: "", dbImportedCount: 0 }); 22 const [lakhFiles, setLakhFiles] = reactExports.useState([]); 23 const [lakhSearch, setLakhSearch] = reactExports.useState(""); 24 const [lakhPage, setLakhPage] = reactExports.useState(1); 25 const [lakhPageSize, setLakhPageSize] = reactExports.useState(10); 26 const [lakhTotalCount, setLakhTotalCount] = reactExports.useState(0); 27 const [lakhSelectedHashes, setLakhSelectedHashes] = reactExports.useState(/* @__PURE__ */ new Set()); 28 const [isSettingUpDemo, setIsSettingUpDemo] = reactExports.useState(false); 29 const [ariaStatus, setAriaStatus] = reactExports.useState({ folderExists: false, totalFiles: 0, ariaPath: "", metadataLoaded: false, indexing: false, dbImportedCount: 0 }); 30 const [ariaFiles, setAriaFiles] = reactExports.useState([]); 31 const [ariaSearch, setAriaSearch] = reactExports.useState(""); 32 const [ariaPage, setAriaPage] = reactExports.useState(1); 33 const [ariaPageSize, setAriaPageSize] = reactExports.useState(10); 34 const [ariaTotalCount, setAriaTotalCount] = reactExports.useState(0); 35 const [ariaSelectedHashes, setAriaSelectedHashes] = reactExports.useState(/* @__PURE__ */ new Set()); 36 const [pianocoreStatus, setPianocoreStatus] = reactExports.useState({ folderExists: false, totalFiles: 0, pianocorePath: "", metadataLoaded: false, indexing: false, dbImportedCount: 0 }); 37 const [pianocoreFiles, setPianocoreFiles] = reactExports.useState([]); 38 const [pianocoreSearch, setPianocoreSearch] = reactExports.useState(""); 39 const [pianocorePage, setPianocorePage] = reactExports.useState(1); 40 const [pianocorePageSize, setPianocorePageSize] = reactExports.useState(10); 41 const [pianocoreTotalCount, setPianocoreTotalCount] = reactExports.useState(0); 42 const [pianocoreSelectedHashes, setPianocoreSelectedHashes] = reactExports.useState(/* @__PURE__ */ new Set()); 43 const [pianocoreImportActive, setPianocoreImportActive] = reactExports.useState(false); 44 const [currentPageInput, setCurrentPageInput] = reactExports.useState("1"); 45 const [lakhPageInput, setLakhPageInput] = reactExports.useState("1"); 46 const [ariaPageInput, setAriaPageInput] = reactExports.useState("1"); 47 const [pianocorePageInput, setPianocorePageInput] = reactExports.useState("1"); 48 const [parserPreset, setParserPreset] = reactExports.useState("genre_artist_title"); 49 const [customPattern, setCustomPattern] = reactExports.useState("(?<artist>[^/]+)-(?<title>[^/\\.]+)\\.mid$"); 50 const [defaultArtist, setDefaultArtist] = reactExports.useState(""); 51 const [defaultGenre, setDefaultGenre] = reactExports.useState(""); 52 const [previewingPath, setPreviewingPath] = reactExports.useState(null); 53 const [previewLoading, setPreviewLoading] = reactExports.useState(false); 54 const [previewPlaying, setPreviewPlaying] = reactExports.useState(false); 55 const [previewTitle, setPreviewTitle] = reactExports.useState(""); 56 const [showStagingPaths, setShowStagingPaths] = reactExports.useState(false); 57 const [showParserSettings, setShowParserSettings] = reactExports.useState(false); 58 const renderStagingNotice = () => { 59 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-950/20 border border-indigo-500/20 rounded-xl p-4 flex items-start gap-3 text-left", children: [
60 /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { className: "text-indigo-400 shrink-0 mt-0.5", size: 18 }), 61 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-300 leading-relaxed w-full", children: [ 62 /* @__PURE__ */ jsxRuntimeExports.jsxs("strong", { className: "text-white flex items-center justify-between gap-1.5 mb-1 cursor-pointer select-none", onClick: () => setShowStagingPaths(!showStagingPaths), children: [ 63 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1.5", children: "Direct File-Drop Staging Paths" }), 64 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-indigo-400 hover:text-indigo-300 font-bold underline", children: showStagingPaths ? "Hide Paths" : "Show Paths" }) 65 ] }), 66 showStagingPaths && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 animate-in fade-in slide-in-from-top-1 duration-200", children: [ 67 "If you have filesystem access, copy or extract files directly inside the server:",
68 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 mt-2", children: [ 69 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 70 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase font-black text-zinc-500 block mb-0.5", children: "Staging Queue Path:" }), 71 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "block p-1.5 bg-zinc-950 text-indigo-400 font-mono select-all rounded border border-zinc-800 text-[10px] break-all", children: stagingPath || "STORAGE_ROOT/import-staging" }) 72 ] }), 73 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 74 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase font-black text-zinc-500 block mb-0.5", children: "Lakh Dataset Folder Path:" }), 75 /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "block p-1.5 bg-zinc-950 text-emerald-400 font-mono select-all rounded border border-zinc-800 text-[10px] break-all", children: lakhStatus.lakhPath || "STORAGE_ROOT/lakh-dataset" }) 76 ] }) 77 ] }) 78 ] }) 79 ] }) 80 ] }); 81 }; 82 const [isImporting, setIsImporting] = reactExports.useState(false); 83 const [uploadLogs, setUploadLogs] = reactExports.useState([]); 84 const [showQueries, setShowQueries] = reactExports.useState(false); 85 const [isScanningLocal, setIsScanningLocal] = reactExports.useState(false); 86 const [processedCount, setProcessedCount] = reactExports.useState(0); 87 const [successCount, setSuccessCount] = reactExports.useState(0); 88 const [duplicateCount, setDuplicateCount] = reactExports.useState(0); 89 const [failedCount, setFailedCount] = reactExports.useState(0); 90 const [mbMatchedCount, setMbMatchedCount] = reactExports.useState(0); 91 const [mbNoMatchCount, setMbNoMatchCount] = reactExports.useState(0); 92 const [importAllError, setImportAllError] = reactExports.useState(null); 93 const folderInputRef = reactExports.useRef(null); 94 const logsEndRef = reactExports.useRef(null); 95 const activeImportRef = reactExports.useRef(false); 96 const [isImportingAll, setIsImportingAll] = reactExports.useState(false); 97 const [lakhImportActive, setLakhImportActive] = reactExports.useState(false); 98 const [ariaImportActive, setAriaImportActive] = reactExports.useState(false); 99 const pollingIntervalRef = reactExports.useRef(null); 100 const pollLakhImportAllStatus = async () => { 101 try { 102 const res = await api.getLakhImportAllStatus(); 103 if (res.success && res.status) { 104 const s = res.status; 105 setIsImportingAll(s.active); 106 setLakhImportActive(s.active); 107 setProcessedCount(s.processed); 108 setSuccessCount(s.successful); 109 setDuplicateCount(s.duplicates); 110 setFailedCount(s.failed); 111 setMbMatchedCount(s.mbMatched || 0); 112 setMbNoMatchCount(s.mbNoMatch || 0); 113 setUploadLogs(s.logs); 114 setImportAllError(s.error); 115 if (s.error) { 116 setIsImportingAll(false); 117 setLakhImportActive(false); 118 if (pollingIntervalRef.current) { 119 clearInterval(pollingIntervalRef.current); 120 pollingIntervalRef.current = null; 121 } 122 } 123 if (!s.active) { 124 if (pollingIntervalRef.current) { 125 clearInterval(pollingIntervalRef.current); 126 pollingIntervalRef.current = null; 127 } 128 } 129 } 130 } catch (err) { 131 console.error("Error polling lakh import-all status:", err); 132 } 133 }; 134 const handleStartLakhImportAll = async () => { 135 setIsImportingAll(true); 136 setLakhImportActive(true); 137 setImportAllError(null); 138 setUploadLogs(["â³ Initiating background full Lakh import job..."]); 139 try { 140 await api.startLakhImportAll(overwriteExisting); 141 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 142 pollingIntervalRef.current = setInterval(pollLakhImportAllStatus, 1e3); 143 } catch (err) { 144 setImportAllError(err.message); 145 setIsImportingAll(false); 146 setLakhImportActive(false); 147 setUploadLogs((prev) => [...prev, `â Failed to start: ${err.message}`]); 148 } 149 }; 150 const handleStopLakhImportAll = async () => { 151 try { 152 setUploadLogs((prev) => [...prev, `â³ Sending stop command...`]); 153 await api.stopLakhImportAll(); 154 } catch (err) { 155 setUploadLogs((prev) => [...prev, `â Failed to stop: ${err.message}`]); 156 } 157 }; 158 const pollAriaImportAllStatus = async () => { 159 try { 160 const res = await api.getAriaImportAllStatus(); 161 if (res.success && res.status) { 162 const s = res.status; 163 setIsImportingAll(s.active); 164 setAriaImportActive(s.active); 165 setProcessedCount(s.processed); 166 setSuccessCount(s.successful); 167 setDuplicateCount(s.duplicates); 168 setFailedCount(s.failed); 169 setMbMatchedCount(s.mbMatched || 0); 170 setMbNoMatchCount(s.mbNoMatch || 0); 171 setUploadLogs(s.logs); 172 setImportAllError(s.error); 173 if (s.error) { 174 setIsImportingAll(false); 175 setAriaImportActive(false); 176 if (pollingIntervalRef.current) { 177 clearInterval(pollingIntervalRef.current); 178 pollingIntervalRef.current = null; 179 } 180 } 181 if (!s.active) { 182 if (pollingIntervalRef.current) { 183 clearInterval(pollingIntervalRef.current); 184 pollingIntervalRef.current = null; 185 } 186 } 187 } 188 } catch (err) { 189 console.error("Error polling Aria import-all status:", err); 190 } 191 }; 192 const handleStartAriaImportAll = async () => { 193 setIsImportingAll(true); 194 setAriaImportActive(true); 195 setImportAllError(null); 196 setUploadLogs(["â³ Initiating background full Aria-MIDI import job..."]); 197 try { 198 await api.startAriaImportAll(overwriteExisting); 199 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 200 pollingIntervalRef.current = setInterval(pollAriaImportAllStatus, 1e3); 201 } catch (err) { 202 setImportAllError(err.message); 203 setIsImportingAll(false); 204 setAriaImportActive(false); 205 setUploadLogs((prev) => [...prev, `â Failed to start: ${err.message}`]); 206 } 207 }; 208 const handleStopAriaImportAll = async () => { 209 try { 210 setUploadLogs((prev) => [...prev, `â³ Sending stop command...`]); 211 await api.stopAriaImportAll(); 212 } catch (err) { 213 setUploadLogs((prev) => [...prev, `â Failed to stop: ${err.message}`]); 214 } 215 }; 216 const pollPianocoreImportAllStatus = async () => { 217 try { 218 const res = await api.getPianocoreImportAllStatus(); 219 if (res.success && res.status) { 220 const s = res.status; 221 setIsImportingAll(s.active); 222 setPianocoreImportActive(s.active); 223 setProcessedCount(s.processed); 224 setSuccessCount(s.successful); 225 setDuplicateCount(s.duplicates); 226 setFailedCount(s.failed); 227 setMbMatchedCount(s.mbMatched || 0); 228 setMbNoMatchCount(s.mbNoMatch || 0); 229 setUploadLogs(s.logs); 230 setImportAllError(s.error); 231 if (s.error) { 232 setIsImportingAll(false); 233 setPianocoreImportActive(false); 234 if (pollingIntervalRef.current) { 235 clearInterval(pollingIntervalRef.current); 236 pollingIntervalRef.current = null; 237 } 238 } 239 if (!s.active) { 240 if (pollingIntervalRef.current) { 241 clearInterval(pollingIntervalRef.current); 242 pollingIntervalRef.current = null; 243 } 244 } 245 } 246 } catch (err) { 247 console.error("Error polling Pianocore import-all status:", err); 248 } 249 }; 250 const handleStartPianocoreImportAll = async () => { 251 setIsImportingAll(true); 252 setPianocoreImportActive(true); 253 setImportAllError(null); 254 setUploadLogs(["â³ Initiating background full Pianocore-A* import job..."]); 255 try { 256 await api.startPianocoreImportAll(overwriteExisting); 257 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 258 pollingIntervalRef.current = setInterval(pollPianocoreImportAllStatus, 1e3); 259 } catch (err) { 260 setImportAllError(err.message); 261 setIsImportingAll(false); 262 setPianocoreImportActive(false); 263 setUploadLogs((prev) => [...prev, `â Failed to start: ${err.message}`]); 264 } 265 }; 266 const handleStopPianocoreImportAll = async () => { 267 try { 268 setUploadLogs((prev) => [...prev, `â³ Sending stop command...`]); 269 await api.stopPianocoreImportAll(); 270 } catch (err) { 271 setUploadLogs((prev) => [...prev, `â Failed to stop: ${err.message}`]); 272 } 273 }; 274 const handleEmergencyStopAll = async () => { 275 try { 276 setUploadLogs((prev) => [...prev, `ð¨ EMERGENCY STOP REQUESTED!`]); 277 const promises = []; 278 if (lakhImportActive) promises.push(api.stopLakhImportAll()); 279 if (ariaImportActive) promises.push(api.stopAriaImportAll()); 280 if (pianocoreImportActive) promises.push(api.stopPianocoreImportAll()); 281 await Promise.all(promises); 282 setLakhImportActive(false); 283 setAriaImportActive(false); 284 setPianocoreImportActive(false); 285 setIsImportingAll(false); 286 setUploadLogs((prev) => [...prev, `ð¨ Stop signals sent successfully to all active background processes.`]); 287 } catch (err) { 288 setUploadLogs((prev) => [...prev, `â Emergency stop failed: ${err.message}`]); 289 } 290 }; 291 const loadLakhImportStatus = async () => { 292 try { 293 const res = await api.getLakhImportAllStatus(); 294 if (res.success && res.status) { 295 const s = res.status; 296 setProcessedCount(s.processed || 0); 297 setSuccessCount(s.successful || 0); 298 setDuplicateCount(s.duplicates || 0); 299 setFailedCount(s.failed || 0); 300 setMbMatchedCount(s.mbMatched || 0); 301 setMbNoMatchCount(s.mbNoMatch || 0); 302 setUploadLogs(s.logs || []); 303 setImportAllError(s.error || null); 304 setIsImportingAll(s.active); 305 setLakhImportActive(s.active); 306 if (s.active) { 307 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 308 pollingIntervalRef.current = setInterval(pollLakhImportAllStatus, 1e3); 309 } 310 } 311 } catch (err) { 312 console.error("Error loading Lakh import status:", err); 313 } 314 }; 315 const loadAriaImportStatus = async () => { 316 try { 317 const res = await api.getAriaImportAllStatus(); 318 if (res.success && res.status) { 319 const s = res.status; 320 setProcessedCount(s.processed || 0); 321 setSuccessCount(s.successful || 0); 322 setDuplicateCount(s.duplicates || 0); 323 setFailedCount(s.failed || 0); 324 setMbMatchedCount(s.mbMatched || 0); 325 setMbNoMatchCount(s.mbNoMatch || 0); 326 setUploadLogs(s.logs || []); 327 setImportAllError(s.error || null); 328 setIsImportingAll(s.active); 329 setAriaImportActive(s.active); 330 if (s.active) { 331 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 332 pollingIntervalRef.current = setInterval(pollAriaImportAllStatus, 1e3); 333 } 334 } 335 } catch (err) { 336 console.error("Error loading Aria import status:", err); 337 } 338 }; 339 const loadPianocoreImportStatus = async () => { 340 try { 341 const res = await api.getPianocoreImportAllStatus(); 342 if (res.success && res.status) { 343 const s = res.status; 344 setProcessedCount(s.processed || 0); 345 setSuccessCount(s.successful || 0); 346 setDuplicateCount(s.duplicates || 0); 347 setFailedCount(s.failed || 0); 348 setMbMatchedCount(s.mbMatched || 0); 349 setMbNoMatchCount(s.mbNoMatch || 0); 350 setUploadLogs(s.logs || []); 351 setImportAllError(s.error || null); 352 setIsImportingAll(s.active); 353 setPianocoreImportActive(s.active); 354 if (s.active) { 355 if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current); 356 pollingIntervalRef.current = setInterval(pollPianocoreImportAllStatus, 1e3); 357 } 358 } 359 } catch (err) { 360 console.error("Error loading Pianocore import status:", err); 361 } 362 }; 363 reactExports.useEffect(() => { 364 const checkActive = async () => {
365 try { 366 const resLakh = await api.getLakhImportAllStatus(); 367 const lakhActive = !!(resLakh.success && resLakh.status && resLakh.status.active); 368 setLakhImportActive(lakhActive); 369 const resAria = await api.getAriaImportAllStatus(); 370 const ariaActive = !!(resAria.success && resAria.status && resAria.status.active); 371 setAriaImportActive(ariaActive); 372 const resPianocore = await api.getPianocoreImportAllStatus(); 373 const pianocoreActive = !!(resPianocore.success && resPianocore.status && resPianocore.status.active); 374 setPianocoreImportActive(pianocoreActive); 375 if (lakhActive) { 376 const s = resLakh.status; 377 setProcessedCount(s.processed || 0); 378 setSuccessCount(s.successful || 0); 379 setDuplicateCount(s.duplicates || 0); 380 setFailedCount(s.failed || 0); 381 setMbMatchedCount(s.mbMatched || 0); 382 setMbNoMatchCount(s.mbNoMatch || 0); 383 if (s.logs && s.logs.length > 0) setUploadLogs(s.logs); 384 setImportAllError(s.error || null); 385 setIsImportingAll(true);
386 setActiveTab("lakh"); 387 if (!pollingIntervalRef.current) { 388 pollingIntervalRef.current = setInterval(pollLakhImportAllStatus, 1e3); 389 } 390 } else if (ariaActive) { 391 const s = resAria.status; 392 setProcessedCount(s.processed || 0); 393 setSuccessCount(s.successful || 0); 394 setDuplicateCount(s.duplicates || 0); 395 setFailedCount(s.failed || 0); 396 setMbMatchedCount(s.mbMatched || 0); 397 setMbNoMatchCount(s.mbNoMatch || 0); 398 if (s.logs && s.logs.length > 0) setUploadLogs(s.logs); 399 setImportAllError(s.error || null); 400 setIsImportingAll(true); 401 setActiveTab("aria"); 402 if (!pollingIntervalRef.current) { 403 pollingIntervalRef.current = setInterval(pollAriaImportAllStatus, 1e3); 404 } 405 } else if (pianocoreActive) { 406 const s = resPianocore.status; 407 setProcessedCount(s.processed || 0); 408 setSuccessCount(s.successful || 0); 409 setDuplicateCount(s.duplicates || 0); 410 setFailedCount(s.failed || 0); 411 setMbMatchedCount(s.mbMatched || 0); 412 setMbNoMatchCount(s.mbNoMatch || 0); 413 if (s.logs && s.logs.length > 0) setUploadLogs(s.logs); 414 setImportAllError(s.error || null); 415 setIsImportingAll(true);
416 setActiveTab("pianocore"); 417 if (!pollingIntervalRef.current) { 418 pollingIntervalRef.current = setInterval(pollPianocoreImportAllStatus, 1e3); 419 } 420 } 421 } catch (e) { 422 console.error("checkActive error:", e); 423 } 424 }; 425 checkActive(); 426 return () => { 427 if (pollingIntervalRef.current) { 428 clearInterval(pollingIntervalRef.current); 429 } 430 }; 431 }, []); 432 reactExports.useEffect(() => { 433 return () => { 434 synth.stop(); 435 }; 436 }, [activeTab]); 437 reactExports.useEffect(() => { 438 var _a; 439 const el = (_a = logsEndRef.current) == null ? void 0 : _a.parentElement; 440 if (el) { 441 const isNearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 150; 442 if (isNearBottom) { 443 el.scrollTop = el.scrollHeight; 444 } 445 } 446 }, [uploadLogs]); 447 reactExports.useEffect(() => { 448 loadStagingStats(); 449 loadLakhStatus(); 450 loadAriaStatus(); 451 }, []); 452 reactExports.useEffect(() => { 453 if (pollingIntervalRef.current) { 454 clearInterval(pollingIntervalRef.current); 455 pollingIntervalRef.current = null; 456 } 457 setProcessedCount(0); 458 setSuccessCount(0); 459 setDuplicateCount(0); 460 setFailedCount(0); 461 setMbMatchedCount(0); 462 setMbNoMatchCount(0); 463 setUploadLogs([]); 464 setImportAllError(null); 465 setIsImportingAll(false); 466 if (activeTab === "lakh") { 467 loadLakhImportStatus(); 468 } else if (activeTab === "aria") { 469 loadAriaImportStatus(); 470 } else if (activeTab === "pianocore") { 471 loadPianocoreImportStatus(); 472 } 473 }, [activeTab]); 474 reactExports.useEffect(() => { 475 if (activeTab === "queue") { 476 loadStagingFiles(); 477 } else if (activeTab === "lakh") { 478 loadLakhFiles(); 479 loadLakhStatus(); 480 } else if (activeTab === "aria") { 481 loadAriaFiles(); 482 loadAriaStatus(); 483 } else if (activeTab === "pianocore") { 484 loadPianocoreFiles(); 485 loadPianocoreStatus(); 486 } 487 }, [currentPage, pageSize, activeTab, lakhPage, lakhPageSize, ariaPage, ariaPageSize, pianocorePage, pianocorePageSize]); 488 reactExports.useEffect(() => { 489 setCurrentPageInput(currentPage.toString()); 490 }, [currentPage]); 491 reactExports.useEffect(() => { 492 setLakhPageInput(lakhPage.toString()); 493 }, [lakhPage]); 494 reactExports.useEffect(() => { 495 setAriaPageInput(ariaPage.toString()); 496 }, [ariaPage]); 497 reactExports.useEffect(() => { 498 setPianocorePageInput(pianocorePage.toString()); 499 }, [pianocorePage]); 500 const loadStagingStats = async () => { 501 try { 502 const stats = await api.getStagingStats(); 503 if (stats.success) { 504 setTotalStagedCount(stats.totalFiles); 505 setStagingPath(stats.stagingPath); 506 } 507 } catch (err) { 508 console.error("Error loading staging stats:", err); 509 } 510 }; 511 const loadLakhStatus = async () => { 512 try { 513 const res = await api.getLakhStatus(); 514 if (res.success) { 515 setLakhStatus({ 516 folderExists: res.folderExists, 517 totalFiles: res.totalFiles, 518 lakhPath: res.lakhPath, 519 dbImportedCount: res.dbImportedCount || 0 520 }); 521 } 522 } catch (err) { 523 console.error("Error loading Lakh status:", err); 524 } 525 }; 526 const loadLakhFiles = async () => { 527 setIsLoading(true); 528 try { 529 const data = await api.getLakhList(lakhPage, lakhPageSize, lakhSearch); 530 if (data.success) { 531 setLakhFiles(data.files.map((item) => ({ 532 ...item, 533 genre: item.genre === "Lakh" ? "" : item.genre || "", 534 artist: item.artist === "Unknown Artist" ? "" : item.artist || "", 535 status: item.status || "pending" 536 }))); 537 setLakhTotalCount(data.total); 538 } 539 } catch (err) { 540 setUploadLogs((prev) => [...prev, `â Error loading Lakh seeds: ${err.message}`]); 541 } finally { 542 setIsLoading(false); 543 } 544 }; 545 const handleLakhSearch = (e) => { 546 e.preventDefault(); 547 setLakhPage(1); 548 loadLakhFiles(); 549 }; 550 const loadAriaStatus = async () => { 551 try { 552 const res = await api.getAriaStatus(); 553 if (res.success) { 554 setAriaStatus({ 555 folderExists: res.folderExists, 556 totalFiles: res.totalFiles, 557 ariaPath: res.ariaPath, 558 metadataLoaded: res.metadataLoaded, 559 indexing: res.indexing, 560 dbImportedCount: res.dbImportedCount || 0 561 }); 562 } 563 } catch (err) { 564 console.error("Error loading Aria status:", err); 565 } 566 }; 567 const loadAriaFiles = async () => { 568 setIsLoading(true); 569 try { 570 const data = await api.getAriaList(ariaPage, ariaPageSize, ariaSearch); 571 if (data.success) { 572 setAriaFiles(data.files.map((item) => ({ 573 ...item, 574 composer: item.composer === "Unknown Composer" ? "" : item.composer || "", 575 performer: item.performer || "", 576 genre: item.genre || "", 577 pieceName: item.pieceName || "", 578 year: item.year || "", 579 status: item.status || "pending" 580 }))); 581 setAriaTotalCount(data.total); 582 } 583 } catch (err) { 584 setUploadLogs((prev) => [...prev, `â Error loading Aria files: ${err.message}`]); 585 } finally { 586 setIsLoading(false); 587 } 588 }; 589 const handleAriaSearch = (e) => { 590 e.preventDefault(); 591 setAriaPage(1); 592 loadAriaFiles(); 593 }; 594 const loadPianocoreStatus = async () => { 595 try { 596 const res = await api.getPianocoreStatus(); 597 if (res.success) { 598 setPianocoreStatus({ 599 folderExists: res.folderExists, 600 totalFiles: res.totalFiles, 601 pianocorePath: res.pianocorePath, 602 metadataLoaded: res.metadataLoaded, 603 indexing: res.indexing, 604 dbImportedCount: res.dbImportedCount || 0 605 }); 606 } 607 } catch (err) { 608 console.error("Error loading Pianocore status:", err); 609 } 610 }; 611 const loadPianocoreFiles = async (refresh = false) => { 612 setIsLoading(true); 613 try { 614 const data = await api.getPianocoreList(pianocorePage, pianocorePageSize, pianocoreSearch, refresh); 615 if (data.success) { 616 setPianocoreFiles(data.files.map((item) => ({ 617 ...item, 618 composer: item.composer === "Unknown Composer" ? "" : item.composer || "", 619 pieceName: item.pieceName || "", 620 movement: item.movement || "", 621 performanceId: item.performanceId || "", 622 pianocoreId: item.pianocoreId || "", 623 sourceDataset: item.sourceDataset || "", 624 isTranscription: !!item.isTranscription, 625 scoreId: item.scoreId || "", 626 existsOnDisk: !!item.existsOnDisk, 627 status: item.status || "pending" 628 }))); 629 setPianocoreTotalCount(data.total); 630 } 631 } catch (err) { 632 setUploadLogs((prev) => [...prev, `â Error loading Pianocore files: ${err.message}`]); 633 } finally { 634 setIsLoading(false); 635 } 636 }; 637 const handlePianocoreSearch = (e) => { 638 e.preventDefault(); 639 setPianocorePage(1); 640 loadPianocoreFiles(); 641 }; 642 const parsePathMetadata = (relPath, fileName, internalArtist = "", internalTitle = "") => { 643 const normalized = relPath.replace(/\\/g, "/"); 644 const parts = normalized.split("/"); 645 let title = internalTitle || fileName.replace(/\.[^/.]+$/, ""); 646 let artist = internalArtist || defaultArtist; 647 const tags = []; 648 if (defaultGenre) { 649 tags.push(defaultGenre); 650 } 651 try { 652 if (parserPreset === "genre_artist_title") { 653 if (parts.length >= 4) { 654 const genreIdx = parts.length - 3; 655 const artistIdx = parts.length - 2; 656 artist = internalArtist || parts[artistIdx]; 657 tags.push(parts[genreIdx]); 658 } else if (parts.length === 3) { 659 artist = internalArtist || parts[0]; 660 } 661 } else if (parserPreset === "artist_title") { 662 const cleanName = parts[parts.length - 1].replace(/\.[^/.]+$/, ""); 663 const splitName = cleanName.split("-"); 664 if (splitName.length >= 2) { 665 artist = internalArtist || splitName[0].trim(); 666 title = internalTitle || splitName.slice(1).join("-").trim(); 667 } 668 } else if (parserPreset === "artist_title_subdir") { 669 if (parts.length >= 2) { 670 const artistIdx = parts.length - 2; 671 artist = internalArtist || parts[artistIdx]; 672 } 673 } else if (parserPreset === "custom") { 674 const rx = new RegExp(customPattern, "i"); 675 const match = normalized.match(rx); 676 if (match && match.groups) { 677 if (match.groups.artist && !internalArtist) artist = match.groups.artist.replace(/_/g, " "); 678 if (match.groups.title && !internalTitle) title = match.groups.title.replace(/_/g, " "); 679 if (match.groups.genre) tags.push(match.groups.genre); 680 } 681 } 682 } catch (err) { 683 } 684 return { 685 title: title || fileName.replace(/\.[^/.]+$/, ""), 686 artist: artist || defaultArtist,
687 tags: tags.filter(Boolean) 688 }; 689 }; 690 const loadStagingFiles = async () => { 691 setIsLoading(true); 692 try { 693 const data = await api.getStagingFiles(currentPage, pageSize, searchTerm); 694 if (data.success) { 695 const mapped = data.files.map((file) => { 696 const parsed = parsePathMetadata(file.relativePath, file.name, file.internalArtist, file.internalTitle); 697 return { 698 relativePath: file.relativePath, 699 name: file.name, 700 size: file.size, 701 internalTitle: file.internalTitle, 702 internalArtist: file.internalArtist, 703 title: parsed.title, 704 artist: parsed.artist, 705 tags: parsed.tags, 706 status: "pending" 707 }; 708 }); 709 setStagedFiles(mapped); 710 setTotalStagedCount(data.total); 711 setSelectedPaths(/* @__PURE__ */ new Set()); 712 } 713 } catch (err) { 714 setUploadLogs((prev) => [...prev, `â Error fetching staged files: ${err.message}`]); 715 } finally { 716 setIsLoading(false); 717 } 718 }; 719 const handleSearch = (e) => { 720 e.preventDefault(); 721 setCurrentPage(1); 722 loadStagingFiles(); 723 }; 724 const applyParserRules = () => { 725 if (activeTab === "queue") { 726 setStagedFiles((prev) => prev.map((f) => { 727 const parsed = parsePathMetadata(f.relativePath, f.name, f.internalArtist, f.internalTitle); 728 return { 729 ...f, 730 title: parsed.title, 731 artist: parsed.artist, 732 tags: parsed.tags 733 }; 734 })); 735 setUploadLogs((prev) => [...prev, `â¹ Re-applied path parsing rules to current preview page.`]); 736 } else if (activeTab === "lakh") { 737 setLakhFiles((prev) => prev.map((f) => { 738 const parsed = parsePathMetadata(f.filePath || "", `${f.hash}.mid`); 739 return { 740 ...f, 741 title: parsed.title, 742 artist: parsed.artist === "Unknown Artist" ? "" : parsed.artist, 743 genre: parsed.tags[0] || (f.genre === "Lakh" ? "" : f.genre) || "" 744 }; 745 })); 746 setUploadLogs((prev) => [...prev, `â¹ Re-applied path parsing rules to Lakh original paths.`]); 747 } 748 }; 749 const handleUploadToStaging = async (e) => { 750 const filesList = e.target.files; 751 if (!filesList || filesList.length === 0) return; 752 setIsScanningLocal(true); 753 setUploadLogs((prev) => [...prev, `â³ Preparing to upload ${filesList.length} files to server staging area...`]); 754 const chunkLimit = 50; 755 let successfullyStaged = 0; 756 for (let i = 0; i < filesList.length; i += chunkLimit) { 757 const chunk = Array.from(filesList).slice(i, i + chunkLimit); 758 const formData = new FormData(); 759 const paths = chunk.map((f) => f.webkitRelativePath || f.name); 760 formData.append("paths", JSON.stringify(paths)); 761 chunk.forEach((f) => { 762 formData.append("files", f); 763 }); 764 try { 765 const batchNum = Math.floor(i / chunkLimit) + 1; 766 const totalBatches = Math.ceil(filesList.length / chunkLimit); 767 setUploadLogs((prev) => [...prev, `Uploading chunk ${batchNum}/${totalBatches}...`]); 768 const res = await api.uploadToStaging(formData); 769 successfullyStaged += res.count; 770 } catch (err) { 771 setUploadLogs((prev) => [...prev, `â Error uploading chunk starting at ${i}: ${err.message}`]); 772 } 773 } 774 setIsScanningLocal(false); 775 setUploadLogs((prev) => [...prev, `â Finished uploading staging chunk: Staged ${successfullyStaged} files successfully.`]); 776 loadStagingStats(); 777 if (folderInputRef.current) folderInputRef.current.value = ""; 778 }; 779 const startImport = async () => { 780 const toImport = stagedFiles.filter((f) => f.status === "pending" && (selectedPaths.size === 0 || selectedPaths.has(f.relativePath))); 781 if (toImport.length === 0) { 782 setUploadLogs((prev) => [...prev, `â No pending staged files selected for import.`]); 783 return; 784 } 785 setIsImporting(true); 786 activeImportRef.current = true; 787 setUploadLogs((prev) => [...prev, `â¶ Starting local server-side import job (${toImport.length} files)...`]); 788 setProcessedCount(0); 789 setSuccessCount(0); 790 setDuplicateCount(0); 791 setFailedCount(0);
792 let currentProcessed = 0; 793 let currentSuccess = 0; 794 let currentDuplicate = 0; 795 let currentFailed = 0; 796 const batchChunkSize = 25; 797 for (let i = 0; i < toImport.length; i += batchChunkSize) { 798 if (!activeImportRef.current) break; 799 const chunk = toImport.slice(i, i + batchChunkSize); 800 const items = chunk.map((item) => ({ 801 relativePath: item.relativePath, 802 title: item.title, 803 artist: item.artist, 804 tags: item.tags 805 })); 806 try { 807 const res = await api.importStagingBatch(items, overwriteExisting); 808 const resultsMap = /* @__PURE__ */ new Map(); 809 res.results.forEach((r) => resultsMap.set(r.relativePath, r)); 810 setStagedFiles((prev) => prev.map((f) => { 811 const matched = resultsMap.get(f.relativePath); 812 if (matched) { 813 return { 814 ...f, 815 status: matched.status, 816 message: matched.message, 817 id: matched.id 818 }; 819 } 820 return f; 821 })); 822 currentProcessed += chunk.length; 823 currentSuccess += res.stats.successful; 824 currentDuplicate += res.stats.duplicates; 825 currentFailed += res.stats.failed; 826 setProcessedCount(currentProcessed); 827 setSuccessCount(currentSuccess); 828 setDuplicateCount(currentDuplicate); 829 setFailedCount(currentFailed); 830 setUploadLogs((prev) => [...prev, `Processed chunk: ${res.stats.successful} saved, ${res.stats.duplicates} duplicates, ${res.stats.failed} failed.`]); 831 } catch (err) { 832 setUploadLogs((prev) => [...prev, `â Error processing chunk: ${err.message}`]); 833 currentProcessed += chunk.length; 834 currentFailed += chunk.length; 835 setProcessedCount(currentProcessed); 836 setFailedCount(currentFailed); 837 } 838 } 839 setIsImporting(false); 840 activeImportRef.current = false; 841 setUploadLogs((prev) => [...prev, `ð Queue import complete! Re-checking remaining staging list...`]); 842 loadStagingStats(); 843 }; 844 const startLakhImport = async () => { 845 const hashesToImport = Array.from(lakhSelectedHashes.size > 0 ? lakhSelectedHashes : new Set(lakhFiles.filter((f) => f.existsOnDisk && f.status === "pending").map((f) => f.hash))); 846 if (hashesToImport.length === 0) { 847 setUploadLogs((prev) => [...prev, `â No available Lakh files on disk selected for import.`]); 848 return; 849 } 850 setIsImporting(true); 851 setUploadLogs((prev) => [...prev, `â¶ Executing Lakh local import job (${hashesToImport.length} tracks)...`]); 852 setProcessedCount(0); 853 setSuccessCount(0); 854 setDuplicateCount(0); 855 setFailedCount(0); 856 let currentProcessed = 0; 857 let currentSuccess = 0; 858 let currentDuplicate = 0; 859 let currentFailed = 0; 860 try { 861 const itemsToImport = lakhFiles.filter((f) => hashesToImport.includes(f.hash)).map((f) => ({ 862 hash: f.hash, 863 title: f.title, 864 artist: f.artist, 865 genre: f.genre 866 })); 867 const res = await api.importLakhTracks(hashesToImport, itemsToImport, overwriteExisting); 868 const resultsMap = /* @__PURE__ */ new Map(); 869 res.results.forEach((r) => resultsMap.set(r.hash, r)); 870 setLakhFiles((prev) => prev.map((f) => { 871 const matched = resultsMap.get(f.hash); 872 if (matched) { 873 return { 874 ...f, 875 status: matched.status, 876 message: matched.message, 877 id: matched.id 878 }; 879 } 880 return f; 881 })); 882 currentProcessed = hashesToImport.length; 883 currentSuccess = res.stats.successful; 884 currentDuplicate = res.stats.duplicates; 885 currentFailed = res.stats.failed; 886 setProcessedCount(currentProcessed); 887 setSuccessCount(currentSuccess); 888 setDuplicateCount(currentDuplicate); 889 setFailedCount(currentFailed); 890 setUploadLogs((prev) => [...prev, `ð Lakh Import Job Completed: ${currentSuccess} imported, ${currentDuplicate} duplicates, ${currentFailed} failed.`]); 891 } catch (err) { 892 setUploadLogs((prev) => [...prev, `â Lakh Import Error: ${err.message}`]); 893 } finally { 894 setIsImporting(false); 895 loadLakhStatus(); 896 } 897 }; 898 const startAriaImport = async () => { 899 const hashesToImport = Array.from(ariaSelectedHashes.size > 0 ? ariaSelectedHashes : new Set(ariaFiles.filter((f) => f.existsOnDisk && f.status === "pending").map((f) => f.hash))); 900 if (hashesToImport.length === 0) { 901 setUploadLogs((prev) => [...prev, `â No available Aria files on disk selected for import.`]); 902 return; 903 } 904 setIsImporting(true); 905 setUploadLogs((prev) => [...prev, `â¶ Executing Aria local import job (${hashesToImport.length} tracks)...`]); 906 setProcessedCount(0); 907 setSuccessCount(0); 908 setDuplicateCount(0); 909 setFailedCount(0);
910 let currentProcessed = 0; 911 let currentSuccess = 0; 912 let currentDuplicate = 0; 913 let currentFailed = 0; 914 try { 915 const itemsToImport = ariaFiles.filter((f) => hashesToImport.includes(f.hash)).map((f) => ({ 916 hash: f.hash, 917 title: f.pieceName, 918 artist: f.composer, 919 genre: f.genre 920 })); 921 const res = await api.importAriaTracks(hashesToImport, itemsToImport, overwriteExisting); 922 const resultsMap = /* @__PURE__ */ new Map(); 923 res.results.forEach((r) => resultsMap.set(r.hash, r)); 924 setAriaFiles((prev) => prev.map((f) => { 925 const matched = resultsMap.get(f.hash); 926 if (matched) { 927 return { 928 ...f, 929 status: matched.status, 930 message: matched.message, 931 id: matched.id 932 }; 933 } 934 return f; 935 })); 936 currentProcessed = hashesToImport.length; 937 currentSuccess = res.stats.successful; 938 currentDuplicate = res.stats.duplicates; 939 currentFailed = res.stats.failed; 940 setProcessedCount(currentProcessed); 941 setSuccessCount(currentSuccess); 942 setDuplicateCount(currentDuplicate); 943 setFailedCount(currentFailed); 944 setUploadLogs((prev) => [...prev, `ð Aria Import Job Completed: ${currentSuccess} imported, ${currentDuplicate} duplicates, ${currentFailed} failed.`]); 945 } catch (err) { 946 setUploadLogs((prev) => [...prev, `â Aria Import Error: ${err.message}`]); 947 } finally { 948 setIsImporting(false); 949 loadAriaStatus(); 950 } 951 }; 952 const startPianocoreImport = async () => { 953 const hashesToImport = Array.from(pianocoreSelectedHashes.size > 0 ? pianocoreSelectedHashes : new Set(pianocoreFiles.filter((f) => f.existsOnDisk && f.status === "pending").map((f) => f.hash))); 954 if (hashesToImport.length === 0) { 955 setUploadLogs((prev) => [...prev, `â No available Pianocore files on disk selected for import.`]); 956 return; 957 } 958 setIsImporting(true); 959 setUploadLogs((prev) => [...prev, `â¶ Executing Pianocore local import job (${hashesToImport.length} tracks)...`]); 960 setProcessedCount(0); 961 setSuccessCount(0); 962 setDuplicateCount(0); 963 setFailedCount(0); 964 let currentProcessed = 0; 965 let currentSuccess = 0; 966 let currentDuplicate = 0; 967 let currentFailed = 0; 968 try { 969 const itemsToImport = pianocoreFiles.filter((f) => hashesToImport.includes(f.hash)).map((f) => ({ 970 hash: f.hash, 971 title: f.pieceName, 972 artist: f.composer 973 })); 974 const res = await api.importPianocoreTracks(hashesToImport, itemsToImport, overwriteExisting); 975 const resultsMap = /* @__PURE__ */ new Map(); 976 res.results.forEach((r) => resultsMap.set(r.hash, r)); 977 setPianocoreFiles((prev) => prev.map((f) => { 978 const matched = resultsMap.get(f.hash); 979 if (matched) { 980 return { 981 ...f, 982 status: matched.status, 983 message: matched.message, 984 id: matched.id 985 }; 986 } 987 return f; 988 })); 989 currentProcessed = hashesToImport.length; 990 currentSuccess = res.stats.successful; 991 currentDuplicate = res.stats.duplicates; 992 currentFailed = res.stats.failed; 993 setProcessedCount(currentProcessed); 994 setSuccessCount(currentSuccess); 995 setDuplicateCount(currentDuplicate); 996 setFailedCount(currentFailed); 997 setUploadLogs((prev) => [...prev, `ð Pianocore Import Job Completed: ${currentSuccess} imported, ${currentDuplicate} duplicates, ${currentFailed} failed.`]); 998 } catch (err) { 999 setUploadLogs((prev) => [...prev, `â Pianocore Import Error: ${err.message}`]); 1000 } finally { 1001 setIsImporting(false); 1002 loadPianocoreStatus(); 1003 } 1004 }; 1005 const stopImport = () => { 1006 setIsImporting(false); 1007 activeImportRef.current = false; 1008 setUploadLogs((prev) => [...prev, `â¹ Importer task stopped by user.`]); 1009 }; 1010 const handlePreviewStaging = async (file) => { 1011 if (previewingPath === file.relativePath && previewPlaying) { 1012 synth.stop(); 1013 setPreviewPlaying(false); 1014 setPreviewingPath(null); 1015 return; 1016 } 1017 synth.stop(); 1018 setPreviewPlaying(false); 1019 setPreviewingPath(file.relativePath); 1020 setPreviewLoading(true); 1021 setPreviewTitle(file.title || file.name); 1022 const previewUrl = `/api/admin/archive-import/staging/preview?path=${encodeURIComponent(file.relativePath)}`; 1023 try { 1024 const parsed = await parseMidiInstruments([previewUrl]); 1025 await synth.loadTracks( 1026 parsed.tracks, 1027 parsed.tickDiv, 1028 parsed.tempo, 1029 "free", 1030 "GeneralUser-GS", 1031 [previewUrl], 1032 () => { 1033 }, 1034 true, 1035 "GeneralUser-GS", 1036 true 1037 ); 1038 await synth.play(); 1039 setPreviewLoading(false); 1040 setPreviewPlaying(true); 1041 setUploadLogs((prev) => [...prev, `â¶ Playing preview: ${file.title || file.name}`]); 1042 } catch (err) { 1043 console.error("Preview failed:", err); 1044 setUploadLogs((prev) => [...prev, `â Preview error: ${err.message}`]); 1045 setPreviewLoading(false); 1046 setPreviewingPath(null); 1047 } 1048 }; 1049 const handlePreviewLakh = async (file) => { 1050 if (previewingPath === file.hash && previewPlaying) { 1051 synth.stop(); 1052 setPreviewPlaying(false); 1053 setPreviewingPath(null); 1054 return; 1055 } 1056 synth.stop(); 1057 setPreviewPlaying(false); 1058 setPreviewingPath(file.hash); 1059 setPreviewLoading(true); 1060 setPreviewTitle(file.title); 1061 const previewUrl = `/api/admin/archive-import/lakh/preview?hash=${file.hash}`; 1062 try { 1063 const parsed = await parseMidiInstruments([previewUrl]); 1064 await synth.loadTracks( 1065 parsed.tracks, 1066 parsed.tickDiv, 1067 parsed.tempo, 1068 "free", 1069 "GeneralUser-GS", 1070 [previewUrl], 1071 () => { 1072 }, 1073 true, 1074 "GeneralUser-GS", 1075 true 1076 ); 1077 await synth.play(); 1078 setPreviewLoading(false); 1079 setPreviewPlaying(true); 1080 setUploadLogs((prev) => [...prev, `â¶ Playing Lakh preview: ${file.title}`]); 1081 } catch (err) { 1082 console.error("Lakh preview failed:", err); 1083 setUploadLogs((prev) => [...prev, `â Lakh preview error: ${err.message}`]); 1084 setPreviewLoading(false); 1085 setPreviewingPath(null); 1086 } 1087 }; 1088 const stopPreview = () => { 1089 synth.stop(); 1090 setPreviewPlaying(false); 1091 setPreviewingPath(null); 1092 setPreviewLoading(false); 1093 }; 1094 const toggleSelectAll = () => { 1095 if (selectedPaths.size === stagedFiles.length) { 1096 setSelectedPaths(/* @__PURE__ */ new Set()); 1097 } else { 1098 setSelectedPaths(new Set(stagedFiles.map((f) => f.relativePath))); 1099 } 1100 }; 1101 const toggleSelectPath = (path) => { 1102 const next = new Set(selectedPaths);
1103 if (next.has(path)) { 1104 next.delete(path); 1105 } else { 1106 next.add(path); 1107 } 1108 setSelectedPaths(next); 1109 }; 1110 const toggleSelectAllLakh = () => { 1111 if (lakhSelectedHashes.size === lakhFiles.length) { 1112 setLakhSelectedHashes(/* @__PURE__ */ new Set()); 1113 } else { 1114 setLakhSelectedHashes(new Set(lakhFiles.map((f) => f.hash))); 1115 } 1116 }; 1117 const toggleSelectLakh = (hash) => { 1118 const next = new Set(lakhSelectedHashes); 1119 if (next.has(hash)) { 1120 next.delete(hash); 1121 } else { 1122 next.add(hash); 1123 } 1124 setLakhSelectedHashes(next); 1125 }; 1126 const [isResolvingMb, setIsResolvingMb] = reactExports.useState(false); 1127 const handleMbLookup = async (f) => { 1128 const startTime = Date.now(); 1129 try { 1130 setUploadLogs((prev) => [...prev, { text: `ð Looking up "${f.title}" via MusicBrainz...` }]); 1131 const response = await fetch("/api/admin/musicbrainz/test-lookup", { 1132 method: "POST", 1133 headers: { "Content-Type": "application/json" }, 1134 body: JSON.stringify({ title: f.title, artist: f.artist === "Unknown Artist" ? "" : f.artist }) 1135 }); 1136 const data = await response.json(); 1137 const duration = ((Date.now() - startTime) / 1e3).toFixed(2); 1138 if (data.success && data.candidates && data.candidates.length > 0) { 1139 const best = data.candidates[0]; 1140 const newArtist = best.artist_name; 1141 const newTitle = best.recording_title; 1142 const newGenre = best.genres && best.genres.length > 0 ? best.genres[0] : f.genre; 1143 setLakhFiles((prev) => prev.map((item) => item.hash === f.hash ? { 1144 ...item, 1145 artist: newArtist, 1146 title: newTitle, 1147 genre: newGenre 1148 } : item)); 1149 setUploadLogs((prev) => [...prev, { 1150 text: `â¡ Resolved metadata for "${f.title}" â "${newArtist} - ${newTitle}" via MusicBrainz (${duration}s)`, 1151 queries: data.sqlQueries 1152 }]); 1153 } else { 1154 setUploadLogs((prev) => [...prev, { 1155 text: `â ï¸ No matches found in MusicBrainz for "${f.title}" (${duration}s)`, 1156 queries: data.sqlQueries 1157 }]); 1158 } 1159 } catch (err) { 1160 const duration = ((Date.now() - startTime) / 1e3).toFixed(2); 1161 setUploadLogs((prev) => [...prev, { text: `â MusicBrainz lookup failed: ${err.message} (${duration}s)` }]); 1162 } 1163 }; 1164 const handleBatchMbLookup = async () => { 1165 const selected = lakhFiles.filter((f) => lakhSelectedHashes.has(f.hash)); 1166 if (selected.length === 0) return; 1167 setIsResolvingMb(true); 1168 setUploadLogs((prev) => [...prev, { text: `â¡ Starting batch lookup for ${selected.length} items via MusicBrainz...` }]); 1169 for (const f of selected) { 1170 const startTime = Date.now(); 1171 try { 1172 const response = await fetch("/api/admin/musicbrainz/test-lookup", { 1173 method: "POST", 1174 headers: { "Content-Type": "application/json" }, 1175 body: JSON.stringify({ title: f.title, artist: f.artist === "Unknown Artist" ? "" : f.artist }) 1176 }); 1177 const data = await response.json(); 1178 const duration = ((Date.now() - startTime) / 1e3).toFixed(2); 1179 if (data.success && data.candidates && data.candidates.length > 0) { 1180 const best = data.candidates[0]; 1181 const newArtist = best.artist_name; 1182 const newTitle = best.recording_title; 1183 const newGenre = best.genres && best.genres.length > 0 ? best.genres[0] : f.genre; 1184 setLakhFiles((prev) => prev.map((item) => item.hash === f.hash ? { 1185 ...item, 1186 artist: newArtist, 1187 title: newTitle, 1188 genre: newGenre 1189 } : item)); 1190 setUploadLogs((prev) => [...prev, { 1191 text: `â¡ Resolved "${f.title}" â "${newArtist} - ${newTitle}" (${duration}s)`, 1192 queries: data.sqlQueries 1193 }]); 1194 } else { 1195 setUploadLogs((prev) => [...prev, { 1196 text: `â ï¸ No matches found for "${f.title}" (${duration}s)`, 1197 queries: data.sqlQueries 1198 }]); 1199 } 1200 } catch (e) { 1201 const duration = ((Date.now() - startTime) / 1e3).toFixed(2); 1202 setUploadLogs((prev) => [...prev, { text: `â MusicBrainz lookup failed for "${f.title}": ${e.message} (${duration}s)` }]); 1203 } 1204 } 1205 setIsResolvingMb(false); 1206 setUploadLogs((prev) => [...prev, { text: `â Batch lookup complete.` }]); 1207 }; 1208 const handleClearStaging = async () => { 1209 if (!confirm("Are you sure you want to permanently clear the staging folder on the server? This will delete all staged MIDI files.")) return; 1210 try { 1211 await api.clearStaging(); 1212 setUploadLogs((prev) => [...prev, `â Server staging folder cleared.`]); 1213 loadStagingStats(); 1214 if (activeTab === "queue") { 1215 loadStagingFiles(); 1216 } 1217 } catch (err) { 1218 alert(err.message); 1219 } 1220 }; 1221 const isLakhRunning = lakhImportActive || isImportingAll || isImporting && activeTab === "lakh"; 1222 const isLakhCalculating = isLakhRunning && processedCount === 0; 1223 const lakhProcessedValue = isLakhRunning ? processedCount : lakhStatus.dbImportedCount || 0; 1224 const lakhImportedValue = isLakhRunning ? successCount : lakhStatus.dbImportedCount || 0; 1225 const isAriaRunning = ariaImportActive || isImportingAll || isImporting && activeTab === "aria"; 1226 const isAriaCalculating = isAriaRunning && processedCount === 0; 1227 const ariaProcessedValue = isAriaRunning ? processedCount : ariaStatus.dbImportedCount || 0; 1228 const ariaImportedValue = isAriaRunning ? successCount : ariaStatus.dbImportedCount || 0; 1229 const isPianocoreRunning = pianocoreImportActive || isImportingAll || isImporting && activeTab === "pianocore"; 1230 const isPianocoreCalculating = isPianocoreRunning && processedCount === 0; 1231 const pianocoreProcessedValue = isPianocoreRunning ? processedCount : pianocoreStatus.dbImportedCount || 0; 1232 const pianocoreImportedValue = isPianocoreRunning ? successCount : pianocoreStatus.dbImportedCount || 0; 1233 const isQueueCalculating = isImporting && activeTab === "queue" && processedCount === 0; 1234 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden bg-zinc-900 text-zinc-100 p-6 h-full", children: [
1235 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row justify-between items-start md:items-center border-b border-zinc-800 pb-4 mb-6", children: [ 1236 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1237 /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-2xl font-black text-white flex items-center gap-2", children: [ 1238 /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "text-indigo-500" }), 1239 " High-Speed Staging Importer Workspace" 1240 ] }), 1241 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-1", children: "Upload or manually drop entire archives on your server once, then work on correcting metadata and importing them in chunks!" }) 1242 ] }), 1243 totalStagedCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs( 1244 "button", 1245 { 1246 onClick: handleClearStaging, 1247 disabled: isImporting, 1248 className: "mt-4 md:mt-0 flex items-center gap-2 px-3 py-1.5 bg-zinc-800 hover:bg-red-950/40 text-zinc-400 hover:text-red-400 rounded-lg border border-zinc-700 hover:border-red-900 transition-all text-xs font-semibold disabled:opacity-50", 1249 children: [
1250 /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 }), 1251 " Clear Staging Directory" 1252 ] 1253 } 1254 ) 1255 ] }), 1256 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row md:items-center justify-between border-b border-zinc-850 gap-4 mb-6 pb-2", children: [ 1257 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-2", children: [ 1258 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1259 "button", 1260 { 1261 onClick: () => setActiveTab("queue"), 1262 className: `px-4 py-2 text-xs font-bold transition-all border-b-2 flex items-center gap-2 ${activeTab === "queue" ? "border-indigo-500 text-indigo-400 bg-indigo-500/5" : "border-transparent text-zinc-400 hover:text-zinc-200"}`, 1263 children: [ 1264 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 14 }), 1265 " Staging Queue Manager (", 1266 totalStagedCount, 1267 " staged)" 1268 ] 1269 } 1270 ),
1271 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1272 "button", 1273 { 1274 onClick: () => setActiveTab("upload"), 1275 className: `px-4 py-2 text-xs font-bold transition-all border-b-2 flex items-center gap-2 ${activeTab === "upload" ? "border-indigo-500 text-indigo-400 bg-indigo-500/5" : "border-transparent text-zinc-400 hover:text-zinc-200"}`, 1276 children: [ 1277 /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { size: 14 }), 1278 " Upload Files to Staging" 1279 ] 1280 } 1281 ), 1282 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1283 "button", 1284 { 1285 onClick: () => setActiveTab("lakh"), 1286 className: `px-4 py-2 text-xs font-bold transition-all border-b-2 flex items-center gap-2 ${activeTab === "lakh" ? "border-emerald-500 text-emerald-400 bg-emerald-500/5" : "border-transparent text-zinc-400 hover:text-zinc-200"}`, 1287 children: [ 1288 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "text-emerald-400 animate-pulse" }), 1289 " Lakh Dataset Explorer (", 1290 lakhStatus.totalFiles, 1291 " loaded)", 1292 lakhImportActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[9px] font-black bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 animate-pulse ml-1", children: [
1293 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400" }), 1294 "Importing" 1295 ] }) 1296 ] 1297 } 1298 ), 1299 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1300 "button", 1301 { 1302 onClick: () => setActiveTab("aria"), 1303 className: `px-4 py-2 text-xs font-bold transition-all border-b-2 flex items-center gap-2 ${activeTab === "aria" ? "border-emerald-500 text-emerald-400 bg-emerald-500/5" : "border-transparent text-zinc-400 hover:text-zinc-200"}`, 1304 children: [ 1305 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "text-emerald-400 animate-pulse" }), 1306 " Aria-MIDI Explorer (", 1307 ariaStatus.totalFiles, 1308 " loaded)", 1309 ariaImportActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[9px] font-black bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 animate-pulse ml-1", children: [
1310 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400" }), 1311 "Importing" 1312 ] }) 1313 ] 1314 } 1315 ), 1316 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1317 "button", 1318 { 1319 onClick: () => setActiveTab("pianocore"), 1320 className: `px-4 py-2 text-xs font-bold transition-all border-b-2 flex items-center gap-2 ${activeTab === "pianocore" ? "border-emerald-500 text-emerald-400 bg-emerald-500/5" : "border-transparent text-zinc-400 hover:text-zinc-200"}`, 1321 children: [ 1322 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "text-emerald-400 animate-pulse" }), 1323 " PianoCoRe Explorer (", 1324 pianocoreStatus.totalFiles, 1325 " loaded)", 1326 pianocoreImportActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[9px] font-black bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 animate-pulse ml-1", children: [
1327 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400" }), 1328 "Importing" 1329 ] }) 1330 ] 1331 } 1332 ) 1333 ] }), 1334 (lakhImportActive || ariaImportActive || pianocoreImportActive) && /* @__PURE__ */ jsxRuntimeExports.jsxs( 1335 "button", 1336 { 1337 onClick: handleEmergencyStopAll, 1338 className: "px-4 py-1.5 rounded-xl bg-red-500/15 hover:bg-red-600 text-red-400 hover:text-zinc-950 border border-red-500/30 text-xs font-black flex items-center gap-1.5 transition-all shadow-lg shadow-red-500/5 hover:scale-[1.02] active:scale-95 duration-200", 1339 children: [ 1340 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 1341 " EMERGENCY STOP ALL IMPORTS" 1342 ] 1343 } 1344 ) 1345 ] }), 1346 activeTab === "upload" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center border-2 border-dashed border-zinc-800 rounded-2xl bg-zinc-950/40 p-12 transition-all hover:border-indigo-500/40 group", children: [
1347 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-6 bg-indigo-500/10 rounded-full border border-indigo-500/20 mb-4 group-hover:scale-105 transition-transform duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { size: 48, className: "text-indigo-400 animate-pulse" }) }), 1348 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-white mb-1", children: "Upload files/folders to Server Staging" }), 1349 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-400 max-w-sm text-center mb-6 leading-relaxed", children: "Select a folder from your PC. It will upload files directly into the server's local import staging area once." }), 1350 /* @__PURE__ */ jsxRuntimeExports.jsx( 1351 "button", 1352 { 1353 onClick: () => { 1354 var _a; 1355 return (_a = folderInputRef.current) == null ? void 0 : _a.click(); 1356 }, 1357 disabled: isScanningLocal, 1358 className: "px-6 py-3 bg-indigo-600 hover:bg-indigo-500 text-white font-bold rounded-xl shadow-lg shadow-indigo-600/20 flex items-center gap-2 transition-all hover:-translate-y-0.5 active:translate-y-0", 1359 children: isScanningLocal ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1360 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 16 }), 1361 " Uploading chunks to staging..." 1362 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 1363 /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 16 }), 1364 " Select Local Folder to Upload" 1365 ] }) 1366 } 1367 ), 1368 /* @__PURE__ */ jsxRuntimeExports.jsx( 1369 "input", 1370 { 1371 ref: folderInputRef, 1372 type: "file", 1373 className: "hidden", 1374 onChange: handleUploadToStaging, 1375 ...{ webkitdirectory: "", directory: "", multiple: true } 1376 } 1377 ) 1378 ] }) : activeTab === "pianocore" ? ( 1379 /* Pianocore Dataset Explorer tab view */ 1380 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-hidden grid grid-cols-12 gap-6", children: [ 1381 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-8 flex flex-col overflow-y-auto overflow-x-hidden border border-zinc-800 bg-zinc-950/30 rounded-2xl", children: [
1382 /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handlePianocoreSearch, className: "p-4 border-b border-zinc-800 flex gap-4 items-stretch justify-between", children: [ 1383 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [ 1384 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-2.5 text-zinc-500", size: 16 }), 1385 /* @__PURE__ */ jsxRuntimeExports.jsx( 1386 "input", 1387 { 1388 value: pianocoreSearch, 1389 onChange: (e) => setPianocoreSearch(e.target.value), 1390 placeholder: "Search Pianocore dataset by Composer, Piece/Composition, Movement, or Performance ID...", 1391 className: "w-full pl-9 pr-4 py-2 bg-zinc-900 border border-zinc-800 rounded-lg text-sm text-white focus:border-emerald-500 outline-none" 1392 } 1393 ) 1394 ] }), 1395 /* @__PURE__ */ jsxRuntimeExports.jsx( 1396 "button", 1397 { 1398 type: "submit", 1399 className: "px-4 py-2 bg-zinc-850 hover:bg-zinc-800 text-xs font-bold rounded-lg border border-zinc-850 text-white", 1400 children: "Search" 1401 } 1402 ) 1403 ] }),
1404 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 bg-zinc-900/40 border-b border-zinc-800 text-xs flex justify-between items-center text-zinc-400", children: [ 1405 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1406 "Status: ", 1407 !pianocoreStatus.pianocorePath ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "Checking folder..." }) : pianocoreStatus.folderExists ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "ð¢ Folder Detected" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-400 font-bold", children: [ 1408 "ð´ Folder Missing at ", 1409 pianocoreStatus.pianocorePath 1410 ] }), 1411 pianocoreStatus.folderExists && !pianocoreStatus.metadataLoaded && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-orange-400 ml-2", children: "â ï¸ metadata.csv missing" }), 1412 pianocoreStatus.indexing && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-cyan-400 ml-2 animate-pulse", children: "ð Scan in progress..." }) 1413 ] }),
1414 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1415 "Total Files on Disk: ", 1416 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-200", children: pianocoreStatus.totalFiles }) 1417 ] }) 1418 ] }), 1419 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-x-auto", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-12 text-center text-zinc-500 flex flex-col items-center justify-center gap-2", children: [ 1420 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin text-emerald-400", size: 24 }), 1421 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Scanning cache files and checking duplicates..." }) 1422 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [ 1423 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-zinc-850 text-zinc-400 text-[10px] font-black uppercase bg-zinc-900/50", children: [
1424 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1425 "button", 1426 { 1427 onClick: () => { 1428 const allPending = pianocoreFiles.filter((f) => f.existsOnDisk && f.status === "pending"); 1429 if (pianocoreSelectedHashes.size === allPending.length && allPending.length > 0) { 1430 setPianocoreSelectedHashes(/* @__PURE__ */ new Set()); 1431 } else { 1432 setPianocoreSelectedHashes(new Set(allPending.map((f) => f.hash))); 1433 } 1434 }, 1435 className: "text-zinc-500 hover:text-emerald-400", 1436 children: pianocoreSelectedHashes.size > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) 1437 } 1438 ) }), 1439 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-12 text-center", children: "Preview" }),
1440 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Composer" }), 1441 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Composition / Piece" }), 1442 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Movement" }), 1443 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Dataset" }), 1444 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Transcription" }), 1445 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Performance ID" }), 1446 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-right", children: "Status" }) 1447 ] }) }), 1448 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-850", children: pianocoreFiles.length > 0 ? pianocoreFiles.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-900/30 text-xs transition-colors group", children: [
1449 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1450 "button", 1451 { 1452 onClick: () => { 1453 setPianocoreSelectedHashes((prev) => { 1454 const next = new Set(prev); 1455 if (next.has(f.hash)) next.delete(f.hash); 1456 else next.add(f.hash); 1457 return next; 1458 }); 1459 }, 1460 disabled: !f.existsOnDisk || f.status === "success" || f.status === "duplicate", 1461 className: "text-zinc-600 hover:text-emerald-400 disabled:opacity-20", 1462 children: pianocoreSelectedHashes.has(f.hash) ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) 1463 } 1464 ) }), 1465 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1466 "button", 1467 { 1468 onClick: async () => { 1469 if (previewingPath === f.hash && previewPlaying) { 1470 synth.stop(); 1471 setPreviewPlaying(false); 1472 setPreviewingPath(null); 1473 } else { 1474 setPreviewingPath(f.hash); 1475 setPreviewLoading(true); 1476 try { 1477 const url = `/api/admin/archive-import/pianocore/preview?hash=${f.hash}`; 1478 const res = await fetch(url, { credentials: "include" }); 1479 if (!res.ok) throw new Error("Preview failed"); 1480 const arrayBuffer = await res.arrayBuffer(); 1481 setPreviewLoading(false); 1482 setPreviewPlaying(true); 1483 setPreviewTitle(`${f.composer ? f.composer + " - " : ""}${f.pieceName}`); 1484 await synth.loadMidi(new Uint8Array(arrayBuffer)); 1485 synth.play((played) => { 1486 if (!played) { 1487 setPreviewPlaying(false); 1488 setPreviewingPath(null); 1489 } 1490 }); 1491 } catch (err) { 1492 setPreviewLoading(false); 1493 setPreviewingPath(null); 1494 } 1495 } 1496 }, 1497 disabled: previewLoading && previewingPath !== f.hash || !f.existsOnDisk, 1498 className: `p-1.5 rounded-lg transition-all border ${previewingPath === f.hash ? "bg-emerald-600 border-emerald-500 text-zinc-950" : "bg-zinc-900 border-zinc-800 hover:border-zinc-700 text-zinc-400 hover:text-zinc-200"} disabled:opacity-20`, 1499 title: previewingPath === f.hash && previewPlaying ? "Pause preview" : "Play preview", 1500 children: previewingPath === f.hash ? previewLoading ? /* @__P
1500URE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }) 1501 } 1502 ) }), 1503 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-semibold text-zinc-200", children: f.composer || "Unknown" }), 1504 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-300 max-w-[200px] truncate", title: f.pieceName, children: f.pieceName }), 1505 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-400", children: f.movement || "-" }), 1506 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-400", children: f.sourceDataset || "-" }), 1507 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-500", children: f.isTranscription ? "Yes" : "No" }), 1508 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-500", children: f.performanceId || "-" }),
1509 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-right", children: f.status === "success" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 rounded text-[10px] font-black uppercase", children: "â Ingested" }) : f.status === "duplicate" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-orange-500/20 text-orange-400 border border-orange-500/30 rounded text-[10px] font-black uppercase", title: `Matched DB ID #${f.id}`, children: "â ï¸ Duplicate" }) : f.existsOnDisk ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded text-[10px] font-black uppercase", children: "ð¢ Found" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-zinc-800 text-zinc-500 rounded text-[10px] font-black uppercase", children: "⪠Missing" }) }) 1510 ] }, f.hash)) : /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 9, className: "p-8 text-center text-zinc-500 text-xs italic", children: "No Pianocore files found matching search. Type a search above or verify the files are on disk." }) }) }) 1511 ] }) }),
1512 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-800 flex justify-between items-center bg-zinc-950/40", children: [ 1513 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-500", children: [ 1514 "Showing page ", 1515 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: pianocorePage }), 1516 " of ", 1517 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: Math.ceil(pianocoreTotalCount / pianocorePageSize) || 1 }), 1518 " (", 1519 pianocoreTotalCount, 1520 " total indexed songs)" 1521 ] }), 1522 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [ 1523 /* @__PURE__ */ jsxRuntimeExports.jsx( 1524 "button", 1525 { 1526 onClick: () => setPianocorePage((prev) => Math.max(1, prev - 1)), 1527 disabled: pianocorePage === 1, 1528 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 1529 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { size: 14 }) 1530 } 1531 ),
1532 /* @__PURE__ */ jsxRuntimeExports.jsx( 1533 "input", 1534 { 1535 type: "text", 1536 value: pianocorePageInput, 1537 onChange: (e) => setPianocorePageInput(e.target.value), 1538 onKeyDown: (e) => { 1539 if (e.key === "Enter") { 1540 const pageNum = parseInt(pianocorePageInput); 1541 const maxPage = Math.ceil(pianocoreTotalCount / pianocorePageSize) || 1; 1542 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 1543 setPianocorePage(pageNum); 1544 } else { 1545 setPianocorePageInput(pianocorePage.toString()); 1546 } 1547 } 1548 }, 1549 onBlur: () => { 1550 const pageNum = parseInt(pianocorePageInput); 1551 const maxPage = Math.ceil(pianocoreTotalCount / pianocorePageSize) || 1; 1552 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 1553 setPianocorePage(pageNum); 1554 } else { 1555 setPianocorePageInput(pianocorePage.toString()); 1556 } 1557 }, 1558 className: "w-12 py-1 bg-zinc-900 border border-zinc-800 text-xs text-white rounded font-bold text-center focus:border-emerald-500 outline-none" 1559 } 1560 ),
1561 /* @__PURE__ */ jsxRuntimeExports.jsx( 1562 "button", 1563 { 1564 onClick: () => setPianocorePage((prev) => prev + 1), 1565 disabled: pianocoreFiles.length < pianocorePageSize, 1566 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 1567 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14 }) 1568 } 1569 ) 1570 ] }) 1571 ] }) 1572 ] }), 1573 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-4 flex flex-col gap-6 overflow-y-auto pr-1 h-full max-h-full scrollbar-thin", children: [ 1574 renderStagingNotice(), 1575 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 1576 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center gap-2", children: [
1577 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: "text-emerald-400" }), 1578 " Import Controls" 1579 ] }), 1580 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-xs text-zinc-400 cursor-pointer hover:text-zinc-300 select-none", children: [ 1581 /* @__PURE__ */ jsxRuntimeExports.jsx( 1582 "input", 1583 { 1584 type: "checkbox", 1585 checked: overwriteExisting, 1586 onChange: (e) => setOverwriteExisting(e.target.checked), 1587 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-4 h-4" 1588 } 1589 ), 1590 "Overwrite duplicates / re-queue in database" 1591 ] }), 1592 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 mt-2", children: [ 1593 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1594 "button", 1595 { 1596 onClick: startPianocoreImport, 1597 disabled: isImporting || pianocoreImportActive || pianocoreFiles.length === 0, 1598 className: "w-full py-3 bg-emerald-600 hover:bg-emerald-500 text-zinc-950 font-bold text-xs rounded-xl shadow-lg shadow-emerald-500/10 flex items-center justify-center gap-2 transition-all hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-30 disabled:pointer-events-none", 1599 children: [
1600 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }), 1601 " Import Selected (", 1602 pianocoreSelectedHashes.size > 0 ? pianocoreSelectedHashes.size : pianocoreFiles.filter((f) => f.existsOnDisk && f.status === "pending").length, 1603 " tracks)" 1604 ] 1605 } 1606 ), 1607 !isImporting && !pianocoreImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 1608 "button", 1609 { 1610 onClick: handleStartPianocoreImportAll, 1611 disabled: !pianocoreStatus.folderExists, 1612 title: !pianocoreStatus.folderExists ? "Dataset folder must exist on server to start background full import" : "Start background process to import all tracks", 1613 className: "w-full py-3 bg-zinc-900 hover:bg-zinc-850 text-white font-bold text-xs rounded-xl border border-zinc-800 flex items-center justify-center gap-2 transition-all hover:border-zinc-700 disabled:opacity-35", 1614 children: [
1615 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 14, className: "text-emerald-400" }), 1616 " Start Background Full Import" 1617 ] 1618 } 1619 ) : pianocoreImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 1620 "button", 1621 { 1622 onClick: handleStopPianocoreImportAll, 1623 className: "w-full py-3 bg-red-950/20 hover:bg-red-950/30 text-red-400 border border-red-900/30 font-bold text-xs rounded-xl flex items-center justify-center gap-2 transition-all animate-pulse", 1624 children: [ 1625 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 1626 " Stop Background Import" 1627 ] 1628 } 1629 ) : /* @__PURE__ */ jsxRuntimeExports.jsxs( 1630 "button", 1631 { 1632 onClick: stopImport, 1633 className: "w-full py-3 bg-red-950/20 hover:bg-red-950/30 text-red-400 border border-red-900/30 font-bold text-xs rounded-xl flex items-center justify-center gap-2 transition-all", 1634 children: [
1635 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 1636 " Abort Local Import Job" 1637 ] 1638 } 1639 ) 1640 ] }) 1641 ] }), 1642 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 1643 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center justify-between w-full", children: [ 1644 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 1645 /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 16, className: "text-indigo-400" }), 1646 " Telemetry Dashboard" 1647 ] }), 1648 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] uppercase font-black px-2 py-0.5 rounded border tracking-wider ${isPianocoreRunning ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20 animate-pulse" : pianocoreStatus.dbImportedCount > 0 ? "bg-zinc-900 text-zinc-400 border-zinc-800" : "bg-zinc-950 text-zinc-600 border-transparent"}`, children: isPianocoreRunning ? "Running" : pianocoreStatus.dbImportedCount > 0 ? "Active" : "Idle" }) 1649 ] }), 1650 importAllError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-xs flex gap-2", children: [
1651 /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 14, className: "shrink-0 mt-0.5" }), 1652 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: importAllError }) 1653 ] }), 1654 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3 mt-1", children: [ 1655 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 1656 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Pianocore Disk Files" }), 1657 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-lg font-black font-mono text-white", children: pianocoreStatus.totalFiles }) 1658 ] }), 1659 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 1660 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Processed" }),
1661 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-white ${isPianocoreCalculating ? "text-xs font-sans text-zinc-400 animate-pulse" : "text-lg font-mono"}`, children: isPianocoreCalculating ? "Calculating..." : pianocoreProcessedValue }) 1662 ] }), 1663 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 1664 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "MB Matched" }), 1665 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isPianocoreCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isPianocoreCalculating ? "Calculating..." : mbMatchedCount }) 1666 ] }), 1667 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [
1668 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "MB No Match" }), 1669 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-zinc-400 ${isPianocoreCalculating ? "text-xs font-sans text-zinc-400/60 animate-pulse" : "text-lg font-mono"}`, children: isPianocoreCalculating ? "Calculating..." : mbNoMatchCount }) 1670 ] }), 1671 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-emerald-500/10 bg-emerald-500/5 col-span-2 flex justify-between items-center", children: [ 1672 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1673 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "Imported to Library" }), 1674 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isPianocoreCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isPianocoreCalculating ? "Calculating..." : pianocoreImportedValue }) 1675 ] }), 1676 duplicateCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [
1677 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-orange-500/70 font-bold uppercase mb-0.5", children: "Duplicates" }), 1678 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black font-mono text-orange-400", children: duplicateCount }) 1679 ] }) 1680 ] }) 1681 ] }) 1682 ] }), 1683 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 border border-zinc-800 bg-zinc-950 p-4 rounded-2xl flex flex-col overflow-hidden min-h-[540px]", children: [ 1684 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center border-b border-zinc-850 pb-2 mb-3", children: [ 1685 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 1686 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-black text-white flex items-center gap-1.5 uppercase tracking-wider", children: [ 1687 /* @__P
1687URE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-zinc-500" }), 1688 " Log Monitor" 1689 ] }), 1690 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-[10px] font-bold text-zinc-400 cursor-pointer hover:text-white select-none", children: [ 1691 /* @__PURE__ */ jsxRuntimeExports.jsx( 1692 "input", 1693 { 1694 type: "checkbox", 1695 checked: showQueries, 1696 onChange: (e) => setShowQueries(e.target.checked), 1697 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-3 h-3" 1698 } 1699 ), 1700 "Show Queries" 1701 ] }) 1702 ] }), 1703 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }) 1704 ] }), 1705 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto font-mono text-[10px] text-zinc-400 flex flex-col gap-1.5 pr-2", children: [ 1706 uploadLogs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-600 italic", children: "Logs active. Waiting for import action..." }) : uploadLogs.map((log, i) => { 1707 const isObject = typeof log === "object" && log !== null; 1708 const text = isObject ? log.text : log; 1709 const queries = isObject ? log.queries : void 0; 1710 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "leading-relaxed whitespace-pre-wrap flex flex-col gap-1", children: [
1711 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: text }), 1712 showQueries && queries && queries.map((q, qi) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 bg-zinc-900/50 p-2 rounded border border-zinc-850/50 pl-3 font-mono text-[9px] mt-1", children: [ 1713 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-emerald-500/80 font-bold uppercase mb-1", children: [ 1714 "SQL Query: ", 1715 q.durationMs !== void 0 ? "(" + q.durationMs + "ms)" : "" 1716 ] }), 1717 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-400 select-all", children: q.query.trim() }), 1718 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 mt-1", children: [ 1719 "Params: ", 1720 JSON.stringify(q.params) 1721 ] }) 1722 ] }, qi)) 1723 ] }, i); 1724 }),
1725 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logsEndRef }) 1726 ] }) 1727 ] }) 1728 ] }) 1729 ] }) 1730 ) : activeTab === "aria" ? ( 1731 /* Aria Dataset Explorer tab view */ 1732 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-hidden grid grid-cols-12 gap-6", children: [ 1733 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-8 flex flex-col overflow-y-auto overflow-x-hidden border border-zinc-800 bg-zinc-950/30 rounded-2xl", children: [ 1734 /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleAriaSearch, className: "p-4 border-b border-zinc-800 flex gap-4 items-stretch justify-between", children: [ 1735 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [ 1736 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-2.5 text-zinc-500", size: 16 }), 1737 /* @__PURE__ */ jsxRuntimeExports.jsx( 1738 "input", 1739 { 1740 value: ariaSearch, 1741 onChange: (e) => setAriaSearch(e.target.value), 1742 placeholder: "Search Aria dataset by Composer, Performer, Title, or Genre...", 1743 className: "w-full pl-9 pr-4 py-2 bg-zinc-900 border border-zinc-800 rounded-lg text-sm text-white focus:border-emerald-500 outline-none" 1744 } 1745 ) 1746 ] }),
1747 /* @__PURE__ */ jsxRuntimeExports.jsx( 1748 "button", 1749 { 1750 type: "submit", 1751 className: "px-4 py-2 bg-zinc-850 hover:bg-zinc-800 text-xs font-bold rounded-lg border border-zinc-850 text-white", 1752 children: "Search" 1753 } 1754 ) 1755 ] }), 1756 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 bg-zinc-900/40 border-b border-zinc-800 text-xs flex justify-between items-center text-zinc-400", children: [ 1757 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1758 "Status: ", 1759 !ariaStatus.ariaPath ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "Checking folder..." }) : ariaStatus.folderExists ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-bold", children: "ð¢ Folder Detected" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-400 font-bold", children: [ 1760 "ð´ Folder Missing at ", 1761 ariaStatus.ariaPath 1762 ] }), 1763 ariaStatus.folderExists && !ariaStatus.metadataLoaded && /* @__P
1763URE__ */ jsxRuntimeExports.jsx("span", { className: "text-orange-400 ml-2", children: "â ï¸ metadata.json missing" }), 1764 ariaStatus.indexing && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-cyan-400 ml-2 animate-pulse", children: "ð Scan in progress..." }) 1765 ] }), 1766 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1767 "Total Files on Disk: ", 1768 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-200", children: ariaStatus.totalFiles }) 1769 ] }) 1770 ] }), 1771 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-x-auto", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-12 text-center text-zinc-500 flex flex-col items-center justify-center gap-2", children: [ 1772 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin text-emerald-400", size: 24 }), 1773 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Scanning cache files and checking duplicates..." }) 1774 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [
1775 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-zinc-850 text-zinc-400 text-[10px] font-black uppercase bg-zinc-900/50", children: [ 1776 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1777 "button", 1778 { 1779 onClick: () => { 1780 const allPending = ariaFiles.filter((f) => f.existsOnDisk && f.status === "pending"); 1781 if (ariaSelectedHashes.size === allPending.length && allPending.length > 0) { 1782 setAriaSelectedHashes(/* @__PURE__ */ new Set()); 1783 } else { 1784 setAriaSelectedHashes(new Set(allPending.map((f) => f.hash))); 1785 } 1786 }, 1787 className: "text-zinc-500 hover:text-emerald-400", 1788 children: ariaSelectedHashes.size > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) 1789 } 1790 ) }),
1791 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-12 text-center", children: "Preview" }), 1792 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Composer / Artist" }), 1793 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Piece Name" }), 1794 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Performer" }), 1795 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Genre" }), 1796 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Year" }), 1797 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-right", children: "Status" }) 1798 ] }) }), 1799 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-850", children: ariaFiles.length > 0 ? ariaFiles.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-900/30 text-xs transition-colors group", children: [
1800 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1801 "button", 1802 { 1803 onClick: () => { 1804 setAriaSelectedHashes((prev) => { 1805 const next = new Set(prev); 1806 if (next.has(f.hash)) next.delete(f.hash); 1807 else next.add(f.hash); 1808 return next; 1809 }); 1810 }, 1811 disabled: !f.existsOnDisk || f.status === "success" || f.status === "duplicate", 1812 className: "text-zinc-600 hover:text-emerald-400 disabled:opacity-20", 1813 children: ariaSelectedHashes.has(f.hash) ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) 1814 } 1815 ) }), 1816 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1817 "button", 1818 { 1819 onClick: async () => { 1820 if (previewingPath === f.hash && previewPlaying) { 1821 synth.stop(); 1822 setPreviewPlaying(false); 1823 setPreviewingPath(null); 1824 } else { 1825 setPreviewingPath(f.hash); 1826 setPreviewLoading(true); 1827 try { 1828 const url = `/api/admin/archive-import/aria/preview?hash=${f.hash}`; 1829 const res = await fetch(url, { credentials: "include" }); 1830 if (!res.ok) throw new Error("Preview failed"); 1831 const arrayBuffer = await res.arrayBuffer(); 1832 setPreviewLoading(false); 1833 setPreviewPlaying(true); 1834 setPreviewTitle(`${f.composer ? f.composer + " - " : ""}${f.pieceName}`); 1835 await synth.loadMidi(new Uint8Array(arrayBuffer)); 1836 synth.play((played) => { 1837 if (!played) { 1838 setPreviewPlaying(false); 1839 setPreviewingPath(null); 1840 } 1841 }); 1842 } catch (err) { 1843 setPreviewLoading(false); 1844 setPreviewingPath(null); 1845 } 1846 } 1847 }, 1848 disabled: previewLoading && previewingPath !== f.hash || !f.existsOnDisk, 1849 className: `p-1.5 rounded-lg transition-all border ${previewingPath === f.hash ? "bg-emerald-600 border-emerald-500 text-zinc-950" : "bg-zinc-900 border-zinc-800 hover:border-zinc-700 text-zinc-400 hover:text-zinc-200"} disabled:opacity-20`, 1850 title: previewingPath === f.hash && previewPlaying ? "Pause preview" : "Play preview", 1851 children: previewingPath === f.hash ? previewLoading ? /* @__P
1851URE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }) 1852 } 1853 ) }), 1854 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-semibold text-zinc-200", children: f.composer || "Unknown" }), 1855 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-300 max-w-[200px] truncate", title: f.pieceName, children: f.pieceName }), 1856 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-400", children: f.performer || "-" }), 1857 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-400", children: f.genre || "-" }), 1858 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-zinc-500", children: f.year || "-" }), 1859 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-right", children: f.status === "success" ? /* @__P
1859URE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 rounded text-[10px] font-black uppercase", children: "â Ingested" }) : f.status === "duplicate" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-orange-500/20 text-orange-400 border border-orange-500/30 rounded text-[10px] font-black uppercase", title: `Matched DB ID #${f.id}`, children: "â ï¸ Duplicate" }) : f.existsOnDisk ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded text-[10px] font-black uppercase", children: "ð¢ Found" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-zinc-800 text-zinc-500 rounded text-[10px] font-black uppercase", children: "⪠Missing" }) }) 1860 ] }, f.hash)) : /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 8, className: "p-8 text-center text-zinc-500 text-xs italic", children: "No Aria files found matching search. Type a search above or verify the files are on disk." }) }) }) 1861 ] }) }),
1862 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-800 flex justify-between items-center bg-zinc-950/40", children: [ 1863 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-500", children: [ 1864 "Showing page ", 1865 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: ariaPage }), 1866 " of ", 1867 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: Math.ceil(ariaTotalCount / ariaPageSize) || 1 }), 1868 " (", 1869 ariaTotalCount, 1870 " total indexed songs)" 1871 ] }), 1872 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [ 1873 /* @__PURE__ */ jsxRuntimeExports.jsx( 1874 "button", 1875 { 1876 onClick: () => setAriaPage((prev) => Math.max(1, prev - 1)), 1877 disabled: ariaPage === 1, 1878 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 1879 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { size: 14 }) 1880 } 1881 ),
1882 /* @__PURE__ */ jsxRuntimeExports.jsx( 1883 "input", 1884 { 1885 type: "text", 1886 value: ariaPageInput, 1887 onChange: (e) => setAriaPageInput(e.target.value), 1888 onKeyDown: (e) => { 1889 if (e.key === "Enter") { 1890 const pageNum = parseInt(ariaPageInput); 1891 const maxPage = Math.ceil(ariaTotalCount / ariaPageSize) || 1; 1892 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 1893 setAriaPage(pageNum); 1894 } else { 1895 setAriaPageInput(ariaPage.toString()); 1896 } 1897 } 1898 }, 1899 onBlur: () => { 1900 const pageNum = parseInt(ariaPageInput); 1901 const maxPage = Math.ceil(ariaTotalCount / ariaPageSize) || 1; 1902 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 1903 setAriaPage(pageNum); 1904 } else { 1905 setAriaPageInput(ariaPage.toString()); 1906 } 1907 }, 1908 className: "w-12 py-1 bg-zinc-900 border border-zinc-800 text-xs text-white rounded font-bold text-center focus:border-emerald-500 outline-none" 1909 } 1910 ),
1911 /* @__PURE__ */ jsxRuntimeExports.jsx( 1912 "button", 1913 { 1914 onClick: () => setAriaPage((prev) => prev + 1), 1915 disabled: ariaFiles.length < ariaPageSize, 1916 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 1917 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14 }) 1918 } 1919 ) 1920 ] }) 1921 ] }) 1922 ] }), 1923 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-4 flex flex-col gap-6 overflow-y-auto pr-1 h-full max-h-full scrollbar-thin", children: [ 1924 renderStagingNotice(), 1925 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 1926 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center gap-2", children: [
1927 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: "text-emerald-400" }), 1928 " Import Controls" 1929 ] }), 1930 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-xs text-zinc-400 cursor-pointer hover:text-zinc-300 select-none", children: [ 1931 /* @__PURE__ */ jsxRuntimeExports.jsx( 1932 "input", 1933 { 1934 type: "checkbox", 1935 checked: overwriteExisting, 1936 onChange: (e) => setOverwriteExisting(e.target.checked), 1937 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-4 h-4" 1938 } 1939 ), 1940 "Overwrite duplicates / re-queue in database" 1941 ] }), 1942 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 mt-2", children: [ 1943 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1944 "button", 1945 { 1946 onClick: startAriaImport, 1947 disabled: isImporting || ariaImportActive || ariaFiles.length === 0, 1948 className: "w-full py-3 bg-emerald-600 hover:bg-emerald-500 text-zinc-950 font-bold text-xs rounded-xl shadow-lg shadow-emerald-500/10 flex items-center justify-center gap-2 transition-all hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-30 disabled:pointer-events-none", 1949 children: [
1950 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }), 1951 " Import Selected (", 1952 ariaSelectedHashes.size > 0 ? ariaSelectedHashes.size : ariaFiles.filter((f) => f.existsOnDisk && f.status === "pending").length, 1953 " tracks)" 1954 ] 1955 } 1956 ), 1957 !isImporting && !ariaImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 1958 "button", 1959 { 1960 onClick: handleStartAriaImportAll, 1961 disabled: !ariaStatus.folderExists, 1962 title: !ariaStatus.folderExists ? "Dataset folder must exist on server to start background full import" : "Start background process to import all tracks", 1963 className: "w-full py-3 bg-zinc-900 hover:bg-zinc-850 text-white font-bold text-xs rounded-xl border border-zinc-800 flex items-center justify-center gap-2 transition-all hover:border-zinc-700 disabled:opacity-35", 1964 children: [
1965 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 14, className: "text-emerald-400" }), 1966 " Start Background Full Import" 1967 ] 1968 } 1969 ) : ariaImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 1970 "button", 1971 { 1972 onClick: handleStopAriaImportAll, 1973 className: "w-full py-3 bg-red-950/20 hover:bg-red-950/30 text-red-400 border border-red-900/30 font-bold text-xs rounded-xl flex items-center justify-center gap-2 transition-all animate-pulse", 1974 children: [ 1975 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 1976 " Stop Background Import" 1977 ] 1978 } 1979 ) : /* @__PURE__ */ jsxRuntimeExports.jsxs( 1980 "button", 1981 { 1982 onClick: stopImport, 1983 className: "w-full py-3 bg-red-950/20 hover:bg-red-950/30 text-red-400 border border-red-900/30 font-bold text-xs rounded-xl flex items-center justify-center gap-2 transition-all", 1984 children: [
1985 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 1986 " Abort Local Import Job" 1987 ] 1988 } 1989 ) 1990 ] }) 1991 ] }), 1992 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 1993 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center justify-between w-full", children: [ 1994 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 1995 /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 16, className: "text-indigo-400" }), 1996 " Telemetry Dashboard" 1997 ] }), 1998 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] uppercase font-black px-2 py-0.5 rounded border tracking-wider ${isAriaRunning ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20 animate-pulse" : ariaStatus.dbImportedCount > 0 ? "bg-zinc-900 text-zinc-400 border-zinc-800" : "bg-zinc-950 text-zinc-600 border-transparent"}`, children: isAriaRunning ? "Running" : ariaStatus.dbImportedCount > 0 ? "Active" : "Idle" }) 1999 ] }), 2000 importAllError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-xs flex gap-2", children: [
2001 /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 14, className: "shrink-0 mt-0.5" }), 2002 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: importAllError }) 2003 ] }), 2004 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3 mt-1", children: [ 2005 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2006 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Aria Disk Files" }), 2007 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-lg font-black font-mono text-white", children: ariaStatus.totalFiles }) 2008 ] }), 2009 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2010 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Processed" }),
2011 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-white ${isAriaCalculating ? "text-xs font-sans text-zinc-400 animate-pulse" : "text-lg font-mono"}`, children: isAriaCalculating ? "Calculating..." : ariaProcessedValue }) 2012 ] }), 2013 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2014 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "MB Matched" }), 2015 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isAriaCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isAriaCalculating ? "Calculating..." : mbMatchedCount }) 2016 ] }), 2017 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2018 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "MB No Match" }),
2019 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-zinc-400 ${isAriaCalculating ? "text-xs font-sans text-zinc-400/60 animate-pulse" : "text-lg font-mono"}`, children: isAriaCalculating ? "Calculating..." : mbNoMatchCount }) 2020 ] }), 2021 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-emerald-500/10 bg-emerald-500/5 col-span-2 flex justify-between items-center", children: [ 2022 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2023 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "Imported to Library" }), 2024 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isAriaCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isAriaCalculating ? "Calculating..." : ariaImportedValue }) 2025 ] }), 2026 duplicateCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [
2027 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-orange-500/70 font-bold uppercase mb-0.5", children: "Duplicates" }), 2028 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black font-mono text-orange-400", children: duplicateCount }) 2029 ] }) 2030 ] }) 2031 ] }) 2032 ] }), 2033 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 border border-zinc-800 bg-zinc-950 p-4 rounded-2xl flex flex-col overflow-hidden min-h-[540px]", children: [ 2034 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center border-b border-zinc-850 pb-2 mb-3", children: [ 2035 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 2036 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-black text-white flex items-center gap-1.5 uppercase tracking-wider", children: [ 2037 /* @__P
2037URE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-zinc-500" }), 2038 " Log Monitor" 2039 ] }), 2040 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-[10px] font-bold text-zinc-400 cursor-pointer hover:text-white select-none", children: [ 2041 /* @__PURE__ */ jsxRuntimeExports.jsx( 2042 "input", 2043 { 2044 type: "checkbox", 2045 checked: showQueries, 2046 onChange: (e) => setShowQueries(e.target.checked), 2047 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-3 h-3" 2048 } 2049 ), 2050 "Show Queries" 2051 ] }) 2052 ] }), 2053 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }) 2054 ] }), 2055 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto font-mono text-[10px] text-zinc-400 flex flex-col gap-1.5 pr-2", children: [ 2056 uploadLogs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-600 italic", children: "Logs active. Waiting for import action..." }) : uploadLogs.map((log, i) => { 2057 const isObject = typeof log === "object" && log !== null; 2058 const text = isObject ? log.text : log; 2059 const queries = isObject ? log.queries : void 0; 2060 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "leading-relaxed whitespace-pre-wrap flex flex-col gap-1", children: [
2061 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: text }), 2062 showQueries && queries && queries.map((q, qi) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 bg-zinc-900/50 p-2 rounded border border-zinc-850/50 pl-3 font-mono text-[9px] mt-1", children: [ 2063 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-emerald-500/80 font-bold uppercase mb-1", children: [ 2064 "SQL Query: ", 2065 q.durationMs !== void 0 ? "(" + q.durationMs + "ms)" : "" 2066 ] }), 2067 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-400 select-all", children: q.query.trim() }), 2068 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 mt-1", children: [ 2069 "Params: ", 2070 JSON.stringify(q.params) 2071 ] }) 2072 ] }, qi)) 2073 ] }, i); 2074 }),
2075 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logsEndRef }) 2076 ] }) 2077 ] }) 2078 ] }) 2079 ] }) 2080 ) : activeTab === "lakh" ? ( 2081 /* Lakh Dataset Explorer tab view */ 2082 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-hidden grid grid-cols-12 gap-6", children: [ 2083 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-8 flex flex-col overflow-y-auto overflow-x-hidden border border-zinc-800 bg-zinc-950/30 rounded-2xl", children: [ 2084 /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleLakhSearch, className: "p-4 border-b border-zinc-800 flex gap-4 items-stretch justify-between", children: [ 2085 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [ 2086 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-2.5 text-zinc-500", size: 16 }), 2087 /* @__PURE__ */ jsxRuntimeExports.jsx( 2088 "input", 2089 { 2090 value: lakhSearch, 2091 onChange: (e) => setLakhSearch(e.target.value), 2092 placeholder: "Search Lakh dataset mapping seeds by Artist, Song Title, or Genre...", 2093 className: "w-full pl-9 pr-4 py-2 bg-zinc-900 border border-zinc-800 rounded-lg text-sm text-white focus:border-emerald-500 outline-none" 2094 } 2095 ) 2096 ] }),
2097 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 shrink-0", children: [ 2098 /* @__PURE__ */ jsxRuntimeExports.jsx( 2099 "button", 2100 { 2101 type: "submit", 2102 className: "px-4 py-2 bg-zinc-800 hover:bg-zinc-750 text-xs font-bold rounded-lg border border-zinc-700 text-white", 2103 children: "Search" 2104 } 2105 ), 2106 lakhSelectedHashes.size > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs( 2107 "button", 2108 { 2109 type: "button", 2110 onClick: handleBatchMbLookup, 2111 disabled: isResolvingMb, 2112 className: "px-4 py-2 bg-emerald-600 hover:bg-emerald-500 text-zinc-950 text-xs font-black rounded-lg flex items-center gap-1.5 shadow-md shadow-emerald-600/10 transition-all", 2113 children: [ 2114 isResolvingMb ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(WandSparkles, { size: 12 }), 2115 "Auto-Fill Selected (", 2116 lakhSelectedHashes.size, 2117 ")" 2118 ] 2119 } 2120 ) 2121 ] }) 2122 ] }),
2123 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-[450px] resize-y overflow-auto border-b border-zinc-800", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center p-12 text-zinc-500 italic", children: [ 2124 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin mb-2 text-emerald-500", size: 24 }), 2125 "Querying Lakh database indexes..." 2126 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-[1000px] w-full text-left border-collapse", children: [ 2127 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-zinc-800 bg-zinc-950/40 text-zinc-400 text-[10px] font-black uppercase", children: [ 2128 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-8", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: toggleSelectAllLakh, className: "text-zinc-500 hover:text-emerald-400", children: lakhSelectedHashes.size === lakhFiles.length && lakhFiles.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) }) }),
2129 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-12 text-center", children: "Preview" }), 2130 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "MD5 Hash Fingerprint" }), 2131 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Artist (Override)" }), 2132 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Title (Override)" }), 2133 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Genre" }), 2134 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Release Year" }), 2135 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-right", children: "Disk Match" }) 2136 ] }) }), 2137 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-850", children: lakhFiles.length > 0 ? lakhFiles.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-900/30 text-xs transition-colors group", children: [
2138 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2139 "button", 2140 { 2141 onClick: () => toggleSelectLakh(f.hash), 2142 disabled: !f.existsOnDisk, 2143 className: "text-zinc-600 hover:text-emerald-400 disabled:opacity-20", 2144 children: lakhSelectedHashes.has(f.hash) ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) 2145 } 2146 ) }), 2147 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2148 "button", 2149 { 2150 onClick: () => handlePreviewLakh(f), 2151 disabled: previewLoading && previewingPath !== f.hash || !f.existsOnDisk, 2152 className: `p-1.5 rounded-lg transition-all border ${previewingPath === f.hash ? "bg-emerald-600 border-emerald-500 text-zinc-950" : "bg-zinc-900 border-zinc-800 hover:border-zinc-700 text-zinc-400 hover:text-zinc-200"} disabled:opacity-20`, 2153 title: previewingPath === f.hash && previewPlaying ? "Pause preview" : "Play preview", 2154 children: previewingPath === f.hash ? previewLoading ? /* @__P
2154URE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }) 2155 } 2156 ) }), 2157 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-mono text-zinc-500 max-w-[140px] truncate", title: f.hash, children: f.hash }), 2158 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2159 "input", 2160 { 2161 type: "text", 2162 value: f.artist, 2163 onChange: (e) => { 2164 const newArtist = e.target.value; 2165 setLakhFiles((prev) => prev.map((item) => item.hash === f.hash ? { ...item, artist: newArtist } : item)); 2166 }, 2167 disabled: isImporting, 2168 className: "bg-zinc-900/60 hover:bg-zinc-900 focus:bg-zinc-950 border border-zinc-800 hover:border-zinc-700 focus:border-emerald-500 rounded px-2 py-1 text-xs text-zinc-300 font-bold outline-none w-full max-w-[140px] transition-all" 2169 } 2170 ) }),
2171 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 w-full", children: [ 2172 /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 12, className: "text-emerald-400 shrink-0" }), 2173 /* @__PURE__ */ jsxRuntimeExports.jsx( 2174 "input", 2175 { 2176 type: "text", 2177 value: f.title, 2178 onChange: (e) => { 2179 const newTitle = e.target.value; 2180 setLakhFiles((prev) => prev.map((item) => item.hash === f.hash ? { ...item, title: newTitle } : item)); 2181 }, 2182 disabled: isImporting, 2183 className: "bg-zinc-900/60 hover:bg-zinc-900 focus:bg-zinc-950 border border-zinc-800 hover:border-zinc-700 focus:border-emerald-500 rounded px-2 py-1 text-xs text-white outline-none w-full transition-all" 2184 } 2185 ),
2186 /* @__PURE__ */ jsxRuntimeExports.jsx( 2187 "button", 2188 { 2189 type: "button", 2190 onClick: () => handleMbLookup(f), 2191 disabled: isImporting, 2192 className: "p-1.5 bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 text-zinc-400 hover:text-emerald-400 rounded transition-colors shrink-0", 2193 title: "Auto-fill artist and title via MusicBrainz database", 2194 children: /* @__PURE__ */ jsxRuntimeExports.jsx(WandSparkles, { size: 12 }) 2195 } 2196 ) 2197 ] }) }), 2198 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2199 "input", 2200 { 2201 type: "text", 2202 value: f.genre, 2203 onChange: (e) => { 2204 const newGenre = e.target.value; 2205 setLakhFiles((prev) => prev.map((item) => item.hash === f.hash ? { ...item, genre: newGenre } : item)); 2206 }, 2207 disabled: isImporting, 2208 className: "bg-zinc-900/60 hover:bg-zinc-900 focus:bg-zinc-950 border border-zinc-800 hover:border-zinc-700 focus:border-emerald-500 rounded px-2 py-1 text-xs text-zinc-300 outline-none w-full max-w-[100px] transition-all" 2209 } 2210 ) }),
2211 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-mono text-zinc-500", children: f.year || "N/A" }), 2212 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-right", children: f.status === "success" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 bg-emerald-500/10 text-emerald-400 rounded text-[10px] font-bold border border-emerald-500/20 inline-flex items-center gap-1", children: [ 2213 "Imported #", 2214 f.id 2215 ] }) : f.status === "duplicate" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-yellow-500/10 text-yellow-400 rounded text-[10px] font-bold border border-yellow-500/20 inline-flex items-center gap-1", children: "In Database" }) : f.existsOnDisk ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 rounded text-[10px] font-black uppercase", children: "ð¢ Found" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-zinc-800 text-zinc-500 rounded text-[10px] font-black uppercase", title: "Download the full dataset or enable demo mode to populate this file.", children: "⪠Missing" }) }) 2216 ] }, f.hash)) : /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 8, className: "p-8 text-center text-zinc-500 text-xs italic", children: "No Lakh seeds found matching search. Type a search above or setup demo seeds!" }) }) }) 2217 ] }) }),
2218 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-800 flex justify-between items-center bg-zinc-950/40", children: [ 2219 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-500", children: [ 2220 "Showing page ", 2221 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: lakhPage }), 2222 " of ", 2223 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: Math.ceil(lakhTotalCount / lakhPageSize) || 1 }), 2224 " (", 2225 lakhTotalCount, 2226 " total indexed songs in prototype)" 2227 ] }), 2228 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [ 2229 /* @__PURE__ */ jsxRuntimeExports.jsx( 2230 "button", 2231 { 2232 onClick: () => setLakhPage((prev) => Math.max(1, prev - 1)), 2233 disabled: lakhPage === 1, 2234 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 2235 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { size: 14 }) 2236 } 2237 ),
2238 /* @__PURE__ */ jsxRuntimeExports.jsx( 2239 "input", 2240 { 2241 type: "text", 2242 value: lakhPageInput, 2243 onChange: (e) => setLakhPageInput(e.target.value), 2244 onKeyDown: (e) => { 2245 if (e.key === "Enter") { 2246 const pageNum = parseInt(lakhPageInput); 2247 const maxPage = Math.ceil(lakhTotalCount / lakhPageSize) || 1; 2248 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 2249 setLakhPage(pageNum); 2250 } else { 2251 setLakhPageInput(lakhPage.toString()); 2252 } 2253 } 2254 }, 2255 onBlur: () => { 2256 const pageNum = parseInt(lakhPageInput); 2257 const maxPage = Math.ceil(lakhTotalCount / lakhPageSize) || 1; 2258 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 2259 setLakhPage(pageNum); 2260 } else { 2261 setLakhPageInput(lakhPage.toString()); 2262 } 2263 }, 2264 className: "w-12 py-1 bg-zinc-900 border border-zinc-800 text-xs text-white rounded font-bold text-center focus:border-emerald-500 outline-none" 2265 } 2266 ),
2267 /* @__PURE__ */ jsxRuntimeExports.jsx( 2268 "button", 2269 { 2270 onClick: () => setLakhPage((prev) => prev + 1), 2271 disabled: lakhFiles.length < lakhPageSize, 2272 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 2273 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14 }) 2274 } 2275 ) 2276 ] }) 2277 ] }) 2278 ] }), 2279 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-4 flex flex-col gap-6 overflow-y-auto pr-1 h-full max-h-full scrollbar-thin", children: [ 2280 renderStagingNotice(), 2281 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 2282 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center gap-2", children: [
2283 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16, className: "text-emerald-400 animate-pulse" }), 2284 " Lakh Integration Manager" 2285 ] }), 2286 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-zinc-400 leading-relaxed bg-zinc-900/60 p-3 rounded-lg border border-zinc-800", children: "Processes the selected Lakh MIDI tracks. Checks hashes to avoid database duplicates, registers high-fidelity track records, and creates tags." }), 2287 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [ 2288 importAllError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-red-950/30 border border-red-900/50 rounded-xl flex items-start gap-2 text-left", children: [ 2289 /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "text-red-400 shrink-0 mt-0.5", size: 14 }), 2290 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-red-300 leading-relaxed", children: [
2291 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-white block font-bold mb-0.5", children: "Task Failed / Halted:" }), 2292 importAllError 2293 ] }) 2294 ] }), 2295 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-xs text-zinc-300 select-none cursor-pointer mb-1", children: [ 2296 /* @__PURE__ */ jsxRuntimeExports.jsx( 2297 "input", 2298 { 2299 type: "checkbox", 2300 checked: overwriteExisting, 2301 onChange: (e) => setOverwriteExisting(e.target.checked), 2302 className: "rounded bg-zinc-900 border-zinc-850 text-emerald-600 focus:ring-emerald-500" 2303 } 2304 ), 2305 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Overwrite (re-do) existing records" }) 2306 ] }), 2307 !isImporting && !lakhImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
2308 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2309 "button", 2310 { 2311 onClick: startLakhImport, 2312 disabled: lakhFiles.filter((f) => f.existsOnDisk && f.status === "pending").length === 0, 2313 className: "w-full py-3 bg-emerald-600 hover:bg-emerald-500 disabled:bg-zinc-800 text-zinc-950 font-black rounded-xl flex items-center justify-center gap-2 transition-colors disabled:opacity-50 text-xs", 2314 children: [ 2315 /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 14 }), 2316 " Import Selected Lakh Songs (", 2317 lakhSelectedHashes.size > 0 ? lakhSelectedHashes.size : lakhFiles.filter((f) => f.existsOnDisk && f.status === "pending").length, 2318 ")" 2319 ] 2320 } 2321 ), 2322 /* @__PURE__ */ jsxRuntimeExports.jsxs( 2323 "button", 2324 { 2325 onClick: handleStartLakhImportAll, 2326 className: "w-full py-3 bg-zinc-800 hover:bg-zinc-750 border border-zinc-700 text-emerald-400 hover:text-emerald-300 font-black rounded-xl flex items-center justify-center gap-2 transition-colors text-xs", 2327 children: [
2328 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 14 }), 2329 " Process All Lakh Songs" 2330 ] 2331 } 2332 ) 2333 ] }) : lakhImportActive ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 2334 "button", 2335 { 2336 onClick: handleStopLakhImportAll, 2337 className: "w-full py-3 bg-zinc-800 hover:bg-red-950/40 border border-zinc-700 text-zinc-400 hover:text-red-400 rounded-xl font-bold transition-colors text-xs flex items-center justify-center gap-2", 2338 children: [ 2339 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 2340 " Stop Process All" 2341 ] 2342 } 2343 ) : /* @__PURE__ */ jsxRuntimeExports.jsxs( 2344 "button", 2345 { 2346 onClick: stopImport, 2347 className: "w-full py-3 bg-zinc-800 hover:bg-red-950/40 border border-zinc-700 text-zinc-400 hover:text-red-400 rounded-xl font-bold transition-colors text-xs flex items-center justify-center gap-2", 2348 children: [
2349 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 14 }), 2350 " Stop Import" 2351 ] 2352 } 2353 ) 2354 ] }) 2355 ] }), 2356 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 2357 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center justify-between w-full", children: [ 2358 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 2359 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 16, className: "text-emerald-400" }), 2360 " Lakh Pipeline Telemetry" 2361 ] }), 2362 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] uppercase font-black px-2 py-0.5 rounded border tracking-wider ${isLakhRunning ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20 animate-pulse" : lakhStatus.dbImportedCount > 0 ? "bg-zinc-900 text-zinc-400 border-zinc-800" : "bg-zinc-950 text-zinc-600 border-transparent"}`, children: isLakhRunning ? "Running" : lakhStatus.dbImportedCount > 0 ? "Active" : "Idle" }) 2363 ] }),
2364 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3 mt-1", children: [ 2365 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2366 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Lakh Disk Files" }), 2367 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-lg font-black font-mono text-white", children: lakhStatus.totalFiles }) 2368 ] }), 2369 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2370 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Processed" }), 2371 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-white ${isLakhCalculating ? "text-xs font-sans text-zinc-400 animate-pulse" : "text-lg font-mono"}`, children: isLakhCalculating ? "Calculating..." : lakhProcessedValue }) 2372 ] }),
2373 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2374 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "MB Matched" }), 2375 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isLakhCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isLakhCalculating ? "Calculating..." : mbMatchedCount }) 2376 ] }), 2377 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2378 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "MB No Match" }), 2379 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-zinc-400 ${isLakhCalculating ? "text-xs font-sans text-zinc-400/60 animate-pulse" : "text-lg font-mono"}`, children: isLakhCalculating ? "Calculating..." : mbNoMatchCount }) 2380 ] }),
2381 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-emerald-500/10 bg-emerald-500/5 col-span-2 flex justify-between items-center", children: [ 2382 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2383 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "Imported to Library" }), 2384 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isLakhCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isLakhCalculating ? "Calculating..." : lakhImportedValue }) 2385 ] }), 2386 duplicateCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [ 2387 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-orange-500/70 font-bold uppercase mb-0.5", children: "Duplicates" }),
2388 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-black font-mono text-orange-400", children: duplicateCount }) 2389 ] }) 2390 ] }) 2391 ] }) 2392 ] }), 2393 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 border border-zinc-800 bg-zinc-950 p-4 rounded-2xl flex flex-col overflow-hidden min-h-[540px]", children: [ 2394 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center border-b border-zinc-850 pb-2 mb-3", children: [ 2395 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 2396 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-black text-white flex items-center gap-1.5 uppercase tracking-wider", children: [ 2397 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-zinc-500" }), 2398 " Log Monitor" 2399 ] }), 2400 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-[10px] font-bold text-zinc-400 cursor-pointer hover:text-white select-none", children: [
2401 /* @__PURE__ */ jsxRuntimeExports.jsx( 2402 "input", 2403 { 2404 type: "checkbox", 2405 checked: showQueries, 2406 onChange: (e) => setShowQueries(e.target.checked), 2407 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-3 h-3" 2408 } 2409 ), 2410 "Show Queries" 2411 ] }) 2412 ] }), 2413 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }) 2414 ] }), 2415 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto font-mono text-[10px] text-zinc-400 flex flex-col gap-1.5 pr-2", children: [ 2416 uploadLogs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-600 italic", children: "Logs active. Waiting for import action..." }) : uploadLogs.map((log, i) => { 2417 const isObject = typeof log === "object" && log !== null; 2418 const text = isObject ? log.text : log; 2419 const queries = isObject ? log.queries : void 0; 2420 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "leading-relaxed whitespace-pre-wrap flex flex-col gap-1", children: [
2421 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: text }), 2422 showQueries && queries && queries.map((q, qi) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 bg-zinc-900/50 p-2 rounded border border-zinc-850/50 pl-3 font-mono text-[9px] mt-1", children: [ 2423 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-emerald-500/80 font-bold uppercase mb-1", children: [ 2424 "SQL Query: ", 2425 q.durationMs !== void 0 ? "(" + q.durationMs + "ms)" : "" 2426 ] }), 2427 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-400 select-all", children: q.query.trim() }), 2428 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 mt-1", children: [ 2429 "Params: ", 2430 JSON.stringify(q.params) 2431 ] }) 2432 ] }, qi)) 2433 ] }, i); 2434 }),
2435 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logsEndRef }) 2436 ] }) 2437 ] }) 2438 ] }) 2439 ] }) 2440 ) : ( 2441 /* Staging Queue tab view */ 2442 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-hidden grid grid-cols-12 gap-6", children: [ 2443 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-8 flex flex-col overflow-y-auto overflow-x-hidden border border-zinc-800 bg-zinc-950/30 rounded-2xl", children: [ 2444 /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSearch, className: "p-4 border-b border-zinc-800 flex gap-4 items-stretch justify-between", children: [ 2445 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [ 2446 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-2.5 text-zinc-500", size: 16 }), 2447 /* @__PURE__ */ jsxRuntimeExports.jsx( 2448 "input", 2449 { 2450 value: searchTerm, 2451 onChange: (e) => setSearchTerm(e.target.value), 2452 placeholder: "Search staged paths on server (Hit enter to search)...", 2453 className: "w-full pl-9 pr-4 py-2 bg-zinc-900 border border-zinc-800 rounded-lg text-sm text-white focus:border-indigo-500 outline-none" 2454 } 2455 ) 2456 ] }),
2457 /* @__PURE__ */ jsxRuntimeExports.jsx( 2458 "button", 2459 { 2460 type: "submit", 2461 className: "px-4 py-2 bg-zinc-800 hover:bg-zinc-750 text-xs font-bold rounded-lg border border-zinc-700 text-white", 2462 children: "Search" 2463 } 2464 ) 2465 ] }), 2466 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 bg-zinc-900/40 border-b border-zinc-800", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-4", children: [ 2467 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [ 2468 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-300 font-bold flex items-center gap-1", children: [ 2469 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 14, className: "text-indigo-400" }), 2470 " Path Regex Parser Settings:" 2471 ] }), 2472 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-xs text-zinc-300 cursor-pointer", children: [
2473 /* @__PURE__ */ jsxRuntimeExports.jsx( 2474 "input", 2475 { 2476 type: "radio", 2477 name: "parser", 2478 checked: parserPreset === "genre_artist_title", 2479 onChange: () => setParserPreset("genre_artist_title") 2480 } 2481 ), 2482 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "[Genre]/[Artist]/[Title].mid" }) 2483 ] }), 2484 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-xs text-zinc-300 cursor-pointer", children: [ 2485 /* @__PURE__ */ jsxRuntimeExports.jsx( 2486 "input", 2487 { 2488 type: "radio", 2489 name: "parser", 2490 checked: parserPreset === "artist_title", 2491 onChange: () => setParserPreset("artist_title") 2492 } 2493 ), 2494 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "[Artist] - [Title].mid" }) 2495 ] }),
2496 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-xs text-zinc-300 cursor-pointer", children: [ 2497 /* @__PURE__ */ jsxRuntimeExports.jsx( 2498 "input", 2499 { 2500 type: "radio", 2501 name: "parser", 2502 checked: parserPreset === "artist_title_subdir", 2503 onChange: () => setParserPreset("artist_title_subdir") 2504 } 2505 ), 2506 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "[Artist]/[Title].mid" }) 2507 ] }), 2508 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-xs text-zinc-300 cursor-pointer", children: [ 2509 /* @__PURE__ */ jsxRuntimeExports.jsx( 2510 "input", 2511 { 2512 type: "radio", 2513 name: "parser", 2514 checked: parserPreset === "flat", 2515 onChange: () => setParserPreset("flat") 2516 } 2517 ),
2518 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Title only (Flat)" }) 2519 ] }), 2520 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-xs text-zinc-300 cursor-pointer", children: [ 2521 /* @__PURE__ */ jsxRuntimeExports.jsx( 2522 "input", 2523 { 2524 type: "radio", 2525 name: "parser", 2526 checked: parserPreset === "custom", 2527 onChange: () => setParserPreset("custom") 2528 } 2529 ), 2530 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "RegExp (Custom)" }) 2531 ] }) 2532 ] }), 2533 parserPreset === "custom" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center bg-zinc-950 p-2.5 rounded-lg border border-zinc-800", children: [ 2534 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500", children: "RegExp:" }),
2535 /* @__PURE__ */ jsxRuntimeExports.jsx( 2536 "input", 2537 { 2538 value: customPattern, 2539 onChange: (e) => setCustomPattern(e.target.value), 2540 className: "flex-1 bg-transparent border-none text-xs text-indigo-400 font-mono outline-none", 2541 placeholder: "e.g. (?<artist>[^/]+)-(?<title>[^/.]+)\\.mid$" 2542 } 2543 ) 2544 ] }), 2545 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [ 2546 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2547 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] text-zinc-500 font-black uppercase mb-1", children: "Fallback Artist Name" }), 2548 /* @__PURE__ */ jsxRuntimeExports.jsx( 2549 "input", 2550 { 2551 value: defaultArtist, 2552 onChange: (e) => setDefaultArtist(e.target.value), 2553 className: "w-full bg-zinc-950 border border-zinc-800 rounded p-1.5 text-xs text-white", 2554 placeholder: "e.g. Various Artists" 2555 } 2556 ) 2557 ] }),
2558 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2559 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] text-zinc-500 font-black uppercase mb-1", children: "Inject Global Genre Tag" }), 2560 /* @__PURE__ */ jsxRuntimeExports.jsx( 2561 "input", 2562 { 2563 value: defaultGenre, 2564 onChange: (e) => setDefaultGenre(e.target.value), 2565 className: "w-full bg-zinc-950 border border-zinc-800 rounded p-1.5 text-xs text-white", 2566 placeholder: "e.g. Rock, Classical" 2567 } 2568 ) 2569 ] }) 2570 ] }), 2571 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2572 "button", 2573 { 2574 onClick: applyParserRules, 2575 className: "px-3 py-1.5 bg-indigo-500/10 hover:bg-indigo-500 text-indigo-400 hover:text-white font-bold rounded-lg border border-indigo-500/20 text-xs transition-colors", 2576 children: "Re-Apply Parser Rules" 2577 } 2578 ) }) 2579 ] }) }),
2580 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-[400px] resize-y overflow-auto border-b border-zinc-800", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center p-12 text-zinc-500 italic", children: [ 2581 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin mb-2 text-indigo-500", size: 24 }), 2582 "Loading staged files from server..." 2583 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "min-w-[1000px] w-full text-left border-collapse", children: [ 2584 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-zinc-800 bg-zinc-950/40 text-zinc-400 text-[10px] font-black uppercase", children: [ 2585 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-8", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: toggleSelectAll, className: "text-zinc-500 hover:text-indigo-400", children: selectedPaths.size === stagedFiles.length && stagedFiles.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-indigo-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) }) }),
2586 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-12 text-center", children: "Preview" }), 2587 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Staging Path" }), 2588 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Artist (Override)" }), 2589 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Title (Override)" }), 2590 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "Tags" }), 2591 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-right", children: "Status" }) 2592 ] }) }), 2593 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-850", children: stagedFiles.length > 0 ? stagedFiles.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-900/30 text-xs transition-colors group", children: [ 2594 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => toggleSelectPath(f.relativePath), className: "text-zinc-600 hover:text-indigo-400", children: selectedPaths.has(f.relativePath) ? /* @__PURE__ */ jsxRuntimeExports.jsx(SquareCheckBig, { size: 14, className: "text-indigo-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14 }) }) }),
2595 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2596 "button", 2597 { 2598 onClick: () => handlePreviewStaging(f), 2599 disabled: previewLoading && previewingPath !== f.relativePath, 2600 className: `p-1.5 rounded-lg transition-all border ${previewingPath === f.relativePath ? "bg-indigo-600 border-indigo-500 text-white" : "bg-zinc-900 border-zinc-800 hover:border-zinc-700 text-zinc-400 hover:text-zinc-200"} disabled:opacity-30`, 2601 title: previewingPath === f.relativePath && previewPlaying ? "Pause preview" : "Play preview", 2602 children: previewingPath === f.relativePath ? previewLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12 }) 2603 } 2604 ) }),
2605 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-mono text-zinc-500 max-w-[200px] truncate", title: f.relativePath, children: f.relativePath }), 2606 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2607 "input", 2608 { 2609 type: "text", 2610 value: f.artist, 2611 onChange: (e) => { 2612 const newArtist = e.target.value; 2613 setStagedFiles((prev) => prev.map((item) => item.relativePath === f.relativePath ? { ...item, artist: newArtist } : item)); 2614 }, 2615 disabled: isImporting, 2616 className: "bg-zinc-900/60 hover:bg-zinc-900 focus:bg-zinc-950 border border-zinc-800 hover:border-zinc-700 focus:border-indigo-500 rounded px-2 py-1 text-xs text-zinc-300 font-bold outline-none w-full max-w-[140px] transition-all" 2617 } 2618 ) }), 2619 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 w-full", children: [
2620 /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 12, className: "text-indigo-400 shrink-0" }), 2621 /* @__PURE__ */ jsxRuntimeExports.jsx( 2622 "input", 2623 { 2624 type: "text", 2625 value: f.title, 2626 onChange: (e) => { 2627 const newTitle = e.target.value; 2628 setStagedFiles((prev) => prev.map((item) => item.relativePath === f.relativePath ? { ...item, title: newTitle } : item)); 2629 }, 2630 disabled: isImporting, 2631 className: "bg-zinc-900/60 hover:bg-zinc-900 focus:bg-zinc-950 border border-zinc-800 hover:border-zinc-700 focus:border-indigo-500 rounded px-2 py-1 text-xs text-white outline-none w-full transition-all" 2632 } 2633 ) 2634 ] }) }), 2635 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1 flex-wrap", children: f.tags.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1 bg-zinc-800 text-zinc-400 rounded text-[10px] flex items-center gap-0.5", children: [
2636 /* @__PURE__ */ jsxRuntimeExports.jsx(Tag, { size: 8 }), 2637 " ", 2638 t 2639 ] }, t)) }) }), 2640 /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-3 text-right", children: [ 2641 f.status === "pending" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-zinc-800 text-zinc-400 rounded text-[10px] font-bold", children: "Staged" }), 2642 f.status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 bg-emerald-500/10 text-emerald-400 rounded text-[10px] font-bold border border-emerald-500/20 inline-flex items-center gap-1", children: [ 2643 "Saved #", 2644 f.id 2645 ] }), 2646 f.status === "duplicate" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-yellow-500/10 text-yellow-400 rounded text-[10px] font-bold border border-yellow-500/20 inline-flex items-center gap-1", title: `Matched ID: ${f.id}`, children: "Duplicate" }), 2647 f.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-red-500/10 text-red-400 rounded text-[10px] font-bold border border-red-500/20 inline-flex items-center gap-1", title: f.message, children: "Failed" }) 2648 ] }) 2649 ] }, f.relativePath)) : /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 6, className: "p-8 text-center text-zinc-500 text-xs italic", children: "No staged files currently found. Extract or upload some to get started!" }) }) }) 2650 ] }) }),
2651 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-t border-zinc-800 flex justify-between items-center bg-zinc-950/40", children: [ 2652 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-500", children: [ 2653 "Showing page ", 2654 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: currentPage }), 2655 " of ", 2656 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-300", children: Math.ceil(totalStagedCount / pageSize) || 1 }), 2657 " (", 2658 totalStagedCount, 2659 " total files staged)" 2660 ] }), 2661 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [ 2662 /* @__PURE__ */ jsxRuntimeExports.jsx( 2663 "button", 2664 { 2665 onClick: () => setCurrentPage((prev) => Math.max(1, prev - 1)), 2666 disabled: currentPage === 1, 2667 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 2668 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { size: 14 }) 2669 } 2670 ),
2671 /* @__PURE__ */ jsxRuntimeExports.jsx( 2672 "input", 2673 { 2674 type: "text", 2675 value: currentPageInput, 2676 onChange: (e) => setCurrentPageInput(e.target.value), 2677 onKeyDown: (e) => { 2678 if (e.key === "Enter") { 2679 const pageNum = parseInt(currentPageInput); 2680 const maxPage = Math.ceil(totalStagedCount / pageSize) || 1; 2681 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 2682 setCurrentPage(pageNum); 2683 } else { 2684 setCurrentPageInput(currentPage.toString()); 2685 } 2686 } 2687 }, 2688 onBlur: () => { 2689 const pageNum = parseInt(currentPageInput); 2690 const maxPage = Math.ceil(totalStagedCount / pageSize) || 1; 2691 if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= maxPage) { 2692 setCurrentPage(pageNum); 2693 } else { 2694 setCurrentPageInput(currentPage.toString()); 2695 } 2696 }, 2697 className: "w-12 py-1 bg-zinc-900 border border-zinc-800 text-xs text-white rounded font-bold text-center focus:border-indigo-500 outline-none" 2698 } 2699 ),
2700 /* @__PURE__ */ jsxRuntimeExports.jsx( 2701 "button", 2702 { 2703 onClick: () => setCurrentPage((prev) => prev + 1), 2704 disabled: stagedFiles.length < pageSize, 2705 className: "p-1.5 bg-zinc-900 border border-zinc-800 rounded hover:bg-zinc-800 disabled:opacity-35", 2706 children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14 }) 2707 } 2708 ) 2709 ] }) 2710 ] }) 2711 ] }), 2712 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-12 lg:col-span-4 flex flex-col gap-6 overflow-y-auto pr-1 h-full max-h-full scrollbar-thin", children: [ 2713 renderStagingNotice(), 2714 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 2715 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center gap-2", children: [
2716 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: "text-indigo-400" }), 2717 " Staging Queue Orchestrator" 2718 ] }), 2719 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-400 leading-relaxed bg-zinc-900/60 p-3 rounded-lg border border-zinc-800", children: [ 2720 "This will process and import **", 2721 selectedPaths.size > 0 ? selectedPaths.size : stagedFiles.filter((f) => f.status === "pending").length, 2722 " pending file(s)** shown above. Highly efficient server-side compilation!" 2723 ] }), 2724 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [ 2725 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-xs text-zinc-300 select-none cursor-pointer mb-1", children: [ 2726 /* @__PURE__ */ jsxRuntimeExports.jsx( 2727 "input", 2728 { 2729 type: "checkbox", 2730 checked: overwriteExisting, 2731 onChange: (e) => setOverwriteExisting(e.target.checked), 2732 className: "rounded bg-zinc-900 border-zinc-850 text-indigo-600 focus:ring-indigo-500" 2733 } 2734 ),
2735 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Overwrite (re-do) existing records" }) 2736 ] }), 2737 !isImporting ? /* @__PURE__ */ jsxRuntimeExports.jsxs( 2738 "button", 2739 { 2740 onClick: startImport, 2741 disabled: stagedFiles.filter((f) => f.status === "pending").length === 0, 2742 className: "w-full py-3 bg-indigo-600 hover:bg-indigo-500 disabled:bg-zinc-800 text-white font-bold rounded-xl flex items-center justify-center gap-2 transition-colors disabled:opacity-50", 2743 children: [ 2744 /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 16 }), 2745 " Import Selected Items" 2746 ] 2747 } 2748 ) : /* @__PURE__ */ jsxRuntimeExports.jsx( 2749 "button", 2750 { 2751 onClick: stopImport, 2752 className: "w-full py-3 bg-zinc-800 hover:bg-red-950/40 border border-zinc-700 text-zinc-400 hover:text-red-400 rounded-xl font-bold transition-colors", 2753 children: "Stop Queue Job" 2754 } 2755 ) 2756 ] }) 2757 ] }),
2758 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-zinc-800 bg-zinc-950/40 p-5 rounded-2xl flex flex-col gap-4", children: [ 2759 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-sm font-black text-white flex items-center gap-2", children: [ 2760 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 16, className: "text-indigo-400" }), 2761 " Pipeline Telemetry" 2762 ] }), 2763 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 2764 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-end text-xs mb-1.5", children: [ 2765 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 font-semibold", children: "Staged Progress" }), 2766 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold font-mono text-white", children: isQueueCalculating ? "Calculating..." : `${stagedFiles.length > 0 ? Math.round(processedCount / stagedFiles.length * 100) : 0}%` }) 2767 ] }),
2768 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-2 bg-zinc-850 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 2769 "div", 2770 { 2771 className: `h-full bg-gradient-to-r from-indigo-500 to-purple-500 transition-all duration-300 ${isQueueCalculating ? "animate-pulse" : ""}`, 2772 style: { width: `${isQueueCalculating ? 100 : stagedFiles.length > 0 ? processedCount / stagedFiles.length * 100 : 0}%` } 2773 } 2774 ) }) 2775 ] }), 2776 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3 mt-2", children: [ 2777 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2778 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Total Staged" }), 2779 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-lg font-black font-mono text-white", children: totalStagedCount }) 2780 ] }),
2781 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3", children: [ 2782 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 font-bold uppercase mb-0.5", children: "Processed" }), 2783 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-white ${isQueueCalculating ? "text-xs font-sans text-zinc-400 animate-pulse" : "text-lg font-mono"}`, children: isQueueCalculating ? "Calculating..." : processedCount }) 2784 ] }), 2785 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-emerald-500/10 bg-emerald-500/5", children: [ 2786 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-emerald-500/70 font-bold uppercase mb-0.5", children: "Successful" }), 2787 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-emerald-400 ${isQueueCalculating ? "text-xs font-sans text-emerald-500/60 animate-pulse" : "text-lg font-mono"}`, children: isQueueCalculating ? "Calculating..." : successCount }) 2788 ] }),
2789 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-yellow-500/10 bg-yellow-500/5", children: [ 2790 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-yellow-500/70 font-bold uppercase mb-0.5", children: "Duplicates" }), 2791 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-yellow-400 ${isQueueCalculating ? "text-xs font-sans text-yellow-500/60 animate-pulse" : "text-lg font-mono"}`, children: isQueueCalculating ? "Calculating..." : duplicateCount }) 2792 ] }), 2793 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-900 border border-zinc-850 rounded-xl p-3 border-red-500/10 bg-red-500/5 col-span-2", children: [ 2794 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-red-500/70 font-bold uppercase mb-0.5", children: "Failed" }), 2795 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `font-black text-red-400 ${isQueueCalculating ? "text-xs font-sans text-red-500/60 animate-pulse" : "text-lg font-mono"}`, children: isQueueCalculating ? "Calculating..." : failedCount }) 2796 ] }) 2797 ] }) 2798 ] }),
2799 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 border border-zinc-800 bg-zinc-950 p-4 rounded-2xl flex flex-col overflow-hidden min-h-[540px]", children: [ 2800 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center border-b border-zinc-850 pb-2 mb-3", children: [ 2801 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 2802 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-xs font-black text-white flex items-center gap-1.5 uppercase tracking-wider", children: [ 2803 /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 14, className: "text-zinc-500" }), 2804 " Log Monitor" 2805 ] }), 2806 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 text-[10px] font-bold text-zinc-400 cursor-pointer hover:text-white select-none", children: [ 2807 /* @__PURE__ */ jsxRuntimeExports.jsx( 2808 "input", 2809 { 2810 type: "checkbox", 2811 checked: showQueries, 2812 onChange: (e) => setShowQueries(e.target.checked), 2813 className: "rounded bg-zinc-900 border-zinc-800 text-emerald-500 focus:ring-0 focus:ring-offset-0 cursor-pointer w-3 h-3" 2814 } 2815 ), 2816 "Show Queries" 2817 ] }) 2818 ] }),
2819 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 rounded-full bg-emerald-500 animate-pulse" }) 2820 ] }), 2821 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto font-mono text-[10px] text-zinc-400 flex flex-col gap-1.5 pr-2", children: [ 2822 uploadLogs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-600 italic", children: "Staged queue inactive. Waiting for job task..." }) : uploadLogs.map((log, i) => { 2823 const isObject = typeof log === "object" && log !== null; 2824 const text = isObject ? log.text : log; 2825 const queries = isObject ? log.queries : void 0; 2826 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "leading-relaxed whitespace-pre-wrap flex flex-col gap-1", children: [ 2827 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: text }), 2828 showQueries && queries && queries.map((q, qi) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 bg-zinc-900/50 p-2 rounded border border-zinc-850/50 pl-3 font-mono text-[9px] mt-1", children: [
2829 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-emerald-500/80 font-bold uppercase mb-1", children: [ 2830 "SQL Query: ", 2831 q.durationMs !== void 0 ? "(" + q.durationMs + "ms)" : "" 2832 ] }), 2833 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-400 select-all", children: q.query.trim() }), 2834 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-zinc-500 mt-1", children: [ 2835 "Params: ", 2836 JSON.stringify(q.params) 2837 ] }) 2838 ] }, qi)) 2839 ] }, i); 2840 }), 2841 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logsEndRef }) 2842 ] }) 2843 ] }) 2844 ] }) 2845 ] }) 2846 ), 2847 previewingPath && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed bottom-6 left-1/2 transform -translate-x-1/2 z-50 bg-zinc-950/90 backdrop-blur-md border border-indigo-500/30 shadow-2xl rounded-2xl px-6 py-4 flex items-center gap-6 min-w-[320px] max-w-[600px] animate-in fade-in slide-in-from-bottom-4 duration-300", children: [
2848 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-indigo-500/10 rounded-xl border border-indigo-500/20 text-indigo-400", children: previewLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 20 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { className: "animate-pulse", size: 20 }) }), 2849 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [ 2850 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase font-black text-indigo-400/70 tracking-wider", children: previewLoading ? "Loading MIDI Synth..." : "Now Auditioning Preview" }), 2851 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-white truncate", title: previewTitle, children: previewTitle }) 2852 ] }), 2853 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 2854 !previewLoading && /* @__PURE__ */ jsxRuntimeExports.jsx( 2855 "button", 2856 { 2857 onClick: () => { 2858 if (previewPlaying) { 2859 synth.stop(); 2860 setPreviewPlaying(false); 2861 } else { 2862 synth.play(); 2863 setPreviewPlaying(true); 2864 } 2865 }, 2866 className: "p-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl transition-all",
2867 children: previewPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 16 }) 2868 } 2869 ), 2870 /* @__PURE__ */ jsxRuntimeExports.jsx( 2871 "button", 2872 { 2873 onClick: stopPreview, 2874 className: "p-2 bg-zinc-800 hover:bg-red-950/40 text-zinc-400 hover:text-red-400 border border-zinc-700 hover:border-red-900 rounded-xl transition-all", 2875 children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { size: 16 }) 2876 } 2877 ) 2878 ] }) 2879 ] }) 2880 ] }); 2881} 2882export { 2883 ArchiveImporter as default 2884};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.