diff --git a/Ombi.Core.Migration/Migrations/Version2200.cs b/Ombi.Core.Migration/Migrations/Version2200.cs index de9f0d77e..4422846f0 100644 --- a/Ombi.Core.Migration/Migrations/Version2200.cs +++ b/Ombi.Core.Migration/Migrations/Version2200.cs @@ -27,26 +27,37 @@ #endregion +using System; using System.Data; using NLog; using Ombi.Core.SettingModels; using Ombi.Store; +using Ombi.Store.Models; +using Ombi.Store.Models.Plex; +using Ombi.Store.Repository; +using Quartz.Collection; namespace Ombi.Core.Migration.Migrations { [Migration(22000, "v2.20.0.0")] public class Version2200 : BaseMigration, IMigration { - public Version2200(ISettingsService custom, ISettingsService ps) + public Version2200(ISettingsService custom, ISettingsService ps, IRepository log, + IRepository content, IRepository plexEp) { Customization = custom; PlexSettings = ps; + Log = log; + PlexContent = content; + PlexEpisodes = plexEp; } public int Version => 22000; - private ISettingsService Customization { get; set; } - private ISettingsService PlexSettings { get; set; } - + private ISettingsService Customization { get; } + private ISettingsService PlexSettings { get; } + private IRepository Log { get; } + private IRepository PlexContent { get; } + private IRepository PlexEpisodes { get; } private static Logger Logger = LogManager.GetCurrentClassLogger(); @@ -56,12 +67,46 @@ namespace Ombi.Core.Migration.Migrations UpdateCustomSettings(); AddNewColumns(con); UpdateSchema(con, Version); + UpdateRecentlyAdded(con); + } + + private void UpdateRecentlyAdded(IDbConnection con) + { + var allContent = PlexContent.GetAll(); + + var content = new HashSet(); + foreach (var plexContent in allContent) + { + content.Add(new RecentlyAddedLog + { + AddedAt = DateTime.UtcNow, + ProviderId = plexContent.ProviderId + }); + } + + Log.BatchInsert(content, "RecentlyAddedLog"); + + var allEp = PlexEpisodes.GetAll(); + content.Clear(); + foreach (var ep in allEp) + { + content.Add(new RecentlyAddedLog + { + AddedAt = DateTime.UtcNow, + ProviderId = ep.ProviderId + }); + } + + Log.BatchInsert(content, "RecentlyAddedLog"); } private void AddNewColumns(IDbConnection con) { con.AlterTable("EmbyContent", "ADD", "AddedAt", true, "VARCHAR(50)"); con.AlterTable("EmbyEpisodes", "ADD", "AddedAt", true, "VARCHAR(50)"); + + con.AlterTable("PlexContent", "ADD", "ItemID", true, "VARCHAR(100)"); + con.AlterTable("PlexContent", "ADD", "AddedAt", true, "VARCHAR(100)"); } private void UpdatePlexSettings() diff --git a/Ombi.Services/Jobs/EmbyEpisodeCacher.cs b/Ombi.Services/Jobs/EmbyEpisodeCacher.cs index 0135592cc..387a7dc98 100644 --- a/Ombi.Services/Jobs/EmbyEpisodeCacher.cs +++ b/Ombi.Services/Jobs/EmbyEpisodeCacher.cs @@ -111,7 +111,7 @@ namespace Ombi.Services.Jobs } // Insert the new items - var result = Repo.BatchInsert(model, TableName, typeof(EmbyEpisodes).GetPropertyNames()); + var result = Repo.BatchInsert(model, TableName); if (!result) { diff --git a/Ombi.Services/Jobs/PlexContentCacher.cs b/Ombi.Services/Jobs/PlexContentCacher.cs index 936a7a60b..041374c6b 100644 --- a/Ombi.Services/Jobs/PlexContentCacher.cs +++ b/Ombi.Services/Jobs/PlexContentCacher.cs @@ -115,7 +115,8 @@ namespace Ombi.Services.Jobs ReleaseYear = video.Year, Title = video.Title, ProviderId = video.ProviderId, - Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, video.RatingKey) + Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, video.RatingKey), + ItemId = video.RatingKey })); } } @@ -145,6 +146,7 @@ namespace Ombi.Services.Jobs ProviderId = x.ProviderId, Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(), Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey), + ItemId= x.RatingKey })); } @@ -271,7 +273,8 @@ namespace Ombi.Services.Jobs ReleaseYear = m.ReleaseYear ?? string.Empty, Title = m.Title, Type = Store.Models.Plex.PlexMediaType.Movie, - Url = m.Url + Url = m.Url, + ItemId = m.ItemId }); } } @@ -311,7 +314,8 @@ namespace Ombi.Services.Jobs Title = t.Title, Type = Store.Models.Plex.PlexMediaType.Show, Url = t.Url, - Seasons = ByteConverterHelper.ReturnBytes(t.Seasons) + Seasons = ByteConverterHelper.ReturnBytes(t.Seasons), + ItemId = t.ItemId }); } } @@ -352,7 +356,7 @@ namespace Ombi.Services.Jobs ReleaseYear = a.ReleaseYear ?? string.Empty, Title = a.Title, Type = Store.Models.Plex.PlexMediaType.Artist, - Url = a.Url + Url = a.Url, }); } } diff --git a/Ombi.Services/Jobs/PlexEpisodeCacher.cs b/Ombi.Services/Jobs/PlexEpisodeCacher.cs index e6d1fc9c9..58ebe7fd3 100644 --- a/Ombi.Services/Jobs/PlexEpisodeCacher.cs +++ b/Ombi.Services/Jobs/PlexEpisodeCacher.cs @@ -134,7 +134,7 @@ namespace Ombi.Services.Jobs Repo.DeleteAll(TableName); // Insert the new items - var result = Repo.BatchInsert(entities.Select(x => x.Key).ToList(), TableName, typeof(PlexEpisodes).GetPropertyNames()); + var result = Repo.BatchInsert(entities.Select(x => x.Key).ToList(), TableName); if (!result) { diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs index b78f64dcc..90e32dcd0 100644 --- a/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs @@ -228,7 +228,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter AddParagraph(sb, info.Overview); } - catch (RequestLimitExceededException limit) + catch (Exception limit) { // We have hit a limit, we need to now wait. Thread.Sleep(TimeSpan.FromSeconds(10)); diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/IPlexNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/IPlexNewsletter.cs new file mode 100644 index 000000000..f22ccf519 --- /dev/null +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/IPlexNewsletter.cs @@ -0,0 +1,7 @@ +namespace Ombi.Services.Jobs.RecentlyAddedNewsletter +{ + public interface IPlexNewsletter + { + string GetNewsletterHtml(bool test); + } +} \ No newline at end of file diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs new file mode 100644 index 000000000..0b37ae813 --- /dev/null +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs @@ -0,0 +1,363 @@ +#region Copyright + +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RecentlyAddedModel.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using NLog; +using Ombi.Api; +using Ombi.Api.Interfaces; +using Ombi.Api.Models.Emby; +using Ombi.Api.Models.Plex; +using Ombi.Core; +using Ombi.Core.SettingModels; +using Ombi.Helpers; +using Ombi.Services.Jobs.Templates; +using Ombi.Store.Models; +using Ombi.Store.Models.Emby; +using Ombi.Store.Models.Plex; +using Ombi.Store.Repository; +using TMDbLib.Objects.Exceptions; +using EmbyMediaType = Ombi.Store.Models.Plex.EmbyMediaType; +using PlexMediaType = Ombi.Store.Models.Plex.PlexMediaType; + +namespace Ombi.Services.Jobs.RecentlyAddedNewsletter +{ + public class PlexRecentlyAddedNewsletter : HtmlTemplateGenerator, IPlexNewsletter + { + public PlexRecentlyAddedNewsletter(IPlexApi api, ISettingsService plexSettings, + ISettingsService email, + ISettingsService newsletter, IRepository log, + IRepository embyContent, IRepository episodes) + { + Api = api; + PlexSettings = plexSettings; + EmailSettings = email; + NewsletterSettings = newsletter; + Content = embyContent; + MovieApi = new TheMovieDbApi(); + TvApi = new TvMazeApi(); + Episodes = episodes; + RecentlyAddedLog = log; + } + + private IPlexApi Api { get; } + private TheMovieDbApi MovieApi { get; } + private TvMazeApi TvApi { get; } + private ISettingsService PlexSettings { get; } + private ISettingsService EmailSettings { get; } + private ISettingsService NewsletterSettings { get; } + private IRepository Content { get; } + private IRepository Episodes { get; } + private IRepository RecentlyAddedLog { get; } + + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + public string GetNewsletterHtml(bool test) + { + try + { + return GetHtml(test); + } + catch (Exception e) + { + Log.Error(e); + return string.Empty; + } + } + + private class PlexRecentlyAddedModel + { + public PlexMetadata Metadata { get; set; } + public PlexContent Content { get; set; } + } + + private string GetHtml(bool test) + { + var sb = new StringBuilder(); + var plexSettings = PlexSettings.GetSettings(); + + var plexContent = Content.GetAll().ToList(); + + var series = plexContent.Where(x => x.Type == PlexMediaType.Show).ToList(); + var episodes = Episodes.GetAll().ToList(); + var movie = plexContent.Where(x => x.Type == PlexMediaType.Movie).ToList(); + + var recentlyAdded = RecentlyAddedLog.GetAll().ToList(); + + var firstRun = !recentlyAdded.Any(); + + var filteredMovies = movie.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList(); + var filteredEp = episodes.Where(m => recentlyAdded.All(x => x.ProviderId != m.ProviderId)).ToList(); + + + var info = new List(); + foreach (var m in filteredMovies) + { + var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, m.ItemId); + info.Add(new PlexRecentlyAddedModel + { + Metadata = i, + Content = m + }); + } + GenerateMovieHtml(info, sb); + + info.Clear(); + foreach (var t in series) + { + var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, t.ItemId); + + //var ep = filteredEp.Where(x => x.ShowTitle == t.Title); + info.Add(new PlexRecentlyAddedModel + { + Metadata = i, + Content = t + }); + //if (ep.Any()) + //{ + // var episodeList = new List(); + // foreach (var embyEpisodese in ep) + // { + // var epInfo = Api.GetInformation(embyEpisodese.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Episode, + // embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri); + // episodeList.Add(epInfo.EpisodeInformation); + // } + // info.Add(new EmbyRecentlyAddedModel + // { + // EmbyContent = t, + // EmbyInformation = i, + // EpisodeInformation = episodeList + // }); + //} + } + GenerateTvHtml(info, sb); + + var template = new RecentlyAddedTemplate(); + var html = template.LoadTemplate(sb.ToString()); + Log.Debug("Loaded the template"); + + if (!test || firstRun) + { + foreach (var a in filteredMovies) + { + RecentlyAddedLog.Insert(new RecentlyAddedLog + { + ProviderId = a.ProviderId, + AddedAt = DateTime.UtcNow + }); + } + foreach (var a in filteredEp) + { + RecentlyAddedLog.Insert(new RecentlyAddedLog + { + ProviderId = a.ProviderId, + AddedAt = DateTime.UtcNow + }); + } + } + + var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray()); + Log.Debug(escapedHtml); + return escapedHtml; + } + + private void GenerateMovieHtml(IEnumerable recentlyAddedMovies, StringBuilder sb) + { + var movies = recentlyAddedMovies?.ToList() ?? new List(); + if (!movies.Any()) + { + return; + } + var orderedMovies = movies.OrderByDescending(x => x.Content.AddedAt).ToList(); + sb.Append("

New Movies:



"); + sb.Append( + ""); + foreach (var movie in orderedMovies) + { + // We have a try within a try so we can catch the rate limit without ending the loop (finally block) + try + { + try + { + + var imdbId = PlexHelper.GetProviderIdFromPlexGuid(movie.Metadata.Video.Guid); + var info = MovieApi.GetMovieInformation(imdbId).Result; + if (info == null) + { + throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi"); + } + AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}"); + + sb.Append(""); + sb.Append( + "
"); + + Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); + Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? 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 (RequestLimitExceededException limit) + { + // We have hit a limit, we need to now wait. + Thread.Sleep(TimeSpan.FromSeconds(10)); + Log.Info(limit); + } + } + catch (Exception e) + { + Log.Error(e); + Log.Error("Error for movie with IMDB Id = {0}", movie.Metadata.Video.Guid); + } + finally + { + EndLoopHtml(sb); + } + + } + sb.Append("


"); + } + + private class TvModel + { + public EmbySeriesInformation Series { get; set; } + public List Episodes { get; set; } + } + private void GenerateTvHtml(IEnumerable recenetlyAddedTv, StringBuilder sb) + { + var tv = recenetlyAddedTv?.ToList() ?? new List(); + + if (!tv.Any()) + { + return; + } + var orderedTv = tv.OrderByDescending(x => x.Content.AddedAt).ToList(); + + // TV + sb.Append("

New Episodes:



"); + sb.Append( + ""); + foreach (var t in orderedTv) + { + //var seriesItem = t.EmbyInformation.SeriesInformation; + //var relatedEpisodes = t.EpisodeInformation; + + + try + { + var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(t.Metadata.Directory.Guid))); + + 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.Content.Title} {t.Content.ReleaseYear}"; + + Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); + Header(sb, 3, title); + EndTag(sb, "a"); + + //var results = relatedEpisodes.GroupBy(p => p.ParentIndexNumber, + // (key, g) => new + // { + // ParentIndexNumber = key, + // IndexNumber = g.ToList() + // } + //); + // Group the episodes + //foreach (var embyEpisodeInformation in results.OrderBy(x => x.ParentIndexNumber)) + //{ + // var epSb = new StringBuilder(); + // for (var i = 0; i < embyEpisodeInformation.IndexNumber.Count; i++) + // { + // var ep = embyEpisodeInformation.IndexNumber[i]; + // if (i < embyEpisodeInformation.IndexNumber.Count) + // { + // epSb.Append($"{ep.IndexNumber},"); + // } + // else + // { + // epSb.Append(ep); + // } + // } + // AddParagraph(sb, $"Season: {embyEpisodeInformation.ParentIndexNumber}, Episode: {epSb}"); + //} + + if (info.genres.Any()) + { + AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + } + + AddParagraph(sb, string.IsNullOrEmpty(t.Metadata.Directory.Summary) ? t.Metadata.Directory.Summary : 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(""); + } + + } +} \ No newline at end of file diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/RecentlyAddedNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/RecentlyAddedNewsletter.cs index e2bbf5ede..330adf60c 100644 --- a/Ombi.Services/Jobs/RecentlyAddedNewsletter/RecentlyAddedNewsletter.cs +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/RecentlyAddedNewsletter.cs @@ -53,18 +53,19 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter public RecentlyAddedNewsletter(IPlexApi api, ISettingsService plexSettings, ISettingsService email, IJobRecord rec, ISettingsService newsletter, - IPlexReadOnlyDatabase db, IUserHelper userHelper, IEmbyAddedNewsletter embyNews, - ISettingsService embyS) + IUserHelper userHelper, IEmbyAddedNewsletter embyNews, + ISettingsService embyS, + IPlexNewsletter plex) { JobRecord = rec; Api = api; PlexSettings = plexSettings; EmailSettings = email; NewsletterSettings = newsletter; - PlexDb = db; UserHelper = userHelper; EmbyNewsletter = embyNews; EmbySettings = embyS; + PlexNewsletter = plex; } private IPlexApi Api { get; } @@ -75,9 +76,9 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter private ISettingsService EmailSettings { get; } private ISettingsService NewsletterSettings { get; } private IJobRecord JobRecord { get; } - private IPlexReadOnlyDatabase PlexDb { get; } private IUserHelper UserHelper { get; } private IEmbyAddedNewsletter EmbyNewsletter { get; } + private IPlexNewsletter PlexNewsletter { get; } private static readonly Logger Log = LogManager.GetCurrentClassLogger(); @@ -144,331 +145,18 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter } else { - var sb = new StringBuilder(); var plexSettings = PlexSettings.GetSettings(); - Log.Debug("Got Plex Settings"); - - var libs = Api.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri); - Log.Debug("Getting Plex Library Sections"); - - var tvSections = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib - Log.Debug("Filtered sections for TV"); - var movieSection = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib - Log.Debug("Filtered sections for Movies"); - - var plexVersion = Api.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri).Version; - - var html = string.Empty; - if (plexVersion.StartsWith("1.3")) - { - var tvMetadata = new List(); - var movieMetadata = new List(); - foreach (var tvSection in tvSections) - { - var item = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, - tvSection?.Key); - if (item?.MediaContainer?.Metadata != null) - { - tvMetadata.AddRange(item?.MediaContainer?.Metadata); - } - } - Log.Debug("Got RecentlyAdded TV Shows"); - foreach (var movie in movieSection) - { - var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key); - if (recentlyAddedMovies?.MediaContainer?.Metadata != null) - { - movieMetadata.AddRange(recentlyAddedMovies?.MediaContainer?.Metadata); - } - } - Log.Debug("Got RecentlyAdded Movies"); - - Log.Debug("Started Generating Movie HTML"); - GenerateMovieHtml(movieMetadata, plexSettings, sb); - Log.Debug("Finished Generating Movie HTML"); - Log.Debug("Started Generating TV HTML"); - GenerateTvHtml(tvMetadata, plexSettings, sb); - Log.Debug("Finished Generating TV HTML"); - - var template = new RecentlyAddedTemplate(); - html = template.LoadTemplate(sb.ToString()); - Log.Debug("Loaded the template"); - } - else - { - // Old API - var tvChild = new List(); - var movieChild = new List(); - foreach (var tvSection in tvSections) - { - var recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key); - if (recentlyAddedTv?._children != null) - { - tvChild.AddRange(recentlyAddedTv?._children); - } - } - - Log.Debug("Got RecentlyAdded TV Shows"); - foreach (var movie in movieSection) - { - var recentlyAddedMovies = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key); - if (recentlyAddedMovies?._children != null) - { - tvChild.AddRange(recentlyAddedMovies?._children); - } - } - Log.Debug("Got RecentlyAdded Movies"); - - Log.Debug("Started Generating Movie HTML"); - GenerateMovieHtml(movieChild, plexSettings, sb); - Log.Debug("Finished Generating Movie HTML"); - Log.Debug("Started Generating TV HTML"); - GenerateTvHtml(tvChild, plexSettings, sb); - Log.Debug("Finished Generating TV HTML"); - - var template = new RecentlyAddedTemplate(); - html = template.LoadTemplate(sb.ToString()); - Log.Debug("Loaded the template"); - } - string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray()); - Log.Debug(escapedHtml); - SendNewsletter(newletterSettings, escapedHtml, testEmail); - } - } - - private void GenerateMovieHtml(List movies, PlexSettings plexSettings, StringBuilder sb) - { - var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List(); - sb.Append("

New Movies:



"); - sb.Append( - ""); - foreach (var movie in orderedMovies) - { - var plexGUID = string.Empty; - try - { - var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - movie.ratingKey.ToString()); - - plexGUID = metaData.Video.Guid; - - var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID); - var info = _movieApi.GetMovieInformation(imdbId).Result; - if (info == null) - { - throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi"); - } - AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}"); - - sb.Append(""); - sb.Append( - "
"); - - Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); - Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? 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) - { - Log.Error(e); - Log.Error( - "Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", - plexGUID); - } - finally - { - EndLoopHtml(sb); - } - - } - sb.Append("


"); - } - - private void GenerateMovieHtml(List movies, PlexSettings plexSettings, StringBuilder sb) - { - var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List(); - sb.Append("

New Movies:



"); - sb.Append( - ""); - foreach (var movie in orderedMovies) - { - var plexGUID = string.Empty; - try - { - var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - movie.ratingKey.ToString()); - - plexGUID = metaData.Video.Guid; - - var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID); - var info = _movieApi.GetMovieInformation(imdbId).Result; - if (info == null) - { - throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi"); - } - AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}"); - - sb.Append(""); - sb.Append( - "
"); - - Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); - Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? 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) - { - Log.Error(e); - Log.Error( - "Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", - plexGUID); - } - finally - { - EndLoopHtml(sb); - } - - } - sb.Append("


"); - } - - private void GenerateTvHtml(List tv, PlexSettings plexSettings, StringBuilder sb) - { - var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList(); - // TV - sb.Append("

New Episodes:



"); - sb.Append( - ""); - foreach (var t in orderedTv) - { - var plexGUID = string.Empty; - try - { - - var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - t.parentRatingKey.ToString()); - - plexGUID = parentMetaData.Directory.Guid; - - var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID))); - - 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.grandparentTitle} - {t.title} {t.originallyAvailableAt?.Substring(0, 4)}"; - - Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); - Header(sb, 3, title); - EndTag(sb, "a"); - - AddParagraph(sb, $"Season: {t.parentIndex}, Episode: {t.index}"); - if (info.genres.Any()) - { - AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); - } - - AddParagraph(sb, string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); - } - catch (Exception e) - { - Log.Error(e); - Log.Error( - "Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", - plexGUID); - } - finally - { - EndLoopHtml(sb); - } - } - sb.Append("


"); - } - - private void GenerateTvHtml(List tv, PlexSettings plexSettings, StringBuilder sb) - { - var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList(); - // TV - sb.Append("

New Episodes:



"); - sb.Append( - ""); - foreach (var t in orderedTv) - { - var plexGUID = string.Empty; - try + if (plexSettings.Enable) { + var html = PlexNewsletter.GetNewsletterHtml(testEmail); - var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - t.parentRatingKey.ToString()); - - plexGUID = parentMetaData.Directory.Guid; - - var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID))); - - 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.grandparentTitle} - {t.title} {t.originallyAvailableAt?.Substring(0, 4)}"; - - Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); - Header(sb, 3, title); - EndTag(sb, "a"); - - AddParagraph(sb, $"Season: {t.parentIndex}, Episode: {t.index}"); - if (info.genres.Any()) - { - AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); - } - - AddParagraph(sb, string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); - } - catch (Exception e) - { - Log.Error(e); - Log.Error( - "Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", - plexGUID); - } - finally - { - EndLoopHtml(sb); + var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray()); + Log.Debug(escapedHtml); + SendNewsletter(newletterSettings, html, testEmail); } } - sb.Append("


"); } - private void SendMassEmail(string html, string subject, bool testEmail) { var settings = EmailSettings.GetSettings(); @@ -507,7 +195,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter SendMail(settings, message); } - // TODO Emby + private void SendNewsletter(NewletterSettings newletterSettings, string html, bool testEmail = false, string subject = "New Content on Plex!") { Log.Debug("Entering SendNewsletter"); @@ -588,17 +276,5 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter Log.Error(e); } } - - 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(""); - } - } } \ No newline at end of file diff --git a/Ombi.Services/Models/PlexMovie.cs b/Ombi.Services/Models/PlexMovie.cs index f0a55e4ce..540055d38 100644 --- a/Ombi.Services/Models/PlexMovie.cs +++ b/Ombi.Services/Models/PlexMovie.cs @@ -7,5 +7,6 @@ public string ReleaseYear { get; set; } public string ProviderId { get; set; } public string Url { get; set; } + public string ItemId { get; set; } } } diff --git a/Ombi.Services/Models/PlexTvShow.cs b/Ombi.Services/Models/PlexTvShow.cs index 60223c233..445296e0f 100644 --- a/Ombi.Services/Models/PlexTvShow.cs +++ b/Ombi.Services/Models/PlexTvShow.cs @@ -8,5 +8,6 @@ public string ProviderId { get; set; } public int[] Seasons { get; set; } public string Url { get; set; } + public string ItemId { get; set; } } } diff --git a/Ombi.Services/Ombi.Services.csproj b/Ombi.Services/Ombi.Services.csproj index 17093e031..923ea6155 100644 --- a/Ombi.Services/Ombi.Services.csproj +++ b/Ombi.Services/Ombi.Services.csproj @@ -108,7 +108,9 @@ + + diff --git a/Ombi.Store/Models/Plex/PlexContent.cs b/Ombi.Store/Models/Plex/PlexContent.cs index 4a24c4e1f..484bb88e0 100644 --- a/Ombi.Store/Models/Plex/PlexContent.cs +++ b/Ombi.Store/Models/Plex/PlexContent.cs @@ -25,6 +25,7 @@ // ************************************************************************/ #endregion +using System; using System.Data.Linq.Mapping; namespace Ombi.Store.Models.Plex @@ -47,5 +48,8 @@ namespace Ombi.Store.Models.Plex /// Only used for Albums /// public string Artist { get; set; } + + public string ItemId { get; set; } + public DateTime AddedAt { get; set; } } } \ No newline at end of file diff --git a/Ombi.Store/Repository/BaseGenericRepository.cs b/Ombi.Store/Repository/BaseGenericRepository.cs index 48469a8d6..a8593eb2a 100644 --- a/Ombi.Store/Repository/BaseGenericRepository.cs +++ b/Ombi.Store/Repository/BaseGenericRepository.cs @@ -286,7 +286,7 @@ namespace Ombi.Store.Repository } } - public bool BatchInsert(IEnumerable entities, string tableName, params string[] values) + public bool BatchInsert(IEnumerable entities, string tableName) { // If we have nothing to update, then it didn't fail... var enumerable = entities as T[] ?? entities.ToArray(); diff --git a/Ombi.Store/Repository/IRepository.cs b/Ombi.Store/Repository/IRepository.cs index 2901e73c9..618b05133 100644 --- a/Ombi.Store/Repository/IRepository.cs +++ b/Ombi.Store/Repository/IRepository.cs @@ -81,7 +81,7 @@ namespace Ombi.Store.Repository bool UpdateAll(IEnumerable entity); Task UpdateAllAsync(IEnumerable entity); - bool BatchInsert(IEnumerable entities, string tableName, params string[] values); + bool BatchInsert(IEnumerable entities, string tableName); IEnumerable Custom(Func> func); Task> CustomAsync(Func>> func); diff --git a/Ombi.Store/SqlTables.sql b/Ombi.Store/SqlTables.sql index cdf5a2f80..50ce19947 100644 --- a/Ombi.Store/SqlTables.sql +++ b/Ombi.Store/SqlTables.sql @@ -174,7 +174,10 @@ CREATE TABLE IF NOT EXISTS PlexContent Url VARCHAR(100) NOT NULL, Artist VARCHAR(100), Seasons BLOB, - Type INTEGER NOT NULL + Type INTEGER NOT NULL, + ItemID VARCHAR(100) NOT NULL, + + AddedAt VARCHAR(100) NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS PlexContent_Id ON PlexContent (Id); diff --git a/Ombi.UI/Modules/Admin/AdminModule.cs b/Ombi.UI/Modules/Admin/AdminModule.cs index a11c473b7..388faf926 100644 --- a/Ombi.UI/Modules/Admin/AdminModule.cs +++ b/Ombi.UI/Modules/Admin/AdminModule.cs @@ -178,7 +178,7 @@ namespace Ombi.UI.Modules.Admin Post["/", true] = async (x, ct) => await SaveAdmin(); - Post["/requestauth"] = _ => RequestAuthToken(); + Post["/requestauth", true] = async (x, ct) => await RequestAuthToken(); Get["/getusers"] = _ => GetUsers(); @@ -319,7 +319,7 @@ namespace Ombi.UI.Modules.Admin : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" }); } - private Response RequestAuthToken() + private async Task RequestAuthToken() { var user = this.Bind(); @@ -335,11 +335,11 @@ namespace Ombi.UI.Modules.Admin return Response.AsJson(new { Result = false, Message = "Incorrect username or password!" }); } - var oldSettings = PlexService.GetSettings(); + var oldSettings = await PlexService.GetSettingsAsync(); if (oldSettings != null) { oldSettings.PlexAuthToken = model.user.authentication_token; - PlexService.SaveSettings(oldSettings); + await PlexService.SaveSettingsAsync(oldSettings); } else { @@ -347,10 +347,14 @@ namespace Ombi.UI.Modules.Admin { PlexAuthToken = model.user.authentication_token }; - PlexService.SaveSettings(newModel); + await PlexService.SaveSettingsAsync(newModel); } - return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token }); + var server = PlexApi.GetServer(model.user.authentication_token); + var machine = + server.Server.FirstOrDefault(x => x.AccessToken == model.user.authentication_token)?.MachineIdentifier; + + return Response.AsJson(new { Result = true, AuthToken = model.user.authentication_token, Identifier = machine }); } diff --git a/Ombi.UI/NinjectModules/ServicesModule.cs b/Ombi.UI/NinjectModules/ServicesModule.cs index 210cdfd3e..7ba02e925 100644 --- a/Ombi.UI/NinjectModules/ServicesModule.cs +++ b/Ombi.UI/NinjectModules/ServicesModule.cs @@ -67,6 +67,7 @@ namespace Ombi.UI.NinjectModules Bind().To(); Bind().To(); Bind().To(); + Bind().To(); Bind().To(); diff --git a/Ombi.UI/Views/Admin/Plex.cshtml b/Ombi.UI/Views/Admin/Plex.cshtml index 50ee63e19..ad8702c33 100644 --- a/Ombi.UI/Views/Admin/Plex.cshtml +++ b/Ombi.UI/Views/Admin/Plex.cshtml @@ -222,6 +222,7 @@ if (response.result === true) { generateNotify("Success!", "success"); $('#authToken').val(response.authToken); + $('#MachineIdentifier').val(response.identifier); } else { generateNotify(response.message, "warning"); }