Skip to content

Commit 210f9a7

Browse files
committed
feat(plex): Cache MusicBrainz IDs
1 parent 50e3a3b commit 210f9a7

File tree

1 file changed

+54
-1
lines changed

1 file changed

+54
-1
lines changed

src/backend/sources/PlexApiSource.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export const LOCAL_USER = 'PLEX_LOCAL_USER';
3333

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

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

@@ -58,6 +62,8 @@ export default class PlexApiSource extends MemoryPositionalSource {
5862
uniqueDropReasons: FixedSizeList<string>;
5963

6064
libraries: {name: string, collectionType: string, uuid: string}[] = [];
65+
66+
private mbIdCache = new Map<string, MbIdCacheEntry>();
6167

6268
declare config: PlexApiSourceConfig;
6369

@@ -519,6 +525,11 @@ ${JSON.stringify(obj)}`);
519525
return null;
520526
}
521527

528+
const cachedMbId = this.getCachedMbId(ratingKey);
529+
if (cachedMbId !== undefined) {
530+
return cachedMbId;
531+
}
532+
522533
try {
523534
// The current version of plexjs (0.39.0) does not return the GUID
524535
// fields, so we make the call manually.
@@ -542,16 +553,58 @@ ${JSON.stringify(obj)}`);
542553
for (const metadata of result.MediaContainer.Metadata ?? []) {
543554
for (const guid of metadata.Guid ?? []) {
544555
if (typeof guid.id === "string" && guid.id.startsWith("mbid://")) {
545-
return guid.id.replace("mbid://", "");
556+
const mbid = guid.id.replace("mbid://", "");
557+
558+
this.setCachedMbId(ratingKey, mbid);
559+
return mbid;
546560
}
547561
}
548562
}
549563
} catch (e) {
550564
this.logger.warn(`Failed to get MusicBrainz IDs from Plex for item ${ratingKey}: ${e}`);
551565
}
552566

567+
this.setCachedMbId(ratingKey, null);
553568
return null;
554569
}
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;
581+
}
582+
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+
}
607+
}
555608
}
556609

557610
async function streamToString(stream: any) {

0 commit comments

Comments
 (0)