mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
feat: Plex Watchlist Error Reporting
This commit is contained in:
parent
6cb0bf58c6
commit
b6d0b8ebfc
7 changed files with 139 additions and 5 deletions
|
@ -1,9 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ombi.Api.Plex.Models
|
||||
{
|
||||
public class PlexWatchlistContainer
|
||||
{
|
||||
public PlexWatchlist MediaContainer { get; set; }
|
||||
[JsonIgnore]
|
||||
public bool AuthError { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -295,9 +296,18 @@ namespace Ombi.Api.Plex
|
|||
var request = new Request("library/sections/watchlist/all", WatchlistUri, HttpMethod.Get);
|
||||
await AddHeaders(request, plexToken);
|
||||
|
||||
var result = await Api.Request<PlexWatchlistContainer>(request, cancellationToken);
|
||||
var result = await Api.Request(request, cancellationToken);
|
||||
|
||||
return result;
|
||||
if (result.StatusCode.Equals(HttpStatusCode.Unauthorized))
|
||||
{
|
||||
return new PlexWatchlistContainer
|
||||
{
|
||||
AuthError = true
|
||||
};
|
||||
}
|
||||
|
||||
var receivedString = await result.Content.ReadAsStringAsync(cancellationToken);
|
||||
return JsonConvert.DeserializeObject<PlexWatchlistContainer>(receivedString);
|
||||
}
|
||||
|
||||
public async Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Moq;
|
||||
using MockQueryable.Moq;
|
||||
using Moq;
|
||||
using Moq.AutoMock;
|
||||
using NUnit.Framework;
|
||||
using Ombi.Api.Plex;
|
||||
|
@ -8,6 +9,7 @@ using Ombi.Core.Engine.Interfaces;
|
|||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Core.Tests;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
@ -32,11 +34,12 @@ namespace Ombi.Schedule.Tests
|
|||
public void Setup()
|
||||
{
|
||||
_mocker = new AutoMocker();
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" } });
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } });
|
||||
_mocker.Use(um);
|
||||
_context = _mocker.GetMock<IJobExecutionContext>();
|
||||
_context.Setup(x => x.CancellationToken).Returns(CancellationToken.None);
|
||||
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
|
||||
_mocker.Setup<IExternalRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>().AsQueryable().BuildMock().Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -53,6 +56,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TerminatesWhenWatchlistIsNotEnabled()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = false });
|
||||
await _subject.Execute(null);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
|
@ -75,9 +79,74 @@ namespace Ombi.Schedule.Tests
|
|||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AuthenticationError()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistUserError>>(x => x.Add(It.Is<PlexWatchlistUserError>(x => x.UserId == "abc")), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FailedWatchListUser_NewToken_ShouldBeRemoved()
|
||||
{
|
||||
_mocker.Setup<IExternalRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>
|
||||
{
|
||||
new PlexWatchlistUserError
|
||||
{
|
||||
UserId = "abc",
|
||||
MediaServerToken = "dead"
|
||||
}
|
||||
}.AsQueryable().BuildMock().Object);
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer { AuthError = false });
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistUserError>>(x => x.Add(It.Is<PlexWatchlistUserError>(x => x.UserId == "abc")), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistUserError>>(x => x.Delete(It.Is<PlexWatchlistUserError>(x => x.UserId == "abc")), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FailedWatchListUser_OldToken_ShouldSkip()
|
||||
{
|
||||
_mocker.Setup<IExternalRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>
|
||||
{
|
||||
new PlexWatchlistUserError
|
||||
{
|
||||
UserId = "abc",
|
||||
MediaServerToken = "token1"
|
||||
}
|
||||
}.AsQueryable().BuildMock().Object);
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer { AuthError = false });
|
||||
await _subject.Execute(_context.Object);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistUserError>>(x => x.Add(It.Is<PlexWatchlistUserError>(x => x.UserId == "abc")), Times.Never);
|
||||
_mocker.Verify<IExternalRepository<PlexWatchlistUserError>>(x => x.Delete(It.Is<PlexWatchlistUserError>(x => x.UserId == "abc")), Times.Never);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task NoPlexUsersWithToken()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser>
|
||||
{
|
||||
|
@ -102,6 +171,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MultipleUsers()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
var um = MockHelper.MockUserManager(new List<OmbiUser>
|
||||
{
|
||||
|
@ -125,6 +195,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_NoGuid()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -175,6 +246,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoGuid()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -224,6 +296,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_AlreadyRequested()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -273,6 +346,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TvRequestFromWatchList_AlreadyRequested()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -322,6 +396,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_NoTmdbGuid()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -371,6 +446,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task TvRequestFromWatchList_NoTmdbGuid()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
@ -420,6 +496,7 @@ namespace Ombi.Schedule.Tests
|
|||
[Test]
|
||||
public async Task MovieRequestFromWatchList_AlreadyImported()
|
||||
{
|
||||
|
||||
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
|
||||
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
|
@ -32,10 +33,11 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
private readonly IHubContext<NotificationHub> _hub;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo;
|
||||
private readonly IExternalRepository<PlexWatchlistUserError> _userError;
|
||||
|
||||
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
|
||||
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, IHubContext<NotificationHub> hub,
|
||||
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo)
|
||||
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IExternalRepository<PlexWatchlistUserError> userError)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_settings = settings;
|
||||
|
@ -45,6 +47,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
_hub = hub;
|
||||
_logger = logger;
|
||||
_watchlistRepo = watchlistRepo;
|
||||
_userError = userError;
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
|
@ -64,9 +67,35 @@ namespace Ombi.Schedule.Jobs.Plex
|
|||
{
|
||||
try
|
||||
{
|
||||
// Check if the user has errors and the token is the same (not refreshed)
|
||||
var failedUser = await _userError.GetAll().Where(x => x.UserId == user.Id).FirstOrDefaultAsync();
|
||||
if (failedUser != null)
|
||||
{
|
||||
if (failedUser.MediaServerToken.Equals(user.MediaServerToken))
|
||||
{
|
||||
_logger.LogInformation($"Skipping Plex Watchlist Import for user '{user.UserName}' as they failed previously and the token has not yet been refreshed");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove that guy
|
||||
await _userError.Delete(failedUser);
|
||||
failedUser = null;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}");
|
||||
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
|
||||
if (watchlist?.AuthError ?? false)
|
||||
{
|
||||
_logger.LogError($"Auth failed for user '{user.UserName}'. Need to re-authenticate with Ombi.");
|
||||
await _userError.Add(new PlexWatchlistUserError
|
||||
{
|
||||
UserId = user.Id,
|
||||
MediaServerToken = user.MediaServerToken,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false))
|
||||
{
|
||||
_logger.LogDebug($"No watchlist found for {user.UserName}");
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Ombi.Store.Context
|
|||
public DbSet<PlexSeasonsContent> PlexSeasonsContent { get; set; }
|
||||
public DbSet<PlexEpisode> PlexEpisode { get; set; }
|
||||
public DbSet<PlexWatchlistHistory> PlexWatchlistHistory { get; set; }
|
||||
public DbSet<PlexWatchlistUserError> PlexWatchListUserError { get; set; }
|
||||
public DbSet<RadarrCache> RadarrCache { get; set; }
|
||||
public DbSet<CouchPotatoCache> CouchPotatoCache { get; set; }
|
||||
public DbSet<EmbyContent> EmbyContent { get; set; }
|
||||
|
|
14
src/Ombi.Store/Entities/PlexWatchlistUserError.cs
Normal file
14
src/Ombi.Store/Entities/PlexWatchlistUserError.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table(nameof(PlexWatchlistUserError))]
|
||||
public class PlexWatchlistUserError : Entity
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public string MediaServerToken { get; set; }
|
||||
|
||||
[ForeignKey(nameof(UserId))]
|
||||
public OmbiUser User { get; set; }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue