Skip to content

Commit bad960d

Browse files
committed
refactor(plex): Use existing cache and simplify failure result
* Use Keyv memory cache * Return undefined instead of null to simplify assignment * Throw error with cause for clearer logging
1 parent 210f9a7 commit bad960d

File tree

2 files changed

+20
-55
lines changed

2 files changed

+20
-55
lines changed

src/backend/common/Cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export class MSCache {
181181
}
182182

183183

184-
export const initMemoryCache = (opts: Parameters<typeof createKeyv>[0] = {}): Keyv | KeyvStoreAdapter => {
184+
export const initMemoryCache = <T = any>(opts: Parameters<typeof createKeyv>[0] = {}): Keyv<T> | KeyvStoreAdapter => {
185185
const {
186186
ttl = '1h',
187187
lruSize = 200,

src/backend/sources/PlexApiSource.ts

Lines changed: 19 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,15 @@ import { Logger } from '@foxxmd/logging';
2626
import { MemoryPositionalSource } from './MemoryPositionalSource.js';
2727
import { FixedSizeList } from 'fixed-size-list';
2828
import { SDKValidationError } from '@lukehagar/plexjs/sdk/models/errors/sdkvalidationerror.js';
29+
import { Keyv } from 'cacheable';
30+
import { initMemoryCache } from "../common/Cache.js";
2931

3032
const shortDeviceId = truncateStringToLength(10, '');
3133

3234
export const LOCAL_USER = 'PLEX_LOCAL_USER';
3335

3436
const THUMB_REGEX = new RegExp(/\/library\/metadata\/(?<ratingkey>\d+)\/thumb\/\d+/)
3537

36-
type MbIdCacheEntry = { value: string | null, expiresAt: number };
37-
const MBID_CACHE_TTL_MS = 10 * 60 * 1000;
38-
const MBID_CACHE_SIZE = 1000;
39-
4038
export default class PlexApiSource extends MemoryPositionalSource {
4139
users: string[] = [];
4240

@@ -63,7 +61,7 @@ export default class PlexApiSource extends MemoryPositionalSource {
6361

6462
libraries: {name: string, collectionType: string, uuid: string}[] = [];
6563

66-
private mbIdCache = new Map<string, MbIdCacheEntry>();
64+
private mbIdCache: Keyv<string>;
6765

6866
declare config: PlexApiSourceConfig;
6967

@@ -76,9 +74,11 @@ export default class PlexApiSource extends MemoryPositionalSource {
7674
this.deviceId = `${name}-ms${internal.version}-${truncateStringToLength(10, '')(hashObject(config))}`;
7775
this.uniqueDropReasons = new FixedSizeList<string>(100);
7876
this.mediaIdsSeen = new FixedSizeList<string>(100);
77+
this.mbIdCache = initMemoryCache<string | null>({lruSize: 1000, ttl: '1m'}) as Keyv<string | null>;
7978
}
8079

8180
protected async doBuildInitData(): Promise<true | string | undefined> {
81+
this.regexCache
8282
const {
8383
data: {
8484
token,
@@ -418,14 +418,14 @@ export default class PlexApiSource extends MemoryPositionalSource {
418418
const prevBrainzMeta = sessionData[0].play.data.meta.brainz ?? {};
419419
sessionData[0].play.data.meta.brainz = {
420420
...prevBrainzMeta,
421-
track: trackMbId ?? undefined,
422-
album: albumMbId ?? undefined,
421+
track: trackMbId,
422+
album: albumMbId,
423423
// Plex doesn't track MBIDs for track artists, so we use the
424424
// album artist MBID instead.
425-
artist: albumArtistMbId
425+
artist: albumArtistMbId !== undefined
426426
? [...new Set([...(prevBrainzMeta.artist ?? []), albumArtistMbId])]
427427
: prevBrainzMeta.artist,
428-
albumArtist: albumArtistMbId
428+
albumArtist: albumArtistMbId !== undefined
429429
? [...new Set([...(prevBrainzMeta.albumArtist ?? []), albumArtistMbId])]
430430
: prevBrainzMeta.albumArtist,
431431
};
@@ -520,15 +520,18 @@ ${JSON.stringify(obj)}`);
520520

521521
getNewPlayer = (logger: Logger, id: PlayPlatformId, opts: PlayerStateOptions) => new PlexPlayerState(logger, id, opts);
522522

523-
getMusicBrainzId = async (ratingKey: string | undefined): Promise<string | null> => {
523+
getMusicBrainzId = async (ratingKey: string | undefined): Promise<string | undefined> => {
524524
if (!ratingKey) {
525525
return null;
526526
}
527527

528-
const cachedMbId = this.getCachedMbId(ratingKey);
529-
if (cachedMbId !== undefined) {
528+
const cachedMbId = await this.mbIdCache.get(ratingKey);
529+
if (cachedMbId !== undefined && cachedMbId !== null) {
530530
return cachedMbId;
531531
}
532+
if(cachedMbId === null) {
533+
return undefined;
534+
}
532535

533536
try {
534537
// The current version of plexjs (0.39.0) does not return the GUID
@@ -555,55 +558,17 @@ ${JSON.stringify(obj)}`);
555558
if (typeof guid.id === "string" && guid.id.startsWith("mbid://")) {
556559
const mbid = guid.id.replace("mbid://", "");
557560

558-
this.setCachedMbId(ratingKey, mbid);
561+
await this.mbIdCache.set(ratingKey, mbid);
559562
return mbid;
560563
}
561564
}
562565
}
563566
} catch (e) {
564-
this.logger.warn(`Failed to get MusicBrainz IDs from Plex for item ${ratingKey}: ${e}`);
565-
}
566-
567-
this.setCachedMbId(ratingKey, null);
568-
return null;
569-
}
570-
571-
getCachedMbId = (ratingKey: string): string | null | undefined => {
572-
const cachedMbId = this.mbIdCache.get(ratingKey);
573-
574-
if (!cachedMbId) {
575-
return undefined;
576-
}
577-
578-
if (cachedMbId.expiresAt < Date.now()) {
579-
this.mbIdCache.delete(ratingKey);
580-
return undefined;
567+
this.logger.warn(new Error(`Failed to get MusicBrainz IDs from Plex for item ${ratingKey}`, {cause: e}));
581568
}
582569

583-
return cachedMbId.value;
584-
}
585-
586-
setCachedMbId = (ratingKey: string, value: string | null) => {
587-
this.mbIdCache.set(ratingKey, {
588-
value,
589-
expiresAt: Date.now() + MBID_CACHE_TTL_MS,
590-
});
591-
592-
// Clean up cache.
593-
if (this.mbIdCache.size > MBID_CACHE_SIZE) {
594-
const now = Date.now();
595-
596-
for (const [key, entry] of this.mbIdCache) {
597-
if (entry.expiresAt <= now) {
598-
this.mbIdCache.delete(key);
599-
}
600-
}
601-
602-
while (this.mbIdCache.size > MBID_CACHE_SIZE) {
603-
const oldestKey = this.mbIdCache.keys().next().value;
604-
this.mbIdCache.delete(oldestKey);
605-
}
606-
}
570+
this.mbIdCache.set(ratingKey, null);
571+
return undefined;
607572
}
608573
}
609574

0 commit comments

Comments
 (0)