From 40c55608ce557e126faec0edf86cd7dc7ff8f184 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 16 Mar 2023 20:25:31 -0700 Subject: [PATCH] New: Improved Plex library updating (cherry picked from commit 98308737cfa78a41ec22a2b23a80bf316bd779ea) Closes #3438 Closes #3445 Closes #3465 --- .../Plex/Server/PlexServerProxy.cs | 64 +--------- .../Plex/Server/PlexServerService.cs | 111 ++++++------------ 2 files changed, 36 insertions(+), 139 deletions(-) diff --git a/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerProxy.cs b/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerProxy.cs index 46fe945a5..cceed18b7 100644 --- a/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerProxy.cs +++ b/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerProxy.cs @@ -14,11 +14,8 @@ namespace NzbDrone.Core.Notifications.Plex.Server public interface IPlexServerProxy { List GetArtistSections(PlexServerSettings settings); - void Update(int sectionId, PlexServerSettings settings); - void UpdateArtist(int metadataId, PlexServerSettings settings); string Version(PlexServerSettings settings); - List Preferences(PlexServerSettings settings); - int? GetMetadataId(int sectionId, string mbId, string language, PlexServerSettings settings); + void Update(int sectionId, string path, PlexServerSettings settings); } public class PlexServerProxy : IPlexServerProxy @@ -63,19 +60,13 @@ namespace NzbDrone.Core.Notifications.Plex.Server .ToList(); } - public void Update(int sectionId, PlexServerSettings settings) + public void Update(int sectionId, string path, PlexServerSettings settings) { var resource = $"library/sections/{sectionId}/refresh"; var request = BuildRequest(resource, HttpMethod.Get, settings); - var response = ProcessRequest(request); - CheckForError(response); - } + request.AddQueryParam("path", path); - public void UpdateArtist(int metadataId, PlexServerSettings settings) - { - var resource = $"library/metadata/{metadataId}/refresh"; - var request = BuildRequest(resource, HttpMethod.Put, settings); var response = ProcessRequest(request); CheckForError(response); @@ -99,55 +90,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server .Version; } - public List Preferences(PlexServerSettings settings) - { - var request = BuildRequest(":/prefs", HttpMethod.Get, settings); - var response = ProcessRequest(request); - - CheckForError(response); - - if (response.Contains("_children")) - { - return Json.Deserialize(response) - .Preferences; - } - - return Json.Deserialize>(response) - .MediaContainer - .Preferences; - } - - public int? GetMetadataId(int sectionId, string mbId, string language, PlexServerSettings settings) - { - var guid = string.Format("com.plexapp.agents.lastfm://{0}?lang={1}", mbId, language); // TODO Plex Route for MB? LastFM? - var resource = $"library/sections/{sectionId}/all?guid={System.Web.HttpUtility.UrlEncode(guid)}"; - var request = BuildRequest(resource, HttpMethod.Get, settings); - var response = ProcessRequest(request); - - CheckForError(response); - - List items; - - if (response.Contains("_children")) - { - items = Json.Deserialize(response) - .Items; - } - else - { - items = Json.Deserialize>(response) - .MediaContainer - .Items; - } - - if (items == null || items.Empty()) - { - return null; - } - - return items.First().Id; - } - private HttpRequestBuilder BuildRequest(string resource, HttpMethod method, PlexServerSettings settings) { var scheme = settings.UseSsl ? "https" : "http"; diff --git a/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerService.cs b/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerService.cs index 989c60e9b..700a20a70 100644 --- a/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerService.cs +++ b/src/NzbDrone.Core/Notifications/Plex/Server/PlexServerService.cs @@ -8,6 +8,7 @@ using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.Extensions; using NzbDrone.Core.Music; +using NzbDrone.Core.RootFolders; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Notifications.Plex.Server @@ -22,15 +23,15 @@ namespace NzbDrone.Core.Notifications.Plex.Server public class PlexServerService : IPlexServerService { private readonly ICached _versionCache; - private readonly ICached _partialUpdateCache; private readonly IPlexServerProxy _plexServerProxy; + private readonly IRootFolderService _rootFolderService; private readonly Logger _logger; - public PlexServerService(ICacheManager cacheManager, IPlexServerProxy plexServerProxy, Logger logger) + public PlexServerService(ICacheManager cacheManager, IPlexServerProxy plexServerProxy, IRootFolderService rootFolderService, Logger logger) { _versionCache = cacheManager.GetCache(GetType(), "versionCache"); - _partialUpdateCache = cacheManager.GetCache(GetType(), "partialUpdateCache"); _plexServerProxy = plexServerProxy; + _rootFolderService = rootFolderService; _logger = logger; } @@ -50,32 +51,10 @@ namespace NzbDrone.Core.Notifications.Plex.Server ValidateVersion(version); var sections = GetSections(settings); - var partialUpdates = _partialUpdateCache.Get(settings.Host, () => PartialUpdatesAllowed(settings, version), TimeSpan.FromHours(2)); - if (partialUpdates) + foreach (var artist in multipleArtist) { - var partiallyUpdated = true; - - foreach (var artist in multipleArtist) - { - partiallyUpdated &= UpdatePartialSection(artist, sections, settings); - - if (!partiallyUpdated) - { - break; - } - } - - // Only update complete sections if all partial updates failed - if (!partiallyUpdated) - { - _logger.Debug("Unable to update partial section, updating all Music sections"); - sections.ForEach(s => UpdateSection(s.Id, settings)); - } - } - else - { - sections.ForEach(s => UpdateSection(s.Id, settings)); + UpdateSections(artist, sections, settings); } _logger.Debug("Finished sending Update Request to Plex Server (took {0} ms)", watch.ElapsedMilliseconds); @@ -94,31 +73,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server return _plexServerProxy.GetArtistSections(settings).ToList(); } - private bool PartialUpdatesAllowed(PlexServerSettings settings, Version version) - { - try - { - if (version >= new Version(0, 9, 12, 0)) - { - var preferences = GetPreferences(settings); - var partialScanPreference = preferences.SingleOrDefault(p => p.Id.Equals("FSEventLibraryPartialScanEnabled")); - - if (partialScanPreference == null) - { - return false; - } - - return Convert.ToBoolean(partialScanPreference.Value); - } - } - catch (Exception ex) - { - _logger.Warn(ex, "Unable to check if partial updates are allowed"); - } - - return false; - } - private void ValidateVersion(Version version) { if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1)) @@ -137,45 +91,47 @@ namespace NzbDrone.Core.Notifications.Plex.Server return version; } - private List GetPreferences(PlexServerSettings settings) + private void UpdateSections(Artist artist, List sections, PlexServerSettings settings) { - _logger.Debug("Getting preferences from Plex host: {0}", settings.Host); - - return _plexServerProxy.Preferences(settings); - } + var rootFolderPath = _rootFolderService.GetBestRootFolderPath(artist.Path); + var artistRelativePath = rootFolderPath.GetRelativePath(artist.Path); - private void UpdateSection(int sectionId, PlexServerSettings settings) - { - _logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, sectionId); + // Try to update a matching section location before falling back to updating all section locations. + foreach (var section in sections) + { + foreach (var location in section.Locations) + { + if (location.Path.PathEquals(rootFolderPath)) + { + _logger.Debug("Updating matching section location, {0}", location.Path); + UpdateSectionPath(artistRelativePath, section, location, settings); - _plexServerProxy.Update(sectionId, settings); - } + return; + } + } + } - private bool UpdatePartialSection(Artist artist, List sections, PlexServerSettings settings) - { - var partiallyUpdated = false; + _logger.Debug("Unable to find matching section location, updating all Music sections"); foreach (var section in sections) { - var metadataId = GetMetadataId(section.Id, artist, section.Language, settings); - - if (metadataId.HasValue) + foreach (var location in section.Locations) { - _logger.Debug("Updating Plex host: {0}, Section: {1}, Artist: {2}", settings.Host, section.Id, artist); - _plexServerProxy.UpdateArtist(metadataId.Value, settings); - - partiallyUpdated = true; + UpdateSectionPath(artistRelativePath, section, location, settings); } } - - return partiallyUpdated; } - private int? GetMetadataId(int sectionId, Artist artist, string language, PlexServerSettings settings) + private void UpdateSectionPath(string artistRelativePath, PlexSection section, PlexSectionLocation location, PlexServerSettings settings) { - _logger.Debug("Getting metadata from Plex host: {0} for artist: {1}", settings.Host, artist); + var separator = location.Path.Contains('\\') ? "\\" : "/"; + var locationRelativePath = artistRelativePath.Replace("\\", separator).Replace("/", separator); + + // Plex location paths trim trailing extraneous separator characters, so it doesn't need to be trimmed + var pathToUpdate = $"{location.Path}{separator}{locationRelativePath}"; - return _plexServerProxy.GetMetadataId(sectionId, artist.Metadata.Value.ForeignArtistId, language, settings); + _logger.Debug("Updating section location, {0}", location.Path); + _plexServerProxy.Update(section.Id, pathToUpdate, settings); } public ValidationFailure Test(PlexServerSettings settings) @@ -183,7 +139,6 @@ namespace NzbDrone.Core.Notifications.Plex.Server try { _versionCache.Remove(settings.Host); - _partialUpdateCache.Remove(settings.Host); var sections = GetSections(settings); if (sections.Empty())