mqtt setup panel: match jellyfin webhook plugin's actual fields
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m4s

the plugin's MQTT destination form uses different field names and
a different shape than what we'd documented:
- single 'Broker URL' → split 'MQTT Server' + 'MQTT Port' (+ 'Use TLS')
- 'Events' → 'Notification Type', and 'Item Updated' doesn't exist;
  jellyfin reports every file change as 'Item Added'
- plugin requires Webhook Name, Status, Use Credentials, Quality of
  Service fields that weren't in the panel

derive server/port/TLS from the saved mqtt_url so the copy buttons
give back the exact values the plugin expects. handler still accepts
ItemUpdated as a safety net in case the plugin adds it later.
This commit is contained in:
2026-04-14 09:07:32 +02:00
parent a27e4f4025
commit 62ec7e0255
2 changed files with 52 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "netfelix-audio-fix",
"version": "2026.04.14.2",
"version": "2026.04.14.3",
"scripts": {
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
"dev:client": "vite",

View File

@@ -28,6 +28,23 @@ const HANDLEBARS_TEMPLATE = `{
"itemType": "{{ItemType}}"
}`;
/**
* Pull host / port / TLS out of the mqtt_url the user saved. The Jellyfin
* plugin's MQTT destination asks for host and port as separate fields
* plus an explicit TLS checkbox, whereas we store a single URL.
*/
function parseBroker(raw: string): { host: string; port: string; useTls: boolean } {
if (!raw) return { host: "", port: "1883", useTls: false };
try {
const u = new URL(raw);
const useTls = u.protocol === "mqtts:" || u.protocol === "wss:";
const port = u.port || (useTls ? "8883" : "1883");
return { host: u.hostname, port, useTls };
} catch {
return { host: "", port: "1883", useTls: false };
}
}
function CopyableValue({ label, value, mono = true }: { label: string; value: string; mono?: boolean }) {
const [copied, setCopied] = useState(false);
const copy = async () => {
@@ -230,13 +247,40 @@ export function MqttSection({ cfg, locked }: { cfg: Record<string, string>; lock
Plugin detected{plugin.plugin?.Version ? ` (v${plugin.plugin.Version})` : ""}. Add an MQTT destination with:
</p>
)}
<div className="mt-2">
<CopyableValue label="Broker URL" value={url || "mqtt://broker:1883"} />
<CopyableValue label="Topic" value={topic || "jellyfin/events"} />
<CopyableValue label="Events" value="Item Added, Item Updated" mono={false} />
<CopyableValue label="Item types" value="Movie, Episode" mono={false} />
<CopyableValue label="Template" value={HANDLEBARS_TEMPLATE} />
</div>
<p className="text-xs text-gray-500 mt-1 mb-2">
Open Jellyfin <span className="font-mono">Dashboard Plugins Webhook Add Generic Destination </span> MQTT
tab, and fill in:
</p>
{(() => {
const broker = parseBroker(url);
const useCredentials = !!(username || cfg.mqtt_password);
return (
<div className="mt-2">
<CopyableValue label="Webhook Name" value="Audio Fix" mono={false} />
<CopyableValue label="Webhook Url" value="n/a" mono={false} />
<CopyableValue label="Status" value="Enabled" mono={false} />
<CopyableValue label="Notification Type" value="Item Added" mono={false} />
<CopyableValue label="Item Type" value="Movies, Episodes" mono={false} />
<CopyableValue label="MQTT Server" value={broker.host || "broker.lan"} />
<CopyableValue label="MQTT Port" value={broker.port} />
<CopyableValue label="Use TLS" value={broker.useTls ? "Enabled" : "Disabled"} mono={false} />
<CopyableValue label="Use Credentials" value={useCredentials ? "Enabled" : "Disabled"} mono={false} />
{useCredentials && (
<>
<CopyableValue label="Username" value={username || "(same as above)"} />
<CopyableValue label="Password" value={cfg.mqtt_password ? "(same as above)" : ""} />
</>
)}
<CopyableValue label="Topic" value={topic || "jellyfin/events"} />
<CopyableValue label="Quality of Service" value="At most once (QoS 0)" mono={false} />
<CopyableValue label="Template" value={HANDLEBARS_TEMPLATE} />
</div>
);
})()}
<p className="text-[11px] text-gray-400 mt-2">
Jellyfin's plugin doesn't expose an "Item Updated" event. When we rewrite a file, Jellyfin treats it as a new add
and fires <span className="font-mono">Item Added</span> that's the event we listen for.
</p>
</div>
</div>
);