diff --git a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs index b4759e280..b45f471d2 100644 --- a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs +++ b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs @@ -10,9 +10,11 @@ using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Schedule.Jobs.Plex; using Ombi.Store.Entities; +using Ombi.Store.Repository; using Ombi.Test.Common; using Quartz; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -45,6 +47,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Never); _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } [Test] public async Task TerminatesWhenWatchlistIsNotEnabled() @@ -54,6 +58,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Never); _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } [Test] @@ -65,6 +71,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } [Test] @@ -86,6 +94,31 @@ namespace Ombi.Schedule.Tests await _subject.Execute(_context.Object); _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Never); _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); + } + + + [Test] + public async Task MultipleUsers() + { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); + var um = MockHelper.MockUserManager(new List + { + new OmbiUser { Id = "abc1", UserType = UserType.PlexUser, MediaServerToken = "abc1", UserName = "abc1", NormalizedUserName = "ABC1" }, + new OmbiUser { Id = "abc2", UserType = UserType.PlexUser, MediaServerToken = "abc2", UserName = "abc2", NormalizedUserName = "ABC2" }, + new OmbiUser { Id = "abc3", UserType = UserType.PlexUser, MediaServerToken = "abc3", UserName = "abc3", NormalizedUserName = "ABC3" }, + new OmbiUser { Id = "abc4", UserType = UserType.PlexUser, MediaServerToken = "abc4", UserName = "abc4", NormalizedUserName = "ABC4" }, + new OmbiUser { Id = "abc5", UserType = UserType.PlexUser, MediaServerToken = "abc5", UserName = "abc5", NormalizedUserName = "ABC5" }, + }); + _mocker.Use(um); + _subject = _mocker.CreateInstance(); + + await _subject.Execute(_context.Object); + _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Exactly(5)); + _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } @@ -134,6 +167,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.Is(x => x.TheMovieDbId == 123)), Times.Once); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + _mocker.Verify>(x => x.GetAll(), Times.Once); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Once); } @@ -182,6 +217,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestTvShow(It.Is(x => x.TheMovieDbId == 123)), Times.Once); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + _mocker.Verify>(x => x.GetAll(), Times.Once); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Once); } [Test] @@ -229,6 +266,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.Is(x => x.TheMovieDbId == 123)), Times.Once); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + _mocker.Verify>(x => x.GetAll(), Times.Once); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } [Test] @@ -276,6 +315,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestTvShow(It.Is(x => x.TheMovieDbId == 123)), Times.Once); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Once); + _mocker.Verify>(x => x.GetAll(), Times.Once); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } [Test] @@ -323,6 +364,8 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); } [Test] @@ -370,6 +413,56 @@ namespace Ombi.Schedule.Tests _mocker.Verify(x => x.RequestTvShow(It.IsAny()), Times.Never); _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Never); + } + + [Test] + public async Task MovieRequestFromWatchList_AlreadyImported() + { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); + _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny())).ReturnsAsync(new PlexWatchlistContainer + { + MediaContainer = new PlexWatchlist + { + Metadata = new List + { + new Metadata + { + type = "movie", + ratingKey = "abc" + } + } + } + }); + _mocker.Setup>(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny())) + .ReturnsAsync(new PlexWatchlistMetadataContainer + { + MediaContainer = new PlexWatchlistMetadata + { + Metadata = new WatchlistMetadata[] + { + new WatchlistMetadata + { + Guid = new List + { + new PlexGuids + { + Id = "tmdb://123" + } + } + } + } + + } + }); + _mocker.Setup, IQueryable>(x => x.GetAll()).Returns(new List { new PlexWatchlistHistory { Id = 1, TmdbId = "123" } }.AsQueryable()); + await _subject.Execute(_context.Object); + _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); + _mocker.Verify(x => x.GetWatchlistMetadata("abc", It.IsAny(), It.IsAny()), Times.Once); + _mocker.Verify(x => x.SetUser(It.Is(x => x.Id == "abc")), Times.Never); + _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); + _mocker.Verify>(x => x.GetAll(), Times.Once); } } } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 170f6b943..9c5a651e2 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -385,7 +385,7 @@ namespace Ombi.Schedule.Jobs.Plex continue; } - var qualities = movie.Media?.Select(x => x.videoResolution); + var qualities = movie?.Media?.Select(x => x?.videoResolution ?? string.Empty) ?? Enumerable.Empty(); var is4k = qualities != null && qualities.Any(x => x.Equals("4k", StringComparison.InvariantCultureIgnoreCase)); var selectedQuality = is4k ? null : qualities?.OrderBy(x => x)?.FirstOrDefault() ?? string.Empty; diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs index ac5d21b16..b62ab0ddf 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs @@ -12,6 +12,7 @@ using Ombi.Helpers; using Ombi.Hubs; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; using Quartz; using System; using System.Collections.Generic; @@ -30,10 +31,11 @@ namespace Ombi.Schedule.Jobs.Plex private readonly ITvRequestEngine _tvRequestEngine; private readonly IHubContext _hub; private readonly ILogger _logger; + private readonly IRepository _watchlistRepo; public PlexWatchlistImport(IPlexApi plexApi, ISettingsService settings, OmbiUserManager ombiUserManager, IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, IHubContext hub, - ILogger logger) + ILogger logger, IRepository watchlistRepo) { _plexApi = plexApi; _settings = settings; @@ -42,6 +44,7 @@ namespace Ombi.Schedule.Jobs.Plex _tvRequestEngine = tvRequestEngine; _hub = hub; _logger = logger; + _watchlistRepo = watchlistRepo; } public async Task Execute(IJobExecutionContext context) @@ -82,6 +85,15 @@ namespace Ombi.Schedule.Jobs.Plex // We need a MovieDbId to support this; continue; } + + // Check to see if we have already imported this item + var alreadyImported = _watchlistRepo.GetAll().Any(x => x.TmdbId == providerIds.TheMovieDb); + if (alreadyImported) + { + _logger.LogDebug($"{item.title} already imported via Plex WatchList, skipping"); + continue; + } + switch (item.type) { case "show": @@ -118,6 +130,13 @@ namespace Ombi.Schedule.Jobs.Plex } else { + // Add to the watchlist history + var history = new PlexWatchlistHistory + { + TmdbId = theMovieDbId.ToString() + }; + await _watchlistRepo.Add(history); + _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); } } @@ -138,6 +157,12 @@ namespace Ombi.Schedule.Jobs.Plex } else { + // Add to the watchlist history + var history = new PlexWatchlistHistory + { + TmdbId = theMovieDbId.ToString() + }; + await _watchlistRepo.Add(history); _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); } } diff --git a/src/Ombi.Store/Context/ExternalContext.cs b/src/Ombi.Store/Context/ExternalContext.cs index b6f8d6485..f13c1e74f 100644 --- a/src/Ombi.Store/Context/ExternalContext.cs +++ b/src/Ombi.Store/Context/ExternalContext.cs @@ -27,6 +27,7 @@ namespace Ombi.Store.Context public DbSet PlexServerContent { get; set; } public DbSet PlexSeasonsContent { get; set; } public DbSet PlexEpisode { get; set; } + public DbSet PlexWatchlistHistory { get; set; } public DbSet RadarrCache { get; set; } public DbSet CouchPotatoCache { get; set; } public DbSet EmbyContent { get; set; } diff --git a/src/Ombi.Store/Entities/PlexWatchlistHistory.cs b/src/Ombi.Store/Entities/PlexWatchlistHistory.cs new file mode 100644 index 000000000..e6aee29b4 --- /dev/null +++ b/src/Ombi.Store/Entities/PlexWatchlistHistory.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table(nameof(PlexWatchlistHistory))] + public class PlexWatchlistHistory : Entity + { + public string TmdbId { get; set; } + } +}