diff --git a/src/Ombi.Api.Plex/Models/PlexWatchlistContainer.cs b/src/Ombi.Api.Plex/Models/PlexWatchlistContainer.cs index b208c189f..2eab1d255 100644 --- a/src/Ombi.Api.Plex/Models/PlexWatchlistContainer.cs +++ b/src/Ombi.Api.Plex/Models/PlexWatchlistContainer.cs @@ -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; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index 9f91c540c..fae4a78b9 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -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(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(receivedString); } public async Task GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken) diff --git a/src/Ombi.Core.Tests/Services/PlexServiceTests.cs b/src/Ombi.Core.Tests/Services/PlexServiceTests.cs new file mode 100644 index 000000000..a61c86b2e --- /dev/null +++ b/src/Ombi.Core.Tests/Services/PlexServiceTests.cs @@ -0,0 +1,146 @@ +using MockQueryable.Moq; +using Moq.AutoMock; +using NUnit.Framework; +using Ombi.Core.Models; +using Ombi.Core.Services; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Test.Common; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using UserType = Ombi.Store.Entities.UserType; + +namespace Ombi.Core.Tests.Services +{ + public class PlexServiceTests + { + + private PlexService _subject; + private AutoMocker _mocker; + + [SetUp] + public void Setup() + { + _mocker = new AutoMocker(); + _subject = _mocker.CreateInstance(); + } + + [Test] + public async Task GetWatchListUsers_AllUsersSynced() + { + var userMock = MockHelper.MockUserManager(new List + { + new OmbiUser + { + MediaServerToken = "token", + Id = "1", + UserName = "user1", + UserType = UserType.PlexUser, + }, + new OmbiUser + { + MediaServerToken = "token", + Id = "2", + UserName = "user2", + UserType = UserType.PlexUser, + }, + new OmbiUser + { + MediaServerToken = "token", + Id = "2", + UserName = "user2", + UserType = UserType.LocalUser, + } + }); + + _mocker.Use(userMock.Object); + _subject = _mocker.CreateInstance(); + + _mocker.Setup, IQueryable>(x => x.GetAll()) + .Returns(new List().AsQueryable().BuildMock().Object); + + var result = await _subject.GetWatchlistUsers(CancellationToken.None); + + Assert.Multiple(() => + { + Assert.That(result.All(x => x.SyncStatus == WatchlistSyncStatus.Successful)); + Assert.That(result.Count, Is.EqualTo(2)); + }); + } + + [Test] + public async Task GetWatchListUsers_NotEnabled() + { + var userMock = MockHelper.MockUserManager(new List + { + new OmbiUser + { + MediaServerToken = "", + Id = "1", + UserName = "user1", + UserType = UserType.PlexUser, + }, + new OmbiUser + { + MediaServerToken = null, + Id = "2", + UserName = "user2", + UserType = UserType.PlexUser, + }, + }); + + _mocker.Use(userMock.Object); + _subject = _mocker.CreateInstance(); + + _mocker.Setup, IQueryable>(x => x.GetAll()) + .Returns(new List().AsQueryable().BuildMock().Object); + + var result = await _subject.GetWatchlistUsers(CancellationToken.None); + + Assert.Multiple(() => + { + Assert.That(result.All(x => x.SyncStatus == WatchlistSyncStatus.NotEnabled)); + Assert.That(result.Count, Is.EqualTo(2)); + }); + } + + + [Test] + public async Task GetWatchListUsers_Failed() + { + var userMock = MockHelper.MockUserManager(new List + { + new OmbiUser + { + MediaServerToken = "test", + Id = "1", + UserName = "user1", + UserType = UserType.PlexUser, + }, + }); + + _mocker.Use(userMock.Object); + _subject = _mocker.CreateInstance(); + + _mocker.Setup, IQueryable>(x => x.GetAll()) + .Returns(new List + { + new PlexWatchlistUserError + { + UserId = "1", + MediaServerToken = "test", + } + }.AsQueryable().BuildMock().Object); + + var result = await _subject.GetWatchlistUsers(CancellationToken.None); + + Assert.Multiple(() => + { + Assert.That(result.All(x => x.SyncStatus == WatchlistSyncStatus.Failed)); + Assert.That(result.Count, Is.EqualTo(1)); + }); + } + } +} diff --git a/src/Ombi.Core/Engine/UserDeletionEngine.cs b/src/Ombi.Core/Engine/UserDeletionEngine.cs index 7df394592..1c8fd63ca 100644 --- a/src/Ombi.Core/Engine/UserDeletionEngine.cs +++ b/src/Ombi.Core/Engine/UserDeletionEngine.cs @@ -26,6 +26,7 @@ namespace Ombi.Core.Engine private readonly IMusicRequestRepository _musicRepository; private readonly IRepository _voteRepository; private readonly IRepository _mobileDevicesRepository; + private readonly IRepository _watchlistUserError; public UserDeletionEngine(IMovieRequestRepository movieRepository, OmbiUserManager userManager, @@ -39,7 +40,8 @@ namespace Ombi.Core.Engine IRepository notificationPreferencesRepo, IRepository qualityProfilesRepo, IRepository voteRepository, - IRepository mobileDevicesRepository + IRepository mobileDevicesRepository, + IRepository watchlistUserError ) { _movieRepository = movieRepository; @@ -56,6 +58,7 @@ namespace Ombi.Core.Engine _userQualityProfiles = qualityProfilesRepo; _voteRepository = voteRepository; _mobileDevicesRepository = mobileDevicesRepository; + _watchlistUserError = watchlistUserError; } @@ -68,6 +71,7 @@ namespace Ombi.Core.Engine var musicRequested = _musicRepository.GetAll().Where(x => x.RequestedUserId == userId); var notificationPreferences = _userNotificationPreferences.GetAll().Where(x => x.UserId == userId); var userQuality = await _userQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == userId); + var watchlistError = await _watchlistUserError.GetAll().FirstOrDefaultAsync(x => x.UserId == userId); if (moviesUserRequested.Any()) { @@ -89,6 +93,10 @@ namespace Ombi.Core.Engine { await _userQualityProfiles.Delete(userQuality); } + if (watchlistError != null) + { + await _watchlistUserError.Delete(watchlistError); + } // Delete any issues and request logs var issues = _issuesRepository.GetAll().Where(x => x.UserReportedId == userId); diff --git a/src/Ombi.Core/Models/PlexUserWatchlistModel.cs b/src/Ombi.Core/Models/PlexUserWatchlistModel.cs new file mode 100644 index 000000000..83652b105 --- /dev/null +++ b/src/Ombi.Core/Models/PlexUserWatchlistModel.cs @@ -0,0 +1,16 @@ +namespace Ombi.Core.Models +{ + public class PlexUserWatchlistModel + { + public string UserId { get; set; } + public WatchlistSyncStatus SyncStatus { get; set; } + public string UserName { get; set; } + } + + public enum WatchlistSyncStatus + { + Successful, + Failed, + NotEnabled + } +} diff --git a/src/Ombi.Core/Services/IPlexService.cs b/src/Ombi.Core/Services/IPlexService.cs new file mode 100644 index 000000000..4cacdc4e5 --- /dev/null +++ b/src/Ombi.Core/Services/IPlexService.cs @@ -0,0 +1,12 @@ +using Ombi.Core.Models; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Ombi.Core.Services +{ + public interface IPlexService + { + Task> GetWatchlistUsers(CancellationToken cancellationToken); + } +} diff --git a/src/Ombi.Core/Services/PlexService.cs b/src/Ombi.Core/Services/PlexService.cs new file mode 100644 index 000000000..4d3f41bda --- /dev/null +++ b/src/Ombi.Core/Services/PlexService.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Core.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using UserType = Ombi.Store.Entities.UserType; + +namespace Ombi.Core.Services +{ + public class PlexService : IPlexService + { + private readonly IRepository _watchlistUserErrors; + private readonly OmbiUserManager _userManager; + + public PlexService(IRepository watchlistUserErrors, OmbiUserManager userManager) + { + _watchlistUserErrors = watchlistUserErrors; + _userManager = userManager; + } + + public async Task> GetWatchlistUsers(CancellationToken cancellationToken) + { + var plexUsers = _userManager.Users.Where(x => x.UserType == UserType.PlexUser); + var userErrors = await _watchlistUserErrors.GetAll().ToListAsync(cancellationToken); + + var model = new List(); + + + foreach(var plexUser in plexUsers) + { + model.Add(new PlexUserWatchlistModel + { + UserId = plexUser.Id, + UserName = plexUser.UserName, + SyncStatus = GetWatchlistSyncStatus(plexUser, userErrors) + }); + } + + return model; + } + + private static WatchlistSyncStatus GetWatchlistSyncStatus(OmbiUser user, List userErrors) + { + if (string.IsNullOrWhiteSpace(user.MediaServerToken)) + { + return WatchlistSyncStatus.NotEnabled; + } + return userErrors.Any(x => x.UserId == user.Id) ? WatchlistSyncStatus.Failed : WatchlistSyncStatus.Successful; + } + } +} diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index c866f4442..9e47042de 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -229,6 +229,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddScoped(); services.AddTransient(); + services.AddTransient(); } public static void RegisterJobs(this IServiceCollection services) diff --git a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs index 950f6f431..64794f010 100644 --- a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs +++ b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs @@ -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 { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "abc", UserName = "abc", NormalizedUserName = "ABC" } }); + var um = MockHelper.MockUserManager(new List { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } }); _mocker.Use(um); _context = _mocker.GetMock(); _context.Setup(x => x.CancellationToken).Returns(CancellationToken.None); _subject = _mocker.CreateInstance(); + _mocker.Setup, IQueryable>(x => x.GetAll()).Returns(new List().AsQueryable().BuildMock().Object); } [Test] @@ -53,6 +56,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task TerminatesWhenWatchlistIsNotEnabled() { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = false }); await _subject.Execute(null); _mocker.Verify(x => x.RequestMovie(It.IsAny()), Times.Never); @@ -75,9 +79,74 @@ namespace Ombi.Schedule.Tests _mocker.Verify>(x => x.Add(It.IsAny()), Times.Never); } + [Test] + public async Task AuthenticationError() + { + + _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 { AuthError = true }); + await _subject.Execute(_context.Object); + _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); + _mocker.Verify>(x => x.Add(It.Is(x => x.UserId == "abc")), Times.Once); + } + + [Test] + public async Task FailedWatchListUser_NewToken_ShouldBeRemoved() + { + _mocker.Setup, IQueryable>(x => x.GetAll()).Returns(new List + { + new PlexWatchlistUserError + { + UserId = "abc", + MediaServerToken = "dead" + } + }.AsQueryable().BuildMock().Object); + + _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 { AuthError = false }); + await _subject.Execute(_context.Object); + _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); + _mocker.Verify>(x => x.Add(It.Is(x => x.UserId == "abc")), Times.Never); + _mocker.Verify>(x => x.Delete(It.Is(x => x.UserId == "abc")), Times.Once); + } + + [Test] + public async Task FailedWatchListUser_OldToken_ShouldSkip() + { + _mocker.Setup, IQueryable>(x => x.GetAll()).Returns(new List + { + new PlexWatchlistUserError + { + UserId = "abc", + MediaServerToken = "token1" + } + }.AsQueryable().BuildMock().Object); + + _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 { AuthError = false }); + await _subject.Execute(_context.Object); + _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); + _mocker.Verify>(x => x.Add(It.Is(x => x.UserId == "abc")), Times.Never); + _mocker.Verify>(x => x.Delete(It.Is(x => x.UserId == "abc")), Times.Never); + } + + [Test] public async Task NoPlexUsersWithToken() { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); var um = MockHelper.MockUserManager(new List { @@ -102,6 +171,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task MultipleUsers() { + _mocker.Setup, Task>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); var um = MockHelper.MockUserManager(new List { @@ -125,6 +195,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task MovieRequestFromWatchList_NoGuid() { + _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 { @@ -175,6 +246,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task TvRequestFromWatchList_NoGuid() { + _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 { @@ -224,6 +296,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task MovieRequestFromWatchList_AlreadyRequested() { + _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 { @@ -273,6 +346,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task TvRequestFromWatchList_AlreadyRequested() { + _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 { @@ -322,6 +396,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task MovieRequestFromWatchList_NoTmdbGuid() { + _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 { @@ -371,6 +446,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task TvRequestFromWatchList_NoTmdbGuid() { + _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 { @@ -420,6 +496,7 @@ namespace Ombi.Schedule.Tests [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 { diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs index 8e6b443ef..82b5a6d1a 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs @@ -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 _hub; private readonly ILogger _logger; private readonly IExternalRepository _watchlistRepo; + private readonly IRepository _userError; public PlexWatchlistImport(IPlexApi plexApi, ISettingsService settings, OmbiUserManager ombiUserManager, IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, IHubContext hub, - ILogger logger, IExternalRepository watchlistRepo) + ILogger logger, IExternalRepository watchlistRepo, IRepository 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}"); diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 77298e719..b41a541d3 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -45,6 +45,7 @@ namespace Ombi.Store.Context public DbSet RequestLogs { get; set; } public DbSet RecentlyAddedLogs { get; set; } public DbSet Votes { get; set; } + public DbSet PlexWatchListUserError { get; set; } public DbSet Audit { get; set; } diff --git a/src/Ombi.Store/Entities/PlexWatchlistUserError.cs b/src/Ombi.Store/Entities/PlexWatchlistUserError.cs new file mode 100644 index 000000000..8e5d2b41b --- /dev/null +++ b/src/Ombi.Store/Entities/PlexWatchlistUserError.cs @@ -0,0 +1,11 @@ +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; } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.Designer.cs b/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.Designer.cs new file mode 100644 index 000000000..e97946e9d --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.Designer.cs @@ -0,0 +1,1302 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.MySql; + +#nullable disable + +namespace Ombi.Store.Migrations.OmbiMySql +{ + [DbContext(typeof(OmbiMySqlContext))] + [Migration("20220823190345_PlexWatchlistUserError")] + partial class PlexWatchlistUserError + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuditArea") + .HasColumnType("int"); + + b.Property("AuditType") + .HasColumnType("int"); + + b.Property("DateTime") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("User") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("PlayerId") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Alias") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("int"); + + b.Property("EpisodeRequestLimitType") + .HasColumnType("int"); + + b.Property("Language") + .HasColumnType("longtext"); + + b.Property("LastLoggedIn") + .HasColumnType("datetime(6)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("MediaServerToken") + .HasColumnType("longtext"); + + b.Property("MovieRequestLimit") + .HasColumnType("int"); + + b.Property("MovieRequestLimitType") + .HasColumnType("int"); + + b.Property("MusicRequestLimit") + .HasColumnType("int"); + + b.Property("MusicRequestLimitType") + .HasColumnType("int"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("ProviderUserId") + .HasColumnType("longtext"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserAccessToken") + .HasColumnType("longtext"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistUserError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("MediaServerToken") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PlexWatchlistUserError"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("AlbumId") + .HasColumnType("longtext"); + + b.Property("ContentId") + .HasColumnType("int"); + + b.Property("ContentType") + .HasColumnType("int"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Completed") + .HasColumnType("datetime(6)"); + + b.Property("Dts") + .HasColumnType("datetime(6)"); + + b.Property("Error") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("ArtistName") + .HasColumnType("longtext"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Cover") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("Disk") + .HasColumnType("longtext"); + + b.Property("ForeignAlbumId") + .HasColumnType("longtext"); + + b.Property("ForeignArtistId") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("Rating") + .HasColumnType("decimal(65,30)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("ParentRequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("SeriesType") + .HasColumnType("int"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("IssuesId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IssueCategoryId") + .HasColumnType("int"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("ProviderId") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("ResovledDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("UserReportedId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Approved4K") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Available4K") + .HasColumnType("tinyint(1)"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("Denied4K") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("DeniedReason4K") + .HasColumnType("longtext"); + + b.Property("DigitalReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("Has4KRequest") + .HasColumnType("tinyint(1)"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("LangCode") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsApproved4K") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable4K") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied4K") + .HasColumnType("datetime(6)"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedDate4k") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("RootPathOverride") + .HasColumnType("int"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("TheMovieDbId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("EpisodeCount") + .HasColumnType("int"); + + b.Property("RequestDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("ExternalProviderId") + .HasColumnType("int"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("LanguageProfile") + .HasColumnType("int"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RootFolder") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("TotalSeasons") + .HasColumnType("int"); + + b.Property("TvDbId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RadarrQualityProfile") + .HasColumnType("int"); + + b.Property("RadarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrQualityProfile") + .HasColumnType("int"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("int"); + + b.Property("SonarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("VoteType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AirDate") + .HasColumnType("datetime(6)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("Requested") + .HasColumnType("tinyint(1)"); + + b.Property("SeasonId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("Url") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ChildRequestId") + .HasColumnType("int"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.cs b/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.cs new file mode 100644 index 000000000..7f3738eca --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20220823190345_PlexWatchlistUserError.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ombi.Store.Migrations.OmbiMySql +{ + public partial class PlexWatchlistUserError : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PlexWatchlistUserError", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + MediaServerToken = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_PlexWatchlistUserError", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PlexWatchlistUserError"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs index e66e29521..ad2a3b852 100644 --- a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs @@ -350,6 +350,23 @@ namespace Ombi.Store.Migrations.OmbiMySql b.ToTable("AspNetUsers", (string)null); }); + modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistUserError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("MediaServerToken") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PlexWatchlistUserError"); + }); + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => { b.Property("Id") diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.Designer.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.Designer.cs new file mode 100644 index 000000000..57d88fb52 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.Designer.cs @@ -0,0 +1,1300 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +#nullable disable + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + [DbContext(typeof(OmbiSqliteContext))] + [Migration("20220823192128_PlexWatchlistUserError")] + partial class PlexWatchlistUserError + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.0"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuditArea") + .HasColumnType("INTEGER"); + + b.Property("AuditType") + .HasColumnType("INTEGER"); + + b.Property("DateTime") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("User") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("PlayerId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("Alias") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("EpisodeRequestLimitType") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("LastLoggedIn") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("MediaServerToken") + .HasColumnType("TEXT"); + + b.Property("MovieRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("MovieRequestLimitType") + .HasColumnType("INTEGER"); + + b.Property("MusicRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("MusicRequestLimitType") + .HasColumnType("INTEGER"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserAccessToken") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistUserError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaServerToken") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PlexWatchlistUserError"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("AlbumId") + .HasColumnType("TEXT"); + + b.Property("ContentId") + .HasColumnType("INTEGER"); + + b.Property("ContentType") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("Dts") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Cover") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("Disk") + .HasColumnType("TEXT"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Rating") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("ParentRequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("SeriesType") + .HasColumnType("INTEGER"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("IssuesId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDate") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IssueCategoryId") + .HasColumnType("INTEGER"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("ResovledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("UserReportedId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Approved4K") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Available4K") + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("Denied4K") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("DeniedReason4K") + .HasColumnType("TEXT"); + + b.Property("DigitalReleaseDate") + .HasColumnType("TEXT"); + + b.Property("Has4KRequest") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("LangCode") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved4K") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable4K") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied4K") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedDate4k") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("RootPathOverride") + .HasColumnType("INTEGER"); + + b.Property("Source") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeCount") + .HasColumnType("INTEGER"); + + b.Property("RequestDate") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("ExternalProviderId") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("LanguageProfile") + .HasColumnType("INTEGER"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RootFolder") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TotalSeasons") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RadarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("RadarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VoteType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AirDate") + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Requested") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChildRequestId") + .HasColumnType("INTEGER"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.cs new file mode 100644 index 000000000..b1d2733cd --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20220823192128_PlexWatchlistUserError.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + public partial class PlexWatchlistUserError : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PlexWatchlistUserError", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: true), + MediaServerToken = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PlexWatchlistUserError", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PlexWatchlistUserError"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs index f9ce86bae..2c4965f3e 100644 --- a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs @@ -348,6 +348,23 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.ToTable("AspNetUsers", (string)null); }); + modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistUserError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaServerToken") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PlexWatchlistUserError"); + }); + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => { b.Property("Id") diff --git a/src/Ombi.Core.Tests/DbHelper.cs b/src/Ombi.Test.Common/DbHelper.cs similarity index 100% rename from src/Ombi.Core.Tests/DbHelper.cs rename to src/Ombi.Test.Common/DbHelper.cs diff --git a/src/Ombi/ClientApp/src/app/interfaces/IPlex.ts b/src/Ombi/ClientApp/src/app/interfaces/IPlex.ts index 6afc13e21..0b5e6878b 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IPlex.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IPlex.ts @@ -107,3 +107,16 @@ export interface IPlexServerResponse { port: string; scheme: string; } + +export interface IPlexWatchlistUsers { + userId: string; + syncStatus: WatchlistSyncStatus; + userName: string; +} + +export enum WatchlistSyncStatus +{ + Successful, + Failed, + NotEnabled +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/services/applications/plex.service.ts b/src/Ombi/ClientApp/src/app/services/applications/plex.service.ts index 39bfea53e..83dd8fc7c 100644 --- a/src/Ombi/ClientApp/src/app/services/applications/plex.service.ts +++ b/src/Ombi/ClientApp/src/app/services/applications/plex.service.ts @@ -1,4 +1,4 @@ -import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; +import { APP_BASE_HREF } from "@angular/common"; import { HttpClient } from "@angular/common/http"; import { Injectable, Inject } from "@angular/core"; @@ -6,7 +6,7 @@ import { Observable } from "rxjs"; import { ServiceHelpers } from "../service.helpers"; -import { IPlexAuthentication, IPlexLibResponse, IPlexLibSimpleResponse, IPlexOAuthViewModel, IPlexServer, IPlexServerAddViewModel, IPlexServerViewModel, IPlexUserAddResponse, IPlexUserViewModel, IUsersModel } from "../../interfaces"; +import { IPlexAuthentication, IPlexLibResponse, IPlexLibSimpleResponse, IPlexOAuthViewModel, IPlexServer, IPlexServerAddViewModel, IPlexServerViewModel, IPlexUserAddResponse, IPlexUserViewModel, IPlexWatchlistUsers, IUsersModel } from "../../interfaces"; @Injectable() export class PlexService extends ServiceHelpers { @@ -45,4 +45,8 @@ export class PlexService extends ServiceHelpers { public oAuth(wizard: IPlexOAuthViewModel): Observable { return this.http.post(`${this.url}oauth`, JSON.stringify(wizard), {headers: this.headers}); } + + public getWatchlistUsers(): Observable { + return this.http.get(`${this.url}WatchlistUsers`, {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html new file mode 100644 index 000000000..dd1ae2f87 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html @@ -0,0 +1,38 @@ +
+
+ Watchlist User Errors +
+

+ If there is an authentication error, this is because of an authentication issue with Plex (Token has expired). + If this happens the user needs to re-login to Ombi. +

+

+ Successfully syncing the watchlist +
+ Authentication error syncing the watchlist +
+ Not enabled for user (They need to log into Ombi via Plex) +

+ + + + + + + + + + + + +
Username {{element.userName}} Watchlist Sync Result + + + +
+
+ + + +
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss new file mode 100644 index 000000000..75248ab8c --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss @@ -0,0 +1,13 @@ +@import "~styles/shared.scss"; +.small-middle-container { + margin: auto; + width: 95%; + margin-top: 10px; +} +.fieldset { + width: 100%; +} + +.key { + width: 40px; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.stories.ts b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.stories.ts new file mode 100644 index 000000000..ee0472f58 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.stories.ts @@ -0,0 +1,68 @@ +// also exported from '@storybook/angular' if you can deal with breaking changes in 6.1 +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { Story, Meta, moduleMetadata } from '@storybook/angular'; +import { Observable, of } from 'rxjs'; +import { IPlexWatchlistUsers, WatchlistSyncStatus } from '../../../../interfaces'; +import { PlexService } from '../../../../services'; +import { SharedModule } from '../../../../shared/shared.module'; +import { PlexWatchlistComponent } from './plex-watchlist.component'; + + +const mockUsers: IPlexWatchlistUsers[] = +[ + { + userName: "Success User", + userId: "a", + syncStatus: WatchlistSyncStatus.Successful + }, + { + userName: "Failed User", + userId: "2", + syncStatus: WatchlistSyncStatus.Failed + }, + { + userName: "Not Enabled", + userId: "2", + syncStatus: WatchlistSyncStatus.NotEnabled + }, +]; + +function plexServiveMock(): Partial { + return { + getWatchlistUsers: () : Observable => of(mockUsers), + }; +} + +// More on default export: https://storybook.js.org/docs/angular/writing-stories/introduction#default-export +export default { + title: 'Plex Watchlist Component', + component: PlexWatchlistComponent, + decorators: [ + moduleMetadata({ + providers: [ + { + provide: APP_BASE_HREF, + useValue: "" + }, + { + provide: PlexService, + useValue: plexServiveMock() + } + ], + imports: [ + CommonModule, + SharedModule + ] + }) + ] +} as Meta; + +// More on component templates: https://storybook.js.org/docs/angular/writing-stories/introduction#using-args +const Template: Story = (args: PlexWatchlistComponent) => ({ + props: args, +}); + +export const Default = Template.bind({}); +Default.args = { +}; + diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.ts b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.ts new file mode 100644 index 000000000..cf558943b --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from "@angular/core"; +import { MatTableDataSource } from "@angular/material/table"; +import { take } from "rxjs"; +import { IPlexWatchlistUsers, WatchlistSyncStatus } from "../../../../interfaces"; +import { PlexService } from "../../../../services"; + +@Component({ + templateUrl: "./plex-watchlist.component.html", + styleUrls: ["./plex-watchlist.component.scss"] +}) +export class PlexWatchlistComponent implements OnInit{ + + public dataSource: MatTableDataSource = new MatTableDataSource(); + public displayedColumns: string[] = ['userName','syncStatus']; + + public WatchlistSyncStatus = WatchlistSyncStatus; + + constructor(private plexService: PlexService) { } + + public ngOnInit() { + this.plexService.getWatchlistUsers().pipe(take(1)).subscribe((x: IPlexWatchlistUsers[]) => { + this.dataSource = new MatTableDataSource(x); + }); + } +} diff --git a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html index 858a94358..63a6140a9 100644 --- a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html +++ b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html @@ -3,8 +3,7 @@
Plex Configuration
-
- +
Enable @@ -20,12 +19,18 @@
+
+
+ +
+ +
-
+
diff --git a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.scss b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.scss index 18f044f29..4f0c2c3d1 100644 --- a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.scss +++ b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.scss @@ -12,6 +12,9 @@ display: inline-table; } -.row { - display: block; -} + +.align-right { + float: right; + width:100%; + text-align:right; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts index 2e9792996..086bc3d15 100644 --- a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts @@ -6,6 +6,8 @@ import { IPlexLibrariesSettings, IPlexServer, IPlexServerResponse, IPlexServerVi import { JobService, NotificationService, PlexService, SettingsService, TesterService } from "../../services"; import { MatTabChangeEvent, MatTabGroup } from "@angular/material/tabs"; import {UntypedFormControl} from '@angular/forms'; +import { MatDialog } from "@angular/material/dialog"; +import { PlexWatchlistComponent } from "./components/watchlist/plex-watchlist.component"; @Component({ templateUrl: "./plex.component.html", @@ -29,7 +31,8 @@ export class PlexComponent implements OnInit, OnDestroy { private notificationService: NotificationService, private plexService: PlexService, private testerService: TesterService, - private jobService: JobService) { } + private jobService: JobService, + private dialog: MatDialog) { } public ngOnInit() { this.settingsService.getPlex().subscribe(x => { @@ -180,6 +183,10 @@ export class PlexComponent implements OnInit, OnDestroy { }); } + public openWatchlistUserLog(): void { + this.dialog.open(PlexWatchlistComponent, { width: "700px", panelClass: 'modal-panel' }); + } + public ngOnDestroy() { this.subscriptions.next(); this.subscriptions.complete(); diff --git a/src/Ombi/ClientApp/src/app/settings/settings.module.ts b/src/Ombi/ClientApp/src/app/settings/settings.module.ts index 144322194..f153be982 100644 --- a/src/Ombi/ClientApp/src/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/src/app/settings/settings.module.ts @@ -83,6 +83,7 @@ import { VoteComponent } from "./vote/vote.component"; import { WebhookComponent } from "./notifications/webhook.component"; import { WhatsAppComponent } from "./notifications/twilio/whatsapp.component"; import { WikiComponent } from "./wiki.component"; +import { PlexWatchlistComponent } from "./plex/components/watchlist/plex-watchlist.component"; const routes: Routes = [ { path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] }, @@ -189,6 +190,7 @@ const routes: Routes = [ FeaturesComponent, CloudMobileComponent, UpdateDialogComponent, + PlexWatchlistComponent, ], exports: [ RouterModule, diff --git a/src/Ombi/Controllers/V1/External/PlexController.cs b/src/Ombi/Controllers/V1/External/PlexController.cs index c8b503dc0..7cc0cc824 100644 --- a/src/Ombi/Controllers/V1/External/PlexController.cs +++ b/src/Ombi/Controllers/V1/External/PlexController.cs @@ -9,6 +9,8 @@ using Ombi.Api.Plex; using Ombi.Api.Plex.Models; using Ombi.Attributes; using Ombi.Core.Authentication; +using Ombi.Core.Models; +using Ombi.Core.Services; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; @@ -23,18 +25,20 @@ namespace Ombi.Controllers.V1.External public class PlexController : Controller { public PlexController(IPlexApi plexApi, ISettingsService plexSettings, - ILogger logger, IPlexOAuthManager manager) + ILogger logger, IPlexOAuthManager manager, IPlexService plexService) { PlexApi = plexApi; PlexSettings = plexSettings; _log = logger; _plexOAuthManager = manager; + _plexService = plexService; } private IPlexApi PlexApi { get; } private ISettingsService PlexSettings { get; } private readonly ILogger _log; private readonly IPlexOAuthManager _plexOAuthManager; + private readonly IPlexService _plexService; /// /// Signs into the Plex API. @@ -300,5 +304,9 @@ namespace Ombi.Controllers.V1.External return new JsonResult(new {url = url.ToString()}); } + + [Admin] + [HttpGet("WatchlistUsers")] + public async Task> GetPlexWatchlistUsers() => await _plexService.GetWatchlistUsers(HttpContext.RequestAborted); } } diff --git a/src/Ombi/databasea.json b/src/Ombi/databasea.json new file mode 100644 index 000000000..c849cd56a --- /dev/null +++ b/src/Ombi/databasea.json @@ -0,0 +1,14 @@ +{ + "OmbiDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Port=3306;Database=Ombi;User=ombi;" + }, + "SettingsDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Port=3306;Database=Ombi;User=ombi;" + }, + "ExternalDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Port=3306;Database=Ombi;User=ombi;" + } +}