mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-12 16:35:49 +01:00
✨ Plex and Jellyfin widget (#713)
This commit is contained in:
108
src/tools/server/sdk/plex/plexClient.ts
Normal file
108
src/tools/server/sdk/plex/plexClient.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Element, xml2js } from 'xml-js';
|
||||
|
||||
import {
|
||||
GenericCurrentlyPlaying,
|
||||
GenericSessionInfo,
|
||||
} from '../../../../types/api/media-server/session-info';
|
||||
|
||||
export class PlexClient {
|
||||
constructor(private readonly apiAddress: string, private readonly token: string) {}
|
||||
|
||||
async getSessions(): Promise<GenericSessionInfo[]> {
|
||||
const response = await fetch(`${this.apiAddress}/status/sessions?X-Plex-Token=${this.token}`);
|
||||
const body = await response.text();
|
||||
|
||||
// convert xml response to objects, as there is no JSON api
|
||||
const data = xml2js(body);
|
||||
|
||||
// TODO: Investigate when there are no media containers
|
||||
const mediaContainer = data.elements[0] as Element;
|
||||
|
||||
// no sessions are open or available
|
||||
if (!mediaContainer.elements?.some((_) => true)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const videoElements = mediaContainer.elements as Element[];
|
||||
|
||||
const videos = videoElements
|
||||
.map((videoElement): GenericSessionInfo | undefined => {
|
||||
// extract the elements from the children
|
||||
const userElement = this.findElement('User', videoElement.elements);
|
||||
const playerElement = this.findElement('Player', videoElement.elements);
|
||||
const mediaElement = this.findElement('Media', videoElement.elements);
|
||||
const sessionElement = this.findElement('Session', videoElement.elements);
|
||||
|
||||
if (!userElement || !playerElement || !mediaElement || !sessionElement) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { videoCodec, videoFrameRate, audioCodec, audioChannels, height, width, bitrate } =
|
||||
mediaElement;
|
||||
|
||||
const transcodingElement = this.findElement('TranscodeSession', videoElement.elements);
|
||||
|
||||
return {
|
||||
id: sessionElement.id as string,
|
||||
username: userElement.title as string,
|
||||
userProfilePicture: userElement.thumb as string,
|
||||
sessionName: `${playerElement.product} (${playerElement.title})`,
|
||||
currentlyPlaying: {
|
||||
name: videoElement.attributes?.title as string,
|
||||
type: this.getCurrentlyPlayingType(videoElement.attributes?.type as string),
|
||||
metadata: {
|
||||
video: {
|
||||
bitrate,
|
||||
height,
|
||||
videoCodec,
|
||||
videoFrameRate,
|
||||
width,
|
||||
},
|
||||
audio: {
|
||||
audioChannels,
|
||||
audioCodec,
|
||||
},
|
||||
transcoding:
|
||||
transcodingElement === undefined
|
||||
? undefined
|
||||
: {
|
||||
audioChannels: transcodingElement.audioChannels,
|
||||
audioCodec: transcodingElement.audioCodec,
|
||||
audioDecision: transcodingElement.audioDecision,
|
||||
container: transcodingElement.container,
|
||||
context: transcodingElement.context,
|
||||
duration: transcodingElement.duration,
|
||||
error: transcodingElement.error === 1,
|
||||
height: transcodingElement.height,
|
||||
sourceAudioCodec: transcodingElement.sourceAudioCodec,
|
||||
sourceVideoCodec: transcodingElement.sourceVideoCodec,
|
||||
timeStamp: transcodingElement.timeStamp,
|
||||
transcodeHwRequested: transcodingElement.transcodeHwRequested === 1,
|
||||
videoCodec: transcodingElement.videoCodec,
|
||||
videoDecision: transcodingElement.videoDecision,
|
||||
width: transcodingElement.width,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as GenericSessionInfo;
|
||||
})
|
||||
.filter((x) => x !== undefined) as GenericSessionInfo[];
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
private findElement(name: string, elements: Element[] | undefined) {
|
||||
return elements?.find((x) => x.name === name)?.attributes;
|
||||
}
|
||||
|
||||
private getCurrentlyPlayingType(type: string): GenericCurrentlyPlaying['type'] {
|
||||
switch (type) {
|
||||
case 'movie':
|
||||
return 'movie';
|
||||
case 'episode':
|
||||
return 'video';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user