feat: add jellyfin integration (#672)

* feat: #655 implement jellyfin media server

* fix: table overflow

* feat: pr feedback

* refactor: format

* refactor: merge existing code

* fix: code smells

* refactor: format commit
This commit is contained in:
Manuel
2024-07-03 20:06:57 +02:00
committed by GitHub
parent 1cf119c768
commit bb8640b162
25 changed files with 435 additions and 17 deletions

View File

@@ -2,6 +2,7 @@ import { analyticsJob } from "./jobs/analytics";
import { iconsUpdaterJob } from "./jobs/icons-updater";
import { smartHomeEntityStateJob } from "./jobs/integrations/home-assistant";
import { mediaOrganizerJob } from "./jobs/integrations/media-organizer";
import { mediaServerJob } from "./jobs/integrations/media-server";
import { pingJob } from "./jobs/ping";
import { createCronJobGroup } from "./lib";
@@ -10,6 +11,7 @@ export const jobGroup = createCronJobGroup({
iconsUpdater: iconsUpdaterJob,
ping: pingJob,
smartHomeEntityState: smartHomeEntityStateJob,
mediaServer: mediaServerJob,
mediaOrganizer: mediaOrganizerJob,
});

View File

@@ -7,7 +7,7 @@ import { db, eq } from "@homarr/db";
import { items } from "@homarr/db/schema/sqlite";
import { SonarrIntegration } from "@homarr/integrations";
import type { CalendarEvent } from "@homarr/integrations/types";
import { createItemWithIntegrationChannel } from "@homarr/redis";
import { createItemAndIntegrationChannel } from "@homarr/redis";
// This import is done that way to avoid circular dependencies.
import type { WidgetComponentProps } from "../../../../widgets";
@@ -50,7 +50,7 @@ export const mediaOrganizerJob = createCronJob("mediaOrganizer", EVERY_MINUTE).w
});
const events = await sonarr.getCalendarEventsAsync(start, end);
const cache = createItemWithIntegrationChannel<CalendarEvent[]>(itemForIntegration.id, integration.integrationId);
const cache = createItemAndIntegrationChannel<CalendarEvent[]>("calendar", integration.integrationId);
await cache.setAsync(events);
}
}

View File

@@ -0,0 +1,45 @@
import { decryptSecret } from "@homarr/common";
import { EVERY_5_SECONDS } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { items } from "@homarr/db/schema/sqlite";
import { JellyfinIntegration } from "@homarr/integrations";
import { createItemAndIntegrationChannel } from "@homarr/redis";
import { createCronJob } from "../../lib";
export const mediaServerJob = createCronJob("mediaServer", EVERY_5_SECONDS).withCallback(async () => {
const itemsForIntegration = await db.query.items.findMany({
where: eq(items.kind, "mediaServer"),
with: {
integrations: {
with: {
integration: {
with: {
secrets: {
columns: {
kind: true,
value: true,
},
},
},
},
},
},
},
});
for (const itemForIntegration of itemsForIntegration) {
for (const integration of itemForIntegration.integrations) {
const jellyfinIntegration = new JellyfinIntegration({
...integration.integration,
decryptedSecrets: integration.integration.secrets.map((secret) => ({
...secret,
value: decryptSecret(secret.value),
})),
});
const streamSessions = await jellyfinIntegration.getCurrentSessionsAsync();
const channel = createItemAndIntegrationChannel("mediaServer", integration.integrationId);
await channel.publishAndUpdateLastStateAsync(streamSessions);
}
}
});