mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-27 06:02:33 +01:00
New: Improved Plex library updating
Closes #8235 Closes #8244 (cherry picked from commit 98308737cfa78a41ec22a2b23a80bf316bd779ea)
This commit is contained in:
parent
9ec1235b62
commit
85aac789f4
@ -14,12 +14,8 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
|||||||
public interface IPlexServerProxy
|
public interface IPlexServerProxy
|
||||||
{
|
{
|
||||||
List<PlexSection> GetMovieSections(PlexServerSettings settings);
|
List<PlexSection> GetMovieSections(PlexServerSettings settings);
|
||||||
void Update(int sectionId, PlexServerSettings settings);
|
|
||||||
void UpdateMovie(string metadataId, PlexServerSettings settings);
|
|
||||||
void UpdatePath(string path, int sectionId, PlexServerSettings settings);
|
|
||||||
string Version(PlexServerSettings settings);
|
string Version(PlexServerSettings settings);
|
||||||
List<PlexPreference> Preferences(PlexServerSettings settings);
|
void Update(int sectionId, string path, PlexServerSettings settings);
|
||||||
string GetMetadataId(int sectionId, string imdbId, string language, PlexServerSettings settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexServerProxy : IPlexServerProxy
|
public class PlexServerProxy : IPlexServerProxy
|
||||||
@ -64,29 +60,13 @@ public List<PlexSection> GetMovieSections(PlexServerSettings settings)
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(int sectionId, PlexServerSettings settings)
|
public void Update(int sectionId, string path, PlexServerSettings settings)
|
||||||
{
|
{
|
||||||
var resource = $"library/sections/{sectionId}/refresh";
|
var resource = $"library/sections/{sectionId}/refresh";
|
||||||
var request = BuildRequest(resource, HttpMethod.Get, settings);
|
var request = BuildRequest(resource, HttpMethod.Get, settings);
|
||||||
var response = ProcessRequest(request);
|
|
||||||
|
|
||||||
CheckForError(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMovie(string metadataId, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
var resource = $"library/metadata/{metadataId}/refresh";
|
|
||||||
var request = BuildRequest(resource, HttpMethod.Put, settings);
|
|
||||||
var response = ProcessRequest(request);
|
|
||||||
|
|
||||||
CheckForError(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdatePath(string path, int sectionId, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
var resource = $"library/sections/{sectionId}/refresh";
|
|
||||||
var request = BuildRequest(resource, HttpMethod.Get, settings);
|
|
||||||
request.AddQueryParam("path", path);
|
request.AddQueryParam("path", path);
|
||||||
|
|
||||||
var response = ProcessRequest(request);
|
var response = ProcessRequest(request);
|
||||||
|
|
||||||
CheckForError(response);
|
CheckForError(response);
|
||||||
@ -110,55 +90,6 @@ public string Version(PlexServerSettings settings)
|
|||||||
.Version;
|
.Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PlexPreference> Preferences(PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
var request = BuildRequest(":/prefs", HttpMethod.Get, settings);
|
|
||||||
var response = ProcessRequest(request);
|
|
||||||
|
|
||||||
CheckForError(response);
|
|
||||||
|
|
||||||
if (response.Contains("_children"))
|
|
||||||
{
|
|
||||||
return Json.Deserialize<PlexPreferencesLegacy>(response)
|
|
||||||
.Preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Json.Deserialize<PlexResponse<PlexPreferences>>(response)
|
|
||||||
.MediaContainer
|
|
||||||
.Preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetMetadataId(int sectionId, string imdbId, string language, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
var guid = $"com.plexapp.agents.imdb://{imdbId}?lang={language}";
|
|
||||||
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<PlexSectionItem> items;
|
|
||||||
|
|
||||||
if (response.Contains("_children"))
|
|
||||||
{
|
|
||||||
items = Json.Deserialize<PlexSectionResponseLegacy>(response)
|
|
||||||
.Items;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(response)
|
|
||||||
.MediaContainer
|
|
||||||
.Items;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items == null || items.Empty())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.First().Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpRequestBuilder BuildRequest(string resource, HttpMethod method, PlexServerSettings settings)
|
private HttpRequestBuilder BuildRequest(string resource, HttpMethod method, PlexServerSettings settings)
|
||||||
{
|
{
|
||||||
var scheme = settings.UseSsl ? "https" : "http";
|
var scheme = settings.UseSsl ? "https" : "http";
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
@ -10,6 +9,7 @@
|
|||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.RootFolders;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Plex.Server
|
namespace NzbDrone.Core.Notifications.Plex.Server
|
||||||
@ -24,17 +24,15 @@ public interface IPlexServerService
|
|||||||
public class PlexServerService : IPlexServerService
|
public class PlexServerService : IPlexServerService
|
||||||
{
|
{
|
||||||
private readonly ICached<Version> _versionCache;
|
private readonly ICached<Version> _versionCache;
|
||||||
private readonly ICached<bool> _partialUpdateCache;
|
|
||||||
private readonly ICached<bool> _pathScanCache;
|
|
||||||
private readonly IPlexServerProxy _plexServerProxy;
|
private readonly IPlexServerProxy _plexServerProxy;
|
||||||
|
private readonly IRootFolderService _rootFolderService;
|
||||||
private readonly Logger _logger;
|
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<Version>(GetType(), "versionCache");
|
_versionCache = cacheManager.GetCache<Version>(GetType(), "versionCache");
|
||||||
_partialUpdateCache = cacheManager.GetCache<bool>(GetType(), "partialUpdateCache");
|
|
||||||
_pathScanCache = cacheManager.GetCache<bool>(GetType(), "pathScanCache");
|
|
||||||
_plexServerProxy = plexServerProxy;
|
_plexServerProxy = plexServerProxy;
|
||||||
|
_rootFolderService = rootFolderService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,52 +52,10 @@ public void UpdateLibrary(IEnumerable<Movie> multipleMovies, PlexServerSettings
|
|||||||
ValidateVersion(version);
|
ValidateVersion(version);
|
||||||
|
|
||||||
var sections = GetSections(settings);
|
var sections = GetSections(settings);
|
||||||
var partialUpdates = _partialUpdateCache.Get(settings.Host, () => PartialUpdatesAllowed(settings, version), TimeSpan.FromHours(2));
|
|
||||||
var pathScanCache = _pathScanCache.Get(settings.Host, () => PathUpdatesAllowed(settings, version), TimeSpan.FromHours(2));
|
|
||||||
|
|
||||||
var pathsUpdated = true;
|
|
||||||
|
|
||||||
if (pathScanCache)
|
|
||||||
{
|
|
||||||
foreach (var movie in multipleMovies)
|
|
||||||
{
|
|
||||||
pathsUpdated &= UpdatePath(movie, sections, settings);
|
|
||||||
|
|
||||||
if (!pathsUpdated)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we couldn't path update then try partial and full update
|
|
||||||
if (!pathScanCache || (pathScanCache && !pathsUpdated))
|
|
||||||
{
|
|
||||||
if (partialUpdates)
|
|
||||||
{
|
|
||||||
var partiallyUpdated = true;
|
|
||||||
|
|
||||||
foreach (var movie in multipleMovies)
|
foreach (var movie in multipleMovies)
|
||||||
{
|
{
|
||||||
partiallyUpdated &= UpdatePartialSection(movie, sections, settings);
|
UpdateSections(movie, 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 Movie sections");
|
|
||||||
sections.ForEach(s => UpdateSection(s.Id, settings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sections.ForEach(s => UpdateSection(s.Id, settings));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Debug("Finished sending Update Request to Plex Server (took {0} ms)", watch.ElapsedMilliseconds);
|
_logger.Debug("Finished sending Update Request to Plex Server (took {0} ms)", watch.ElapsedMilliseconds);
|
||||||
@ -118,56 +74,6 @@ private List<PlexSection> GetSections(PlexServerSettings settings)
|
|||||||
return _plexServerProxy.GetMovieSections(settings).ToList();
|
return _plexServerProxy.GetMovieSections(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 bool PathUpdatesAllowed(PlexServerSettings settings, Version version)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (version >= new Version(1, 20, 0, 3125))
|
|
||||||
{
|
|
||||||
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 path updates are allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ValidateVersion(Version version)
|
private void ValidateVersion(Version version)
|
||||||
{
|
{
|
||||||
if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1))
|
if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1))
|
||||||
@ -186,77 +92,57 @@ private Version GetVersion(PlexServerSettings settings)
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PlexPreference> GetPreferences(PlexServerSettings settings)
|
private void UpdateSections(Movie movie, List<PlexSection> sections, PlexServerSettings settings)
|
||||||
{
|
{
|
||||||
_logger.Debug("Getting preferences from Plex host: {0}", settings.Host);
|
var rootFolderPath = _rootFolderService.GetBestRootFolderPath(movie.Path);
|
||||||
|
var movieRelativePath = rootFolderPath.GetRelativePath(movie.Path);
|
||||||
|
|
||||||
return _plexServerProxy.Preferences(settings);
|
// 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)
|
||||||
|
{
|
||||||
|
var rootFolder = new OsPath(rootFolderPath);
|
||||||
|
var mappedPath = new OsPath(settings.MapTo) + (rootFolder - new OsPath(settings.MapFrom));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (location.Path.PathEquals(mappedPath.FullPath))
|
||||||
|
{
|
||||||
|
_logger.Debug("Updating matching section location, {0}", location.Path);
|
||||||
|
UpdateSectionPath(movieRelativePath, section, location, settings);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
// Swallow argument exception that is thrown by path comparison when comparing paths from different OSes
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSection(int sectionId, PlexServerSettings settings)
|
_logger.Debug("Unable to find matching section location, updating all Movie sections");
|
||||||
{
|
|
||||||
_logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, sectionId);
|
|
||||||
|
|
||||||
_plexServerProxy.Update(sectionId, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool UpdatePartialSection(Movie movie, List<PlexSection> sections, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
var partiallyUpdated = false;
|
|
||||||
|
|
||||||
foreach (var section in sections)
|
foreach (var section in sections)
|
||||||
{
|
{
|
||||||
var metadataId = GetMetadataId(section.Id, movie, section.Language, settings);
|
foreach (var location in section.Locations)
|
||||||
|
|
||||||
if (metadataId.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
{
|
||||||
_logger.Debug("Updating Plex host: {0}, Section: {1}, Movie: {2}", settings.Host, section.Id, movie);
|
UpdateSectionPath(movieRelativePath, section, location, settings);
|
||||||
_plexServerProxy.UpdateMovie(metadataId, settings);
|
}
|
||||||
|
|
||||||
partiallyUpdated = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return partiallyUpdated;
|
private void UpdateSectionPath(string movieRelativePath, PlexSection section, PlexSectionLocation location, PlexServerSettings settings)
|
||||||
}
|
|
||||||
|
|
||||||
private bool UpdatePath(Movie movie, List<PlexSection> sections, PlexServerSettings settings)
|
|
||||||
{
|
{
|
||||||
var pathUpdated = false;
|
var separator = location.Path.Contains('\\') ? "\\" : "/";
|
||||||
|
var locationRelativePath = movieRelativePath.Replace("\\", separator).Replace("/", separator);
|
||||||
|
|
||||||
var movieLocation = new OsPath(movie.Path);
|
// Plex location paths trim trailing extraneous separator characters, so it doesn't need to be trimmed
|
||||||
var mappedPath = movieLocation;
|
var pathToUpdate = $"{location.Path}{separator}{locationRelativePath}";
|
||||||
|
|
||||||
if (settings.MapTo.IsNotNullOrWhiteSpace())
|
_logger.Debug("Updating section location, {0}", location.Path);
|
||||||
{
|
_plexServerProxy.Update(section.Id, pathToUpdate, settings);
|
||||||
mappedPath = new OsPath(settings.MapTo) + (movieLocation - new OsPath(settings.MapFrom));
|
|
||||||
|
|
||||||
_logger.Trace("Mapping Path from {0} to {1} for partial scan", movieLocation, mappedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchingSection = sections.FirstOrDefault(section => section.Locations.Any(location => location.Path.IsParentPath(mappedPath.FullPath)));
|
|
||||||
|
|
||||||
if (matchingSection != null)
|
|
||||||
{
|
|
||||||
_logger.Debug("Updating Path on Plex host: {0}, Section: {1}, Path: {2}", settings.Host, matchingSection.Id, mappedPath);
|
|
||||||
|
|
||||||
_plexServerProxy.UpdatePath(mappedPath.FullPath, matchingSection.Id, settings);
|
|
||||||
|
|
||||||
pathUpdated = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Unable to update path, mapped movie path {0} is not a child of any plex libraries: {1}", mappedPath, sections.SelectMany(s => s.Locations.Select(l => l.Path)).Join(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetMetadataId(int sectionId, Movie movie, string language, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
_logger.Debug("Getting metadata from Plex host: {0} for movie: {1}", settings.Host, movie);
|
|
||||||
|
|
||||||
return _plexServerProxy.GetMetadataId(sectionId, movie.ImdbId, language, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationFailure Test(PlexServerSettings settings)
|
public ValidationFailure Test(PlexServerSettings settings)
|
||||||
@ -264,7 +150,6 @@ public ValidationFailure Test(PlexServerSettings settings)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_versionCache.Remove(settings.Host);
|
_versionCache.Remove(settings.Host);
|
||||||
_partialUpdateCache.Remove(settings.Host);
|
|
||||||
var sections = GetSections(settings);
|
var sections = GetSections(settings);
|
||||||
|
|
||||||
if (sections.Empty())
|
if (sections.Empty())
|
||||||
|
Loading…
Reference in New Issue
Block a user