@@ -45,6 +45,14 @@ final class PlexGuid implements iGuid
4545 'com.plexapp.agents.cmdb ' ,
4646 ];
4747
48+ /**
49+ * @var array<array-key,string> List of native plex NFO agents.
50+ */
51+ private array $ guidNfo = [
52+ 'tv.plex.agents.nfo.movie ' ,
53+ 'tv.plex.agents.nfo.series ' ,
54+ ];
55+
4856 /**
4957 * @var array<array-key,string> List of local plex agents.
5058 */
@@ -359,6 +367,10 @@ private function ListExternalIds(array $guids, array $context = [], bool $log =
359367 $ val = $ this ->parseLegacyAgent (guid: $ val , context: $ context , log: $ log );
360368 }
361369
370+ if (true === str_starts_with ($ val , 'tv.plex.agents.nfo. ' )) {
371+ $ val = $ this ->parseNfoAgent (guid: $ val , context: $ context , log: $ log );
372+ }
373+
362374 if (false === str_contains ($ val , ':// ' )) {
363375 if (true === $ log ) {
364376 $ this ->logger ->info ("PlexGuid: Unable to parse '{backend}: {agent}' identifier. " , [
@@ -398,6 +410,10 @@ private function ListExternalIds(array $guids, array $context = [], bool $log =
398410
399411 // -- Plex in their infinite wisdom, sometimes report two keys for same data source.
400412 if (null !== ($ guid [$ this ->guidMapper [$ key ]] ?? null )) {
413+ if ($ guid [$ this ->guidMapper [$ key ]] === $ value ) {
414+ continue ;
415+ }
416+
401417 if (true === $ log ) {
402418 $ this ->logger ->warning (
403419 "PlexGuid: '{client}: {backend}' reported multiple ids for same data source '{key}: {ids}' for {item.type} '{item.id}: {item.title}'. " ,
@@ -514,6 +530,74 @@ private function parseLegacyAgent(string $guid, array $context = [], bool $log =
514530 }
515531 }
516532
533+ /**
534+ * Parse native Plex NFO agents.
535+ *
536+ * Typed NFO GUIDs include the source in the last path segment, such as
537+ * `tv.plex.agents.nfo.movie://movie/tmdb_383498`. We normalize those to the
538+ * same `<source>://<id>` format the rest of the parser already understands.
539+ * Fallback NFO ids like `...://movie/858024` do not identify the source and
540+ * are intentionally left untouched.
541+ *
542+ * @param string $guid Guid to parse.
543+ * @param array $context Context data.
544+ * @param bool $log Log errors. default true.
545+ *
546+ * @return string The parsed GUID.
547+ */
548+ private function parseNfoAgent (string $ guid , array $ context = [], bool $ log = true ): string
549+ {
550+ if (false === in_array (before ($ guid , ':// ' ), $ this ->guidNfo , true )) {
551+ return $ guid ;
552+ }
553+
554+ try {
555+ $ payload = after ($ guid , ':// ' );
556+ $ token = trim ((string ) basename ($ payload ));
557+
558+ if ('' === $ token || false === str_contains ($ token , '_ ' )) {
559+ return $ guid ;
560+ }
561+
562+ [$ source , $ sourceId ] = explode ('_ ' , $ token , 2 );
563+ $ source = strtolower (trim ($ source ));
564+ $ sourceId = trim ($ sourceId );
565+
566+ if ('' === $ source || '' === $ sourceId || null === ($ this ->guidMapper [$ source ] ?? null )) {
567+ return $ guid ;
568+ }
569+
570+ return $ source . ':// ' . before ($ sourceId , '? ' );
571+ } catch (Throwable $ e ) {
572+ if (true === $ log ) {
573+ $ this ->logger ->error (
574+ message: "PlexGuid: Exception '{error.kind}' was thrown unhandled during '{client}: {backend}' parsing NFO agent '{agent}' identifier. Error '{error.message}' at '{error.file}:{error.line}. " ,
575+ context: [
576+ 'backend ' => $ this ->context ->backendName ,
577+ 'client ' => $ this ->context ->clientName ,
578+ 'error ' => [
579+ 'kind ' => $ e ::class,
580+ 'line ' => $ e ->getLine (),
581+ 'message ' => $ e ->getMessage (),
582+ 'file ' => after ($ e ->getFile (), ROOT_PATH ),
583+ ],
584+ 'agent ' => $ guid ,
585+ 'exception ' => [
586+ 'file ' => $ e ->getFile (),
587+ 'line ' => $ e ->getLine (),
588+ 'kind ' => get_class ($ e ),
589+ 'message ' => $ e ->getMessage (),
590+ 'trace ' => $ e ->getTrace (),
591+ ],
592+ ...$ context ,
593+ ],
594+ );
595+ }
596+
597+ return $ guid ;
598+ }
599+ }
600+
517601 /**
518602 * Get the Plex Guid configuration.
519603 *
@@ -524,6 +608,7 @@ public function getConfig(): array
524608 return [
525609 'guidMapper ' => $ this ->guidMapper ,
526610 'guidLegacy ' => $ this ->guidLegacy ,
611+ 'guidNfo ' => $ this ->guidNfo ,
527612 'guidLocal ' => $ this ->guidLocal ,
528613 'guidReplacer ' => $ this ->guidReplacer ,
529614 ];
0 commit comments