diff --git a/package.json b/package.json index f7e1a74..cc0caea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "netfelix-audio-fix", - "version": "2026.04.14.3", + "version": "2026.04.14.4", "scripts": { "dev:server": "NODE_ENV=development bun --hot server/index.tsx", "dev:client": "vite", diff --git a/src/features/settings/MqttSection.tsx b/src/features/settings/MqttSection.tsx index 079516e..ddeaf43 100644 --- a/src/features/settings/MqttSection.tsx +++ b/src/features/settings/MqttSection.tsx @@ -45,12 +45,41 @@ function parseBroker(raw: string): { host: string; port: string; useTls: boolean } } +async function copyText(text: string): Promise { + // Secure-context fast path + if (navigator.clipboard?.writeText) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + /* fall through to legacy */ + } + } + // HTTP LAN fallback: temporary textarea + execCommand. + try { + const el = document.createElement("textarea"); + el.value = text; + el.setAttribute("readonly", ""); + el.style.position = "fixed"; + el.style.top = "0"; + el.style.left = "0"; + el.style.opacity = "0"; + document.body.appendChild(el); + el.select(); + const ok = document.execCommand("copy"); + document.body.removeChild(el); + return ok; + } catch { + return false; + } +} + function CopyableValue({ label, value, mono = true }: { label: string; value: string; mono?: boolean }) { - const [copied, setCopied] = useState(false); + const [copied, setCopied] = useState<"ok" | "fail" | null>(null); const copy = async () => { - await navigator.clipboard.writeText(value); - setCopied(true); - setTimeout(() => setCopied(false), 1500); + const ok = await copyText(value); + setCopied(ok ? "ok" : "fail"); + setTimeout(() => setCopied(null), 1500); }; return (
@@ -63,7 +92,7 @@ function CopyableValue({ label, value, mono = true }: { label: string; value: st onClick={copy} className="text-xs px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-100 flex-shrink-0" > - {copied ? "Copied" : "Copy"} + {copied === "ok" ? "Copied" : copied === "fail" ? "Select & ⌘C" : "Copy"}
); @@ -257,7 +286,10 @@ export function MqttSection({ cfg, locked }: { cfg: Record; lock return (
- + @@ -278,8 +310,9 @@ export function MqttSection({ cfg, locked }: { cfg: Record; lock ); })()}

- Jellyfin's plugin doesn't expose an "Item Updated" event. When we rewrite a file, Jellyfin treats it as a new add - and fires Item Added — that's the event we listen for. + Notes: the plugin's "Webhook Url" field is for HTTP destinations; MQTT ignores it, but the form won't save empty, + so we put the broker URL there as a placeholder. Jellyfin doesn't expose an "Item Updated" event — any file + change fires Item Added, which is what we listen for.