@@ -33,6 +33,10 @@ export const LOCAL_USER = 'PLEX_LOCAL_USER';
3333
3434const THUMB_REGEX = new RegExp ( / \/ l i b r a r y \/ m e t a d a t a \/ (?< ratingkey > \d + ) \/ t h u m b \/ \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+
3640export 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
557610async function streamToString ( stream : any ) {
0 commit comments