From cfe5797fdce2eb2c4c8c801f2cd0816a057b03a6 Mon Sep 17 00:00:00 2001 From: Florian Dupret <34862846+sephrat@users.noreply.github.com> Date: Sat, 15 Jan 2022 18:10:31 +0100 Subject: [PATCH] Attempt at abstracting repositories (WIP) --- .../Jobs/Emby/EmbyContentSync.cs | 4 +- .../Jobs/Jellyfin/JellyfinContentSync.cs | 4 +- .../Jobs/Ombi/HtmlTemplateGenerator.cs | 17 +- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 198 ++++++++---------- .../Jobs/Plex/PlexContentSync.cs | 6 +- src/Ombi.Store/Entities/Entity.cs | 2 +- src/Ombi.Store/Entities/IEntity.cs | 10 + .../Entities/IMediaServerContent.cs | 4 +- src/Ombi.Store/Entities/MediaServerContent.cs | 4 + .../Repository/EmbyContentRepository.cs | 17 +- .../Repository/IEmbyContentRepository.cs | 2 +- .../Repository/IExternalRepository.cs | 3 +- .../Repository/IJellyfinContentRepository.cs | 2 +- .../IMediaServerContentRepository.cs | 4 +- .../IMediaServerContentRepositoryLight.cs | 14 ++ .../Repository/IPlexContentRepository.cs | 2 +- src/Ombi.Store/Repository/IRepository.cs | 3 +- .../Repository/JellyfinContentRepository.cs | 19 +- .../Repository/MediaServerRepository.cs | 30 +++ .../Repository/PlexContentRepository.cs | 19 +- 20 files changed, 199 insertions(+), 165 deletions(-) create mode 100644 src/Ombi.Store/Entities/IEntity.cs create mode 100644 src/Ombi.Store/Repository/MediaServerRepository.cs diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index dd9be69e0..490b40bf6 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -168,7 +168,8 @@ namespace Ombi.Schedule.Jobs.Emby Type = MediaType.Series, EmbyId = tvShow.Id, Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), - AddedAt = DateTime.UtcNow + AddedAt = DateTime.UtcNow, + Repository = _repo }); } else @@ -259,6 +260,7 @@ namespace Ombi.Schedule.Jobs.Emby EmbyId = movieInfo.Id, Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), AddedAt = DateTime.UtcNow, + Repository = _repo }); } else diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs index 8906e3a60..c9399f521 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs @@ -146,7 +146,8 @@ namespace Ombi.Schedule.Jobs.Jellyfin Type = MediaType.Series, JellyfinId = tvShow.Id, Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), - AddedAt = DateTime.UtcNow + AddedAt = DateTime.UtcNow, + Repository = _repo }); } else @@ -227,6 +228,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin JellyfinId = movieInfo.Id, Url = JellyfinHelper.GetJellyfinMediaUrl(movieInfo.Id, server?.ServerId, server.ServerHostname), AddedAt = DateTime.UtcNow, + Repository = _repo }); } else diff --git a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs index 54d1d1133..9d49fe2f1 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/HtmlTemplateGenerator.cs @@ -5,7 +5,8 @@ namespace Ombi.Schedule.Jobs.Ombi { public abstract class HtmlTemplateGenerator { - protected virtual void AddBackgroundInsideTable(StringBuilder sb, string url) + protected StringBuilder sb; + protected virtual void AddBackgroundInsideTable(string url) { sb.Append(""); sb.AppendFormat("", url); @@ -14,14 +15,14 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); } - protected virtual void AddPosterInsideTable(StringBuilder sb, string url) + protected virtual void AddPosterInsideTable(string url) { sb.Append(""); sb.Append(""); @@ -552,7 +533,7 @@ namespace Ombi.Schedule.Jobs.Ombi return sb.ToString(); } - private async Task ProcessMovies(IQueryable plexContentToSend, StringBuilder sb, string defaultLanguageCode, string mediaServerUrl) + private async Task ProcessMovies(IQueryable plexContentToSend, string defaultLanguageCode, string mediaServerUrl) { int count = 0; var ordered = plexContentToSend.OrderByDescending(x => x.AddedAt); @@ -571,7 +552,7 @@ namespace Ombi.Schedule.Jobs.Ombi } try { - CreateMovieHtmlContent(sb, info, mediaurl); + CreateMovieHtmlContent(info, mediaurl); count += 1; } catch (Exception e) @@ -580,7 +561,7 @@ namespace Ombi.Schedule.Jobs.Ombi } finally { - EndLoopHtml(sb); + EndLoopHtml(); } if (count == 2) @@ -591,7 +572,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } } - private async Task ProcessAlbums(HashSet albumsToSend, StringBuilder sb) + private async Task ProcessAlbums(HashSet albumsToSend) { var settings = await _lidarrSettings.GetSettingsAsync(); int count = 0; @@ -605,7 +586,7 @@ namespace Ombi.Schedule.Jobs.Ombi } try { - CreateAlbumHtmlContent(sb, info); + CreateAlbumHtmlContent(info); count += 1; } catch (Exception e) @@ -614,7 +595,7 @@ namespace Ombi.Schedule.Jobs.Ombi } finally { - EndLoopHtml(sb); + EndLoopHtml(); } if (count == 2) @@ -626,13 +607,13 @@ namespace Ombi.Schedule.Jobs.Ombi } } - private void CreateMovieHtmlContent(StringBuilder sb, MovieResponseDto info, string mediaurl) + private void CreateMovieHtmlContent(MovieResponseDto info, string mediaurl) { - AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}"); - AddPosterInsideTable(sb, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); + AddBackgroundInsideTable($"https://image.tmdb.org/t/p/w1280/{info.BackdropPath}"); + AddPosterInsideTable($"https://image.tmdb.org/t/p/original{info.PosterPath}"); - AddMediaServerUrl(sb, mediaurl, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); - AddInfoTable(sb); + AddMediaServerUrl(mediaurl, $"https://image.tmdb.org/t/p/original{info.PosterPath}"); + AddInfoTable(); var releaseDate = string.Empty; try @@ -646,7 +627,7 @@ namespace Ombi.Schedule.Jobs.Ombi // Swallow, couldn't parse the date } - AddTitle(sb, $"https://www.imdb.com/title/{info.ImdbId}/", $"{info.Title} {releaseDate}"); + AddTitle($"https://www.imdb.com/title/{info.ImdbId}/", $"{info.Title} {releaseDate}"); var summary = info.Overview; if (summary.Length > 280) @@ -654,16 +635,15 @@ namespace Ombi.Schedule.Jobs.Ombi summary = summary.Remove(280); summary = summary + "...

"; } - AddParagraph(sb, summary); + AddParagraph(summary); if (info.Genres.Any()) { - AddGenres(sb, - $"Genres: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + AddGenres($"Genres: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); } } - private void CreateAlbumHtmlContent(StringBuilder sb, AlbumLookup info) + private void CreateAlbumHtmlContent(AlbumLookup info) { var cover = info.images .FirstOrDefault(x => x.coverType.Equals("cover", StringComparison.InvariantCultureIgnoreCase))?.url; @@ -671,21 +651,21 @@ namespace Ombi.Schedule.Jobs.Ombi { cover = info.remoteCover; } - AddBackgroundInsideTable(sb, cover); + AddBackgroundInsideTable(cover); var disk = info.images .FirstOrDefault(x => x.coverType.Equals("disc", StringComparison.InvariantCultureIgnoreCase))?.url; if (disk.IsNullOrEmpty()) { disk = info.remoteCover; } - AddPosterInsideTable(sb, disk); + AddPosterInsideTable(disk); - AddMediaServerUrl(sb, string.Empty, string.Empty); - AddInfoTable(sb); + AddMediaServerUrl(string.Empty, string.Empty); + AddInfoTable(); var releaseDate = $"({info.releaseDate.Year})"; - AddTitle(sb, string.Empty, $"{info.title} {releaseDate}"); + AddTitle(string.Empty, $"{info.title} {releaseDate}"); var summary = info.artist?.artistName ?? string.Empty; if (summary.Length > 280) @@ -693,12 +673,12 @@ namespace Ombi.Schedule.Jobs.Ombi summary = summary.Remove(280); summary = summary + "...

"; } - AddParagraph(sb, summary); + AddParagraph(summary); - AddGenres(sb, $"Type: {info.albumType}"); + AddGenres($"Type: {info.albumType}"); } - private async Task ProcessTv(IEnumerable episodes, StringBuilder sb, string languageCode, string serverHostname) + private async Task ProcessTv(IEnumerable episodes, string languageCode, string serverHostname) { var series = new List(); foreach (var episode in episodes) @@ -768,17 +748,17 @@ namespace Ombi.Schedule.Jobs.Ombi if (tvInfo != null && tvInfo.backdrop_path.HasValue()) { - AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}"); + AddBackgroundInsideTable($"https://image.tmdb.org/t/p/w500{tvInfo.backdrop_path}"); } else { - AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/"); + AddBackgroundInsideTable($"https://image.tmdb.org/t/p/w1280/"); } - AddPosterInsideTable(sb, banner); - AddMediaServerUrl(sb, PlexHelper.BuildPlexMediaUrl(t.Url, serverHostname), banner); - AddInfoTable(sb); + AddPosterInsideTable(banner); + AddMediaServerUrl(PlexHelper.BuildPlexMediaUrl(t.Url, serverHostname), banner); + AddInfoTable(); - AddTvTitle(sb, info, tvInfo); + AddTvTitle(info, tvInfo); // Group by the season number var results = t.Episodes.GroupBy(p => p.SeasonNumber, @@ -801,7 +781,7 @@ namespace Ombi.Schedule.Jobs.Ombi finalsb.Append("
"); } - AddTvEpisodesSummaryGenres(sb, finalsb.ToString(), tvInfo); + AddTvEpisodesSummaryGenres(finalsb.ToString(), tvInfo); } catch (Exception e) @@ -810,7 +790,7 @@ namespace Ombi.Schedule.Jobs.Ombi } finally { - EndLoopHtml(sb); + EndLoopHtml(); count += 1; } @@ -823,7 +803,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } - private void AddTvTitle(StringBuilder sb, Api.TvMaze.Models.TvMazeShow info, TvInfo tvInfo) + private void AddTvTitle(Api.TvMaze.Models.TvMazeShow info, TvInfo tvInfo) { var title = ""; if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) @@ -834,10 +814,10 @@ namespace Ombi.Schedule.Jobs.Ombi { title = $"{tvInfo.name}"; } - AddTitle(sb, $"https://www.imdb.com/title/{info.externals.imdb}/", title); + AddTitle($"https://www.imdb.com/title/{info.externals.imdb}/", title); } - private void AddTvEpisodesSummaryGenres(StringBuilder sb, string episodes, TvInfo tvInfo) + private void AddTvEpisodesSummaryGenres(string episodes, TvInfo tvInfo) { var summary = tvInfo.overview; if (summary.Length > 280) @@ -845,15 +825,15 @@ namespace Ombi.Schedule.Jobs.Ombi summary = summary.Remove(280); summary = summary + "...

"; } - AddTvParagraph(sb, episodes, summary); + AddTvParagraph(episodes, summary); if (tvInfo.genres.Any()) { - AddGenres(sb, $"Genres: {string.Join(", ", tvInfo.genres.Select(x => x.name.ToString()).ToArray())}"); + AddGenres($"Genres: {string.Join(", ", tvInfo.genres.Select(x => x.name.ToString()).ToArray())}"); } } - private void EndLoopHtml(StringBuilder sb) + private void EndLoopHtml() { //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 diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 637d7591d..c2a9d20bf 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -353,7 +353,8 @@ namespace Ombi.Schedule.Jobs.Plex Title = movie.title, Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, movie.ratingKey), Seasons = new List(), - Quality = movie.Media?.FirstOrDefault()?.videoResolution ?? string.Empty + Quality = movie.Media?.FirstOrDefault()?.videoResolution ?? string.Empty, + Repository = Repo }; if (providerIds.ImdbId.HasValue()) { @@ -556,7 +557,8 @@ namespace Ombi.Schedule.Jobs.Plex Type = MediaType.Series, Title = show.title, Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), - Seasons = new List() + Seasons = new List(), + Repository = Repo }; await GetProviderIds(showMetadata, item); diff --git a/src/Ombi.Store/Entities/Entity.cs b/src/Ombi.Store/Entities/Entity.cs index 8e1cd2887..fac70de91 100644 --- a/src/Ombi.Store/Entities/Entity.cs +++ b/src/Ombi.Store/Entities/Entity.cs @@ -2,7 +2,7 @@ namespace Ombi.Store.Entities { - public abstract class Entity + public abstract class Entity: IEntity { [Key] public int Id { get; set; } diff --git a/src/Ombi.Store/Entities/IEntity.cs b/src/Ombi.Store/Entities/IEntity.cs new file mode 100644 index 000000000..004d214f1 --- /dev/null +++ b/src/Ombi.Store/Entities/IEntity.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Ombi.Store.Entities +{ + public interface IEntity + { + [Key] + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/IMediaServerContent.cs b/src/Ombi.Store/Entities/IMediaServerContent.cs index 6b5efd595..806602ee9 100644 --- a/src/Ombi.Store/Entities/IMediaServerContent.cs +++ b/src/Ombi.Store/Entities/IMediaServerContent.cs @@ -5,14 +5,14 @@ using Ombi.Store.Repository; namespace Ombi.Store.Entities { - public interface IMediaServerContent + public interface IMediaServerContent: IEntity { - public int Id { get; set; } public string Title { get; set; } public string ImdbId { get; set; } public string TvDbId { get; set; } public string TheMovieDbId { get; set; } public MediaType Type { get; set; } + public IMediaServerContentRepositoryLight Repository { get; } public string Url { get; set; } diff --git a/src/Ombi.Store/Entities/MediaServerContent.cs b/src/Ombi.Store/Entities/MediaServerContent.cs index 74220425c..8055d0d95 100644 --- a/src/Ombi.Store/Entities/MediaServerContent.cs +++ b/src/Ombi.Store/Entities/MediaServerContent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Ombi.Store.Repository; namespace Ombi.Store.Entities { @@ -26,6 +27,9 @@ namespace Ombi.Store.Entities [NotMapped] public bool HasTheMovieDb => !string.IsNullOrEmpty(TheMovieDbId); + + [NotMapped] //TODO: instantiate this variable upon read // something in ExternalContext.cs? + public IMediaServerContentRepositoryLight Repository { get; set; } } public abstract class MediaServerEpisode: Entity, IMediaServerEpisode diff --git a/src/Ombi.Store/Repository/EmbyContentRepository.cs b/src/Ombi.Store/Repository/EmbyContentRepository.cs index 7eb1e4e3f..adc260946 100644 --- a/src/Ombi.Store/Repository/EmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/EmbyContentRepository.cs @@ -35,17 +35,13 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class EmbyContentRepository : ExternalRepository, IEmbyContentRepository + public class EmbyContentRepository : MediaServerContentRepository, IEmbyContentRepository { public EmbyContentRepository(ExternalContext db):base(db) { - Db = db; } - private ExternalContext Db { get; } - - public async Task GetByImdbId(string imdbid) { return await Db.EmbyContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid); @@ -69,18 +65,18 @@ namespace Ombi.Store.Repository return await Db.EmbyContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.EmbyId == embyId); } - public async Task Update(IMediaServerContent existingContent) + public override async Task Update(IMediaServerContent existingContent) { Db.EmbyContent.Update((EmbyContent)existingContent); await InternalSaveChanges(); } - public IQueryable GetAllEpisodes() + public override IQueryable GetAllEpisodes() { return Db.EmbyEpisode.AsQueryable(); } - public async Task Add(IMediaServerEpisode content) + public override async Task Add(IMediaServerEpisode content) { await Db.EmbyEpisode.AddAsync((EmbyEpisode)content); await InternalSaveChanges(); @@ -91,16 +87,17 @@ namespace Ombi.Store.Repository return await Db.EmbyEpisode.FirstOrDefaultAsync(x => x.EmbyId == key); } - public async Task AddRange(IEnumerable content) + public override async Task AddRange(IEnumerable content) { Db.EmbyEpisode.AddRange((EmbyEpisode)content); await InternalSaveChanges(); } - public void UpdateWithoutSave(IMediaServerContent existingContent) + public override void UpdateWithoutSave(IMediaServerContent existingContent) { Db.EmbyContent.Update((EmbyContent)existingContent); } + public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Emby; } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IEmbyContentRepository.cs b/src/Ombi.Store/Repository/IEmbyContentRepository.cs index e3857cfb2..c171fef20 100644 --- a/src/Ombi.Store/Repository/IEmbyContentRepository.cs +++ b/src/Ombi.Store/Repository/IEmbyContentRepository.cs @@ -6,7 +6,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IEmbyContentRepository : IMediaServerContentRepository + public interface IEmbyContentRepository : IMediaServerContentRepository { Task GetByEmbyId(string embyId); Task GetEpisodeByEmbyId(string key); diff --git a/src/Ombi.Store/Repository/IExternalRepository.cs b/src/Ombi.Store/Repository/IExternalRepository.cs index b22cb5ea8..ec7b27769 100644 --- a/src/Ombi.Store/Repository/IExternalRepository.cs +++ b/src/Ombi.Store/Repository/IExternalRepository.cs @@ -9,7 +9,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IExternalRepository where T : Entity + public interface IExternalRepository where T : IEntity { Task Find(object key); IQueryable GetAll(); @@ -25,6 +25,5 @@ namespace Ombi.Store.Repository where TEntity : class; Task ExecuteSql(string sql); - DbSet _db { get; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IJellyfinContentRepository.cs b/src/Ombi.Store/Repository/IJellyfinContentRepository.cs index eed242f0b..30efa1a17 100644 --- a/src/Ombi.Store/Repository/IJellyfinContentRepository.cs +++ b/src/Ombi.Store/Repository/IJellyfinContentRepository.cs @@ -6,7 +6,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IJellyfinContentRepository : IMediaServerContentRepository + public interface IJellyfinContentRepository : IMediaServerContentRepository { Task GetByJellyfinId(string jellyfinId); Task GetEpisodeByJellyfinId(string key); diff --git a/src/Ombi.Store/Repository/IMediaServerContentRepository.cs b/src/Ombi.Store/Repository/IMediaServerContentRepository.cs index ef4ad7a55..1f81a40ba 100644 --- a/src/Ombi.Store/Repository/IMediaServerContentRepository.cs +++ b/src/Ombi.Store/Repository/IMediaServerContentRepository.cs @@ -5,8 +5,8 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IMediaServerContentRepository : IExternalRepository, IMediaServerContentRepositoryLight - where Content : Entity + public interface IMediaServerContentRepository : IExternalRepository, IMediaServerContentRepositoryLight + where Content : IMediaServerContent { } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/IMediaServerContentRepositoryLight.cs b/src/Ombi.Store/Repository/IMediaServerContentRepositoryLight.cs index 1eb69e10c..1f1749ae8 100644 --- a/src/Ombi.Store/Repository/IMediaServerContentRepositoryLight.cs +++ b/src/Ombi.Store/Repository/IMediaServerContentRepositoryLight.cs @@ -5,9 +5,23 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { + // TOOD: this is a mess done to bypass the fact that + // I can't pass around IMediaServerContentRepository as a parameter + // because I want to pass it a generic IMediaServerContent as a 'type' + // and casting from concrete classes doesn't work due to my poor C# knowledge + + // My workaround so far has been to use this lightened interface, + // but the ever-growing number of wrapper methods for methods coming from IRepository + // is starting to smell (see implementing class MediaServerContentRepository). public interface IMediaServerContentRepositoryLight { + RecentlyAddedType RecentlyAddedType{ get; } Task Update(IMediaServerContent existingContent); + + // IQueryable GetAllContent(); + // Task FindContent(object key); + // Task SaveChangesAsync(); + IQueryable GetAllEpisodes(); Task Add(IMediaServerEpisode content); Task AddRange(IEnumerable content); diff --git a/src/Ombi.Store/Repository/IPlexContentRepository.cs b/src/Ombi.Store/Repository/IPlexContentRepository.cs index bbdd256de..8203959bc 100644 --- a/src/Ombi.Store/Repository/IPlexContentRepository.cs +++ b/src/Ombi.Store/Repository/IPlexContentRepository.cs @@ -8,7 +8,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IPlexContentRepository : IMediaServerContentRepository + public interface IPlexContentRepository : IMediaServerContentRepository { Task ContentExists(string providerId); Task Get(string providerId, ProviderType type); diff --git a/src/Ombi.Store/Repository/IRepository.cs b/src/Ombi.Store/Repository/IRepository.cs index b93b07d45..a6142462f 100644 --- a/src/Ombi.Store/Repository/IRepository.cs +++ b/src/Ombi.Store/Repository/IRepository.cs @@ -10,7 +10,7 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public interface IRepository where T : Entity + public interface IRepository where T : IEntity { Task Find(object key); Task Find(object key, CancellationToken cancellationToken); @@ -27,6 +27,5 @@ namespace Ombi.Store.Repository where TEntity : class; Task ExecuteSql(string sql); - DbSet _db { get; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Repository/JellyfinContentRepository.cs b/src/Ombi.Store/Repository/JellyfinContentRepository.cs index 5b09cf84a..800473248 100644 --- a/src/Ombi.Store/Repository/JellyfinContentRepository.cs +++ b/src/Ombi.Store/Repository/JellyfinContentRepository.cs @@ -35,17 +35,13 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class JellyfinContentRepository : ExternalRepository, IJellyfinContentRepository + public class JellyfinContentRepository : MediaServerContentRepository, IJellyfinContentRepository { public JellyfinContentRepository(ExternalContext db):base(db) { - Db = db; } - private ExternalContext Db { get; } - - public async Task GetByImdbId(string imdbid) { return await Db.JellyfinContent.FirstOrDefaultAsync(x => x.ImdbId == imdbid); @@ -69,18 +65,18 @@ namespace Ombi.Store.Repository return await Db.JellyfinContent./*Include(x => x.Seasons).*/FirstOrDefaultAsync(x => x.JellyfinId == jellyfinId); } - public async Task Update(IMediaServerContent existingContent) + public override async Task Update(IMediaServerContent existingContent) { Db.JellyfinContent.Update((JellyfinContent)existingContent); await InternalSaveChanges(); } - public IQueryable GetAllEpisodes() + public override IQueryable GetAllEpisodes() { return Db.JellyfinEpisode.AsQueryable(); } - public async Task Add(IMediaServerEpisode content) + public override async Task Add(IMediaServerEpisode content) { await Db.JellyfinEpisode.AddAsync((JellyfinEpisode)content); await InternalSaveChanges(); @@ -91,16 +87,17 @@ namespace Ombi.Store.Repository return await Db.JellyfinEpisode.FirstOrDefaultAsync(x => x.JellyfinId == key); } - public async Task AddRange(IEnumerable content) + public override async Task AddRange(IEnumerable content) { Db.JellyfinEpisode.AddRange((JellyfinEpisode)content); await InternalSaveChanges(); } - public void UpdateWithoutSave(IMediaServerContent existingContent) + public override void UpdateWithoutSave(IMediaServerContent existingContent) { Db.JellyfinContent.Update((JellyfinContent)existingContent); } - + + public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Jellyfin; } } diff --git a/src/Ombi.Store/Repository/MediaServerRepository.cs b/src/Ombi.Store/Repository/MediaServerRepository.cs new file mode 100644 index 000000000..3fdc35ea8 --- /dev/null +++ b/src/Ombi.Store/Repository/MediaServerRepository.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Store.Repository +{ + public abstract class MediaServerContentRepository : ExternalRepository, IMediaServerContentRepository where T : MediaServerContent + { + protected ExternalContext Db { get; } + public abstract RecentlyAddedType RecentlyAddedType { get; } + + public MediaServerContentRepository(ExternalContext db) : base(db) + { + Db = db; + } + + public abstract Task Update(IMediaServerContent existingContent); + + // TOOD: this smells: trying to wrap ExternalRepository methods in IMediaServerContentRepositoryLight for generic consumption + public IQueryable GetAllContent() => (IQueryable)GetAll(); + public async Task FindContent(object key) => (IMediaServerContent)await Find(key); + + public abstract IQueryable GetAllEpisodes(); + public abstract Task Add(IMediaServerEpisode content); + public abstract Task AddRange(IEnumerable content); + public abstract void UpdateWithoutSave(IMediaServerContent existingContent); + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index 61139d4c6..6429e679f 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -37,17 +37,13 @@ using Ombi.Store.Entities; namespace Ombi.Store.Repository { - public class PlexServerContentRepository : ExternalRepository, IPlexContentRepository + public class PlexServerContentRepository : MediaServerContentRepository, IPlexContentRepository { - + public override RecentlyAddedType RecentlyAddedType => RecentlyAddedType.Plex; public PlexServerContentRepository(ExternalContext db) : base(db) { - Db = db; } - private ExternalContext Db { get; } - - public async Task ContentExists(string providerId) { var any = await Db.PlexServerContent.AnyAsync(x => x.ImdbId == providerId); @@ -114,12 +110,12 @@ namespace Ombi.Store.Repository .FirstOrDefaultAsync(predicate); } - public async Task Update(IMediaServerContent existingContent) + public override async Task Update(IMediaServerContent existingContent) { Db.PlexServerContent.Update((PlexServerContent)existingContent); await InternalSaveChanges(); } - public void UpdateWithoutSave(IMediaServerContent existingContent) + public override void UpdateWithoutSave(IMediaServerContent existingContent) { Db.PlexServerContent.Update((PlexServerContent)existingContent); } @@ -130,7 +126,7 @@ namespace Ombi.Store.Repository await InternalSaveChanges(); } - public IQueryable GetAllEpisodes() + public override IQueryable GetAllEpisodes() { return Db.PlexEpisode.Include(x => x.Series).AsQueryable(); } @@ -145,7 +141,7 @@ namespace Ombi.Store.Repository Db.PlexEpisode.Remove(content); } - public async Task Add(IMediaServerEpisode content) + public override async Task Add(IMediaServerEpisode content) { await Db.PlexEpisode.AddAsync((PlexEpisode)content); await InternalSaveChanges(); @@ -162,10 +158,11 @@ namespace Ombi.Store.Repository { return await Db.PlexEpisode.FirstOrDefaultAsync(x => x.Key == key); } - public async Task AddRange(IEnumerable content) + public override async Task AddRange(IEnumerable content) { Db.PlexEpisode.AddRange((PlexEpisode)content); await InternalSaveChanges(); } + } } \ No newline at end of file
"); sb.AppendFormat("", url); } - protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url) + protected virtual void AddMediaServerUrl(string mediaurl, string url) { if (url.HasValue()) { @@ -41,14 +42,14 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append(""); } - protected virtual void AddInfoTable(StringBuilder sb) + protected virtual void AddInfoTable() { sb.Append( ""); foreach (var mediaServerContent in episodes) { - await ProcessTv(mediaServerContent, sb, ombiSettings.DefaultLanguageCode, /* plexSettings.Servers.FirstOrDefault()?.ServerHostname ?? */ string.Empty); + await ProcessTv(mediaServerContent, ombiSettings.DefaultLanguageCode, /* plexSettings.Servers.FirstOrDefault()?.ServerHostname ?? */ string.Empty); } sb.Append(""); sb.Append("
"); sb.Append(""); } - protected virtual void AddTitle(StringBuilder sb, string url, string title) + protected virtual void AddTitle( string url, string title) { sb.Append(""); sb.Append(""); } - protected virtual void AddParagraph(StringBuilder sb, string text) + protected virtual void AddParagraph(string text) { sb.Append(""); sb.Append(""); } - protected virtual void AddTvParagraph(StringBuilder sb, string episodes, string summary) + protected virtual void AddTvParagraph(string episodes, string summary) { sb.Append(""); sb.Append(""); } - protected virtual void AddGenres(StringBuilder sb, string text) + protected virtual void AddGenres(string text) { sb.Append(""); sb.Append(""); foreach (var mediaServerContent in contentToSend) { - await ProcessMovies(mediaServerContent, sb, ombiSettings.DefaultLanguageCode, /*plexSettings.Servers?.FirstOrDefault()?.ServerHostname ?? */ string.Empty); + await ProcessMovies(mediaServerContent, ombiSettings.DefaultLanguageCode, /*plexSettings.Servers?.FirstOrDefault()?.ServerHostname ?? */ string.Empty); } sb.Append(""); sb.Append("
"); @@ -59,7 +60,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); @@ -68,7 +69,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); @@ -78,7 +79,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 249dbb693..f4bcc1a33 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -129,9 +129,12 @@ namespace Ombi.Schedule.Jobs.Ombi var jellyfinContent = (IQueryable)_jellyfin.GetAll().Include(x => x.Episodes).AsNoTracking(); // MOVIES - var plexContentMoviesToSend = await GetMoviesContent(plexContent, RecentlyAddedType.Plex); - var embyContentMoviesToSend = await GetMoviesContent(embyContent, RecentlyAddedType.Emby); - var jellyfinContentMoviesToSend = await GetMoviesContent(jellyfinContent, RecentlyAddedType.Jellyfin); + var moviesContents = new List>(); + // these explicit casts won't work because: + // Unable to cast object of type 'PlexServerContentRepository' to type 'IMediaServerContentRepository`1[IMediaServerContent] + moviesContents.Add((await GetMoviesContent((IMediaServerContentRepository)_plex)).AsQueryable()); + moviesContents.Add((await GetMoviesContent((IMediaServerContentRepository)_emby)).AsQueryable()); + moviesContents.Add((await GetMoviesContent((IMediaServerContentRepository)_jellyfin)).AsQueryable()); // MUSIC var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking().ToList().Where(x => x.FullyAvailable); @@ -176,12 +179,12 @@ namespace Ombi.Schedule.Jobs.Ombi var jellyfint = _jellyfin.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); - var moviesProviders = new List>() { + var moviesProviders = new List>() { plexm, embym, jellyfinm }; - var seriesProviders = new List>() { + var seriesProviders = new List>() { plext, embyt, jellyfint @@ -190,18 +193,13 @@ namespace Ombi.Schedule.Jobs.Ombi } else { - var moviesProviders = new List>() { - plexContentMoviesToSend.AsQueryable(), - embyContentMoviesToSend.AsQueryable(), - jellyfinContentMoviesToSend.AsQueryable() - }; - var seriesProviders = new List>() { + var seriesProviders = new List>() { plexEpisodesToSend, embyEpisodesToSend, jellyfinEpisodesToSend }; - body = await BuildHtml(moviesProviders, seriesProviders, lidarrContentAlbumsToSend, settings, embySettings, jellyfinSettings, plexSettings); + body = await BuildHtml(moviesContents, seriesProviders, lidarrContentAlbumsToSend, settings, embySettings, jellyfinSettings, plexSettings); if (body.IsNullOrEmpty()) { return; @@ -260,17 +258,7 @@ namespace Ombi.Schedule.Jobs.Ombi // Now add all of this to the Recently Added log var recentlyAddedLog = new HashSet(); - foreach (var p in plexContentMoviesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog - { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Parent, - ContentId = StringHelper.IntParseLinq(p.TheMovieDbId), - }); - - } + AddToRecentlyAddedLog(moviesContents, recentlyAddedLog); foreach (var p in plexEpisodesToSend) { @@ -284,19 +272,6 @@ namespace Ombi.Schedule.Jobs.Ombi SeasonNumber = p.SeasonNumber }); } - foreach (var e in embyContentMoviesToSend) - { - if (e.Type == MediaType.Movie) - { - recentlyAddedLog.Add(new RecentlyAddedLog - { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Emby, - ContentType = ContentType.Parent, - ContentId = StringHelper.IntParseLinq(e.TheMovieDbId), - }); - } - } foreach (var p in embyEpisodesToSend) { @@ -311,20 +286,6 @@ namespace Ombi.Schedule.Jobs.Ombi }); } - foreach (var e in jellyfinContentMoviesToSend) - { - if (e.Type == MediaType.Movie) - { - recentlyAddedLog.Add(new RecentlyAddedLog - { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Jellyfin, - ContentType = ContentType.Parent, - ContentId = StringHelper.IntParseLinq(e.TheMovieDbId), - }); - } - } - foreach (var p in jellyfinEpisodesToSend) { recentlyAddedLog.Add(new RecentlyAddedLog @@ -377,29 +338,49 @@ namespace Ombi.Schedule.Jobs.Ombi .SendAsync(NotificationHub.NotificationEvent, "Newsletter Finished"); } + private void AddToRecentlyAddedLog(List> moviesContents, + HashSet recentlyAddedLog) + { + foreach (var contentProvider in moviesContents) + { + foreach (var p in contentProvider) + { + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = p.Repository.RecentlyAddedType, + ContentType = ContentType.Parent, + ContentId = StringHelper.IntParseLinq(p.TheMovieDbId), + }); + + } + } + } + private void GetRecentlyAddedMoviesData(List addedLog, out HashSet addedAlbumLogIds) { var lidarrParent = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album); addedAlbumLogIds = lidarrParent != null && lidarrParent.Any() ? (lidarrParent?.Select(x => x.AlbumId)?.ToHashSet() ?? new HashSet()) : new HashSet(); } - private async Task> GetMoviesContent(IQueryable content, RecentlyAddedType recentlyAddedType) + private async Task> GetMoviesContent(IMediaServerContentRepository repository) { + var content = repository.GetAll().Include(x => x.Episodes).AsNoTracking(); var localDataset = content.Where(x => x.Type == MediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet(); // Filter out the ones that we haven't sent yet var addedLog = _recentlyAddedLog.GetAll().ToList(); - var plexParent = addedLog.Where(x => x.Type == recentlyAddedType + var parent = addedLog.Where(x => x.Type == repository.RecentlyAddedType && x.ContentType == ContentType.Parent).ToList(); - var addedPlexMovieLogIds = plexParent != null && plexParent.Any() ? (plexParent?.Select(x => x.ContentId)?.ToHashSet() ?? new HashSet()) : new HashSet(); - var plexContentMoviesToSend = localDataset.Where(x => !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet(); - _log.LogInformation("Movies to send: {0}", plexContentMoviesToSend.Count()); + var addedMovieLogIds = parent != null && parent.Any() ? (parent?.Select(x => x.ContentId)?.ToHashSet() ?? new HashSet()) : new HashSet(); + var contentMoviesToSend = localDataset.Where(x => !addedMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))).ToHashSet(); + _log.LogInformation("Movies to send: {0}", contentMoviesToSend.Count()); // Find the movies that do not yet have MovieDbIds - var needsMovieDbPlex = content.Where(x => x.Type == MediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet(); - var newPlexMovies = await GetMoviesWithoutId(addedPlexMovieLogIds, needsMovieDbPlex); - plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet(); + var needsMovieDb = content.Where(x => x.Type == MediaType.Movie && !string.IsNullOrEmpty(x.TheMovieDbId)).ToHashSet(); + var newMovies = await GetMoviesWithoutId(addedMovieLogIds, needsMovieDb, repository); + contentMoviesToSend = contentMoviesToSend.Union(newMovies).ToHashSet(); - return plexContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet(); + return contentMoviesToSend.DistinctBy(x => x.Id).ToHashSet(); } @@ -418,21 +399,21 @@ namespace Ombi.Schedule.Jobs.Ombi return b.ToString(); } - private async Task> GetMoviesWithoutId(HashSet addedMovieLogIds, HashSet needsMovieDbPlex) + private async Task> GetMoviesWithoutId(HashSet addedMovieLogIds, HashSet needsMovieDb, IMediaServerContentRepository repository) { - foreach (var movie in needsMovieDbPlex) + foreach (var movie in needsMovieDb) { var id = await _refreshMetadata.GetTheMovieDbId(false, true, null, movie.ImdbId, movie.Title, true); movie.TheMovieDbId = id.ToString(); } - var result = needsMovieDbPlex.Where(x => x.HasTheMovieDb && !addedMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); - await UpdateTheMovieDbId(result); + var result = needsMovieDb.Where(x => x.HasTheMovieDb && !addedMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); + await UpdateTheMovieDbId(result, repository); // Filter them out now return result.ToHashSet(); } - private async Task UpdateTheMovieDbId(IEnumerable content) + private async Task UpdateTheMovieDbId(IEnumerable content, IMediaServerContentRepository repository) { foreach (var movie in content) { @@ -440,15 +421,15 @@ namespace Ombi.Schedule.Jobs.Ombi { continue; } - var entity = await _plex.Find(movie.Id); + var entity = await repository.Find(movie.Id); if (entity == null) { return; } entity.TheMovieDbId = movie.TheMovieDbId; - _plex.UpdateWithoutSave(entity); + repository.UpdateWithoutSave(entity); } - await _plex.SaveChangesAsync(); + await repository.SaveChangesAsync(); } public async Task Execute(IJobExecutionContext job) @@ -489,7 +470,7 @@ namespace Ombi.Schedule.Jobs.Ombi PlexSettings plexSettings) { var ombiSettings = await _ombiSettings.GetSettingsAsync(); - var sb = new StringBuilder(); + sb = new StringBuilder(); if (!settings.DisableMovies) { @@ -502,7 +483,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); @@ -522,7 +503,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); @@ -541,7 +522,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); sb.Append(""); sb.Append(""); - await ProcessAlbums(albums, sb); + await ProcessAlbums(albums); sb.Append(""); sb.Append("
"); sb.Append("