From 95e3c62e3089a2baf24965ce794246345da1d619 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 4 Oct 2017 20:28:18 +0100 Subject: [PATCH] Some errors fixed and some ui improvements #865 Also reworked sonarr and how episodes work --- src/Ombi.Api.Sonarr/Models/NewSeries.cs | 1 - .../Rule/Rules/Search/SonarrCacheRule.cs | 28 +- src/Ombi.Core/Senders/TvSender.cs | 198 +++-- src/Ombi.Schedule/JobSetup.cs | 2 +- src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs | 23 +- src/Ombi.Store/Context/IOmbiContext.cs | 1 + src/Ombi.Store/Context/OmbiContext.cs | 5 + src/Ombi.Store/Entities/SonarrEpisodeCache.cs | 12 + .../20171004155149_SonarrEpisodes.Designer.cs | 763 ++++++++++++++++++ .../20171004155149_SonarrEpisodes.cs | 33 + .../Migrations/OmbiContextModelSnapshot.cs | 16 + src/Ombi/ClientApp/app/app.component.ts | 18 +- .../ClientApp/app/login/login.component.scss | 3 +- .../app/search/moviesearch.component.html | 2 +- .../search/seriesinformation.component.html | 2 +- .../app/search/tvsearch.component.html | 2 +- .../app/search/tvsearch.component.ts | 1 + .../ClientApp/app/services/service.helpers.ts | 4 +- .../emailnotification.component.html | 10 +- .../usermanagement.component.html | 2 +- src/Ombi/ClientApp/styles/Themes/plex.scss | 9 + src/Ombi/ClientApp/styles/base.scss | 5 + .../Controllers/External/RadarrController.cs | 12 +- 23 files changed, 1016 insertions(+), 136 deletions(-) create mode 100644 src/Ombi.Store/Entities/SonarrEpisodeCache.cs create mode 100644 src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.cs diff --git a/src/Ombi.Api.Sonarr/Models/NewSeries.cs b/src/Ombi.Api.Sonarr/Models/NewSeries.cs index 1647d2b32..4d2c17308 100644 --- a/src/Ombi.Api.Sonarr/Models/NewSeries.cs +++ b/src/Ombi.Api.Sonarr/Models/NewSeries.cs @@ -34,7 +34,6 @@ namespace Ombi.Api.Sonarr.Models public string Validate() { - var errors = new List(); var sb = new StringBuilder(); if(this.tvdbId == 0) { diff --git a/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs index d444222cf..562b550fe 100644 --- a/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; @@ -20,12 +21,31 @@ namespace Ombi.Core.Rule.Rules.Search { if (obj.Type == RequestType.TvShow) { + var vm = (SearchTvShowViewModel) obj; // Check if it's in Radarr - var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == obj.Id); + var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id); if (result != null) { - obj.Approved = - true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something? + vm.Approved = true; + + if (vm.SeasonRequests.Any()) + { + var sonarrEpisodes = _ctx.SonarrEpisodeCache; + foreach (var season in vm.SeasonRequests) + { + foreach (var ep in season.Episodes) + { + // Check if we have it + var monitoredInSonarr = sonarrEpisodes.Any(x => + x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == season.SeasonNumber + && x.TvDbId == vm.Id); + if (monitoredInSonarr) + { + ep.Approved = true; + } + } + } + } } } return Success(); diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 574cf6978..267bc3a3f 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -36,6 +36,10 @@ namespace Ombi.Core.Senders public async Task SendToSonarr(ChildRequests model, string qualityId = null) { var s = await Settings.GetSettingsAsync(); + if (!s.Enabled) + { + return null; + } if(string.IsNullOrEmpty(s.ApiKey)) { return null; @@ -86,136 +90,126 @@ namespace Ombi.Core.Senders // Montitor the correct seasons, // If we have that season in the model then it's monitored! var seasonsToAdd = new List(); - for (int i = 1; i < model.ParentRequest.TotalSeasons + 1; i++) + for (var i = 1; i < model.ParentRequest.TotalSeasons + 1; i++) { + var index = i; var season = new Season { seasonNumber = i, - monitored = model.SeasonRequests.Any(x => x.SeasonNumber == i) + monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index) }; seasonsToAdd.Add(season); } newSeries.seasons = seasonsToAdd; var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri); + existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri); + await SendToSonarr(model, existingSeries, s); + } + else + { + await SendToSonarr(model, existingSeries, s); + } - // Ok, now let's sort out the episodes. - var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri); - while (!sonarrEpisodes.Any()) - { - // It could be that the series metadata is not ready yet. So wait - sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri); - await Task.Delay(300); - } + return new NewSeries + { + id = existingSeries.id, + seasons = existingSeries.seasons.ToList(), + cleanTitle = existingSeries.cleanTitle, + title = existingSeries.title, + tvdbId = existingSeries.tvdbId + }; + } + catch (Exception e) + { + Logger.LogError(LoggingEvents.SonarrSender, e, "Exception thrown when attempting to send series over to Sonarr"); + throw; + } + } - var episodesToUpdate = new List(); - foreach (var req in model.SeasonRequests) - { - foreach (var ep in req.Episodes) - { - var sonarrEp = sonarrEpisodes.FirstOrDefault(x => x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber); - if (sonarrEp != null) - { - sonarrEp.monitored = true; - episodesToUpdate.Add(sonarrEp); - } - } - } + private async Task SendToSonarr(ChildRequests model, SonarrSeries result, SonarrSettings s) + { + var episodesToUpdate = new List(); + // Ok, now let's sort out the episodes. - // Now update the episodes that need updating - foreach (var epToUpdate in episodesToUpdate) - { - await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); - } + var sonarrEpisodes = await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri); + var sonarrEpList = sonarrEpisodes.ToList() ?? new List(); + while (!sonarrEpList.Any()) + { + // It could be that the series metadata is not ready yet. So wait + sonarrEpList = (await SonarrApi.GetEpisodes(result.id, s.ApiKey, s.FullUri)).ToList(); + await Task.Delay(500); + } - // TODO possibly update the season as it might be unmonitored due to the clash with the AddOptions - if (!s.AddOnly) + foreach (var req in model.SeasonRequests) + { + foreach (var ep in req.Episodes) + { + var sonarrEp = sonarrEpList.FirstOrDefault(x => + x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber); + if (sonarrEp != null) { - foreach (var season in model.SeasonRequests) - { - var sonarrSeason = sonarrEpisodes.Where(x => x.seasonNumber == season.SeasonNumber); - var sonarrEpCount = sonarrSeason.Count(); - var ourRequestCount = season.Episodes.Count(); - - if (sonarrEpCount == ourRequestCount) - { - // We have the same amount of requests as all of the episodes in the season. - // Do a season search - await SonarrApi.SeasonSearch(result.id, season.SeasonNumber, s.ApiKey, s.FullUri); - } - else - { - // There is a miss-match, let's search the episodes indiviaully - await SonarrApi.EpisodeSearch(episodesToUpdate.Select(x => x.id).ToArray(), s.ApiKey, s.FullUri); - } - } + sonarrEp.monitored = true; + episodesToUpdate.Add(sonarrEp); } + } + } + var seriesChanges = false; + foreach (var season in model.SeasonRequests) + { + var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); + var sonarrEpCount = sonarrSeason.Count(); + var ourRequestCount = season.Episodes.Count; - return result; + if (sonarrEpCount == ourRequestCount) + { + // We have the same amount of requests as all of the episodes in the season. + var existingSeason = + result.seasons.First(x => x.seasonNumber == season.SeasonNumber); + existingSeason.monitored = true; + seriesChanges = true; } else { - - var sonarrEpisodes = await SonarrApi.GetEpisodes(existingSeries.id, s.ApiKey, s.FullUri); - - var episodesToUpdate = new List(); - foreach (var req in model.SeasonRequests) - { - foreach (var ep in req.Episodes) - { - var sonarrEp = sonarrEpisodes.FirstOrDefault(x => x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == ep.Season.SeasonNumber); - if (sonarrEp != null) - { - sonarrEp.monitored = true; - episodesToUpdate.Add(sonarrEp); - } - } - } - // Now update the episodes that need updating - foreach (var epToUpdate in episodesToUpdate) + foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber)) { await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri); } - - // TODO possibly update the season as it might be unmonitored due to the clash with the AddOptions - - if (!s.AddOnly) - { - foreach (var season in model.SeasonRequests) - { - var sonarrSeason = sonarrEpisodes.Where(x => x.seasonNumber == season.SeasonNumber); - var sonarrEpCount = sonarrSeason.Count(); - var ourRequestCount = season.Episodes.Count; - - if (sonarrEpCount == ourRequestCount) - { - // We have the same amount of requests as all of the episodes in the season. - // Do a season search - await SonarrApi.SeasonSearch(existingSeries.id, season.SeasonNumber, s.ApiKey, s.FullUri); - } - else - { - // There is a miss-match, let's search the episodes indiviaully - await SonarrApi.EpisodeSearch(episodesToUpdate.Select(x => x.id).ToArray(), s.ApiKey, s.FullUri); - } - } - } } + } + if (seriesChanges) + { + await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri); + } - return new NewSeries - { - id = existingSeries.id, - seasons = existingSeries.seasons.ToList(), - cleanTitle = existingSeries.cleanTitle, - title = existingSeries.title, - tvdbId = existingSeries.tvdbId - }; + + if (!s.AddOnly) + { + await SearchForRequest(model, sonarrEpList, result, s, episodesToUpdate); } - catch (Exception e) + } + + private async Task SearchForRequest(ChildRequests model, IEnumerable sonarrEpList, SonarrSeries existingSeries, SonarrSettings s, + IReadOnlyCollection episodesToUpdate) + { + foreach (var season in model.SeasonRequests) { - Logger.LogError(LoggingEvents.SonarrSender, e, "Exception thrown when attempting to send series over to Sonarr"); - throw; + var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); + var sonarrEpCount = sonarrSeason.Count(); + var ourRequestCount = season.Episodes.Count; + + if (sonarrEpCount == ourRequestCount) + { + // We have the same amount of requests as all of the episodes in the season. + // Do a season search + await SonarrApi.SeasonSearch(existingSeries.id, season.SeasonNumber, s.ApiKey, s.FullUri); + } + else + { + // There is a miss-match, let's search the episodes indiviaully + await SonarrApi.EpisodeSearch(episodesToUpdate.Select(x => x.id).ToArray(), s.ApiKey, s.FullUri); + } } } diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index c0fb6d5f8..8049afac5 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -35,7 +35,7 @@ namespace Ombi.Schedule { RecurringJob.AddOrUpdate(() => PlexContentCacher.CacheContent(), Cron.Hourly(20)); RecurringJob.AddOrUpdate(() => EmbyContentCacher.Start(), Cron.Hourly(5)); - RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(10)); + RecurringJob.AddOrUpdate(() => SonarrCacher.Start(), Cron.Hourly(10)); RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(15)); RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily(5)); RecurringJob.AddOrUpdate(() => EmbyUserImporter.Start(), Cron.Daily); diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs index d2687f432..e2b0e0247 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Sonarr; +using Ombi.Api.Sonarr.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; @@ -21,6 +23,7 @@ namespace Ombi.Schedule.Jobs.Sonarr _settings = s; _api = api; _log = l; + _ctx = ctx; } private readonly ISettingsService _settings; @@ -42,12 +45,28 @@ namespace Ombi.Schedule.Jobs.Sonarr var series = await _api.GetSeries(settings.ApiKey, settings.FullUri); if (series != null) { - var ids = series.Select(x => x.tvdbId); + var sonarrSeries = series as IList ?? series.ToList(); + var ids = sonarrSeries.Select(x => x.tvdbId); await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrCache"); - var entites = ids.Select(id => new SonarrCache {TvDbId = id}).ToList(); + var entites = ids.Select(id => new SonarrCache { TvDbId = id }).ToList(); await _ctx.SonarrCache.AddRangeAsync(entites); + + var episodesToAdd = new List(); + foreach (var s in sonarrSeries) + { + var episodes = await _api.GetEpisodes(s.id, settings.ApiKey, settings.FullUri); + var monitoredEpisodes = episodes.Where(x => x.monitored || x.hasFile); + episodesToAdd.AddRange(monitoredEpisodes.Select(episode => new SonarrEpisodeCache + { + EpisodeNumber = episode.episodeNumber, + SeasonNumber = episode.seasonNumber, + TvDbId = s.tvdbId + })); + } + + await _ctx.SonarrEpisodeCache.AddRangeAsync(episodesToAdd); await _ctx.SaveChangesAsync(); } } diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 63612ea8c..665b93ab9 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -34,6 +34,7 @@ namespace Ombi.Store.Context DbSet TvIssues { get; set; } DbSet Tokens { get; set; } DbSet SonarrCache { get; set; } + DbSet SonarrEpisodeCache { get; set; } EntityEntry Update(object entity); EntityEntry Update(TEntity entity) where TEntity : class; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 138d6806c..be3ddea5b 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -38,12 +38,17 @@ namespace Ombi.Store.Context public DbSet Audit { get; set; } public DbSet Tokens { get; set; } public DbSet SonarrCache { get; set; } + public DbSet SonarrEpisodeCache { get; set; } public DbSet ApplicationConfigurations { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { var i = StoragePathSingleton.Instance; + if (string.IsNullOrEmpty(i.StoragePath)) + { + i.StoragePath = string.Empty; + } optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath,"Ombi.db")}"); } diff --git a/src/Ombi.Store/Entities/SonarrEpisodeCache.cs b/src/Ombi.Store/Entities/SonarrEpisodeCache.cs new file mode 100644 index 000000000..f32dbb82d --- /dev/null +++ b/src/Ombi.Store/Entities/SonarrEpisodeCache.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("SonarrEpisodeCache")] + public class SonarrEpisodeCache : Entity + { + public int SeasonNumber { get; set; } + public int EpisodeNumber { get; set; } + public int TvDbId { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.Designer.cs b/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.Designer.cs new file mode 100644 index 000000000..b8b71af8d --- /dev/null +++ b/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.Designer.cs @@ -0,0 +1,763 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20171004155149_SonarrEpisodes")] + partial class SonarrEpisodes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ProviderId"); + + b.Property("Title"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("Key"); + + b.Property("ProviderId"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("Title"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("MovieId"); + + b.Property("Subect"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("Subect"); + + b.Property("TvId"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("TvId"); + + b.ToTable("TvIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexContent") + .WithMany("Seasons") + .HasForeignKey("PlexContentId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "Child") + .WithMany() + .HasForeignKey("TvId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.cs b/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.cs new file mode 100644 index 000000000..71668ee6b --- /dev/null +++ b/src/Ombi.Store/Migrations/20171004155149_SonarrEpisodes.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class SonarrEpisodes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SonarrEpisodeCache", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + EpisodeNumber = table.Column(type: "INTEGER", nullable: false), + SeasonNumber = table.Column(type: "INTEGER", nullable: false), + TvDbId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SonarrEpisodeCache", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SonarrEpisodeCache"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index e744a7fd6..03410a385 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -545,6 +545,22 @@ namespace Ombi.Store.Migrations b.ToTable("SonarrCache"); }); + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => { b.Property("Id") diff --git a/src/Ombi/ClientApp/app/app.component.ts b/src/Ombi/ClientApp/app/app.component.ts index c7c7c4412..2e67aeb40 100644 --- a/src/Ombi/ClientApp/app/app.component.ts +++ b/src/Ombi/ClientApp/app/app.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; +import { NavigationStart, Router } from "@angular/router"; import { AuthService } from "./auth/auth.service"; import { ILocalUser } from "./auth/IUserLogin"; import { NotificationService } from "./services"; @@ -30,14 +30,16 @@ export class AppComponent implements OnInit { this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); - this.router.events.subscribe(() => { - this.user = this.authService.claims(); - this.showNav = this.authService.loggedIn(); + this.router.events.subscribe((event: NavigationStart) => { + if (event instanceof NavigationStart) { + this.user = this.authService.claims(); + this.showNav = this.authService.loggedIn(); - if (this.user !== null && this.user.name) { - this.jobService.getCachedUpdate().subscribe(x => { - this.updateAvailable = (x === true); - }); + if (this.user !== null && this.user.name) { + this.jobService.getCachedUpdate().subscribe(x => { + this.updateAvailable = x; + }); + } } }); } diff --git a/src/Ombi/ClientApp/app/login/login.component.scss b/src/Ombi/ClientApp/app/login/login.component.scss index 2c8b5ee1c..0bb04318b 100644 --- a/src/Ombi/ClientApp/app/login/login.component.scss +++ b/src/Ombi/ClientApp/app/login/login.component.scss @@ -100,8 +100,7 @@ img.bg { } .profile-img-card { - width: 96px; - height: 96px; + width: 100%; margin: 0 auto 10px; display: block; -moz-border-radius: 50%; diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index 663b5bb34..ac46926aa 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -2,7 +2,7 @@
-
+