mqtt: split end-to-end test into its own labelled block with instructions
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
the test button doesn't belong next to Save — running it is an optional verification, not part of saving config. pulled it into a dedicated 'End-to-end test' section below the plugin setup panel with a numbered 4-step recipe: add Playback Start temporarily, hit Start test, start playback in jellyfin, read the result, then remove Playback Start. Notification Type in the setup panel now lists only 'Item Added' since that's all production needs.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "netfelix-audio-fix",
|
||||
"version": "2026.04.14.7",
|
||||
"version": "2026.04.14.8",
|
||||
"scripts": {
|
||||
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
||||
"dev:client": "vite",
|
||||
|
||||
@@ -286,35 +286,9 @@ export function MqttSection({ cfg, locked }: { cfg: Record<string, string>; lock
|
||||
<Button onClick={save} disabled={saving || allLocked}>
|
||||
{saving ? "Saving…" : "Save"}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={runTest} disabled={testing || !url}>
|
||||
{testing ? "Testing…" : "Test end-to-end"}
|
||||
</Button>
|
||||
{savedMsg && <span className="text-sm text-green-700">✓ {savedMsg}</span>}
|
||||
</div>
|
||||
|
||||
{testResult && (
|
||||
<div className="mt-3 text-sm space-y-1">
|
||||
{/* Step 1: broker reachable */}
|
||||
<div className={testResult.brokerConnected ? "text-green-700" : "text-red-700"}>
|
||||
{testResult.brokerConnected ? "✓" : "✗"} Broker reachable & credentials accepted
|
||||
</div>
|
||||
{/* Step 2: webhook received */}
|
||||
{testResult.brokerConnected && (
|
||||
<div className={testResult.receivedMessage ? "text-green-700" : "text-amber-700"}>
|
||||
{testResult.receivedMessage
|
||||
? `✓ Received webhook on topic "${topic || "jellyfin/events"}" — the loop is closed.`
|
||||
: "⚠ No webhook in 30s. Start playing any movie or episode in Jellyfin (or add/edit an item) while the test is running. If that still produces nothing: verify the plugin destination is Enabled, Notification Type includes Playback Start (and Item Added), Item Type covers Movies/Episodes, and the Jellyfin host can reach this broker."}
|
||||
</div>
|
||||
)}
|
||||
{testResult.samplePayload && (
|
||||
<pre className="text-xs bg-gray-50 border rounded px-2 py-1 font-mono overflow-x-auto">
|
||||
{testResult.samplePayload}
|
||||
</pre>
|
||||
)}
|
||||
{testResult.error && <div className="text-red-700">✗ {testResult.error}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Plugin + setup instructions */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||
<div className="text-sm font-medium mb-2">Jellyfin Webhook plugin setup</div>
|
||||
@@ -344,12 +318,7 @@ export function MqttSection({ cfg, locked }: { cfg: Record<string, string>; lock
|
||||
<SetupValue label="Webhook Name" value="Audio Fix" mono={false} />
|
||||
<SetupValue label="Webhook Url" value={url || "(your broker URL)"} />
|
||||
<SetupValue label="Status" value="Enabled" mono={false} copyable={false} />
|
||||
<SetupValue
|
||||
label="Notification Type"
|
||||
value="Item Added, Playback Start"
|
||||
mono={false}
|
||||
copyable={false}
|
||||
/>
|
||||
<SetupValue label="Notification Type" value="Item Added" mono={false} copyable={false} />
|
||||
<SetupValue label="Item Type" value="Movies, Episodes" mono={false} copyable={false} />
|
||||
|
||||
<div className="text-[11px] font-semibold text-gray-500 uppercase mt-3 mb-1">MQTT settings</div>
|
||||
@@ -375,13 +344,61 @@ export function MqttSection({ cfg, locked }: { cfg: Record<string, string>; lock
|
||||
<SetupValue label="Template" value={HANDLEBARS_TEMPLATE} />
|
||||
</div>
|
||||
<p className="text-[11px] text-gray-400 mt-3">
|
||||
Notes: "Server Url" is Jellyfin's own base URL (used for rendered links). "Webhook Url" is required by
|
||||
the form even for MQTT destinations; paste your broker URL so validation passes. We subscribe to{" "}
|
||||
<span className="font-mono">Item Added</span> for the real ping-pong (post-ffmpeg file mutations show up
|
||||
as new adds in Jellyfin) and also enable <span className="font-mono">Playback Start</span> — it's a
|
||||
reliable trigger for the Test button and is ignored by the production handler.
|
||||
Notes: "Server Url" is Jellyfin's own base URL (used for rendered links). "Webhook Url" is required by the form
|
||||
even for MQTT destinations; paste your broker URL so validation passes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* End-to-end test */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||
<div className="text-sm font-medium mb-2">End-to-end test</div>
|
||||
<p className="text-xs text-gray-500 mb-3">
|
||||
Optional. Verifies the full loop — broker reachable, plugin publishing, our subscriber receiving. Not required
|
||||
for day-to-day operation.
|
||||
</p>
|
||||
<ol className="text-xs text-gray-600 list-decimal list-inside space-y-1 mb-3">
|
||||
<li>
|
||||
In the Jellyfin Webhook destination, temporarily add <span className="font-mono">Playback Start</span> to the
|
||||
Notification Types (a library <span className="font-mono">Item Added</span> on demand is hard; starting playback
|
||||
is reliable).
|
||||
</li>
|
||||
<li>
|
||||
Click <strong>Start test</strong> below — it opens a 30-second listening window on your topic.
|
||||
</li>
|
||||
<li>While the timer runs, hit play on any movie or episode in Jellyfin, then stop.</li>
|
||||
<li>
|
||||
Result appears below. Afterwards you can remove <span className="font-mono">Playback Start</span> from the
|
||||
webhook — only <span className="font-mono">Item Added</span> is needed in production.
|
||||
</li>
|
||||
</ol>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="secondary" onClick={runTest} disabled={testing || !url}>
|
||||
{testing ? "Listening (30s)…" : "Start test"}
|
||||
</Button>
|
||||
{testing && <span className="text-xs text-gray-500">play something in Jellyfin now</span>}
|
||||
</div>
|
||||
|
||||
{testResult && (
|
||||
<div className="mt-3 text-sm space-y-1">
|
||||
<div className={testResult.brokerConnected ? "text-green-700" : "text-red-700"}>
|
||||
{testResult.brokerConnected ? "✓" : "✗"} Broker reachable & credentials accepted
|
||||
</div>
|
||||
{testResult.brokerConnected && (
|
||||
<div className={testResult.receivedMessage ? "text-green-700" : "text-amber-700"}>
|
||||
{testResult.receivedMessage
|
||||
? `✓ Received webhook on topic "${topic || "jellyfin/events"}" — the loop is closed.`
|
||||
: "⚠ No webhook in 30s. Check that the destination is Enabled, Item Type covers Movies/Episodes, the MQTT server/topic match, the Jellyfin host can reach this broker, and Playback Start is in the Notification Types while testing."}
|
||||
</div>
|
||||
)}
|
||||
{testResult.samplePayload && (
|
||||
<pre className="text-xs bg-gray-50 border rounded px-2 py-1 font-mono overflow-x-auto">
|
||||
{testResult.samplePayload}
|
||||
</pre>
|
||||
)}
|
||||
{testResult.error && <div className="text-red-700">✗ {testResult.error}</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user