@@ -449,18 +449,18 @@ func (m *Manager) handlePlayingNotification(targetID int64, target PlexTargetInt
449449 return
450450 }
451451 m .enqueueTask (targetID , config .MaxConcurrent , func () {
452- m .processPlayingSession (targetID , target , cache , playing .ClientIdentifier , ratingKey , config )
452+ m .processPlayingSession (targetID , target , cache , sessionKey , playing .ClientIdentifier , ratingKey , config )
453453 })
454454 } else {
455455 // Session stopped - cleanup but also check for final track changes
456456 m .enqueueTask (targetID , config .MaxConcurrent , func () {
457- m .processPlaybackStopped (targetID , target , playing .ClientIdentifier , ratingKey , config )
457+ m .processPlaybackStopped (targetID , target , sessionKey , playing .ClientIdentifier , ratingKey , config )
458458 })
459459 }
460460}
461461
462462// processPlayingSession handles an active playing session to detect track changes
463- func (m * Manager ) processPlayingSession (targetID int64 , target PlexTargetInterface , cache * Cache , clientIdentifier string , ratingKey string , config * database.PlexAutoLanguagesConfig ) {
463+ func (m * Manager ) processPlayingSession (targetID int64 , target PlexTargetInterface , cache * Cache , sessionKey string , clientIdentifier string , ratingKey string , config * database.PlexAutoLanguagesConfig ) {
464464 ctx , cancel := context .WithTimeout (m .ctx , 30 * time .Second )
465465 defer cancel ()
466466
@@ -489,29 +489,28 @@ func (m *Manager) processPlayingSession(targetID int64, target PlexTargetInterfa
489489 userToken = ""
490490 }
491491
492- // Fetch episode metadata using the user's token to see their stream preferences
493- var episode * Episode
494- if userToken != "" {
495- episode , err = target .GetEpisodeWithStreamsAsUser (ctx , ratingKey , userToken )
496- if err != nil {
497- log .Debug ().Err (err ).Str ("ratingKey" , ratingKey ).Str ("user" , userLabel ).Msg ("Plex Auto Languages: Failed to get episode as user, clearing token cache" )
498- // Token might be invalid, clear it and try again next time
499- cache .ClearUserToken (user .ID )
500- userToken = ""
501- // Fall back to admin token
502- episode , err = target .GetEpisodeWithStreams (ctx , ratingKey )
503- if err != nil {
504- log .Debug ().Err (err ).Str ("ratingKey" , ratingKey ).Str ("user" , userLabel ).Msg ("Plex Auto Languages: Failed to get episode" )
505- return
506- }
492+ // Prefer live session data to avoid repeated library metadata calls.
493+ episode , err := target .GetSessionEpisodeWithStreams (ctx , clientIdentifier , ratingKey )
494+ if err != nil {
495+ if errors .Is (err , ErrNoActiveSessions ) {
496+ log .Trace ().
497+ Str ("ratingKey" , ratingKey ).
498+ Str ("clientIdentifier" , clientIdentifier ).
499+ Msg ("No active sessions found, falling back to cached metadata" )
500+ } else {
501+ log .Trace ().
502+ Err (err ).
503+ Str ("ratingKey" , ratingKey ).
504+ Str ("clientIdentifier" , clientIdentifier ).
505+ Msg ("Failed to get live session episode, falling back to cached metadata" )
507506 }
508- } else {
509- // No user token available, use admin token
510- episode , err = target .GetEpisodeWithStreams (ctx , ratingKey )
507+ episode , err = m .getSessionCachedEpisode (ctx , target , cache , sessionKey , ratingKey , user .ID , userLabel , & userToken )
511508 if err != nil {
512509 log .Debug ().Err (err ).Str ("ratingKey" , ratingKey ).Str ("user" , userLabel ).Msg ("Plex Auto Languages: Failed to get episode" )
513510 return
514511 }
512+ } else if cache != nil && sessionKey != "" {
513+ cache .SetSessionEpisode (sessionKey , ratingKey , episode )
515514 }
516515
517516 if episode .GrandparentKey == "" || len (episode .Parts ) == 0 {
@@ -625,10 +624,18 @@ func (m *Manager) processPlayingSession(targetID int64, target PlexTargetInterfa
625624}
626625
627626// processPlaybackStopped handles when a user stops playing an episode
628- func (m * Manager ) processPlaybackStopped (targetID int64 , target PlexTargetInterface , clientIdentifier string , ratingKey string , config * database.PlexAutoLanguagesConfig ) {
627+ func (m * Manager ) processPlaybackStopped (targetID int64 , target PlexTargetInterface , sessionKey string , clientIdentifier string , ratingKey string , config * database.PlexAutoLanguagesConfig ) {
629628 ctx , cancel := context .WithTimeout (m .ctx , 30 * time .Second )
630629 defer cancel ()
631630
631+ m .mu .RLock ()
632+ cache := m .caches [targetID ]
633+ m .mu .RUnlock ()
634+
635+ if cache != nil && sessionKey != "" {
636+ defer cache .ClearSessionEpisode (sessionKey )
637+ }
638+
632639 // Get the episode with streams to see what was selected (prefer live session data)
633640 liveData := true
634641 episode , err := target .GetSessionEpisodeWithStreams (ctx , clientIdentifier , ratingKey )
@@ -675,11 +682,6 @@ func (m *Manager) processPlaybackStopped(targetID int64, target PlexTargetInterf
675682 // If live data is gone (session already stopped), try to reapply the last known stream
676683 // selection from cache so we don't lose mid-playback changes.
677684 if ! liveData {
678- var cache * Cache
679- m .mu .RLock ()
680- cache = m .caches [targetID ]
681- m .mu .RUnlock ()
682-
683685 if cache != nil {
684686 if cachedStreams , ok := cache .GetDefaultStreams (user .ID , ratingKey ); ok {
685687 part := & episode .Parts [0 ]
@@ -746,10 +748,6 @@ func (m *Manager) processPlaybackStopped(targetID int64, target PlexTargetInterf
746748 Msg ("Plex Auto Languages: User track preference saved" )
747749
748750 // Apply preference to other episodes
749- m .mu .RLock ()
750- cache := m .caches [targetID ]
751- m .mu .RUnlock ()
752-
753751 userToken , err := m .getUserToken (ctx , cache , target , user .ID )
754752 if err != nil {
755753 log .Debug ().Err (err ).Str ("user" , userLabel ).Msg ("Plex Auto Languages: Failed to get user token, skipping apply" )
@@ -1352,6 +1350,52 @@ func (m *Manager) getUserToken(ctx context.Context, cache *Cache, target PlexTar
13521350 return token , nil
13531351}
13541352
1353+ func (m * Manager ) getSessionCachedEpisode (
1354+ ctx context.Context ,
1355+ target PlexTargetInterface ,
1356+ cache * Cache ,
1357+ sessionKey string ,
1358+ ratingKey string ,
1359+ userID string ,
1360+ userLabel string ,
1361+ userToken * string ,
1362+ ) (* Episode , error ) {
1363+ if cache != nil {
1364+ if cached , ok := cache .GetSessionEpisode (sessionKey , ratingKey ); ok {
1365+ return cached , nil
1366+ }
1367+ }
1368+
1369+ if userToken != nil && * userToken != "" {
1370+ episode , err := target .GetEpisodeWithStreamsAsUser (ctx , ratingKey , * userToken )
1371+ if err == nil {
1372+ if cache != nil && sessionKey != "" {
1373+ cache .SetSessionEpisode (sessionKey , ratingKey , episode )
1374+ }
1375+ return episode , nil
1376+ }
1377+
1378+ log .Debug ().
1379+ Err (err ).
1380+ Str ("ratingKey" , ratingKey ).
1381+ Str ("user" , userLabel ).
1382+ Msg ("Plex Auto Languages: Failed to get episode as user, clearing token cache" )
1383+ if cache != nil {
1384+ cache .ClearUserToken (userID )
1385+ }
1386+ * userToken = ""
1387+ }
1388+
1389+ episode , err := target .GetEpisodeWithStreams (ctx , ratingKey )
1390+ if err != nil {
1391+ return nil , err
1392+ }
1393+ if cache != nil && sessionKey != "" {
1394+ cache .SetSessionEpisode (sessionKey , ratingKey , episode )
1395+ }
1396+ return episode , nil
1397+ }
1398+
13551399// tracksMatchPreference checks if selected tracks match the stored preference
13561400func (m * Manager ) tracksMatchPreference (audio * AudioStream , subtitle * SubtitleStream , pref * database.PlexAutoLanguagesPreference ) bool {
13571401 if pref == nil {
0 commit comments