[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #317 from FoxxMD/GH-314/plexjsVersion

fix(Plex): Temporary plex validation fix

authored by

Matt Foxx and committed by
GitHub
(Jul 21, 2025, 8:50 AM EDT) 1e488b57 d3d8eb40

+36 -8
+4 -4
package-lock.json
··· 25 25 "@gr2m/net-interceptor": "^1.0.0", 26 26 "@jellyfin/sdk": "^0.11.0", 27 27 "@kenyip/backoff-strategies": "^1.0.4", 28 - "@lukehagar/plexjs": "^0.32.1", 28 + "@lukehagar/plexjs": "^0.39.0", 29 29 "@react-nano/use-event-source": "^0.13.0", 30 30 "@reduxjs/toolkit": "^1.9.5", 31 31 "@supercharge/promise-pool": "^3.0.0", ··· 1543 1543 "integrity": "sha512-vduQZw2ctS3kIuSnCSSRiE4J90Y8WShR9xVG+e1lvFWksU2aTxjdkArcQqJ+XLm22JS380OZmrIPY1U06TAsng==" 1544 1544 }, 1545 1545 "node_modules/@lukehagar/plexjs": { 1546 - "version": "0.32.1", 1547 - "resolved": "https://registry.npmjs.org/@lukehagar/plexjs/-/plexjs-0.32.1.tgz", 1548 - "integrity": "sha512-cEIpepR1/BJYQey2lbzPhy342ZR/JmkChXKS+bOQkGLg1F17n45Zf650W67sBKMvF63effNVT0iUF3Fi3D7yPQ==", 1546 + "version": "0.39.0", 1547 + "resolved": "https://registry.npmjs.org/@lukehagar/plexjs/-/plexjs-0.39.0.tgz", 1548 + "integrity": "sha512-BXNHTh1Z9K0bb5qFKgYZv7gOC9+wYzOsatp/+DRGShGhp40bH0lTShzD6acKjqlIvqi2Pm7UJnF8+yBipE9Qmg==", 1549 1549 "peerDependencies": { 1550 1550 "zod": ">= 3" 1551 1551 }
+1 -1
package.json
··· 57 57 "@gr2m/net-interceptor": "^1.0.0", 58 58 "@jellyfin/sdk": "^0.11.0", 59 59 "@kenyip/backoff-strategies": "^1.0.4", 60 - "@lukehagar/plexjs": "^0.32.1", 60 + "@lukehagar/plexjs": "^0.39.0", 61 61 "@react-nano/use-event-source": "^0.13.0", 62 62 "@reduxjs/toolkit": "^1.9.5", 63 63 "@supercharge/promise-pool": "^3.0.0",
+31 -3
src/backend/sources/PlexApiSource.ts
··· 15 15 import { buildStatePlayerPlayIdententifyingInfo, parseArrayFromMaybeString } from "../utils/StringUtils.js"; 16 16 import { GetSessionsMetadata } from "@lukehagar/plexjs/sdk/models/operations/getsessions.js"; 17 17 import { PlexAPI } from "@lukehagar/plexjs"; 18 - import { 19 - SDKValidationError, 20 - } from "@lukehagar/plexjs/sdk/models/errors"; 21 18 import { PlexApiSourceConfig } from "../common/infrastructure/config/source/plex.js"; 22 19 import { isPortReachable, joinedUrl } from '../utils/NetworkUtils.js'; 23 20 import normalizeUrl from 'normalize-url'; ··· 29 26 import { Logger } from '@foxxmd/logging'; 30 27 import { MemoryPositionalSource } from './MemoryPositionalSource.js'; 31 28 import { FixedSizeList } from 'fixed-size-list'; 29 + import { SDKValidationError } from '@lukehagar/plexjs/sdk/models/errors/sdkvalidationerror.js'; 32 30 33 31 const shortDeviceId = truncateStringToLength(10, ''); 34 32 ··· 187 185 188 186 this.libraries = libraries.object.mediaContainer.directory.map(x => ({name: x.title, collectionType: x.type, uuid: x.uuid})); 189 187 } catch (e) { 188 + if(e instanceof SDKValidationError) { 189 + if((e.rawValue as any).object?.MediaContainer?.Directory !== undefined) { 190 + // ensure directory has required values 191 + const ok = (e.rawValue as any).object?.MediaContainer?.Directory.every(x => x.title !== undefined && x.type !== undefined && x.uuid !== undefined); 192 + if(ok) { 193 + this.libraries = (e.rawValue as any).object.MediaContainer.Directory.map(x => ({name: x.title, collectionType: x.type, uuid: x.uuid})); 194 + return; 195 + } 196 + } 197 + this.logger.debug({ rawValue: e.rawValue }, 'Plex Response'); 198 + } 190 199 throw new Error('Unable to get server libraries', {cause: e}); 191 200 } 192 201 ··· 452 461 453 462 getNewPlayer = (logger: Logger, id: PlayPlatformId, opts: PlayerStateOptions) => new PlexPlayerState(logger, id, opts); 454 463 } 464 + 465 + async function streamToString(stream: any) { 466 + const reader = stream.getReader(); 467 + const textDecoder = new TextDecoder(); 468 + let result = ''; 469 + 470 + async function read() { 471 + const { done, value } = await reader.read(); 472 + 473 + if (done) { 474 + return result; 475 + } 476 + 477 + result += textDecoder.decode(value, { stream: true }); 478 + return read(); 479 + } 480 + 481 + return read(); 482 + }