mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-29 23:22:39 +01:00
New: Group updates for the same movie for Kodi and Emby / Jellyfin
(cherry picked from commit 46c7de379c872f757847a311b21714e905466360) Closes #10150
This commit is contained in:
parent
d5fb1c55c6
commit
017fa5ad80
@ -33,9 +33,10 @@ public void Setup()
|
||||
|
||||
Subject.Definition = new NotificationDefinition();
|
||||
Subject.Definition.Settings = new XbmcSettings
|
||||
{
|
||||
UpdateLibrary = true
|
||||
};
|
||||
{
|
||||
Host = "localhost",
|
||||
UpdateLibrary = true
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenOldFiles()
|
||||
@ -48,16 +49,18 @@ private void GivenOldFiles()
|
||||
.ToList();
|
||||
|
||||
Subject.Definition.Settings = new XbmcSettings
|
||||
{
|
||||
UpdateLibrary = true,
|
||||
CleanLibrary = true
|
||||
};
|
||||
{
|
||||
Host = "localhost",
|
||||
UpdateLibrary = true,
|
||||
CleanLibrary = true
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_clean_if_no_movie_was_replaced()
|
||||
{
|
||||
Subject.OnDownload(_downloadMessage);
|
||||
Subject.ProcessQueue();
|
||||
|
||||
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Never());
|
||||
}
|
||||
@ -67,6 +70,7 @@ public void should_clean_if_movie_was_replaced()
|
||||
{
|
||||
GivenOldFiles();
|
||||
Subject.OnDownload(_downloadMessage);
|
||||
Subject.ProcessQueue();
|
||||
|
||||
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Once());
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public void should_update_using_movie_path()
|
||||
.With(s => s.ImdbId = IMDB_ID)
|
||||
.Build();
|
||||
|
||||
Subject.UpdateMovie(_settings, movie);
|
||||
Subject.Update(_settings, movie);
|
||||
|
||||
Mocker.GetMock<IXbmcJsonApiProxy>()
|
||||
.Verify(v => v.UpdateLibrary(_settings, It.IsAny<string>()), Times.Once());
|
||||
@ -59,7 +59,7 @@ public void should_update_all_paths_when_movie_path_not_found()
|
||||
.With(s => s.Title = "Not A Real Movie")
|
||||
.Build();
|
||||
|
||||
Subject.UpdateMovie(_settings, fakeMovie);
|
||||
Subject.Update(_settings, fakeMovie);
|
||||
|
||||
Mocker.GetMock<IXbmcJsonApiProxy>()
|
||||
.Verify(v => v.UpdateLibrary(_settings, null), Times.Once());
|
||||
|
@ -1,5 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
@ -9,10 +12,18 @@ namespace NzbDrone.Core.Notifications.Emby
|
||||
public class MediaBrowser : NotificationBase<MediaBrowserSettings>
|
||||
{
|
||||
private readonly IMediaBrowserService _mediaBrowserService;
|
||||
private readonly MediaServerUpdateQueue<MediaBrowser, string> _updateQueue;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MediaBrowser(IMediaBrowserService mediaBrowserService)
|
||||
private static string Created = "Created";
|
||||
private static string Deleted = "Deleted";
|
||||
private static string Modified = "Modified";
|
||||
|
||||
public MediaBrowser(IMediaBrowserService mediaBrowserService, ICacheManager cacheManager, Logger logger)
|
||||
{
|
||||
_mediaBrowserService = mediaBrowserService;
|
||||
_updateQueue = new MediaServerUpdateQueue<MediaBrowser, string>(cacheManager);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override string Link => "https://emby.media/";
|
||||
@ -33,18 +44,12 @@ public override void OnDownload(DownloadMessage message)
|
||||
_mediaBrowserService.Notify(Settings, MOVIE_DOWNLOADED_TITLE_BRANDED, message.Message);
|
||||
}
|
||||
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_mediaBrowserService.Update(Settings, message.Movie, "Created");
|
||||
}
|
||||
UpdateIfEnabled(message.Movie, Created);
|
||||
}
|
||||
|
||||
public override void OnMovieRename(Movie movie, List<RenamedMovieFile> renamedFiles)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_mediaBrowserService.Update(Settings, movie, "Modified");
|
||||
}
|
||||
UpdateIfEnabled(movie, Modified);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck message)
|
||||
@ -80,10 +85,7 @@ public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
||||
_mediaBrowserService.Notify(Settings, MOVIE_DELETED_TITLE_BRANDED, deleteMessage.Message);
|
||||
}
|
||||
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_mediaBrowserService.Update(Settings, deleteMessage.Movie, "Deleted");
|
||||
}
|
||||
UpdateIfEnabled(deleteMessage.Movie, Deleted);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,9 +96,34 @@ public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
||||
_mediaBrowserService.Notify(Settings, MOVIE_FILE_DELETED_TITLE_BRANDED, deleteMessage.Message);
|
||||
}
|
||||
|
||||
UpdateIfEnabled(deleteMessage.Movie, Deleted);
|
||||
}
|
||||
|
||||
public override void ProcessQueue()
|
||||
{
|
||||
_updateQueue.ProcessQueue(Settings.Host, (items) =>
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_logger.Debug("Performing library update for {0} movies", items.Count);
|
||||
|
||||
items.ForEach(item =>
|
||||
{
|
||||
// If there is only one update type for the movie use that, otherwise send null and let Emby decide
|
||||
var updateType = item.Info.Count == 1 ? item.Info.First() : null;
|
||||
|
||||
_mediaBrowserService.Update(Settings, item.Movie, updateType);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateIfEnabled(Movie movie, string updateType)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_mediaBrowserService.Update(Settings, deleteMessage.Movie, "Deleted");
|
||||
_logger.Debug("Scheduling library update for movie {0} {1}", movie.Id, movie.Title);
|
||||
_updateQueue.Add(Settings.Host, movie, updateType);
|
||||
}
|
||||
}
|
||||
|
||||
|
105
src/NzbDrone.Core/Notifications/MediaServerUpdateQueue.cs
Normal file
105
src/NzbDrone.Core/Notifications/MediaServerUpdateQueue.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public class MediaServerUpdateQueue<TQueueHost, TItemInfo>
|
||||
where TQueueHost : class
|
||||
{
|
||||
private class UpdateQueue
|
||||
{
|
||||
public Dictionary<int, UpdateQueueItem<TItemInfo>> Pending { get; } = new ();
|
||||
public bool Refreshing { get; set; }
|
||||
}
|
||||
|
||||
private readonly ICached<UpdateQueue> _pendingMoviesCache;
|
||||
|
||||
public MediaServerUpdateQueue(ICacheManager cacheManager)
|
||||
{
|
||||
_pendingMoviesCache = cacheManager.GetRollingCache<UpdateQueue>(typeof(TQueueHost), "pendingMovies", TimeSpan.FromDays(1));
|
||||
}
|
||||
|
||||
public void Add(string identifier, Movie movie, TItemInfo info)
|
||||
{
|
||||
var queue = _pendingMoviesCache.Get(identifier, () => new UpdateQueue());
|
||||
|
||||
lock (queue)
|
||||
{
|
||||
var item = queue.Pending.TryGetValue(movie.Id, out var value)
|
||||
? value
|
||||
: new UpdateQueueItem<TItemInfo>(movie);
|
||||
|
||||
item.Info.Add(info);
|
||||
|
||||
queue.Pending[movie.Id] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessQueue(string identifier, Action<List<UpdateQueueItem<TItemInfo>>> update)
|
||||
{
|
||||
var queue = _pendingMoviesCache.Find(identifier);
|
||||
|
||||
if (queue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Refreshing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
queue.Refreshing = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
List<UpdateQueueItem<TItemInfo>> items;
|
||||
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Pending.Empty())
|
||||
{
|
||||
queue.Refreshing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
items = queue.Pending.Values.ToList();
|
||||
queue.Pending.Clear();
|
||||
}
|
||||
|
||||
update(items);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
lock (queue)
|
||||
{
|
||||
queue.Refreshing = false;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateQueueItem<TItemInfo>
|
||||
{
|
||||
public Movie Movie { get; set; }
|
||||
public HashSet<TItemInfo> Info { get; set; }
|
||||
|
||||
public UpdateQueueItem(Movie movie)
|
||||
{
|
||||
Movie = movie;
|
||||
Info = new HashSet<TItemInfo>();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,23 +18,15 @@ public class PlexServer : NotificationBase<PlexServerSettings>
|
||||
{
|
||||
private readonly IPlexServerService _plexServerService;
|
||||
private readonly IPlexTvService _plexTvService;
|
||||
private readonly MediaServerUpdateQueue<PlexServer, bool> _updateQueue;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private class PlexUpdateQueue
|
||||
{
|
||||
public Dictionary<int, Movie> Pending { get; } = new Dictionary<int, Movie>();
|
||||
public bool Refreshing { get; set; }
|
||||
}
|
||||
|
||||
private readonly ICached<PlexUpdateQueue> _pendingMoviesCache;
|
||||
|
||||
public PlexServer(IPlexServerService plexServerService, IPlexTvService plexTvService, ICacheManager cacheManager, Logger logger)
|
||||
{
|
||||
_plexServerService = plexServerService;
|
||||
_plexTvService = plexTvService;
|
||||
_updateQueue = new MediaServerUpdateQueue<PlexServer, bool>(cacheManager);
|
||||
_logger = logger;
|
||||
|
||||
_pendingMoviesCache = cacheManager.GetRollingCache<PlexUpdateQueue>(GetType(), "pendingSeries", TimeSpan.FromDays(1));
|
||||
}
|
||||
|
||||
public override string Link => "https://www.plex.tv/";
|
||||
@ -70,66 +62,20 @@ private void UpdateIfEnabled(Movie movie)
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_logger.Debug("Scheduling library update for movie {0} {1}", movie.Id, movie.Title);
|
||||
var queue = _pendingMoviesCache.Get(Settings.Host, () => new PlexUpdateQueue());
|
||||
lock (queue)
|
||||
{
|
||||
queue.Pending[movie.Id] = movie;
|
||||
}
|
||||
_updateQueue.Add(Settings.Host, movie, false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProcessQueue()
|
||||
{
|
||||
var queue = _pendingMoviesCache.Find(Settings.Host);
|
||||
|
||||
if (queue == null)
|
||||
_updateQueue.ProcessQueue(Settings.Host, (items) =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Refreshing)
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
return;
|
||||
_logger.Debug("Performing library update for {0} movies", items.Count);
|
||||
_plexServerService.UpdateLibrary(items.Select(i => i.Movie), Settings);
|
||||
}
|
||||
|
||||
queue.Refreshing = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
List<Movie> refreshingMovies;
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Pending.Empty())
|
||||
{
|
||||
queue.Refreshing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
refreshingMovies = queue.Pending.Values.ToList();
|
||||
queue.Pending.Clear();
|
||||
}
|
||||
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_logger.Debug("Performing library update for {0} movies", refreshingMovies.Count);
|
||||
_plexServerService.UpdateLibrary(refreshingMovies, Settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
lock (queue)
|
||||
{
|
||||
queue.Refreshing = false;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
@ -203,13 +149,6 @@ public override object RequestAction(string action, IDictionary<string, string>
|
||||
{
|
||||
var result = new List<FieldSelectStringOption>();
|
||||
|
||||
// result.Add(new FieldSelectStringOption
|
||||
// {
|
||||
// Value = s.Name,
|
||||
// Name = s.Name,
|
||||
// IsDisabled = true
|
||||
// });
|
||||
|
||||
s.Connections.ForEach(c =>
|
||||
{
|
||||
var isSecure = c.Protocol == "https";
|
||||
|
@ -3,6 +3,7 @@
|
||||
using System.Net.Sockets;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
@ -12,11 +13,13 @@ namespace NzbDrone.Core.Notifications.Xbmc
|
||||
public class Xbmc : NotificationBase<XbmcSettings>
|
||||
{
|
||||
private readonly IXbmcService _xbmcService;
|
||||
private readonly MediaServerUpdateQueue<Xbmc, bool> _updateQueue;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public Xbmc(IXbmcService xbmcService, Logger logger)
|
||||
public Xbmc(IXbmcService xbmcService, ICacheManager cacheManager, Logger logger)
|
||||
{
|
||||
_xbmcService = xbmcService;
|
||||
_updateQueue = new MediaServerUpdateQueue<Xbmc, bool>(cacheManager);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -34,12 +37,12 @@ public override void OnDownload(DownloadMessage message)
|
||||
const string header = "Radarr - Downloaded";
|
||||
|
||||
Notify(Settings, header, message.Message);
|
||||
UpdateAndCleanMovie(message.Movie, message.OldMovieFiles.Any());
|
||||
UpdateAndClean(message.Movie, message.OldMovieFiles.Any());
|
||||
}
|
||||
|
||||
public override void OnMovieRename(Movie movie, List<RenamedMovieFile> renamedFiles)
|
||||
{
|
||||
UpdateAndCleanMovie(movie);
|
||||
UpdateAndClean(movie);
|
||||
}
|
||||
|
||||
public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
||||
@ -47,7 +50,7 @@ public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
||||
const string header = "Radarr - Deleted";
|
||||
|
||||
Notify(Settings, header, deleteMessage.Message);
|
||||
UpdateAndCleanMovie(deleteMessage.Movie, true);
|
||||
UpdateAndClean(deleteMessage.Movie, true);
|
||||
}
|
||||
|
||||
public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
||||
@ -57,7 +60,7 @@ public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
||||
const string header = "Radarr - Deleted";
|
||||
|
||||
Notify(Settings, header, deleteMessage.Message);
|
||||
UpdateAndCleanMovie(deleteMessage.Movie, true);
|
||||
UpdateAndClean(deleteMessage.Movie, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +86,35 @@ public override void OnManualInteractionRequired(ManualInteractionRequiredMessag
|
||||
|
||||
public override string Name => "Kodi";
|
||||
|
||||
public override void ProcessQueue()
|
||||
{
|
||||
_updateQueue.ProcessQueue(Settings.Host, (items) =>
|
||||
{
|
||||
_logger.Debug("Performing library update for {0} movies", items.Count);
|
||||
|
||||
items.ForEach(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_xbmcService.Update(Settings, item.Movie);
|
||||
}
|
||||
|
||||
if (item.Info.Contains(true) && Settings.CleanLibrary)
|
||||
{
|
||||
_xbmcService.Clean(Settings);
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
var logMessage = string.Format("Unable to connect to Kodi Host: {0}:{1}", Settings.Host, Settings.Port);
|
||||
_logger.Debug(ex, logMessage);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
@ -108,24 +140,12 @@ private void Notify(XbmcSettings settings, string header, string message)
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAndCleanMovie(Movie movie, bool clean = true)
|
||||
private void UpdateAndClean(Movie movie, bool clean = true)
|
||||
{
|
||||
try
|
||||
if (Settings.UpdateLibrary || Settings.CleanLibrary)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_xbmcService.UpdateMovie(Settings, movie);
|
||||
}
|
||||
|
||||
if (clean && Settings.CleanLibrary)
|
||||
{
|
||||
_xbmcService.Clean(Settings);
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
var logMessage = string.Format("Unable to connect to Kodi Host: {0}:{1}", Settings.Host, Settings.Port);
|
||||
_logger.Debug(ex, logMessage);
|
||||
_logger.Debug("Scheduling library update for movie {0} {1}", movie.Id, movie.Title);
|
||||
_updateQueue.Add(Settings.Host, movie, clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
|
||||
public interface IXbmcService
|
||||
{
|
||||
void Notify(XbmcSettings settings, string title, string message);
|
||||
void UpdateMovie(XbmcSettings settings, Movie movie);
|
||||
void Update(XbmcSettings settings, Movie movie);
|
||||
void Clean(XbmcSettings settings);
|
||||
ValidationFailure Test(XbmcSettings settings, string message);
|
||||
}
|
||||
@ -34,7 +34,7 @@ public void Notify(XbmcSettings settings, string title, string message)
|
||||
_proxy.Notify(settings, title, message);
|
||||
}
|
||||
|
||||
public void UpdateMovie(XbmcSettings settings, Movie movie)
|
||||
public void Update(XbmcSettings settings, Movie movie)
|
||||
{
|
||||
if (CheckIfVideoPlayerOpen(settings))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user