diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 48c22b40e..92ecf8282 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -174,6 +174,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } } diff --git a/src/Ombi.Helpers/NotificationType.cs b/src/Ombi.Helpers/NotificationType.cs index d6466096c..8ea542063 100644 --- a/src/Ombi.Helpers/NotificationType.cs +++ b/src/Ombi.Helpers/NotificationType.cs @@ -13,5 +13,6 @@ WelcomeEmail = 8, IssueResolved = 9, IssueComment = 10, + Newsletter = 11, } } diff --git a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs index 4814945e7..b29122be0 100644 --- a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs +++ b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs @@ -4,19 +4,26 @@ using System.Text; namespace Ombi.Notifications.Templates { - public class EmailBasicTemplate : IEmailBasicTemplate + public class EmailBasicTemplate : TemplateBase, IEmailBasicTemplate { - public string TemplateLocation + public override string TemplateLocation { get { + if (string.IsNullOrEmpty(_templateLocation)) + { #if DEBUG - return Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.0", "Templates", "BasicTemplate.html"); + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.0", "Templates", + "BasicTemplate.html"); #else - return Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); #endif + } + return _templateLocation; } } + + private string _templateLocation; private const string SubjectKey = "{@SUBJECT}"; private const string BodyKey = "{@BODY}"; @@ -31,7 +38,7 @@ namespace Ombi.Notifications.Templates sb.Replace(BodyKey, body); sb.Replace(DateKey, DateTime.Now.ToString("f")); sb.Replace(Poster, string.IsNullOrEmpty(imgsrc) ? string.Empty : $"\"Poster\""); - sb.Replace(Logo, string.IsNullOrEmpty(logo) ? "http://i.imgur.com/qQsN78U.png" : logo); + sb.Replace(Logo, string.IsNullOrEmpty(logo) ? OmbiLogo : logo); return sb.ToString(); } diff --git a/src/Ombi.Notifications.Templates/INewsletterTemplate.cs b/src/Ombi.Notifications.Templates/INewsletterTemplate.cs new file mode 100644 index 000000000..e3302710d --- /dev/null +++ b/src/Ombi.Notifications.Templates/INewsletterTemplate.cs @@ -0,0 +1,7 @@ +namespace Ombi.Notifications.Templates +{ + public interface INewsletterTemplate + { + string LoadTemplate(string subject, string intro, string tableHtml, string logo); + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications.Templates/NewsletterTemplate.cs b/src/Ombi.Notifications.Templates/NewsletterTemplate.cs new file mode 100644 index 000000000..389ff5cd6 --- /dev/null +++ b/src/Ombi.Notifications.Templates/NewsletterTemplate.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Text; + +namespace Ombi.Notifications.Templates +{ + public class NewsletterTemplate : TemplateBase, INewsletterTemplate + { + public override string TemplateLocation + { + get + { + if (string.IsNullOrEmpty(_templateLocation)) + { +#if DEBUG + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp2.0", "Templates", "NewsletterTemplate.html"); +#else + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html"); +#endif + } + return _templateLocation; + } + } + + private string _templateLocation; + + private const string SubjectKey = "{@SUBJECT}"; + private const string DateKey = "{@DATENOW}"; + private const string Logo = "{@LOGO}"; + private const string TableLocation = "{@RECENTLYADDED}"; + private const string IntroText = "{@INTRO}"; + + + public string LoadTemplate(string subject, string intro, string tableHtml, string logo) + { + var sb = new StringBuilder(File.ReadAllText(TemplateLocation)); + sb.Replace(SubjectKey, subject); + sb.Replace(TableLocation, tableHtml); + sb.Replace(IntroText, intro); + sb.Replace(DateKey, DateTime.Now.ToString("f")); + sb.Replace(Logo, string.IsNullOrEmpty(logo) ? OmbiLogo : logo); + + return sb.ToString(); + } + } +} diff --git a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj index 085c44dc1..cf310acc9 100644 --- a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj +++ b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj @@ -9,6 +9,9 @@ + + Always + Always diff --git a/src/Ombi.Notifications.Templates/TemplateBase.cs b/src/Ombi.Notifications.Templates/TemplateBase.cs new file mode 100644 index 000000000..9b9207c9a --- /dev/null +++ b/src/Ombi.Notifications.Templates/TemplateBase.cs @@ -0,0 +1,8 @@ +namespace Ombi.Notifications.Templates +{ + public abstract class TemplateBase + { + public abstract string TemplateLocation { get; } + public virtual string OmbiLogo => "http://i.imgur.com/qQsN78U.png"; + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html new file mode 100644 index 000000000..21a10aebb --- /dev/null +++ b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html @@ -0,0 +1,187 @@ + + + + + + Ombi + + + + + + + + + +
  +
+ + + + + + + + + + + +
+ + + + + + + +
+ +
+
+
+

Here is a list of Movies and TV Shows that have recently been added!

+ +
+ + {@RECENTLYADDED} + +
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/src/Ombi.Notifications/GenericEmailProvider.cs b/src/Ombi.Notifications/GenericEmailProvider.cs index e28b48c27..462f8918e 100644 --- a/src/Ombi.Notifications/GenericEmailProvider.cs +++ b/src/Ombi.Notifications/GenericEmailProvider.cs @@ -107,9 +107,13 @@ namespace Ombi.Notifications var body = new BodyBuilder { HtmlBody = model.Message, - TextBody = model.Other["PlainTextBody"] }; + if (model.Other.ContainsKey("PlainTextBody")) + { + body.TextBody = model.Other["PlainTextBody"]; + } + var message = new MimeMessage { Body = body.ToMessageBody(), diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index e73302387..3e8589fea 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -17,7 +17,8 @@ namespace Ombi.Schedule public JobSetup(IPlexContentSync plexContentSync, IRadarrSync radarrSync, IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter, IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, - ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh) + ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, + INewsletterJob newsletter) { _plexContentSync = plexContentSync; _radarrSync = radarrSync; @@ -30,6 +31,7 @@ namespace Ombi.Schedule _jobSettings = jobsettings; _srSync = srSync; _refreshMetadata = refresh; + _newsletter = newsletter; } private readonly IPlexContentSync _plexContentSync; @@ -43,6 +45,7 @@ namespace Ombi.Schedule private readonly ISickRageSync _srSync; private readonly ISettingsService _jobSettings; private readonly IRefreshMetadata _refreshMetadata; + private readonly INewsletterJob _newsletter; public void Setup() { @@ -60,6 +63,7 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); + BackgroundJob.Enqueue(() => _newsletter.Start()); } } } diff --git a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs new file mode 100644 index 000000000..61f4bd7c8 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs @@ -0,0 +1,46 @@ +using System.Text; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public abstract class HtmlTemplateGenerator + { + protected virtual void AddParagraph(StringBuilder stringBuilder, string text, int fontSize = 14, string fontWeight = "normal") + { + stringBuilder.AppendFormat("

{0}

", text, fontSize, fontWeight); + } + + protected virtual void AddImageInsideTable(StringBuilder sb, string url, int size = 400) + { + sb.Append(""); + sb.Append(""); + sb.Append($""); + sb.Append(""); + sb.Append(""); + } + + protected virtual void Href(StringBuilder sb, string url) + { + sb.AppendFormat("", url); + } + + protected virtual void TableData(StringBuilder sb) + { + sb.Append( + ""); + } + + protected virtual void EndTag(StringBuilder sb, string tag) + { + sb.AppendFormat("", tag); + } + + protected virtual void Header(StringBuilder sb, int size, string text, string fontWeight = "normal") + { + sb.AppendFormat( + "{1}", + size, text, fontWeight); + } + + + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/INewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/INewsletterJob.cs new file mode 100644 index 000000000..e5e587bec --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/INewsletterJob.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public interface INewsletterJob : IBaseJob + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs new file mode 100644 index 000000000..544bbc6dd --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -0,0 +1,439 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Api.TvMaze; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Notifications; +using Ombi.Notifications.Models; +using Ombi.Notifications.Templates; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; +using Ombi.Store.Repository; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class NewsletterJob : HtmlTemplateGenerator, INewsletterJob + { + public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository addedLog, + IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService custom, + ISettingsService emailSettings, INotificationTemplatesRepository templateRepo) + { + _plex = plex; + _emby = emby; + _recentlyAddedLog = addedLog; + _movieApi = movieApi; + _tvApi = tvApi; + _email = email; + _customizationSettings = custom; + _templateRepo = templateRepo; + _emailSettings = emailSettings; + } + + private readonly IPlexContentRepository _plex; + private readonly IEmbyContentRepository _emby; + private readonly IRepository _recentlyAddedLog; + private readonly IMovieDbApi _movieApi; + private readonly ITvMazeApi _tvApi; + private readonly IEmailProvider _email; + private readonly ISettingsService _customizationSettings; + private readonly INotificationTemplatesRepository _templateRepo; + private readonly ISettingsService _emailSettings; + + public async Task Start() + { + var template = await _templateRepo.GetTemplate(NotificationAgent.Email, NotificationType.Newsletter); + if (!template.Enabled) + { + return; + } + + var emailSettings = await _emailSettings.GetSettingsAsync(); + if (!ValidateConfiguration(emailSettings)) + { + return; + } + + // Get the Content + var plexContent = _plex.GetAll().Include(x => x.Episodes); + var embyContent = _emby.GetAll().Include(x => x.Episodes); + + var addedLog = _recentlyAddedLog.GetAll(); + var addedPlexLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex).Select(x => x.ContentId); + var addedEmbyLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby).Select(x => x.ContentId); + + // Filter out the ones that we haven't sent yet + var plexContentToSend = plexContent.Where(x => !addedPlexLogIds.Contains(x.Id)); + var embyContentToSend = embyContent.Where(x => !addedEmbyLogIds.Contains(x.Id)); + + var body = await BuildHtml(plexContentToSend, embyContentToSend); + + var email = new NewsletterTemplate(); + + var customization = await _customizationSettings.GetSettingsAsync(); + + var html = email.LoadTemplate(template.Subject, template.Message, body, customization.Logo); + + await _email.Send(new NotificationMessage {Message = html, Subject = template.Subject, To = "tidusjar@gmail.com"}, emailSettings); + } + + private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend) + { + var sb = new StringBuilder(); + + sb.Append("

New Movies:



"); + await ProcessPlexMovies(plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie), sb); + await ProcessEmbyMovies(embyContentToSend.Where(x => x.Type == EmbyMediaType.Movie), sb); + + sb.Append("

New Episodes:



"); + await ProcessPlexTv(plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Show), sb); + await ProcessEmbyMovies(embyContentToSend.Where(x => x.Type == EmbyMediaType.Series), sb); + + return sb.ToString(); + } + + private async Task ProcessPlexMovies(IQueryable plexContentToSend, StringBuilder sb) + { + sb.Append( + ""); + var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt); + foreach (var content in ordered) + { + if (content.TheMovieDbId.IsNullOrEmpty()) + { + // Maybe we should try the ImdbId? + if (content.ImdbId.HasValue()) + { + var findResult = await _movieApi.Find(content.ImdbId, ExternalSource.imdb_id); + + var movieId = findResult.movie_results?[0]?.id ?? 0; + content.TheMovieDbId = movieId.ToString(); + } + } + + int.TryParse(content.TheMovieDbId, out var movieDbId); + var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId); + if (info == null) + { + continue; + } + try + { + AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/original{info.BackdropPath}"); + + sb.Append(""); + TableData(sb); + + Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); + Header(sb, 3, $"{info.Title} {info.ReleaseDate ?? string.Empty}"); + EndTag(sb, "a"); + + if (info.Genres.Any()) + { + AddParagraph(sb, + $"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + } + + AddParagraph(sb, info.Overview); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + finally + { + EndLoopHtml(sb); + } + } + } + private async Task ProcessEmbyMovies(IQueryable embyContent, StringBuilder sb) + { + sb.Append( + "
"); + var ordered = embyContent.OrderByDescending(x => x.AddedAt); + foreach (var content in ordered) + { + int.TryParse(content.ProviderId, out var movieDbId); + var info = await _movieApi.GetMovieInformationWithExtraInfo(movieDbId); + if (info == null) + { + continue; + } + try + { + AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/original{info.BackdropPath}"); + + sb.Append(""); + TableData(sb); + + Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); + Header(sb, 3, $"{info.Title} {info.ReleaseDate ?? string.Empty}"); + EndTag(sb, "a"); + + if (info.Genres.Any()) + { + AddParagraph(sb, + $"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + } + + AddParagraph(sb, info.Overview); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + finally + { + EndLoopHtml(sb); + } + } + } + + private async Task ProcessPlexTv(IQueryable plexContent, StringBuilder sb) + { + var orderedTv = plexContent.OrderByDescending(x => x.AddedAt); + sb.Append( + "
"); + foreach (var t in orderedTv) + { + try + { + if (!t.HasTvDb) + { + // We may need to use themoviedb for the imdbid or their own id to get info + if (t.HasTheMovieDb) + { + int.TryParse(t.TheMovieDbId, out var movieId); + var externals = await _movieApi.GetTvExternals(movieId); + if (externals == null || externals.tvdb_id <= 0) + { + continue; + } + t.TvDbId = externals.tvdb_id.ToString(); + } + // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. + //else if(t.HasImdb) + //{ + // // Check the imdbid + // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); + // if (externals?.tv_results == null || externals.tv_results.Length <= 0) + // { + // continue; + // } + // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); + //} + + } + + int.TryParse(t.TvDbId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + var banner = info.image?.original; + if (!string.IsNullOrEmpty(banner)) + { + banner = banner.Replace("http", "https"); // Always use the Https banners + } + AddImageInsideTable(sb, banner); + + sb.Append(""); + sb.Append( + "
"); + + var title = $"{t.Title} {t.ReleaseYear}"; + + Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); + Header(sb, 3, title); + EndTag(sb, "a"); + + // Group by the season number + var results = t.Episodes?.GroupBy(p => p.SeasonNumber, + (key, g) => new + { + SeasonNumber = key, + Episodes = g.ToList() + } + ); + + // Group the episodes + foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) + { + var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); + var epSb = new StringBuilder(); + for (var i = 0; i < orderedEpisodes.Count; i++) + { + var ep = orderedEpisodes[i]; + if (i < orderedEpisodes.Count - 1) + { + epSb.Append($"{ep.EpisodeNumber},"); + } + else + { + epSb.Append($"{ep.EpisodeNumber}"); + } + + } + AddParagraph(sb, $"Season: {epInformation.SeasonNumber}, Episode: {epSb}"); + } + + if (info.genres.Any()) + { + AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + } + + AddParagraph(sb, info.summary); + } + catch (Exception e) + { + //Log.Error(e); + } + finally + { + EndLoopHtml(sb); + } + } + sb.Append("


"); + + } + + private async Task ProcessEmbyTv(IQueryable plexContent, StringBuilder sb) + { + var orderedTv = plexContent.OrderByDescending(x => x.AddedAt); + sb.Append( + ""); + foreach (var t in orderedTv) + { + try + { + int.TryParse(t.ProviderId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + var banner = info.image?.original; + if (!string.IsNullOrEmpty(banner)) + { + banner = banner.Replace("http", "https"); // Always use the Https banners + } + AddImageInsideTable(sb, banner); + + sb.Append(""); + sb.Append( + "
"); + + Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); + Header(sb, 3, t.Title); + EndTag(sb, "a"); + + // Group by the season number + var results = t.Episodes?.GroupBy(p => p.SeasonNumber, + (key, g) => new + { + SeasonNumber = key, + Episodes = g.ToList() + } + ); + + // Group the episodes + foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) + { + var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); + var epSb = new StringBuilder(); + for (var i = 0; i < orderedEpisodes.Count; i++) + { + var ep = orderedEpisodes[i]; + if (i < orderedEpisodes.Count - 1) + { + epSb.Append($"{ep.EpisodeNumber},"); + } + else + { + epSb.Append($"{ep.EpisodeNumber}"); + } + + } + AddParagraph(sb, $"Season: {epInformation.SeasonNumber}, Episode: {epSb}"); + } + + if (info.genres.Any()) + { + AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + } + + AddParagraph(sb, info.summary); + } + catch (Exception e) + { + //Log.Error(e); + } + finally + { + EndLoopHtml(sb); + } + } + sb.Append("


"); + } + + private void EndLoopHtml(StringBuilder sb) + { + //NOTE: BR have to be in TD's as per html spec or it will be put outside of the table... + //Source: http://stackoverflow.com/questions/6588638/phantom-br-tag-rendered-by-browsers-prior-to-table-tag + sb.Append("
"); + sb.Append("
"); + sb.Append("
"); + sb.Append(""); + sb.Append(""); + } + + protected bool ValidateConfiguration(EmailNotificationSettings settings) + { + if (!settings.Enabled) + { + return false; + } + if (settings.Authentication) + { + if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password)) + { + return false; + } + } + if (string.IsNullOrEmpty(settings.Host) || string.IsNullOrEmpty(settings.AdminEmail) || string.IsNullOrEmpty(settings.Port.ToString())) + { + return false; + } + + return true; + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _plex?.Dispose(); + _emby?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index 5f5dd4635..225efb7d3 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -218,12 +218,6 @@ namespace Ombi.Schedule.Jobs.Ombi } return string.Empty; } - - - private async Task StartEmby() - { - - } private bool _disposed; @@ -236,6 +230,7 @@ namespace Ombi.Schedule.Jobs.Ombi { _plexRepo?.Dispose(); _embyRepo?.Dispose(); + _plexSettings?.Dispose(); } _disposed = true; } diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 64f23669e..55d7db563 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -41,5 +41,6 @@ namespace Ombi.Store.Context DbSet SickRageCache { get; set; } DbSet SickRageEpisodeCache { get; set; } DbSet RequestLogs { get; set; } + DbSet RecentlyAddedLogs { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 0ccf8ebe5..65c212d2b 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -15,7 +15,7 @@ namespace Ombi.Store.Context public OmbiContext() { if (_created) return; - + _created = true; Database.Migrate(); } @@ -37,6 +37,7 @@ namespace Ombi.Store.Context public DbSet IssueCategories { get; set; } public DbSet IssueComments { get; set; } public DbSet RequestLogs { get; set; } + public DbSet RecentlyAddedLogs { get; set; } public DbSet Audit { get; set; } @@ -55,7 +56,7 @@ namespace Ombi.Store.Context { i.StoragePath = string.Empty; } - optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath,"Ombi.db")}"); + optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath, "Ombi.db")}"); } protected override void OnModelCreating(ModelBuilder builder) @@ -70,7 +71,7 @@ namespace Ombi.Store.Context .WithMany(b => b.Episodes) .HasPrincipalKey(x => x.EmbyId) .HasForeignKey(p => p.ParentId); - + base.OnModelCreating(builder); } @@ -218,6 +219,16 @@ namespace Ombi.Store.Context break; case NotificationType.AdminNote: continue; + case NotificationType.Newsletter: + notificationToAdd = new NotificationTemplates + { + NotificationType = notificationType, + Message = "Here is a list of Movies and TV Shows that have recently been added!", + Subject = "{ApplicationName}: Recently Added Content!", + Agent = agent, + Enabled = true, + }; + break; default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Ombi.Store/Entities/PlexServerContent.cs b/src/Ombi.Store/Entities/PlexServerContent.cs index f62dc7ff9..14028cb57 100644 --- a/src/Ombi.Store/Entities/PlexServerContent.cs +++ b/src/Ombi.Store/Entities/PlexServerContent.cs @@ -56,6 +56,15 @@ namespace Ombi.Store.Entities public int Key { get; set; } public DateTime AddedAt { get; set; } public string Quality { get; set; } + + [NotMapped] + public bool HasImdb => !string.IsNullOrEmpty(ImdbId); + + [NotMapped] + public bool HasTvDb => !string.IsNullOrEmpty(TvDbId); + + [NotMapped] + public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId); } [Table("PlexSeasonsContent")] diff --git a/src/Ombi.Store/Entities/RecentlyAddedLog.cs b/src/Ombi.Store/Entities/RecentlyAddedLog.cs new file mode 100644 index 000000000..52b047993 --- /dev/null +++ b/src/Ombi.Store/Entities/RecentlyAddedLog.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("RecentlyAddedLog")] + public class RecentlyAddedLog : Entity + { + public RecentlyAddedType Type { get; set; } + public int ContentId { get; set; } // This is dependant on the type + public DateTime AddedAt { get; set; } + } + + public enum RecentlyAddedType + { + Plex, + Emby + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/20180322085345_RecentlyAddedLog.Designer.cs b/src/Ombi.Store/Migrations/20180322085345_RecentlyAddedLog.Designer.cs new file mode 100644 index 000000000..23f23bc4b --- /dev/null +++ b/src/Ombi.Store/Migrations/20180322085345_RecentlyAddedLog.Designer.cs @@ -0,0 +1,934 @@ +// +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 Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180322085345_RecentlyAddedLog")] + partial class RecentlyAddedLog + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + 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.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + 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.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + 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("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + 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("UserAccessToken"); + + 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.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("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + 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("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + 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(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + 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.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + 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.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + 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("HasFile"); + + 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.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + 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.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + 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/20180322085345_RecentlyAddedLog.cs b/src/Ombi.Store/Migrations/20180322085345_RecentlyAddedLog.cs new file mode 100644 index 000000000..0e815a06f --- /dev/null +++ b/src/Ombi.Store/Migrations/20180322085345_RecentlyAddedLog.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RecentlyAddedLog : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RecentlyAddedLog", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AddedAt = table.Column(nullable: false), + ContentId = table.Column(nullable: false), + Type = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RecentlyAddedLog", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RecentlyAddedLog"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 35c5755da..7571c6f11 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -20,7 +20,7 @@ namespace Ombi.Store.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -430,6 +430,22 @@ namespace Ombi.Store.Migrations b.ToTable("RadarrCache"); }); + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => { b.Property("Id") diff --git a/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts b/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts index d540a6b2d..40025486f 100644 --- a/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts +++ b/src/Ombi/ClientApp/app/recentlyAdded/recentlyAdded.component.ts @@ -79,19 +79,19 @@ export class RecentlyAddedComponent implements OnInit { this.recentlyAddedService.getRecentlyAddedTv().subscribe(x => { this.tv = x; - this.tv.forEach((t) => { - if(t.theMovieDbId) { - this.imageService.getTvPoster(t.imdbId).subscribe(p => { - t.posterPath = p; - }); - } else if(t.imdbId) { - this.imageService.getMoviePoster(t.imdbId).subscribe(p => { - t.posterPath = p; - }); - } else { - t.posterPath = ""; - } - }); + //this.tv.forEach((t) => { + // if(t.theMovieDbId) { + // this.imageService.getTvPoster(t.imdbId).subscribe(p => { + // t.posterPath = p; + // }); + // } else if(t.imdbId) { + // this.imageService.getMoviePoster(t.imdbId).subscribe(p => { + // t.posterPath = p; + // }); + // } else { + // t.posterPath = ""; + // } + //}); }); } diff --git a/src/Ombi/webpack.config.vendor.ts b/src/Ombi/webpack.config.vendor.ts index d4fe38c3a..1fdacb684 100644 --- a/src/Ombi/webpack.config.vendor.ts +++ b/src/Ombi/webpack.config.vendor.ts @@ -80,7 +80,7 @@ module.exports = (env: any) => { }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", Hammer: "hammerjs/hammer" }), // Global identifiers - new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname, './client')), // Workaround for https://github.com/angular/angular/issues/20357 + new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname,"./client")), // Workaround for https://github.com/angular/angular/issues/20357 new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/11580 new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898 extractCSS,