Files
homarr/packages/integrations/src/jellyfin/jellyfin-integration.ts
T
Meier Lukas 8c36c3e36b feat(certificates): handle self signed certificates (#1951)
* wip: add page and loading of certificates in folder

* wip: add certificate addition and removal

* feat: add removal ui for certificates

* feat: migrate integrations to fetch or agent with trusted certificates

* fix: lock file issues

* fix: typecheck issue

* fix: inconsistent package versions

* chore: address pull request feedback

* fix: add missing navigation item and restrict access to page

* chore: address pull request feedback

* fix: inconsistent undici dependency version

* fix: inconsistent undici dependency version
2025-01-17 00:08:40 +01:00

84 lines
3.0 KiB
TypeScript

import { Jellyfin } from "@jellyfin/sdk";
import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
import { getSystemApi } from "@jellyfin/sdk/lib/utils/api/system-api";
import { createAxiosCertificateInstanceAsync } from "@homarr/certificates/server";
import { Integration } from "../base/integration";
import type { StreamSession } from "../interfaces/media-server/session";
export class JellyfinIntegration extends Integration {
private readonly jellyfin: Jellyfin = new Jellyfin({
clientInfo: {
name: "Homarr",
version: "0.0.1",
},
deviceInfo: {
name: "Homarr",
id: "homarr",
},
});
public async testConnectionAsync(): Promise<void> {
const api = await this.getApiAsync();
const systemApi = getSystemApi(api);
await systemApi.getPingSystem();
}
public async getCurrentSessionsAsync(): Promise<StreamSession[]> {
const api = await this.getApiAsync();
const sessionApi = getSessionApi(api);
const sessions = await sessionApi.getSessions();
if (sessions.status !== 200) {
throw new Error(`Jellyfin server ${this.url("/")} returned a non successful status code: ${sessions.status}`);
}
return sessions.data.map((sessionInfo): StreamSession => {
let nowPlaying: StreamSession["currentlyPlaying"] | null = null;
if (sessionInfo.NowPlayingItem) {
nowPlaying = {
type: "tv",
name: sessionInfo.NowPlayingItem.Name ?? "",
seasonName: sessionInfo.NowPlayingItem.SeasonName ?? "",
episodeName: sessionInfo.NowPlayingItem.EpisodeTitle,
albumName: sessionInfo.NowPlayingItem.Album ?? "",
episodeCount: sessionInfo.NowPlayingItem.EpisodeCount,
};
}
return {
sessionId: `${sessionInfo.Id}`,
sessionName: `${sessionInfo.Client} (${sessionInfo.DeviceName})`,
user: {
profilePictureUrl: this.url(`/Users/${sessionInfo.UserId}/Images/Primary`).toString(),
userId: sessionInfo.UserId ?? "",
username: sessionInfo.UserName ?? "",
},
currentlyPlaying: nowPlaying,
};
});
}
/**
* Constructs an ApiClient synchronously with an ApiKey or asynchronously
* with a username and password.
* @returns An instance of Api that has been authenticated
*/
private async getApiAsync() {
const httpsAgent = await createAxiosCertificateInstanceAsync();
if (this.hasSecretValue("apiKey")) {
const apiKey = this.getSecretValue("apiKey");
return this.jellyfin.createApi(this.url("/").toString(), apiKey, httpsAgent);
}
const apiClient = this.jellyfin.createApi(this.url("/").toString(), undefined, httpsAgent);
// Authentication state is stored internally in the Api class, so now
// requests that require authentication can be made normally.
// see https://typescript-sdk.jellyfin.org/#usage
await apiClient.authenticateUserByName(this.getSecretValue("username"), this.getSecretValue("password"));
return apiClient;
}
}