PageSourceSearch

https://www.hamienet.com/assets/MediaLibrary-CsRy_8DG.js

js hamienet.com collected 2026-09-24 19:00:38 UTC 67,661 bytes, 950 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, R as React } 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 { b as Drawer, a as DataTable } from "./AdminShared-P_hor2hS.js";
5import { h as formatFileSize, g as getColorsFromHash } from "./utils-BYCVoSTN.js";
6import { fR as Speaker, aW as X, bV as RefreshCcw, bv as CircleCheckBig, cL as RefreshCw, b2 as Upload, cj as Layers, bQ as ExternalLink, aZ as Mail, a$ as Shield, b3 as FileHeadphone, d8 as Trash, eu as Terminal, cB as ChevronDown, b1 as CircleAlert, d6 as SquarePen, cp as Settings2, ch as Database, bA as Eye, cf as Activity, dg as Square, bU as Play, fS as Brain, cA as ChevronUp } from "./vendor-dVNaLJx_.js";
7import { T as TagAutocomplete, u as useWaveform, W as Waveform } from "./Waveform-MxtdejK7.js";
8import { U as UserAvatar } from "./UserAvatar-BQN4jlY-.js";
9import { G as GlobeFlag } from "./GlobeFlag-V1uaxVEK.js";
10import "./vendor-i18n-R-bMoYp_.js";
11import "./vendor-react-three-MDLA5jog.js";
12import "./wasm-synth-adapter-BrqgBX1y.js";
13import "./vendor-spessasynth-DH8n6o_V.js";
14import "./vendor-audio-DH3w0BzW.js";
15import "./vendor-framer-C8eEgkdL.js";
16const SoundFontModal = ({ isOpen, onClose }) => {
17  const [soundfonts, setSoundfonts] = reactExports.useState([]);
18  const [activeFont, setActiveFont] = reactExports.useState(null);
19  const [uploading, setUploading] = reactExports.useState(false);
20  reactExports.useEffect(() => {
21    if (isOpen) loadSoundfonts();
22  }, [isOpen]);
23  const loadSoundfonts = async () => {
24    const res = await api.getSoundFonts();
25    setSoundfonts(res.files || []);
26    setActiveFont(res.active);
27  };
28  const handleUpload = async (e) => {
29    var _a;
30    if ((_a = e.target.files) == null ? void 0 : _a[0]) {
31      setUploading(true);
32      try {
33        await api.uploadSoundFont(e.target.files[0]);
34        await loadSoundfonts();
35      } catch (e2) {
36        alert("Upload failed.");
37      } finally {
38        setUploading(false);
39      }
40    }
41  };
42  const handleSetActive = async (file) => {
43    await api.setActiveSoundFont(file);
44    setActiveFont(file);
45  };
46  if (!isOpen) return null;
47  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [
48    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: onClose }),
49    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-lg bg-white dark:bg-zinc-900 rounded-xl shadow-2xl p-6 border border-zinc-200 dark:border-zinc-800 animate-in zoom-in-95", children: [
50      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-6", children: [
51        /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
52          /* @__PURE__ */ jsxRuntimeExports.jsx(Speaker, { className: "text-indigo-500" }),
53          " SoundFont Manager"
54        ] }),
55        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20, className: "text-zinc-500" }) })
56      ] }),
57      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 p-4 rounded-lg border border-zinc-200 dark:border-zinc-800 mb-4 flex justify-between items-center", children: [
58        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
59          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mb-1", children: "Active SoundFont:" }),
60          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-sm font-bold text-zinc-900 dark:text-white truncate max-w-[250px]", children: activeFont || "Default (System setting)" })
61        ] }),
62        activeFont && /* @__PURE__ */ jsxRuntimeExports.jsxs(
63          "button",
64          {
65            onClick: () => handleSetActive(null),
66            className: "text-xs text-indigo-600 dark:text-indigo-400 font-bold hover:underline flex items-center gap-1",
67            children: [
68              /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCcw, { size: 12 }),
69              " Reset to Default"
70            ]
71          }
72        )
73      ] }),
74      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 max-h-60 overflow-y-auto mb-4 custom-scrollbar pr-1", children: [
75        soundfonts.map((sf) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center p-3 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg group transition-colors border border-transparent hover:border-zinc-200 dark:hover:border-zinc-700", children: [
76          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col min-w-0", children: [
77            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-zinc-900 dark:text-white truncate", children: sf.path.split("/").pop() }),
78            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-500 truncate font-mono", children: [
79              sf.path,
80              " • ",
81              formatFileSize(sf.size)
82            ] })
83          ] }),
84          activeFont === sf.path ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-500 text-xs font-bold flex items-center gap-1 shrink-0 ml-2", children: [
85            /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { size: 14 }),
86            " Active"
87          ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => handleSetActive(sf.path), className: "px-3 py-1 bg-zinc-200 dark:bg-zinc-700 hover:bg-indigo-100 dark:hover:bg-indigo-900/30 text-zinc-600 dark:text-zinc-300 hover:text-indigo-600 dark:hover:text-indigo-400 rounded text-xs font-bold opacity-0 group-hover:opacity-100 transition-all shrink-0 ml-2", children: "Set Active" })
88        ] }, sf.path)),
89        soundfonts.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-sm text-zinc-400 py-4", children: "No custom SoundFonts uploaded." })
90      ] }),
91      /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: `w-full py-3 border-2 border-dashed border-zinc-300 dark:border-zinc-700 rounded-lg flex items-center justify-center gap-2 text-sm font-bold text-zinc-500 hover:border-indigo-500 hover:text-indigo-500 cursor-pointer transition-colors ${uploading ? "opacity-50 pointer-events-none" : ""}`, children: [
92        uploading ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 16 }),
93        uploading ? "Uploading..." : "Upload .sf2 / .sfArk",
94        /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "file", className: "hidden", accept: ".sf2,.sfArk", onChange: handleUpload })
95      ] }),
96      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-400 mt-2 text-center", children: "Note: .sfArk files require additional conversion. .sf2 is recommended." })
97    ] })
98  ] });
99};
100const BulkReprocessModal = ({ onClose, onComplete }) => {
101  const [startId, setStartId] = reactExports.useState(0);
102  const [limit, setLimit] = reactExports.useState(100);
103  const [isProcessing, setIsProcessing] = reactExports.useState(false);
104  const handleExecute = async () => {
105    setIsProcessing(true);
106    try {
107      await api.reprocessTracks({ startId, limit });
108      onComplete();
109    } catch (e) {
110      alert("Failed to queue jobs.");
111    } finally {
112      setIsProcessing(false);
113    }
114  };
115  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [
116    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: onClose }),
117    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-sm bg-white dark:bg-zinc-900 rounded-xl shadow-2xl p-6 border border-zinc-200 dark:border-zinc-800 animate-in zoom-in-95", children: [
118      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
119        /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [
120          /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 18, className: "text-indigo-500" }),
121          " Bulk Reprocess"
122        ] }),
123        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 18, className: "text-zinc-500" }) })
124      ] }),
125      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-600 dark:text-zinc-400 mb-6 bg-zinc-50 dark:bg-zinc-800/50 p-3 rounded-lg border border-zinc-100 dark:border-zinc-800", children: [
126        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-2 font-bold text-zinc-800 dark:text-zinc-200", children: "This will fully regenerate:" }),
127        /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "list-disc list-inside space-y-1 ml-1", children: [
128          /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "Opus Audio & Waveforms" }),
129          /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "Sheet Music (SVG)" }),
130          /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "Piano Roll & Event Data (JSON)" }),
131          /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "Track Metadata (Key, Difficulty)" })
132        ] }),
133        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 opacity-75", children: "Processing relies on the original uploaded MIDI file." })
134      ] }),
135      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
136        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
137          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Start MIDI ID" }),
138          /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", value: startId, onChange: (e) => setStartId(parseInt(e.target.value)), className: "w-full bg-zinc-50 dark:bg-black border border-zinc-300 dark:border-zinc-700 rounded p-2 text-sm outline-none focus:ring-2 focus:ring-indigo-500 text-zinc-900 dark:text-white" })
139        ] }),
140        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
141          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-1", children: "Limit (Batch Size)" }),
142          /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "number", value: limit, onChange: (e) => setLimit(parseInt(e.target.value)), className: "w-full bg-zinc-50 dark:bg-black border border-zinc-300 dark:border-zinc-700 rounded p-2 text-sm outline-none focus:ring-2 focus:ring-indigo-500 text-zinc-900 dark:text-white" })
143        ] })
144      ] }),
145      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mt-6", children: [
146        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-2 text-xs font-bold text-zinc-500 hover:text-zinc-900 dark:hover:text-white", children: "Cancel" }),
147        /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleExecute, disabled: isProcessing, className: "px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold flex items-center gap-2 disabled:opacity-50", children: [
148          isProcessing ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }),
149          " Queue Jobs"
150        ] })
151      ] })
152    ] })
153  ] });
154};
155const BatchEditModal = ({ trackIds, onClose, onComplete }) => {
156  const [updates, setUpdates] = reactExports.useState({ status: "", artist: "", tags: "" });
157  const [isSaving, setIsSaving] = reactExports.useState(false);
158  const handleSave = async () => {
159    try {
160      setIsSaving(true);
161      const finalUpdates = {};
162      if (updates.status) finalUpdates.status = updates.status;
163      if (updates.artist) finalUpdates.artist = updates.artist;
164      if (updates.tags) {
165        finalUpdates.tags = updates.tags.split(",").map((t) => t.trim());
166      }
167      await api.bulkUpdateTrackMetadata(trackIds, finalUpdates);
168      onComplete();
169    } catch (err) {
170      console.error(err);
171      alert(err.message || "Failed to save batch changes.");
172    } finally {
173      setIsSaving(false);
174    }
175  };
176  const handleReprocessAudio = async () => {
177    if (!confirm(`Queue ${trackIds.length} tracks for full regeneration (Audio, Sheet, JSON)?`)) return;
178    try {
179      setIsSaving(true);
180      await api.reprocessTracks({ ids: trackIds });
181      onComplete();
182    } catch (err) {
183      console.error(err);
184      alert(err.message || "Failed to reprocess tracks.");
185    } finally {
186      setIsSaving(false);
187    }
188  };
189  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [
190    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: onClose }),
191    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full max-w-lg bg-white dark:bg-zinc-900 rounded-xl shadow-2xl p-6 border border-zinc-200 dark:border-zinc-800 animate-in zoom-in-95", children: [
192      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "absolute top-4 right-4 p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full text-zinc-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 20 }) }),
193      /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-bold mb-6 text-zinc-900 dark:text-white", children: [
194        "Batch Edit ",
195        trackIds.length,
196        " Items"
197      ] }),
198      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 mb-6", children: [
199        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
200          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase", children: "Artist" }),
201          /* @__PURE__ */ jsxRuntimeExports.jsx(TagAutocomplete, { placeholder: "Leave blank to keep unchanged", value: updates.artist, onChange: (val) => setUpdates({ ...updates, artist: val }) })
202        ] }),
203        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
204          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase", children: "Tags" }),
205          /* @__PURE__ */ jsxRuntimeExports.jsx(TagAutocomplete, { placeholder: "Leave blank to keep unchanged", value: updates.tags, onChange: (val) => setUpdates({ ...updates, tags: val }) })
206        ] }),
207        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
208          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase", children: "Status" }),
209          /* @__PURE__ */ jsxRuntimeExports.jsxs("select", { className: "w-full bg-zinc-50 dark:bg-black border border-zinc-300 dark:border-zinc-700 rounded p-2 text-sm mt-1 text-zinc-900 dark:text-white", value: updates.status, onChange: (e) => setUpdates({ ...updates, status: e.target.value }), children: [
210            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "-- Unchanged --" }),
211            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "completed", children: "Completed" }),
212            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "pending", children: "Queued" }),
213            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "rejected", children: "Rejected" }),
214            /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "suspended", children: "Suspended" })
215          ] })
216        ] })
217      ] }),
218      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-indigo-50 dark:bg-indigo-900/10 rounded-lg border border-indigo-100 dark:border-indigo-800 mb-6 flex items-center justify-between", children: [
219        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
220          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "font-bold text-sm text-indigo-900 dark:text-indigo-200", children: "Full Pipeline Reprocess" }),
221          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-indigo-700 dark:text-indigo-300", children: "Regenerate Audio, Sheets, and JSON Data." })
222        ] }),
223        /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleReprocessAudio, disabled: isSaving, className: "px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded text-xs font-bold shadow-md flex items-center gap-2", children: [
224          /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 12, className: isSaving ? "animate-spin" : "" }),
225          " Reprocess"
226        ] })
227      ] }),
228      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 border-t border-zinc-200 dark:border-zinc-800 pt-4", children: [
229        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-2 rounded-lg text-zinc-600 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:bg-zinc-800 font-bold text-sm", children: "Cancel" }),
230        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleSave, disabled: isSaving, className: "px-4 py-2 rounded-lg bg-zinc-900 dark:bg-white text-white dark:text-black font-bold text-sm hover:opacity-90", children: isSaving ? "Saving..." : "Apply Metadata" })
231      ] })
232    ] })
233  ] });
234};
235const MediaInspector = ({ track, onClose, onRefresh }) => {
236  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
237  const [formData, setFormData] = reactExports.useState(track);
238  const [tagString, setTagString] = reactExports.useState(track.tags.map((t) => typeof t === "string" ? t : t.name).join(", "));
239  const [saving, setSaving] = reactExports.useState(false);
240  const [edits, setEdits] = reactExports.useState([]);
241  const { waveform } = useWaveform(formData.id, formData.waveformJsonPath, formData.waveform);
242  React.useEffect(() => {
243    if (track && track.userId === -1) {
244      fetch(`/api/admin/tracks/${track.id}/edits`).then((res) => res.json()).then((data) => {
245        if (Array.isArray(data)) setEdits(data);
246      }).catch(console.error);
247    }
248  }, [track]);
249  if (!formData) return null;
250  const handleSave = async () => {
251    try {
252      setSaving(true);
253      await api.updateTrackMetadata(formData.id, { ...formData, tags: tagString.split(",").map((t) => t.trim()).filter(Boolean) });
254      onRefresh();
255      onClose();
256    } catch (err) {
257      console.error(err);
258      alert(err.message || "Failed to save changes.");
259    } finally {
260      setSaving(false);
261    }
262  };
263  const handleDelete = async () => {
264    if (confirm("Are you sure you want to delete this track? This action cannot be undone.")) {
265      await api.deleteTrack(formData.id);
266      onRefresh();
267      onClose();
268    }
269  };
270  return /* @__PURE__ */ jsxRuntimeExports.jsx(Drawer, { isOpen: !!track, onClose, title: "Media Inspector", width: "max-w-5xl", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full", children: [
271    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-8 pb-20", children: [
272      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
273        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
274          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase", children: "Title" }),
275          /* @__PURE__ */ jsxRuntimeExports.jsx("input", { className: "w-full bg-zinc-100 dark:bg-zinc-800 border-none rounded p-2 mt-1 text-zinc-900 dark:text-white", value: formData.title, onChange: (e) => setFormData({ ...formData, title: e.target.value }) })
276        ] }),
277        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
278          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase", children: "Artist" }),
279          /* @__PURE__ */ jsxRuntimeExports.jsx(TagAutocomplete, { value: formData.artist, onChange: (val) => setFormData({ ...formData, artist: val }), placeholder: "Andrew Lloyd Webber, Joe Hisaishi..." })
280        ] }),
281        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
282          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase", children: "Description" }),
283          /* @__PURE__ */ jsxRuntimeExports.jsx("textarea", { className: "w-full bg-zinc-100 dark:bg-zinc-800 border-none rounded p-2 mt-1 h-32 resize-none text-zinc-900 dark:text-white", value: formData.description, onChange: (e) => setFormData({ ...formData, description: e.target.value }) })
284        ] }),
285        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
286          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase", children: "Tags" }),
287          /* @__PURE__ */ jsxRuntimeExports.jsx(TagAutocomplete, { value: tagString, onChange: setTagString, placeholder: "piano, virtuoso, 8-bit" })
288        ] }),
289        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg", children: [
290          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 block", children: "Uploader Information" }),
291          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
292            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
293              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: (_a = formData.uploader) == null ? void 0 : _a.avatar, name: ((_b = formData.uploader) == null ? void 0 : _b.name) || "User", size: 40, className: "border border-zinc-200 dark:border-zinc-700" }) }),
294              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
295                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
296                  formData.userId === -1 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
297                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-zinc-900 dark:text-white truncate", children: "Community Contributor" }),
298                    formData.countryCode && /* @__PURE__ */ jsxRuntimeExports.jsx(GlobeFlag, { countryCode: formData.countryCode, size: 16 })
299                  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
300                    "a",
301                    {
302                      href: `/admin/users?userId=${((_c = formData.uploader) == null ? void 0 : _c.id) || formData.userId}`,
303                      target: "_blank",
304                      rel: "noopener noreferrer",
305                      className: "font-bold text-indigo-600 dark:text-indigo-400 hover:underline flex items-center gap-1 truncate",
306                      children: [
307                        ((_d = formData.uploader) == null ? void 0 : _d.name) || `User #${formData.userId}`,
308                        ((_e = formData.uploader) == null ? void 0 : _e.showCountryBadge) && ((_f = formData.uploader) == null ? void 0 : _f.country) && /* @__PURE__ */ jsxRuntimeExports.jsx(GlobeFlag, { countryCode: formData.uploader.country, size: 16 }),
309                        /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 12 })
310                      ]
311                    }
312                  ),
313                  formData.userId === -1 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1.5 py-0.5 bg-amber-100 text-amber-700 text-[9px] uppercase font-bold rounded", children: "Wiki" })
314                ] }),
315                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-500 font-mono", children: [
316                  "ID: ",
317                  formData.userId === -1 ? "N/A" : ((_g = formData.uploader) == null ? void 0 : _g.id) || formData.userId
318                ] })
319              ] })
320            ] }),
321            formData.userId !== -1 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2 pt-2 border-t border-zinc-200 dark:border-zinc-700", children: [
322              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-zinc-600 dark:text-zinc-400", children: [
323                /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { size: 12, className: "shrink-0" }),
324                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: (_h = formData.uploader) == null ? void 0 : _h.email, children: ((_i = formData.uploader) == null ? void 0 : _i.email) || "No Email" })
325              ] }),
326              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
327                /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 12, className: Number(((_j = formData.uploader) == null ? void 0 : _j.trustScore) || 0) < 50 ? "text-red-500" : "text-emerald-500" }),
328                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-zinc-700 dark:text-zinc-300", children: [
329                  "Trust: ",
330                  ((_k = formData.uploader) == null ? void 0 : _k.trustScore) ?? "??"
331                ] })
332              ] })
333            ] })
334          ] })
335        ] }),
336        track.userId === -1 && /* @__P
336URE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg", children: [
337          /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 text-amber-600 dark:text-amber-500", children: "Public Wiki Edits" }),
338          edits.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500", children: "No edits yet." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3 max-h-48 overflow-y-auto custom-scrollbar", children: edits.map((edit) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm border-l-2 border-amber-500 pl-3", children: [
339            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-bold text-zinc-700 dark:text-zinc-300", children: [
340              edit.userName,
341              " ",
342              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-normal text-zinc-500 ml-2", children: new Date(edit.timestamp).toLocaleString() })
343            ] }),
344            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-600 dark:text-zinc-400 text-xs mt-1", children: edit.details })
345          ] }, edit.id)) })
346        ] })
347      ] }),
348      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
349        (() => {
350          const hashColors = getColorsFromHash(formData.title + (formData.artist || ""));
351          return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group rounded-xl overflow-hidden bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800", children: [
352            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-40 relative overflow-hidden", children: [
353              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { background: hashColors.gradient }, className: "w-full h-full absolute inset-0 transition-transform duration-500 group-hover:scale-105 opacity-65" }),
354              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/85 via-black/20 to-black/60 pointer-events-none" }),
355              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-16", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Waveform, { data: waveform, active: true }) }) })
356            ] }),
357            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 bg-zinc-50/80 dark:bg-zinc-950/80 backdrop-blur-sm border-t border-zinc-200 dark:border-zinc-800 flex items-center justify-between text-[10px] font-mono text-zinc-500 px-4", children: [
358              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
359                "DURATION: ",
360                formData.duration ? `${Math.floor(formData.duration / 60)}:${(formData.duration % 60).toString().padStart(2, "0")}` : "N/A"
361              ] }),
362              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
363                "TYPE: ",
364                formData.type || "MIDI"
365              ] })
366            ] })
367          ] });
368        })(),
369        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg", children: [
370          /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: [
371            /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 14, className: "text-indigo-500" }),
372            " Moderation & Level"
373          ] }),
374          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
375            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
376              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-400 uppercase mb-2 block", children: "Difficulty" }),
377              /* @__PURE__ */ jsxRuntimeExports.jsxs("select", { className: "w-full bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-2 text-sm text-zinc-900 dark:text-white outline-none focus:ring-1 focus:ring-indigo-500", value: formData.difficulty, onChange: (e) => setFormData({ ...formData, difficulty: e.target.value }), children: [
378                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { children: "Beginner" }),
379                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { children: "Intermediate" }),
380                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { children: "Advanced" }),
381                /* @__PURE__ */ jsxRuntimeExports.jsx("option", { children: "Virtuoso" })
382              ] })
383            ] }),
384            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
385              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-400 uppercase mb-2 block", children: "Worker Queue Status" }),
386              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-3 gap-2", children: ["pending", "processing", "completed", "paused", "failed"].map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(
387                "button",
388                {
389                  type: "button",
390                  onClick: () => setFormData({ ...formData, status: s }),
391                  className: `px-2 py-1.5 text-[10px] font-bold rounded capitalize border transition-all ${formData.status === s ? "bg-indigo-600 border-indigo-600 text-white" : "bg-transparent border-zinc-300 dark:border-zinc-700 text-zinc-500 hover:border-indigo-500 hover:text-indigo-500"}`,
392                  children: s === "pending" ? "Queued" : s
393                },
394                s
395              )) })
396            ] }),
397            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4", children: [
398              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-400 uppercase mb-2 block", children: "Moderation Visibility" }),
399              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
400                ["active", "suspended", "rejected", "user-deleted"].map((v) => /* @__PURE__ */ jsxRuntimeExports.jsx(
401                  "button",
402                  {
403                    type: "button",
404                    onClick: () => setFormData({ ...formData, visibility: v }),
405                    className: `px-2 py-1.5 text-[10px] font-bold rounded capitalize border transition-all ${formData.visibility === v ? "bg-indigo-600 border-indigo-600 text-white" : "bg-transparent border-zinc-300 dark:border-zinc-700 text-zinc-500 hover:border-indigo-500 hover:text-indigo-500"}`,
406                    children: v.replace("-", " ")
407                  },
408                  v
409                )),
410                /* @__PURE__ */ jsxRuntimeExports.jsx(
411                  "button",
412                  {
413                    type: "button",
414                    onClick: () => {
415                      const presets = ["active", "suspended", "rejected", "user-deleted"];
416                      if (presets.includes(formData.visibility)) {
417                        setFormData({ ...formData, visibility: "" });
418                      }
419                    },
420                    className: `px-2 py-1.5 text-[10px] font-bold rounded capitalize border transition-all ${!["active", "suspended", "rejected", "user-deleted"].includes(formData.visibility) ? "bg-indigo-600 border-indigo-600 text-white" : "bg-transparent border-zinc-300 dark:border-zinc-700 text-zinc-500 hover:border-indigo-500 hover:text-indigo-500"}`,
421                    children: "Custom"
422                  }
423                )
424              ] }),
425              !["active", "suspended", "rejected", "user-deleted"].includes(formData.visibility) && /* @__PURE__ */ jsxRuntimeExports.jsx(
426                "input",
427                {
428                  type: "text",
429                  value: formData.visibility || "",
430                  onChange: (e) => setFormData({ ...formData, visibility: e.target.value }),
431                  placeholder: "Enter custom visibility (e.g. copyright-hold)",
432                  className: "w-full bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded p-2 text-sm text-zinc-900 dark:text-white outline-none focus:ring-1 focus:ring-indigo-500 mt-2 font-mono text-xs"
433                }
434              ),
435              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-zinc-400 dark:text-zinc-500 mt-2 leading-relaxed bg-zinc-50 dark:bg-zinc-900/50 p-2 rounded border border-zinc-150 dark:border-zinc-800", children: [
436                formData.visibility === "active" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-600 dark:text-emerald-400 font-medium", children: "Active: Publicly visible, playable, and searchable across the site." }),
437                formData.visibility === "suspended" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-600 dark:text-amber-400 font-medium", children: "Suspended: Temporarily hidden from public. Original files remain in place. Best for pending reviews or copyright holds." }),
438                formData.visibility === "rejected" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-600 dark:text-red-400 font-medium", children: "Rejected: Permanently banned and physically quarantined off the public asset directory. Used for copyright hits." }),
439                formData.visibility === "user-deleted" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-indigo-600 dark:text-indigo-400 font-medium", children: "User Deleted: Soft-deleted by the original uploader. Completely hidden from public view and uploader menus." }),
440                !["active", "suspended", "rejected", "user-deleted"].includes(formData.visibility) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-600 dark:text-indigo-400 font-medium", children: [
441                  "Custom Visibility: '",
442                  formData.visibility || "",
443                  "'. Visible according to custom DB status mapping."
444                ] })
445              ] })
446            ] })
447          ] })
448        ] }),
449        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg", children: [
450          /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-3 flex items-center gap-2", children: [
451            /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 14, className: "text-indigo-500" }),
452            " File Assets"
453          ] }),
454          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
455            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-xs", children: [
456              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2 text-zinc-700 dark:text-zinc-300", children: [
457                /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 12 }),
458                " Opus Preview"
459              ] }),
460              formData.mp3Path ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "text-emerald-500", size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 text-[10px] uppercase font-bold text-xs", children: "Pending" })
461            ] }),
462            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-xs", children: [
463              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2 text-zinc-700 dark:text-zinc-300", children: [
464                /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 12 }),
465                " Clean MIDI"
466              ] }),
467              formData.cleanMidiPath ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "text-emerald-500", size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 text-[10px] uppercase font-bold", children: "Pending" })
468            ] }),
469            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center text-xs", children: [
470              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2 text-zinc-700 dark:text-zinc-300", children: [
471                /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 12 }),
472                " Original MIDI"
473              ] }),
474              formData.midiPath ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "text-emerald-500", size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 text-[10px] uppercase font-bold", children: "N/A" })
475            ] }),
476            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-3 border-t border-zinc-200 dark:border-zinc-700", children: [
477              /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: formData.cleanMidiPath || "#", download: true, className: "flex-1 text-center py-2 bg-white dark:bg-zinc-900 rounded border border-zinc-200 dark:border-zinc-700 text-[10px] font-bold text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors", children: "CLEAN MIDI" }),
478              /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: formData.midiPath || "#", download: true, className: "flex-1 text-center py-2 bg-white dark:bg-zinc-900 rounded border border-zinc-200 dark:border-zinc-700 text-[10px] font-bold text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors", children: "ORIGINAL" })
479            ] }),
480            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-3 border-t border-zinc-200 dark:border-zinc-700", children: [
481              /* @__PURE__ */ jsxRuntimeExports.jsx(
482                "input",
483                {
484                  type: "file",
485                  accept: ".mid,.midi",
486                  onChange: async (e) => {
487                    var _a2;
488                    const file = (_a2 = e.target.files) == null ? void 0 : _a2[0];
489                    if (!file) return;
490                    if (!confirm(`Are you sure you want to replace the MIDI file for track ID #${formData.id}? This will purge all old assets and queue it for full reprocessing.`)) {
491                      e.target.value = "";
492                      return;
493                    }
494                    try {
495                      setSaving(true);
496                      const res = await api.replaceMidi(Number(formData.id), file);
497                      alert(res.message || "MIDI file replaced successfully!");
498                      onRefresh();
499                      onClose();
500                    } catch (err) {
501                      alert(`Replacement failed: ${err.message}`);
502                    } finally {
503                      setSaving(false);
504                    }
505                  },
506                  style: { display: "none" },
507                  id: "replace-midi-file-input"
508                }
509              ),
510              /* @__PURE__ */ jsxRuntimeExports.jsxs(
511                "button",
512                {
513                  type: "button",
514                  onClick: () => {
515                    var _a2;
516                    return (_a2 = document.getElementById("replace-midi-file-input")) == null ? void 0 : _a2.click();
517                  },
518                  disabled: saving,
519                  className: "w-full text-center py-2 bg-indigo-50 dark:bg-indigo-950/20 text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-900/30 rounded border border-indigo-150 dark:border-indigo-900/40 text-[10px] font-bold transition-colors flex items-center justify-center gap-1.5",
520                  children: [
521                    /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 12 }),
522                    "REPLACE MIDI FILE"
523                  ]
524                }
525              )
526            ] })
527          ] })
528        ] })
529      ] })
530    ] }),
531    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute bottom-0 left-0 right-0 p-6 bg-white/80 dark:bg-[#1a1a1a]/80 backdrop-blur-md border-t border-zinc-200 dark:border-zinc-800 flex gap-4 z-20", children: [
532      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleDelete, className: "px-6 py-3 bg-red-100 text-red-600 hover:bg-red-200 rounded-xl font-bold transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash, { size: 20 }) }),
533      /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleSave, disabled: saving, className: "flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 rounded-xl flex items-center justify-center gap-2 shadow-lg shadow-indigo-500/20 transition-all disabled:opacity-50", children: [
534        saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "animate-spin", size: 20 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { size: 20 }),
535        "Save Changes"
536      ] })
537    ] })
538  ] }) });
539};
540const WorkerConsole = ({ isOpen, onClose }) => {
541  const [status, setStatus] = reactExports.useState({ pending: 0, processing: 0, completed: 0, metadataOnly: 0, rejected: 0, lastHeartbeat: null });
542  const [isLoading, setIsLoading] = reactExports.useState(true);
543  const intervalRef = reactExports.useRef(null);
544  const pollStatus = async () => {
545    try {
546      const res = await api.getWorkerStatus();
547      setStatus(res);
548    } catch (e) {
549    } finally {
550      setIsLoading(false);
551    }
552  };
553  reactExports.useEffect(() => {
554    if (isOpen) {
555      setIsLoading(true);
556      pollStatus();
557      intervalRef.current = setInterval(pollStatus, 2e3);
558    } else {
559      if (intervalRef.current) clearInterval(intervalRef.current);
560    }
561    return () => {
562      if (intervalRef.current) clearInterval(intervalRef.current);
563    };
564  }, [isOpen]);
565  const hb = status.lastHeartbeat;
566  const hbTime = hb ? new Date(hb.endsWith("Z") ? hb : hb + "Z").getTime() : 0;
567  const isWorkerOnline = hb && Date.now() - hbTime < 6e4;
568  const lastSeen = hb ? new Date(hb.endsWith("Z") ? hb : hb + "Z").toLocaleTimeString() : "Never";
569  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `border-t border-zinc-200 dark:border-zinc-800 bg-black transition-all duration-300 ${isOpen ? "h-48" : "h-0 overflow-hidden"}`, children: [
570    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-8 flex items-center justify-between px-4 bg-zinc-900 border-b border-zinc-800", children: [
571      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] font-bold text-zinc-500 uppercase flex items-center gap-2", children: [
572        /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 10 }),
573        " Worker Console"
574      ] }),
575      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
576        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: pollStatus, title: "Ping Worker", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 12, className: "text-zinc-500 hover:text-white" }) }),
577        /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14, className: "text-zinc-500" }) })
578      ] })
579    ] }),
580    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 flex gap-8", children: [
581      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
582        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-white", children: status.pending }),
583        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-zinc-500", children: "Pending" })
584      ] }),
585      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
586        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-indigo-500 animate-pulse", children: status.processing }),
587        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-zinc-500", children: "Processing" })
588      ] }),
589      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
590        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-emerald-500", children: status.completed }),
591        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-zinc-500", children: "Completed" })
592      ] }),
593      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
594        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-teal-500", children: status.metadataOnly || 0 }),
595        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-zinc-500", children: "Metadata Only" })
596      ] }),
597      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center", children: [
598        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-red-500", children: status.rejected }),
599        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-zinc-500", children: "Failed" })
600      ] }),
601      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 border-l border-zinc-800 pl-8 flex flex-col justify-center", children: [
602        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
603          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-2 h-2 rounded-full ${isLoading ? "bg-zinc-500 animate-pulse" : isWorkerOnline ? "bg-emerald-500 animate-pulse" : "bg-red-500"}` }),
604          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-sm font-bold ${isWorkerOnline ? "text-emerald-500" : "text-red-500"}`, children: isLoading ? "Loading..." : isWorkerOnline ? "Worker Online" : "Worker Offline" })
605        ] }),
606        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-zinc-500 font-mono", children: isLoading ? "Checking worker..." : `Last Seen: ${lastSeen}` }),
607        !isLoading && !isWorkerOnline && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 text-[10px] text-amber-500 flex items-center gap-1", children: [
608          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 10 }),
609          " Check container logs."
610        ] })
611      ] })
612    ] })
613  ] });
614};
615const MediaLibrary = ({ refreshTrigger, onUploadClick }) => {
616  const [inspectingTrack, setInspectingTrack] = reactExports.useState(null);
617  const [localRefresh, setLocalRefresh] = reactExports.useState(0);
618  const [selectedIds, setSelectedIds] = reactExports.useState([]);
619  const [isBatchEditOpen, setIsBatchEditOpen] = reactExports.useState(false);
620  const [isSoundFontModalOpen, setIsSoundFontModalOpen] = reactExports.useState(false);
621  const [isBulkReprocessOpen, setIsBulkReprocessOpen] = reactExports.useState(false);
622  const [isConsoleOpen, setIsConsoleOpen] = reactExports.useState(false);
623  const [playingTrackId, setPlayingTrackId] = reactExports.useState(null);
624  const [playbackEngine, setPlaybackEngine] = reactExports.useState(null);
625  const audioRef = reactExports.useRef(null);
626  const activePreviewTrackIdRef = reactExports.useRef(null);
627  reactExports.useEffect(() => {
628    const params = new URLSearchParams(window.location.search);
629    const id = params.get("id");
630    if (id) {
631      api.getAdminTrack(id).then(setInspectingTrack).catch(console.error);
632    }
633    return () => {
634      if (audioRef.current) {
635        audioRef.current.pause();
636      }
637      synth.stop();
638    };
639  }, []);
640  const refresh = () => setLocalRefresh((p) => p + 1);
641  const fetcher = reactExports.useCallback((params) => api.getAdminTracks(params), []);
642  const handleReprocessComplete = () => {
643    refresh();
644    setIsBulkReprocessOpen(false);
645    setIsConsoleOpen(true);
646  };
647  const handleSingleReprocess = async (id) => {
648    try {
649      await api.reprocessTracks({ ids: [id] });
650      refresh();
651    } catch (e) {
652      console.error("Failed to queue job", e);
653    }
654  };
655  const handleAnalyzeStructure = async (id) => {
656    try {
657      await api.analyzeTrackStructure(id);
658      alert("Structural analysis queued for track " + id);
659      refresh();
660    } catch (e) {
661      console.error("Failed to queue analysis", e);
662      alert("Analysis failed: " + e.message);
663    }
664  };
665  const togglePreview = async (track) => {
666    if (playingTrackId === track.id) {
667      activePreviewTrackIdRef.current = null;
668      if (audioRef.current) {
669        audioRef.current.pause();
670      }
671      synth.stop();
672      setPlayingTrackId(null);
673      setPlaybackEngine(null);
674    } else {
675      activePreviewTrackIdRef.current = track.id;
676      if (audioRef.current) {
677        audioRef.current.pause();
678      }
679      synth.stop();
680      if (track.opusPath) {
681        const audio = new Audio(track.opusPath);
682        audio.onended = () => {
683          if (activePreviewTrackIdRef.current === track.id) {
684            setPlayingTrackId(null);
685            setPlaybackEngine(null);
686            activePreviewTrackIdRef.current = null;
687          }
688        };
689        audio.play().catch((e) => {
690          console.error("Failed to play preview", e);
691          if (activePreviewTrackIdRef.current === track.id) {
692            setPlayingTrackId(null);
693            setPlaybackEngine(null);
694            activePreviewTrackIdRef.current = null;
695          }
696        });
697        audioRef.current = audio;
698        setPlayingTrackId(track.id);
699        setPlaybackEngine("Opus/MP3 Audio");
700      } else {
701        const midiUrl = track.cleanMidiPath || track.midiPath;
702        if (midiUrl) {
703          setPlayingTrackId(track.id);
704          setPlaybackEngine("HTML5 MIDI Synth (Loading...)");
705          try {
706            const parsed = await parseMidiInstruments([midiUrl]);
707            if (activePreviewTrackIdRef.current !== track.id) {
708              return;
709            }
710            await synth.loadTracks(
711              parsed.tracks,
712              parsed.tickDiv,
713              parsed.tempo,
714              "free",
715              "GeneralUser-GS",
716              [midiUrl],
717              () => {
718              },
719              true,
720              "GeneralUser-GS",
721              true
722            );
723            if (activePreviewTrackIdRef.current !== track.id) {
724              synth.stop();
725              return;
726            }
727            await synth.play();
728            setPlaybackEngine("HTML5 MIDI Synth");
729          } catch (e) {
730            console.error("Failed to play MIDI preview", e);
731            if (activePreviewTrackIdRef.current === track.id) {
732              alert("Failed to play MIDI preview: " + e.message);
733              setPlayingTrackId(null);
734              setPlaybackEngine(null);
735              activePreviewTrackIdRef.current = null;
736            }
737          }
738        } else {
739          alert("No audio preview or MIDI file available");
740        }
741      }
742    }
743  };
744  const actions = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center", children: [
745    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `transition-opacity flex gap-2 ${selectedIds.length > 0 ? "opacity-100" : "opacity-0 pointer-events-none"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
746      "button",
747      {
748        onClick: () => setIsBatchEditOpen(true),
749        className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 text-xs font-bold",
750        children: [
751          /* @__PURE__ */ jsxRuntimeExports.jsx(SquarePen, { size: 14 }),
752          " Batch Edit (",
753          selectedIds.length,
754          ")"
755        ]
756      }
757    ) }),
758    /* @__PURE__ */ jsxRuntimeExports.jsxs(
759      "button",
760      {
761        onClick: () => setIsBulkReprocessOpen(true),
762        className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-xs font-bold border border-zinc-200 dark:border-zinc-700",
763        title: "Reprocess MIDI ID Range",
764        children: [
765          /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 14 }),
766          " Bulk Reprocess"
767        ]
768      }
769    ),
770    /* @__PURE__ */ jsxRuntimeExports.jsxs(
771      "button",
772      {
773        onClick: () => setIsSoundFontModalOpen(true),
774        className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-xs font-bold border border-zinc-200 dark:border-zinc-700",
775        children: [
776          /* @__PURE__ */ jsxRuntimeExports.jsx(Settings2, { size: 14 }),
777          " SoundFonts"
778        ]
779      }
780    ),
781    /* @__PURE__ */ jsxRuntimeExports.jsxs(
782      "button",
783      {
784        onClick: onUploadClick,
785        className: "flex items-center gap-2 px-3 py-1.5 rounded-lg bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-200 dark:hover:bg-zinc-700 text-xs font-bold",
786        children: [
787          /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 14 }),
788          " New Upload"
789        ]
790      }
791    )
792  ] });
793  const COLUMNS = [
794    { label: "MIDI ID", key: "id" },
795    { label: "Track", key: "title" },
796    // Maps to title for sorting
797    { label: "User ID", key: "userId" },
798    { label: "Description", key: "description" },
799    { label: "Views", key: "views" },
800    { label: "Health", key: "healthScore" },
801    { label: "Created", key: "createdAt" },
802    { label: "Processed", key: "lastProcessedAt" },
803    { label: "Status", key: "status" },
804    { label: "Actions", key: "actions" }
805  ];
806  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full", children: [
807    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
808      DataTable,
809      {
810        title: "Media Library",
811        icon: Database,
812        placeholder: "Search tracks...",
813        filters: [
814          { key: "status", label: "Status", options: [
815            { label: "Completed", value: "completed" },
816            { label: "Queued", value: "pending" },
817            { label: "Processing", value: "processing" },
818            { label: "Paused", value: "paused" },
819            { label: "Failed", value: "failed" },
820            { label: "Suspended", value: "suspended" },
821            { label: "Rejected", value: "rejected" },
822            { label: "User Deleted", value: "user-deleted" }
823          ] },
824          { key: "id", label: "MIDI ID", type: "text", placeholder: "MIDI ID" },
825          { key: "userId", label: "User ID", type: "text", placeholder: "User ID" }
826        ],
827        onFetch: fetcher,
828        reloadTrigger: refreshTrigger + localRefresh,
829        onRowClick: setInspectingTrack,
830        initialSearch: new URLSearchParams(window.location.search).get("id") || "",
831        initialFilters: new URLSearchParams(window.location.search).get("id") ? { id: new URLSearchParams(window.location.search).get("id") } : {},
832        columnHeaders: COLUMNS,
833        dense: true,
834        columns: (track) => {
835          var _a;
836          return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
837            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-2 text-zinc-500 font-mono text-xs w-[7ch] text-center", children: track.id }),
838            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
839              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-6 h-6 rounded bg-zinc-100 dark:bg-zinc-800 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: track.thumbnail, className: "w-full h-full object-cover" }) }),
840              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
841                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-900 dark:text-white truncate text-xs max-w-[150px]", title: track.title, children: track.title }),
842                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500 truncate max-w-[150px]", children: track.artist })
843              ] })
844            ] }) }),
845            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-xs font-mono text-zinc-600 dark:text-zinc-400", title: (_a = track.uploader) == null ? void 0 : _a.name, children: String(track.userId || "Unknown") }),
846            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-xs text-zinc-500 truncate max-w-[200px]", title: track.description, children: track.description ? track.description.length > 50 ? track.description.substring(0, 50) + "..." : track.description : "-" }),
847            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-xs font-mono text-zinc-600 dark:text-zinc-400", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
848              /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 10 }),
849              " ",
850              track.views
851            ] }) }),
852            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-1 font-bold ${Number(track.healthScore || 0) < 50 ? "text-red-500" : "text-emerald-500"}`, children: [
853              /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 10 }),
854              " ",
855              String(track.healthScore || 0),
856              "%"
857            ] }) }),
858            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-xs text-zinc-500 whitespace-nowrap", children: new Date(track.createdAt || track.uploadedAt).toLocaleDateString() }),
859            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-[10px] text-zinc-500 whitespace-nowrap font-mono", children: track.lastProcessedAt ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
860              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-600 dark:text-zinc-400", children: new Date(track.lastProcessedAt).toLocaleDateString() }),
861              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "opacity-70", children: new Date(track.lastProcessedAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
862            ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "opacity-30 italic", children: "Never" }) }),
863            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
864              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-bold uppercase tracking-wide leading-none ${track.status === "completed" ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400" : track.status === "pending" ? "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" : track.status === "processing" ? "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400 animate-pulse" : track.status === "paused" ? "bg-zinc-100 text-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-400" : "bg-rose-100 dark:bg-rose-900/30 text-rose-700 dark:text-rose-400"}`, children: track.status === "pending" ? "queued" : track.status }),
865              track.visibility !== "active" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-bold uppercase tracking-wide leading-none ${track.visibility === "suspended" ? "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400" : track.visibility === "rejected" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-zinc-100 text-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-400"}`, children: track.visibility === "user-deleted" ? "deleted" : track.visibility })
866            ] }) }),
867            /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
868              /* @__PURE__ */ jsxRuntimeExports.jsx(
869                "button",
870                {
871                  onClick: (e) => {
872                    e.stopPropagation();
873                    togglePreview(track);
874                  },
875                  className: `p-1.5 rounded transition-colors ${playingTrackId === track.id ? "text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-950/40 bg-indigo-50 dark:bg-indigo-950/20 animate-pulse" : "text-zinc-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"}`,
876                  title: playingTrackId === track.id ? `Stop (${playbackEngine})` : "Play Preview",
877                  children: playingTrackId === track.id ? /* @__PURE__ */ jsxRuntimeExports.jsx(Square, { size: 14, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 14 })
878                }
879              ),
880              /* @__PURE__ */ jsxRuntimeExports.jsx(
881                "a",
882                {
883                  href: `/watch?v=${track.id}`,
884                  target: "_blank",
885                  rel: "noopener noreferrer",
886                  onClick: (e) => e.stopPropagation(),
887                  className: "p-1.5 text-zinc-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors",
888                  title: "Open Watch Page",
889                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 14 })
890                }
891              ),
892              /* @__PURE__ */ jsxRuntimeExports.jsx(
893                "button",
894                {
895                  onClick: (e) => {
896                    e.stopPropagation();
897                    handleSingleReprocess(track.id);
898                  },
899                  className: "p-1.5 text-zinc-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 rounded transition-colors",
900                  title: "Reprocess Track",
901                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 })
902                }
903              ),
904              /* @__PURE__ */ jsxRuntimeExports.jsx(
905                "button",
906                {
907                  onClick: (e) => {
908                    e.stopPropagation();
909                    handleAnalyzeStructure(track.id);
910                  },
911                  className: "p-1.5 text-zinc-500 hover:text-amber-600 hover:bg-amber-50 dark:hover:bg-amber-900/20 rounded transition-colors",
912                  title: "Request AI Structural Analysis",
913                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { size: 14 })
914                }
915              )
916            ] }) })
917          ] });
918        },
919        selectable: true,
920        onSelectionChange: setSelectedIds,
921        actions
922      }
923    ) }),
924    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-8 bg-zinc-100 dark:bg-zinc-900 border-t border-zinc-200 dark:border-zinc-800 flex justify-between items-center px-4 cursor-pointer hover:bg-zinc-200 dark:hover:bg-zinc-800", onClick: () => setIsConsoleOpen(!isConsoleOpen), children: [
925      /* @__P
925URE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-bold text-zinc-500 flex items-center gap-2", children: [
926        /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 12 }),
927        " Worker Status"
928      ] }),
929      playingTrackId && playbackEngine && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-semibold text-indigo-600 dark:text-indigo-400 flex items-center gap-1.5 animate-pulse", children: [
930        /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 10, fill: "currentColor" }),
931        " Previewing Track ",
932        playingTrackId,
933        " via ",
934        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "underline", children: playbackEngine })
935      ] }),
936      isConsoleOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14, className: "text-zinc-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14, className: "text-zinc-500" })
937    ] }),
938    /* @__PURE__ */ jsxRuntimeExports.jsx(WorkerConsole, { isOpen: isConsoleOpen, onClose: () => setIsConsoleOpen(false) }),
939    inspectingTrack && /* @__PURE__ */ jsxRuntimeExports.jsx(MediaInspector, { track: inspectingTrack, onClose: () => setInspectingTrack(null), onRefresh: refresh }, inspectingTrack.id),
940    isBatchEditOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(BatchEditModal, { trackIds: selectedIds, onClose: () => setIsBatchEditOpen(false), onComplete: () => {
941      refresh();
942      setIsBatchEditOpen(false);
943    } }),
944    isBulkReprocessOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(BulkReprocessModal, { onClose: () => setIsBulkReprocessOpen(false), onComplete: handleReprocessComplete }),
945    /* @__PURE__ */ jsxRuntimeExports.jsx(SoundFontModal, { isOpen: isSoundFontModalOpen, onClose: () => setIsSoundFontModalOpen(false) })
946  ] });
947};
948export {
949  MediaLibrary as default
950};

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.