Tautulli reached plex at 172.18.0.1, the gateway of a HAL network that goes away with HAL, and the plan was to retype it by hand in the window. Tautulli now requires plex-api, and where plex is comes from the binding. Tautulli keeps the connection only in config.ini, reads it at start and writes its whole config back on every shutdown; its API cannot set it and its settings form needs an admin login. So a step after start would be overwritten the moment the container is recreated. The write is made where nothing can overwrite it: the linuxserver image's custom-init runs plex/mesh-plex.py as root before Tautulli starts, and the server restarts on its binding and credential, so a moved plex or an accepted token lands. It writes only [PMS] keys, only when they differ, every other line byte for byte: pms_ip, pms_port, pms_ssl and pms_url from the binding; pms_identifier from plex's /identity; pms_token only when plex takes it. A minted value - before the operator accepts the server's X-Plex-Token for this pair - is never written, while the address still is, so Tautulli's own working token keeps working at plex's new address. A failure in custom-init is a log line nobody reads, so a run-once `plex` step, declared last so it gates nothing (ADR 0136), checks what the mesh can report: plex takes the credential (else it names the secret accept), Tautulli holds the bound URL, and Tautulli says it is connected. It writes nothing. The script is kept as plex/mesh-plex.py and plex/50-mesh-plex; module.json carries copies, and a test fails when they differ. Tests run the script with python3 against a fake plex (skipped where there is none) and the step against fakes; `npm test` builds first.
48 lines
2.0 KiB
TypeScript
48 lines
2.0 KiB
TypeScript
// tautulli's plex step — run once by the host after Tautulli's server container, and again whenever
|
|
// its binding, its pair credential or the server changed (the container's `restart-on`, novox/hq
|
|
// ADR 0099). It checks; it writes nothing (plex/check.ts says why, and where the write is).
|
|
//
|
|
// Exits non-zero when Tautulli does not reach plex as the mesh says, so the node reports the step
|
|
// failed. Declared last in the manifest, so its failing gates nothing else of tautulli's (novox/hq
|
|
// ADR 0136). Never prints a key or a token.
|
|
|
|
import { readFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { keyOfTautulli } from "../client.js";
|
|
import { check, tautulliReady, PROVISION, type Binding, type Http } from "./check.js";
|
|
|
|
const dir = process.env.MESH_PLEX_DIR ?? "/run/plex";
|
|
const url = process.env.MESH_TAUTULLI_URL ?? "http://127.0.0.1:8181";
|
|
const waitSeconds = Number(process.env.MESH_TAUTULLI_WAIT_SECONDS ?? "180");
|
|
|
|
const http: Http = { fetch: (u, init) => fetch(u, init) };
|
|
const read = (path: string) => readFile(path, "utf8").catch(() => undefined);
|
|
|
|
const apiKey = keyOfTautulli(process.env.MESH_TAUTULLI_CONFIG_DIR);
|
|
if (!apiKey) {
|
|
console.error("[tautulli-plex] Tautulli's config.ini holds no API key yet — it writes one on its first start");
|
|
process.exit(1);
|
|
}
|
|
const tautulli = { url, apiKey };
|
|
|
|
if (!(await tautulliReady(http, tautulli, waitSeconds * 1000))) {
|
|
console.error(`[tautulli-plex] Tautulli did not answer at ${url} within ${waitSeconds}s`);
|
|
process.exit(1);
|
|
}
|
|
|
|
let binding: Binding | undefined;
|
|
try {
|
|
const raw = await read(join(dir, `${PROVISION}.json`));
|
|
binding = raw === undefined ? undefined : (JSON.parse(raw) as Binding);
|
|
} catch {
|
|
binding = undefined;
|
|
}
|
|
const outcome = await check(http, tautulli, binding, await read(join(dir, `${PROVISION}.secret`)));
|
|
if (outcome.result === "connected") {
|
|
console.log(`[tautulli-plex] Tautulli reaches plex at ${outcome.url} as the mesh says; connected`);
|
|
} else {
|
|
console.error(`[tautulli-plex] ${outcome.problem}`);
|
|
process.exitCode = 1;
|
|
}
|