tidusjar 8 years ago
parent 844938a92c
commit 0da49440e2

@ -27,26 +27,37 @@
#endregion #endregion
using System;
using System.Data; using System.Data;
using NLog; using NLog;
using Ombi.Core.SettingModels; using Ombi.Core.SettingModels;
using Ombi.Store; using Ombi.Store;
using Ombi.Store.Models;
using Ombi.Store.Models.Plex;
using Ombi.Store.Repository;
using Quartz.Collection;
namespace Ombi.Core.Migration.Migrations namespace Ombi.Core.Migration.Migrations
{ {
[Migration(22000, "v2.20.0.0")] [Migration(22000, "v2.20.0.0")]
public class Version2200 : BaseMigration, IMigration public class Version2200 : BaseMigration, IMigration
{ {
public Version2200(ISettingsService<CustomizationSettings> custom, ISettingsService<PlexSettings> ps) public Version2200(ISettingsService<CustomizationSettings> custom, ISettingsService<PlexSettings> ps, IRepository<RecentlyAddedLog> log,
IRepository<PlexContent> content, IRepository<PlexEpisodes> plexEp)
{ {
Customization = custom; Customization = custom;
PlexSettings = ps; PlexSettings = ps;
Log = log;
PlexContent = content;
PlexEpisodes = plexEp;
} }
public int Version => 22000; public int Version => 22000;
private ISettingsService<CustomizationSettings> Customization { get; set; } private ISettingsService<CustomizationSettings> Customization { get; }
private ISettingsService<PlexSettings> PlexSettings { get; set; } private ISettingsService<PlexSettings> PlexSettings { get; }
private IRepository<RecentlyAddedLog> Log { get; }
private IRepository<PlexContent> PlexContent { get; }
private IRepository<PlexEpisodes> PlexEpisodes { get; }
private static Logger Logger = LogManager.GetCurrentClassLogger(); private static Logger Logger = LogManager.GetCurrentClassLogger();
@ -56,12 +67,46 @@ namespace Ombi.Core.Migration.Migrations
UpdateCustomSettings(); UpdateCustomSettings();
AddNewColumns(con); AddNewColumns(con);
UpdateSchema(con, Version); UpdateSchema(con, Version);
UpdateRecentlyAdded(con);
}
private void UpdateRecentlyAdded(IDbConnection con)
{
var allContent = PlexContent.GetAll();
var content = new HashSet<RecentlyAddedLog>();
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) private void AddNewColumns(IDbConnection con)
{ {
con.AlterTable("EmbyContent", "ADD", "AddedAt", true, "VARCHAR(50)"); con.AlterTable("EmbyContent", "ADD", "AddedAt", true, "VARCHAR(50)");
con.AlterTable("EmbyEpisodes", "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() private void UpdatePlexSettings()

@ -111,7 +111,7 @@ namespace Ombi.Services.Jobs
} }
// Insert the new items // Insert the new items
var result = Repo.BatchInsert(model, TableName, typeof(EmbyEpisodes).GetPropertyNames()); var result = Repo.BatchInsert(model, TableName);
if (!result) if (!result)
{ {

@ -115,7 +115,8 @@ namespace Ombi.Services.Jobs
ReleaseYear = video.Year, ReleaseYear = video.Year,
Title = video.Title, Title = video.Title,
ProviderId = video.ProviderId, 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, ProviderId = x.ProviderId,
Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(), Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(),
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey), Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey),
ItemId= x.RatingKey
})); }));
} }
@ -271,7 +273,8 @@ namespace Ombi.Services.Jobs
ReleaseYear = m.ReleaseYear ?? string.Empty, ReleaseYear = m.ReleaseYear ?? string.Empty,
Title = m.Title, Title = m.Title,
Type = Store.Models.Plex.PlexMediaType.Movie, 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, Title = t.Title,
Type = Store.Models.Plex.PlexMediaType.Show, Type = Store.Models.Plex.PlexMediaType.Show,
Url = t.Url, 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, ReleaseYear = a.ReleaseYear ?? string.Empty,
Title = a.Title, Title = a.Title,
Type = Store.Models.Plex.PlexMediaType.Artist, Type = Store.Models.Plex.PlexMediaType.Artist,
Url = a.Url Url = a.Url,
}); });
} }
} }

@ -134,7 +134,7 @@ namespace Ombi.Services.Jobs
Repo.DeleteAll(TableName); Repo.DeleteAll(TableName);
// Insert the new items // 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) if (!result)
{ {

@ -228,7 +228,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
AddParagraph(sb, info.Overview); AddParagraph(sb, info.Overview);
} }
catch (RequestLimitExceededException limit) catch (Exception limit)
{ {
// We have hit a limit, we need to now wait. // We have hit a limit, we need to now wait.
Thread.Sleep(TimeSpan.FromSeconds(10)); Thread.Sleep(TimeSpan.FromSeconds(10));

@ -0,0 +1,7 @@
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
public interface IPlexNewsletter
{
string GetNewsletterHtml(bool test);
}
}

@ -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> plexSettings,
ISettingsService<EmailNotificationSettings> email,
ISettingsService<NewletterSettings> newsletter, IRepository<RecentlyAddedLog> log,
IRepository<PlexContent> embyContent, IRepository<PlexEpisodes> 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> PlexSettings { get; }
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IRepository<PlexContent> Content { get; }
private IRepository<PlexEpisodes> Episodes { get; }
private IRepository<RecentlyAddedLog> 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<PlexRecentlyAddedModel>();
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<EmbyEpisodeInformation>();
// 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<PlexRecentlyAddedModel> recentlyAddedMovies, StringBuilder sb)
{
var movies = recentlyAddedMovies?.ToList() ?? new List<PlexRecentlyAddedModel>();
if (!movies.Any())
{
return;
}
var orderedMovies = movies.OrderByDescending(x => x.Content.AddedAt).ToList();
sb.Append("<h1>New Movies:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
}
private class TvModel
{
public EmbySeriesInformation Series { get; set; }
public List<EmbyEpisodeInformation> Episodes { get; set; }
}
private void GenerateTvHtml(IEnumerable<PlexRecentlyAddedModel> recenetlyAddedTv, StringBuilder sb)
{
var tv = recenetlyAddedTv?.ToList() ?? new List<PlexRecentlyAddedModel>();
if (!tv.Any())
{
return;
}
var orderedTv = tv.OrderByDescending(x => x.Content.AddedAt).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
}
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("<hr />");
sb.Append("<br />");
sb.Append("<br />");
sb.Append("</td>");
sb.Append("</tr>");
}
}
}

@ -53,18 +53,19 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
public RecentlyAddedNewsletter(IPlexApi api, ISettingsService<PlexSettings> plexSettings, public RecentlyAddedNewsletter(IPlexApi api, ISettingsService<PlexSettings> plexSettings,
ISettingsService<EmailNotificationSettings> email, IJobRecord rec, ISettingsService<EmailNotificationSettings> email, IJobRecord rec,
ISettingsService<NewletterSettings> newsletter, ISettingsService<NewletterSettings> newsletter,
IPlexReadOnlyDatabase db, IUserHelper userHelper, IEmbyAddedNewsletter embyNews, IUserHelper userHelper, IEmbyAddedNewsletter embyNews,
ISettingsService<EmbySettings> embyS) ISettingsService<EmbySettings> embyS,
IPlexNewsletter plex)
{ {
JobRecord = rec; JobRecord = rec;
Api = api; Api = api;
PlexSettings = plexSettings; PlexSettings = plexSettings;
EmailSettings = email; EmailSettings = email;
NewsletterSettings = newsletter; NewsletterSettings = newsletter;
PlexDb = db;
UserHelper = userHelper; UserHelper = userHelper;
EmbyNewsletter = embyNews; EmbyNewsletter = embyNews;
EmbySettings = embyS; EmbySettings = embyS;
PlexNewsletter = plex;
} }
private IPlexApi Api { get; } private IPlexApi Api { get; }
@ -75,9 +76,9 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
private ISettingsService<EmailNotificationSettings> EmailSettings { get; } private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
private ISettingsService<NewletterSettings> NewsletterSettings { get; } private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IJobRecord JobRecord { get; } private IJobRecord JobRecord { get; }
private IPlexReadOnlyDatabase PlexDb { get; }
private IUserHelper UserHelper { get; } private IUserHelper UserHelper { get; }
private IEmbyAddedNewsletter EmbyNewsletter { get; } private IEmbyAddedNewsletter EmbyNewsletter { get; }
private IPlexNewsletter PlexNewsletter { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
@ -144,331 +145,18 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
} }
else else
{ {
var sb = new StringBuilder();
var plexSettings = PlexSettings.GetSettings(); var plexSettings = PlexSettings.GetSettings();
Log.Debug("Got Plex Settings"); if (plexSettings.Enable)
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<Metadata>();
var movieMetadata = new List<Metadata>();
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<RecentlyAddedChild>();
var movieChild = new List<RecentlyAddedChild>();
foreach (var tvSection in tvSections)
{ {
var recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key); var html = PlexNewsletter.GetNewsletterHtml(testEmail);
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(); var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml); Log.Debug(escapedHtml);
SendNewsletter(newletterSettings, escapedHtml, testEmail); SendNewsletter(newletterSettings, html, testEmail);
}
}
private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb)
{
var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List<RecentlyAddedChild>();
sb.Append("<h1>New Movies:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
}
private void GenerateMovieHtml(List<Metadata> movies, PlexSettings plexSettings, StringBuilder sb)
{
var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List<Metadata>();
sb.Append("<h1>New Movies:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
} }
private void GenerateTvHtml(List<RecentlyAddedChild> tv, PlexSettings plexSettings, StringBuilder sb)
{
var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
}
private void GenerateTvHtml(List<Metadata> tv, PlexSettings plexSettings, StringBuilder sb)
{
var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br /><br />");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
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("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
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("</table><br /><br />");
}
private void SendMassEmail(string html, string subject, bool testEmail) private void SendMassEmail(string html, string subject, bool testEmail)
{ {
var settings = EmailSettings.GetSettings(); var settings = EmailSettings.GetSettings();
@ -507,7 +195,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
SendMail(settings, message); SendMail(settings, message);
} }
// TODO Emby
private void SendNewsletter(NewletterSettings newletterSettings, string html, bool testEmail = false, string subject = "New Content on Plex!") private void SendNewsletter(NewletterSettings newletterSettings, string html, bool testEmail = false, string subject = "New Content on Plex!")
{ {
Log.Debug("Entering SendNewsletter"); Log.Debug("Entering SendNewsletter");
@ -588,17 +276,5 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
Log.Error(e); 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("<hr />");
sb.Append("<br />");
sb.Append("<br />");
sb.Append("</td>");
sb.Append("</tr>");
}
} }
} }

@ -7,5 +7,6 @@
public string ReleaseYear { get; set; } public string ReleaseYear { get; set; }
public string ProviderId { get; set; } public string ProviderId { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string ItemId { get; set; }
} }
} }

@ -8,5 +8,6 @@
public string ProviderId { get; set; } public string ProviderId { get; set; }
public int[] Seasons { get; set; } public int[] Seasons { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string ItemId { get; set; }
} }
} }

@ -108,7 +108,9 @@
<Compile Include="Jobs\EmbyEpisodeCacher.cs" /> <Compile Include="Jobs\EmbyEpisodeCacher.cs" />
<Compile Include="Jobs\EmbyUserChecker.cs" /> <Compile Include="Jobs\EmbyUserChecker.cs" />
<Compile Include="Jobs\RadarrCacher.cs" /> <Compile Include="Jobs\RadarrCacher.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\PlexRecentlyAddedNewsletter.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\EmbyRecentlyAddedNewsletter.cs" /> <Compile Include="Jobs\RecentlyAddedNewsletter\EmbyRecentlyAddedNewsletter.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\IPlexNewsletter.cs" />
<Compile Include="Jobs\RecentlyAddedNewsletter\IEmbyAddedNewsletter.cs" /> <Compile Include="Jobs\RecentlyAddedNewsletter\IEmbyAddedNewsletter.cs" />
<Compile Include="Jobs\Templates\MassEmailTemplate.cs" /> <Compile Include="Jobs\Templates\MassEmailTemplate.cs" />
<Compile Include="Jobs\WatcherCacher.cs" /> <Compile Include="Jobs\WatcherCacher.cs" />

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using System.Data.Linq.Mapping; using System.Data.Linq.Mapping;
namespace Ombi.Store.Models.Plex namespace Ombi.Store.Models.Plex
@ -47,5 +48,8 @@ namespace Ombi.Store.Models.Plex
/// Only used for Albums /// Only used for Albums
/// </summary> /// </summary>
public string Artist { get; set; } public string Artist { get; set; }
public string ItemId { get; set; }
public DateTime AddedAt { get; set; }
} }
} }

@ -286,7 +286,7 @@ namespace Ombi.Store.Repository
} }
} }
public bool BatchInsert(IEnumerable<T> entities, string tableName, params string[] values) public bool BatchInsert(IEnumerable<T> entities, string tableName)
{ {
// If we have nothing to update, then it didn't fail... // If we have nothing to update, then it didn't fail...
var enumerable = entities as T[] ?? entities.ToArray(); var enumerable = entities as T[] ?? entities.ToArray();

@ -81,7 +81,7 @@ namespace Ombi.Store.Repository
bool UpdateAll(IEnumerable<T> entity); bool UpdateAll(IEnumerable<T> entity);
Task<bool> UpdateAllAsync(IEnumerable<T> entity); Task<bool> UpdateAllAsync(IEnumerable<T> entity);
bool BatchInsert(IEnumerable<T> entities, string tableName, params string[] values); bool BatchInsert(IEnumerable<T> entities, string tableName);
IEnumerable<T> Custom(Func<IDbConnection, IEnumerable<T>> func); IEnumerable<T> Custom(Func<IDbConnection, IEnumerable<T>> func);
Task<IEnumerable<T>> CustomAsync(Func<IDbConnection, Task<IEnumerable<T>>> func); Task<IEnumerable<T>> CustomAsync(Func<IDbConnection, Task<IEnumerable<T>>> func);

@ -174,7 +174,10 @@ CREATE TABLE IF NOT EXISTS PlexContent
Url VARCHAR(100) NOT NULL, Url VARCHAR(100) NOT NULL,
Artist VARCHAR(100), Artist VARCHAR(100),
Seasons BLOB, 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); CREATE UNIQUE INDEX IF NOT EXISTS PlexContent_Id ON PlexContent (Id);

@ -178,7 +178,7 @@ namespace Ombi.UI.Modules.Admin
Post["/", true] = async (x, ct) => await SaveAdmin(); Post["/", true] = async (x, ct) => await SaveAdmin();
Post["/requestauth"] = _ => RequestAuthToken(); Post["/requestauth", true] = async (x, ct) => await RequestAuthToken();
Get["/getusers"] = _ => GetUsers(); 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" }); : new JsonResponseModel { Result = false, Message = "We could not save to the database, please try again" });
} }
private Response RequestAuthToken() private async Task<Response> RequestAuthToken()
{ {
var user = this.Bind<PlexAuth>(); var user = this.Bind<PlexAuth>();
@ -335,11 +335,11 @@ namespace Ombi.UI.Modules.Admin
return Response.AsJson(new { Result = false, Message = "Incorrect username or password!" }); return Response.AsJson(new { Result = false, Message = "Incorrect username or password!" });
} }
var oldSettings = PlexService.GetSettings(); var oldSettings = await PlexService.GetSettingsAsync();
if (oldSettings != null) if (oldSettings != null)
{ {
oldSettings.PlexAuthToken = model.user.authentication_token; oldSettings.PlexAuthToken = model.user.authentication_token;
PlexService.SaveSettings(oldSettings); await PlexService.SaveSettingsAsync(oldSettings);
} }
else else
{ {
@ -347,10 +347,14 @@ namespace Ombi.UI.Modules.Admin
{ {
PlexAuthToken = model.user.authentication_token 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 });
} }

@ -67,6 +67,7 @@ namespace Ombi.UI.NinjectModules
Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>(); Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>();
Bind<IEmbyUserChecker>().To<EmbyUserChecker>(); Bind<IEmbyUserChecker>().To<EmbyUserChecker>();
Bind<IEmbyAddedNewsletter>().To<EmbyAddedNewsletter>(); Bind<IEmbyAddedNewsletter>().To<EmbyAddedNewsletter>();
Bind<IPlexNewsletter>().To<PlexRecentlyAddedNewsletter>();
Bind<IAnalytics>().To<Analytics>(); Bind<IAnalytics>().To<Analytics>();

@ -222,6 +222,7 @@
if (response.result === true) { if (response.result === true) {
generateNotify("Success!", "success"); generateNotify("Success!", "success");
$('#authToken').val(response.authToken); $('#authToken').val(response.authToken);
$('#MachineIdentifier').val(response.identifier);
} else { } else {
generateNotify(response.message, "warning"); generateNotify(response.message, "warning");
} }

Loading…
Cancel
Save