diff --git a/package.json b/package.json index cc0caea..15451f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "netfelix-audio-fix", - "version": "2026.04.14.4", + "version": "2026.04.14.5", "scripts": { "dev:server": "NODE_ENV=development bun --hot server/index.tsx", "dev:client": "vite", diff --git a/server/api/settings.ts b/server/api/settings.ts index 1e04152..755c5e4 100644 --- a/server/api/settings.ts +++ b/server/api/settings.ts @@ -107,12 +107,20 @@ app.patch("/schedule", async (c) => { // ─── MQTT ──────────────────────────────────────────────────────────────────── app.post("/mqtt", async (c) => { - const body = await c.req.json<{ url?: string; topic?: string; username?: string; password?: string }>(); + const body = await c.req.json<{ + enabled?: boolean; + url?: string; + topic?: string; + username?: string; + password?: string; + }>(); + const enabled = body.enabled === true; const url = (body.url ?? "").trim(); const topic = (body.topic ?? "jellyfin/events").trim(); const username = (body.username ?? "").trim(); const password = body.password ?? ""; + setConfig("mqtt_enabled", enabled ? "1" : "0"); setConfig("mqtt_url", url); setConfig("mqtt_topic", topic || "jellyfin/events"); setConfig("mqtt_username", username); diff --git a/server/db/index.ts b/server/db/index.ts index ee59631..a4516f0 100644 --- a/server/db/index.ts +++ b/server/db/index.ts @@ -22,6 +22,7 @@ const ENV_MAP: Record = { sonarr_api_key: "SONARR_API_KEY", sonarr_enabled: "SONARR_ENABLED", audio_languages: "AUDIO_LANGUAGES", + mqtt_enabled: "MQTT_ENABLED", mqtt_url: "MQTT_URL", mqtt_topic: "MQTT_TOPIC", mqtt_username: "MQTT_USERNAME", diff --git a/server/db/schema.ts b/server/db/schema.ts index 0686aed..ad9b2f0 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -144,6 +144,7 @@ export const DEFAULT_CONFIG: Record = { process_schedule_start: "01:00", process_schedule_end: "07:00", + mqtt_enabled: "0", mqtt_url: "", mqtt_topic: "jellyfin/events", mqtt_username: "", diff --git a/server/services/mqtt.ts b/server/services/mqtt.ts index fda5616..dd1244f 100644 --- a/server/services/mqtt.ts +++ b/server/services/mqtt.ts @@ -35,6 +35,7 @@ function setStatus(next: MqttStatus, err: string | null = null): void { } function readConfig(): MqttConfig | null { + if (getConfig("mqtt_enabled") !== "1") return null; const url = getConfig("mqtt_url") ?? ""; if (!url) return null; return { diff --git a/src/features/settings/MqttSection.tsx b/src/features/settings/MqttSection.tsx index ddeaf43..4bc68f7 100644 --- a/src/features/settings/MqttSection.tsx +++ b/src/features/settings/MqttSection.tsx @@ -74,7 +74,22 @@ async function copyText(text: string): Promise { } } -function CopyableValue({ label, value, mono = true }: { label: string; value: string; mono?: boolean }) { +/** + * One row of the plugin-setup checklist. `copyable=false` is for fields the + * user selects from a dropdown / toggles (Status, Notification Type, Use TLS, + * etc.) — copying their value doesn't help, so we just display it. + */ +function SetupValue({ + label, + value, + mono = true, + copyable = true, +}: { + label: string; + value: string; + mono?: boolean; + copyable?: boolean; +}) { const [copied, setCopied] = useState<"ok" | "fail" | null>(null); const copy = async () => { const ok = await copyText(value); @@ -83,22 +98,27 @@ function CopyableValue({ label, value, mono = true }: { label: string; value: st }; return (
-
{label}
+
{label}
 				{value}
 			
- + {copyable ? ( + + ) : ( + select + )}
); } export function MqttSection({ cfg, locked }: { cfg: Record; locked: Set }) { + const [enabled, setEnabled] = useState(cfg.mqtt_enabled === "1"); const [url, setUrl] = useState(cfg.mqtt_url ?? ""); const [topic, setTopic] = useState(cfg.mqtt_topic || "jellyfin/events"); const [username, setUsername] = useState(cfg.mqtt_username ?? ""); @@ -142,7 +162,7 @@ export function MqttSection({ cfg, locked }: { cfg: Record; lock setSaving(true); setSavedMsg(""); try { - await api.post("/api/settings/mqtt", { url, topic, username, password }); + await api.post("/api/settings/mqtt", { enabled, url, topic, username, password }); setSavedMsg(password ? "Saved." : "Saved (password unchanged)."); setPassword(""); setTimeout(() => setSavedMsg(""), 2500); @@ -173,6 +193,10 @@ export function MqttSection({ cfg, locked }: { cfg: Record; lock ? "text-amber-700 bg-amber-50 border-amber-300" : "text-gray-600 bg-gray-50 border-gray-300"; + const broker = parseBroker(url); + const useCredentials = !!(username || cfg.mqtt_password); + const jellyfinBase = (cfg.jellyfin_url ?? "").replace(/\/$/, "") || "http://jellyfin.lan:8096"; + return (
@@ -186,56 +210,68 @@ export function MqttSection({ cfg, locked }: { cfg: Record; lock re-analyze the item, confirming it as done or flipping it back to pending if something didn't stick.

-