Servarr events entrypoints don't crash the runtime when unconfigured #7

Merged
jschoubben merged 1 commits from fix/arr-events-guard into main 2026-09-05 21:14:44 +00:00
4 changed files with 72 additions and 16 deletions
Showing only changes of commit 419dd68145 - Show all commits
+18 -4
View File
@@ -19,7 +19,17 @@
import { emit } from "@novox/mesh-sdk/events";
import { BookshelfClient, type BookshelfQueueItem } from "./client.js";
const bookshelf = BookshelfClient.fromEnv();
// Building the client throws when Bookshelf has no URL/key yet. Like the tools (see tools/index.ts),
// the events entrypoint must not crash the runtime for that — it stays idle until configured.
function buildClient(): BookshelfClient | null {
try {
return BookshelfClient.fromEnv();
} catch {
return null;
}
}
const bookshelf = buildClient();
// Bookshelf removes an item from the queue once it has been imported; a "warning"/"failed" status is
// how a stuck or broken grab shows itself, so we do not call those a completion when they vanish.
@@ -28,7 +38,7 @@ const FAILED_STATUSES = new Set(["failed", "warning"]);
const inQueue = new Map<number, BookshelfQueueItem>();
let primed = false;
async function pollQueue(): Promise<void> {
async function pollQueue(bookshelf: BookshelfClient): Promise<void> {
const { items } = await bookshelf.getQueue();
const now = new Map(items.map((i) => [i.id, i]));
@@ -55,6 +65,10 @@ const tick = (fn: () => Promise<void>, everyMs: number): void => {
setInterval(run, everyMs);
run();
};
tick(pollQueue, 30_000);
console.log("[bookshelf] watching the download queue, emitting grabs and completions");
if (bookshelf) {
tick(() => pollQueue(bookshelf), 30_000);
console.log("[bookshelf] watching the download queue, emitting grabs and completions");
} else {
console.log("[bookshelf] not configured — events idle until an API key is available");
}
+18 -4
View File
@@ -14,7 +14,17 @@
import { emit } from "@novox/mesh-sdk/events";
import { LidarrClient, type LidarrQueueItem } from "./client.js";
const lidarr = LidarrClient.fromEnv();
// Building the client throws when Lidarr has no URL/key yet. Like the tools (see tools/index.ts),
// the events entrypoint must not crash the runtime for that — it stays idle until configured.
function buildClient(): LidarrClient | null {
try {
return LidarrClient.fromEnv();
} catch {
return null;
}
}
const lidarr = buildClient();
// Lidarr removes an item from the queue once it has been imported; a "warning"/"failed" status is
// how a stuck or broken grab shows itself, so we do not call those a completion when they vanish.
@@ -23,7 +33,7 @@ const FAILED_STATUSES = new Set(["failed", "warning"]);
const inQueue = new Map<number, LidarrQueueItem>();
let primed = false;
async function pollQueue(): Promise<void> {
async function pollQueue(lidarr: LidarrClient): Promise<void> {
const { items } = await lidarr.getQueue();
const now = new Map(items.map((i) => [i.id, i]));
@@ -50,6 +60,10 @@ const tick = (fn: () => Promise<void>, everyMs: number): void => {
setInterval(run, everyMs);
run();
};
tick(pollQueue, 30_000);
console.log("[lidarr] watching the download queue, emitting grabs and completions");
if (lidarr) {
tick(() => pollQueue(lidarr), 30_000);
console.log("[lidarr] watching the download queue, emitting grabs and completions");
} else {
console.log("[lidarr] not configured — events idle until an API key is available");
}
+18 -4
View File
@@ -14,7 +14,17 @@
import { emit } from "@novox/mesh-sdk/events";
import { RadarrClient, type RadarrQueueItem } from "./client.js";
const radarr = RadarrClient.fromEnv();
// Building the client throws when Radarr has no URL/key yet. Like the tools (see tools/index.ts),
// the events entrypoint must not crash the runtime for that — it stays idle until configured.
function buildClient(): RadarrClient | null {
try {
return RadarrClient.fromEnv();
} catch {
return null;
}
}
const radarr = buildClient();
// Radarr removes an item from the queue once it has been imported; a "warning"/"failed" status is
// how a stuck or broken grab shows itself, so we do not call those a completion when they vanish.
@@ -23,7 +33,7 @@ const FAILED_STATUSES = new Set(["failed", "warning"]);
const inQueue = new Map<number, RadarrQueueItem>();
let primed = false;
async function pollQueue(): Promise<void> {
async function pollQueue(radarr: RadarrClient): Promise<void> {
const { items } = await radarr.getQueue();
const now = new Map(items.map((i) => [i.id, i]));
@@ -50,6 +60,10 @@ const tick = (fn: () => Promise<void>, everyMs: number): void => {
setInterval(run, everyMs);
run();
};
tick(pollQueue, 30_000);
console.log("[radarr] watching the download queue, emitting grabs and completions");
if (radarr) {
tick(() => pollQueue(radarr), 30_000);
console.log("[radarr] watching the download queue, emitting grabs and completions");
} else {
console.log("[radarr] not configured — events idle until an API key is available");
}
+18 -4
View File
@@ -14,7 +14,17 @@
import { emit } from "@novox/mesh-sdk/events";
import { SonarrClient, type SonarrQueueItem } from "./client.js";
const sonarr = SonarrClient.fromEnv();
// Building the client throws when Sonarr has no URL/key yet. Like the tools (see tools/index.ts),
// the events entrypoint must not crash the runtime for that — it stays idle until configured.
function buildClient(): SonarrClient | null {
try {
return SonarrClient.fromEnv();
} catch {
return null;
}
}
const sonarr = buildClient();
// Sonarr removes an item from the queue once it has been imported; a "warning"/"failed" status is
// how a stuck or broken grab shows itself, so we do not call those a completion when they vanish.
@@ -23,7 +33,7 @@ const FAILED_STATUSES = new Set(["failed", "warning"]);
const inQueue = new Map<number, SonarrQueueItem>();
let primed = false;
async function pollQueue(): Promise<void> {
async function pollQueue(sonarr: SonarrClient): Promise<void> {
const { items } = await sonarr.getQueue();
const now = new Map(items.map((i) => [i.id, i]));
@@ -50,6 +60,10 @@ const tick = (fn: () => Promise<void>, everyMs: number): void => {
setInterval(run, everyMs);
run();
};
tick(pollQueue, 30_000);
console.log("[sonarr] watching the download queue, emitting grabs and completions");
if (sonarr) {
tick(() => pollQueue(sonarr), 30_000);
console.log("[sonarr] watching the download queue, emitting grabs and completions");
} else {
console.log("[sonarr] not configured — events idle until an API key is available");
}