Reworked the newsletter for Emby! Need to rework it for Plex and use the new way to do it.

Fixed collections for Emby #435
This commit is contained in:
tidusjar 2017-02-03 20:26:28 +00:00
parent 86144f59bb
commit 8393a31a48
17 changed files with 631 additions and 134 deletions

View file

@ -9,6 +9,7 @@ namespace Ombi.Api.Interfaces
EmbyItemContainer<EmbyMovieItem> GetAllMovies(string apiKey, string userId, Uri baseUri); EmbyItemContainer<EmbyMovieItem> GetAllMovies(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbySeriesItem> GetAllShows(string apiKey, string userId, Uri baseUri); EmbyItemContainer<EmbySeriesItem> GetAllShows(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbyEpisodeItem> GetAllEpisodes(string apiKey, string userId, Uri baseUri); EmbyItemContainer<EmbyEpisodeItem> GetAllEpisodes(string apiKey, string userId, Uri baseUri);
EmbyItemContainer<EmbyMovieInformation> GetCollection(string mediaId, string apiKey, string userId, Uri baseUrl);
List<EmbyUser> GetUsers(Uri baseUri, string apiKey); List<EmbyUser> GetUsers(Uri baseUri, string apiKey);
EmbyItemContainer<EmbyLibrary> ViewLibrary(string apiKey, string userId, Uri baseUri); EmbyItemContainer<EmbyLibrary> ViewLibrary(string apiKey, string userId, Uri baseUri);
EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri); EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri);

View file

@ -103,6 +103,27 @@ namespace Ombi.Api
return GetAll<EmbyEpisodeItem>("Episode", apiKey, userId, baseUri); return GetAll<EmbyEpisodeItem>("Episode", apiKey, userId, baseUri);
} }
public EmbyItemContainer<EmbyMovieInformation> GetCollection(string mediaId, string apiKey, string userId, Uri baseUrl)
{
var request = new RestRequest
{
Resource = "emby/users/{userId}/items?parentId={mediaId}",
Method = Method.GET
};
request.AddUrlSegment("userId", userId);
request.AddUrlSegment("mediaId", mediaId);
AddHeaders(request, apiKey);
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetCollections for Emby, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(5)
});
return policy.Execute(() => Api.ExecuteJson<EmbyItemContainer<EmbyMovieInformation>>(request, baseUrl));
}
public EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri) public EmbyInformation GetInformation(string mediaId, EmbyMediaType type, string apiKey, string userId, Uri baseUri)
{ {
var request = new RestRequest var request = new RestRequest

View file

@ -30,6 +30,7 @@
using System.Data; using System.Data;
using NLog; using NLog;
using Ombi.Core.SettingModels; using Ombi.Core.SettingModels;
using Ombi.Store;
namespace Ombi.Core.Migration.Migrations namespace Ombi.Core.Migration.Migrations
{ {
@ -53,9 +54,16 @@ namespace Ombi.Core.Migration.Migrations
{ {
UpdatePlexSettings(); UpdatePlexSettings();
UpdateCustomSettings(); UpdateCustomSettings();
AddNewColumns(con);
UpdateSchema(con, Version); UpdateSchema(con, Version);
} }
private void AddNewColumns(IDbConnection con)
{
con.AlterTable("EmbyContent", "ADD", "AddedAt", true, "VARCHAR(50)");
con.AlterTable("EmbyEpisodes", "ADD", "AddedAt", true, "VARCHAR(50)");
}
private void UpdatePlexSettings() private void UpdatePlexSettings()
{ {
#if !DEBUG #if !DEBUG

View file

@ -46,7 +46,7 @@ namespace Ombi.Services.Jobs
public class EmbyContentCacher : IJob, IEmbyContentCacher public class EmbyContentCacher : IJob, IEmbyContentCacher
{ {
public EmbyContentCacher(ISettingsService<EmbySettings> embySettings, IRequestService request, IEmbyApi emby, ICacheProvider cache, public EmbyContentCacher(ISettingsService<EmbySettings> embySettings, IRequestService request, IEmbyApi emby, ICacheProvider cache,
IJobRecord rec, IRepository<EmbyEpisodes> repo,IRepository<EmbyContent> content) IJobRecord rec, IRepository<EmbyEpisodes> repo, IRepository<EmbyContent> content)
{ {
Emby = embySettings; Emby = embySettings;
RequestService = request; RequestService = request;
@ -107,36 +107,24 @@ namespace Ombi.Services.Jobs
var movies = GetMovies(); var movies = GetMovies();
foreach (var m in movies) foreach (var m in movies)
{
if (m.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase))
{
var info = EmbyApi.GetCollection(m.Id, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri);
foreach (var item in info.Items)
{
var movieInfo = EmbyApi.GetInformation(item.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
ProcessMovies(movieInfo);
}
}
else
{ {
var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey, var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation; embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb)) ProcessMovies(movieInfo);
{
Log.Error("Provider Id on movie {0} is null", movieInfo.Name);
continue;
}
// Check if it exists
var item = EmbyContent.Custom(connection =>
{
connection.Open();
var media = connection.QueryFirstOrDefault<EmbyContent>("select * from EmbyContent where ProviderId = @ProviderId and type = @type", new { ProviderId = movieInfo.ProviderIds.Imdb, type = 0 });
connection.Dispose();
return media;
});
if (item == null)
{
// Doesn't exist, insert it
EmbyContent.Insert(new EmbyContent
{
ProviderId = movieInfo.ProviderIds.Imdb,
PremierDate = movieInfo.PremiereDate,
Title = movieInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Movie,
EmbyId = m.Id
});
} }
} }
@ -170,7 +158,8 @@ namespace Ombi.Services.Jobs
PremierDate = tvInfo.PremiereDate, PremierDate = tvInfo.PremiereDate,
Title = tvInfo.Name, Title = tvInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Series, Type = Store.Models.Plex.EmbyMediaType.Series,
EmbyId = t.Id EmbyId = t.Id,
AddedAt = DateTime.UtcNow
}); });
} }
} }
@ -249,5 +238,36 @@ namespace Ombi.Services.Jobs
Job.SetRunning(false, JobNames.EmbyCacher); Job.SetRunning(false, JobNames.EmbyCacher);
} }
} }
private void ProcessMovies(EmbyMovieInformation movieInfo)
{
if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb))
{
Log.Error("Provider Id on movie {0} is null", movieInfo.Name);
return;
}
// Check if it exists
var item = EmbyContent.Custom(connection =>
{
connection.Open();
var media = connection.QueryFirstOrDefault<EmbyContent>("select * from EmbyContent where ProviderId = @ProviderId and type = @type", new { ProviderId = movieInfo.ProviderIds.Imdb, type = 0 });
connection.Dispose();
return media;
});
if (item == null)
{
// Doesn't exist, insert it
EmbyContent.Insert(new EmbyContent
{
ProviderId = movieInfo.ProviderIds.Imdb,
PremierDate = movieInfo.PremiereDate,
Title = movieInfo.Name,
Type = Store.Models.Plex.EmbyMediaType.Movie,
EmbyId = movieInfo.Id,
AddedAt = DateTime.UtcNow
});
}
}
} }
} }

View file

@ -28,6 +28,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Dapper;
using NLog; using NLog;
using Ombi.Api.Interfaces; using Ombi.Api.Interfaces;
using Ombi.Api.Models.Emby; using Ombi.Api.Models.Emby;
@ -65,7 +66,8 @@ namespace Ombi.Services.Jobs
private const string TableName = "EmbyEpisodes"; private const string TableName = "EmbyEpisodes";
// Note, once an episode exists, we store it and it always exists.
// We might want to look at checking if something has been removed from the server in the future.
public void CacheEpisodes(EmbySettings settings) public void CacheEpisodes(EmbySettings settings)
{ {
var allEpisodes = EmbyApi.GetAllEpisodes(settings.ApiKey, settings.AdministratorId, settings.FullUri); var allEpisodes = EmbyApi.GetAllEpisodes(settings.ApiKey, settings.AdministratorId, settings.FullUri);
@ -78,6 +80,22 @@ namespace Ombi.Services.Jobs
{ {
continue; continue;
} }
// Check it this episode exists
var item = Repo.Custom(connection =>
{
connection.Open();
var media =
connection.QueryFirstOrDefault<EmbyEpisodes>(
"select * from EmbyEpisodes where ProviderId = @ProviderId",
new {ProviderId = epInfo.EpisodeInformation?.ProviderIds?.Tvdb});
connection.Dispose();
return media;
});
if (item == null)
{
// add it
model.Add(new EmbyEpisodes model.Add(new EmbyEpisodes
{ {
EmbyId = ep.Id, EmbyId = ep.Id,
@ -86,12 +104,11 @@ namespace Ombi.Services.Jobs
EpisodeTitle = ep.Name, EpisodeTitle = ep.Name,
ParentId = ep.SeriesId, ParentId = ep.SeriesId,
ShowTitle = ep.SeriesName, ShowTitle = ep.SeriesName,
ProviderId = epInfo.EpisodeInformation.ProviderIds.Tvdb ProviderId = epInfo.EpisodeInformation.ProviderIds.Tvdb,
AddedAt = DateTime.UtcNow
}); });
} }
}
// Delete all of the current items
Repo.DeleteAll(TableName);
// Insert the new items // Insert the new items
var result = Repo.BatchInsert(model, TableName, typeof(EmbyEpisodes).GetPropertyNames()); var result = Repo.BatchInsert(model, TableName, typeof(EmbyEpisodes).GetPropertyNames());

View file

@ -0,0 +1,341 @@
#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 NLog;
using Ombi.Api;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.Emby;
using Ombi.Core;
using Ombi.Core.SettingModels;
using Ombi.Services.Jobs.Templates;
using Ombi.Store.Models;
using Ombi.Store.Models.Emby;
using Ombi.Store.Repository;
using EmbyMediaType = Ombi.Store.Models.Plex.EmbyMediaType;
namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{
public class EmbyAddedNewsletter : HtmlTemplateGenerator, IEmbyAddedNewsletter
{
public EmbyAddedNewsletter(IEmbyApi api, ISettingsService<EmbySettings> embySettings,
ISettingsService<EmailNotificationSettings> email,
ISettingsService<NewletterSettings> newsletter, IRepository<RecentlyAddedLog> log,
IRepository<EmbyContent> embyContent, IRepository<EmbyEpisodes> episodes)
{
Api = api;
EmbySettings = embySettings;
EmailSettings = email;
NewsletterSettings = newsletter;
Content = embyContent;
MovieApi = new TheMovieDbApi();
TvApi = new TvMazeApi();
Episodes = episodes;
RecentlyAddedLog = log;
}
private IEmbyApi Api { get; }
private TheMovieDbApi MovieApi { get; }
private TvMazeApi TvApi { get; }
private ISettingsService<EmbySettings> EmbySettings { get; }
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IRepository<EmbyContent> Content { get; }
private IRepository<EmbyEpisodes> 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 EmbyRecentlyAddedModel
{
public EmbyInformation EmbyInformation { get; set; }
public EmbyContent EmbyContent { get; set; }
public List<EmbyEpisodeInformation> EpisodeInformation { get; set; }
}
private string GetHtml(bool test)
{
var sb = new StringBuilder();
var embySettings = EmbySettings.GetSettings();
var embyContent = Content.GetAll().ToList();
var series = embyContent.Where(x => x.Type == EmbyMediaType.Series).ToList();
var episodes = Episodes.GetAll().ToList();
var movie = embyContent.Where(x => x.Type == EmbyMediaType.Movie).ToList();
var recentlyAdded = RecentlyAddedLog.GetAll();
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<EmbyRecentlyAddedModel>();
foreach (var m in filteredMovies)
{
var i = Api.GetInformation(m.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Movie,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
info.Add(new EmbyRecentlyAddedModel
{
EmbyInformation = i,
EmbyContent = m
});
}
GenerateMovieHtml(info, sb);
info.Clear();
foreach (var t in series)
{
var i = Api.GetInformation(t.EmbyId, Ombi.Api.Models.Emby.EmbyMediaType.Series,
embySettings.ApiKey, embySettings.AdministratorId, embySettings.FullUri);
var ep = filteredEp.Where(x => x.ParentId == t.EmbyId);
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)
{
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<EmbyRecentlyAddedModel> movies, StringBuilder sb)
{
if (!movies.Any())
{
return;
}
var orderedMovies = movies.OrderByDescending(x => x.EmbyContent.AddedAt).Select(x => x.EmbyInformation.MovieInformation).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)
{
try
{
var imdbId = movie.ProviderIds.Imdb;
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("Error for movie with IMDB Id = {0}", movie.ProviderIds.Imdb);
}
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(List<EmbyRecentlyAddedModel> tv, StringBuilder sb)
{
if (!tv.Any())
{
return;
}
var orderedTv = tv.OrderByDescending(x => x.EmbyContent.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(seriesItem.ProviderIds.Tvdb));
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 = $"{seriesItem.Name} {seriesItem.PremiereDate.Year}";
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(seriesItem.Overview) ? info.summary : seriesItem.Overview);
}
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>");
}
}
}

View file

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

View file

@ -46,14 +46,15 @@ using Ombi.Services.Interfaces;
using Ombi.Services.Jobs.Templates; using Ombi.Services.Jobs.Templates;
using Quartz; using Quartz;
namespace Ombi.Services.Jobs namespace Ombi.Services.Jobs.RecentlyAddedNewsletter
{ {
public class RecentlyAdded : HtmlTemplateGenerator, IJob, IRecentlyAdded, IMassEmail public class RecentlyAddedNewsletter : HtmlTemplateGenerator, IJob, IRecentlyAdded, IMassEmail
{ {
public RecentlyAdded(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) IPlexReadOnlyDatabase db, IUserHelper userHelper, IEmbyAddedNewsletter embyNews,
ISettingsService<EmbySettings> embyS)
{ {
JobRecord = rec; JobRecord = rec;
Api = api; Api = api;
@ -62,17 +63,21 @@ namespace Ombi.Services.Jobs
NewsletterSettings = newsletter; NewsletterSettings = newsletter;
PlexDb = db; PlexDb = db;
UserHelper = userHelper; UserHelper = userHelper;
EmbyNewsletter = embyNews;
EmbySettings = embyS;
} }
private IPlexApi Api { get; } private IPlexApi Api { get; }
private TvMazeApi TvApi = new TvMazeApi(); private TvMazeApi TvApi = new TvMazeApi();
private readonly TheMovieDbApi _movieApi = new TheMovieDbApi(); private readonly TheMovieDbApi _movieApi = new TheMovieDbApi();
private ISettingsService<PlexSettings> PlexSettings { get; } private ISettingsService<PlexSettings> PlexSettings { get; }
private ISettingsService<EmbySettings> EmbySettings { get; }
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 IPlexReadOnlyDatabase PlexDb { get; }
private IUserHelper UserHelper { get; } private IUserHelper UserHelper { get; }
private IEmbyAddedNewsletter EmbyNewsletter { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
@ -127,6 +132,17 @@ namespace Ombi.Services.Jobs
} }
private void StartNewsLetter(NewletterSettings newletterSettings, bool testEmail = false) private void StartNewsLetter(NewletterSettings newletterSettings, bool testEmail = false)
{
var embySettings = EmbySettings.GetSettings();
if (embySettings.Enable)
{
var html = EmbyNewsletter.GetNewsletterHtml(testEmail);
var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
SendNewsletter(newletterSettings, escapedHtml, testEmail);
}
else
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
var plexSettings = PlexSettings.GetSettings(); var plexSettings = PlexSettings.GetSettings();
@ -214,12 +230,11 @@ namespace Ombi.Services.Jobs
html = template.LoadTemplate(sb.ToString()); html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template"); Log.Debug("Loaded the template");
} }
string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray()); string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml); Log.Debug(escapedHtml);
SendNewsletter(newletterSettings, escapedHtml, testEmail); SendNewsletter(newletterSettings, escapedHtml, testEmail);
} }
}
private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb) private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb)
{ {

View file

@ -108,6 +108,8 @@
<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\EmbyRecentlyAddedNewsletter.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" />
<Compile Include="Jobs\HtmlTemplateGenerator.cs" /> <Compile Include="Jobs\HtmlTemplateGenerator.cs" />
@ -117,7 +119,7 @@
<Compile Include="Jobs\JobNames.cs" /> <Compile Include="Jobs\JobNames.cs" />
<Compile Include="Jobs\PlexContentCacher.cs" /> <Compile Include="Jobs\PlexContentCacher.cs" />
<Compile Include="Jobs\PlexEpisodeCacher.cs" /> <Compile Include="Jobs\PlexEpisodeCacher.cs" />
<Compile Include="Jobs\RecentlyAdded.cs" /> <Compile Include="Jobs\RecentlyAddedNewsletter\RecentlyAddedNewsletter.cs" />
<Compile Include="Jobs\StoreBackup.cs" /> <Compile Include="Jobs\StoreBackup.cs" />
<Compile Include="Jobs\PlexUserChecker.cs" /> <Compile Include="Jobs\PlexUserChecker.cs" />
<Compile Include="Jobs\StoreCleanup.cs" /> <Compile Include="Jobs\StoreCleanup.cs" />

View file

@ -39,5 +39,6 @@ namespace Ombi.Store.Models.Emby
public DateTime PremierDate { get; set; } public DateTime PremierDate { get; set; }
public string ProviderId { get; set; } public string ProviderId { get; set; }
public EmbyMediaType Type { get; set; } public EmbyMediaType Type { get; set; }
public DateTime AddedAt { get; set; }
} }
} }

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
namespace Ombi.Store.Models.Emby namespace Ombi.Store.Models.Emby
@ -39,5 +40,6 @@ namespace Ombi.Store.Models.Emby
public int SeasonNumber { get; set; } public int SeasonNumber { get; set; }
public string ParentId { get; set; } public string ParentId { get; set; }
public string ProviderId { get; set; } public string ProviderId { get; set; }
public DateTime AddedAt { get; set; }
} }
} }

View file

@ -0,0 +1,40 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: LogEntity.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 Dapper.Contrib.Extensions;
using Newtonsoft.Json;
namespace Ombi.Store.Models
{
[Table("RecentlyAddedLog")]
public class RecentlyAddedLog : Entity
{
public string ProviderId { get; set; }
public DateTime AddedAt { get; set; }
}
}

View file

@ -68,6 +68,7 @@
<Compile Include="Models\Emby\EmbyContent.cs" /> <Compile Include="Models\Emby\EmbyContent.cs" />
<Compile Include="Models\Emby\EmbyEpisodes.cs" /> <Compile Include="Models\Emby\EmbyEpisodes.cs" />
<Compile Include="Models\IssueBlobs.cs" /> <Compile Include="Models\IssueBlobs.cs" />
<Compile Include="Models\RecenetlyAddedLog.cs" />
<Compile Include="Models\Plex\PlexEpisodes.cs" /> <Compile Include="Models\Plex\PlexEpisodes.cs" />
<Compile Include="Models\Emby\EmbyUsers.cs" /> <Compile Include="Models\Emby\EmbyUsers.cs" />
<Compile Include="Models\Plex\PlexUsers.cs" /> <Compile Include="Models\Plex\PlexUsers.cs" />

View file

@ -187,7 +187,8 @@ CREATE TABLE IF NOT EXISTS EmbyEpisodes
SeasonNumber INTEGER NOT NULL, SeasonNumber INTEGER NOT NULL,
EpisodeNumber INTEGER NOT NULL, EpisodeNumber INTEGER NOT NULL,
ParentId VARCHAR(100) NOT NULL, ParentId VARCHAR(100) NOT NULL,
ProviderId VARCHAR(100) NOT NULL ProviderId VARCHAR(100) NOT NULL,
AddedAt VARCHAR(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id); CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id);
@ -198,9 +199,21 @@ CREATE TABLE IF NOT EXISTS EmbyContent
PremierDate VARCHAR(100) NOT NULL, PremierDate VARCHAR(100) NOT NULL,
EmbyId VARCHAR(100) NOT NULL, EmbyId VARCHAR(100) NOT NULL,
ProviderId VARCHAR(100) NOT NULL, ProviderId VARCHAR(100) NOT NULL,
Type INTEGER NOT NULL Type INTEGER NOT NULL,
AddedAt VARCHAR(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id); CREATE UNIQUE INDEX IF NOT EXISTS EmbyEpisodes_Id ON EmbyEpisodes (Id);
CREATE TABLE IF NOT EXISTS RecentlyAddedLog
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
ProviderId VARCHAR(100) NOT NULL,
AddedAt VARCHAR(100) NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS RecentlyAddedLog_Id ON RecentlyAddedLog (Id);
CREATE INDEX IF NOT EXISTS RecentlyAddedLog_ProviderId ON RecentlyAddedLog (ProviderId);
COMMIT; COMMIT;

View file

@ -35,6 +35,7 @@ using Ombi.Core;
using Ombi.Core.SettingModels; using Ombi.Core.SettingModels;
using Ombi.Services.Interfaces; using Ombi.Services.Interfaces;
using Ombi.Services.Jobs; using Ombi.Services.Jobs;
using Ombi.Services.Jobs.RecentlyAddedNewsletter;
using Ombi.UI.Helpers; using Ombi.UI.Helpers;
using Quartz; using Quartz;
using Quartz.Impl; using Quartz.Impl;
@ -70,7 +71,7 @@ namespace Ombi.UI.Jobs
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(), JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(), JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(), JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build(), JobBuilder.Create<RecentlyAddedNewsletter>().WithIdentity("RecentlyAddedModel", "Email").Build(),
JobBuilder.Create<FaultQueueHandler>().WithIdentity("FaultQueueHandler", "Fault").Build(), JobBuilder.Create<FaultQueueHandler>().WithIdentity("FaultQueueHandler", "Fault").Build(),
JobBuilder.Create<RadarrCacher>().WithIdentity("RadarrCacher", "Cache").Build(), JobBuilder.Create<RadarrCacher>().WithIdentity("RadarrCacher", "Cache").Build(),

View file

@ -42,6 +42,7 @@ using Nancy.Validation;
using NLog; using NLog;
using Ombi.Api; using Ombi.Api;
using Ombi.Api.Interfaces; using Ombi.Api.Interfaces;
using Ombi.Api.Models.Movie;
using Ombi.Core; using Ombi.Core;
using Ombi.Core.Models; using Ombi.Core.Models;
using Ombi.Core.SettingModels; using Ombi.Core.SettingModels;
@ -823,6 +824,10 @@ namespace Ombi.UI.Modules.Admin
{ {
return Response.AsJson(valid.SendJsonError()); return Response.AsJson(valid.SendJsonError());
} }
if (!settings.Enabled)
{
return Response.AsJson(new CouchPotatoProfiles{list = new List<ProfileList>()});
}
var profiles = CpApi.GetProfiles(settings.FullUri, settings.ApiKey); var profiles = CpApi.GetProfiles(settings.FullUri, settings.ApiKey);
// set the cache // set the cache

View file

@ -32,6 +32,7 @@ using Ombi.Helpers.Analytics;
using Ombi.Services.Interfaces; using Ombi.Services.Interfaces;
using Ombi.Services.Jobs; using Ombi.Services.Jobs;
using Ombi.Services.Jobs.Interfaces; using Ombi.Services.Jobs.Interfaces;
using Ombi.Services.Jobs.RecentlyAddedNewsletter;
using Ombi.UI.Jobs; using Ombi.UI.Jobs;
using Quartz; using Quartz;
using Quartz.Impl; using Quartz.Impl;
@ -48,8 +49,8 @@ namespace Ombi.UI.NinjectModules
Bind<IWatcherCacher>().To<WatcherCacher>(); Bind<IWatcherCacher>().To<WatcherCacher>();
Bind<ISonarrCacher>().To<SonarrCacher>(); Bind<ISonarrCacher>().To<SonarrCacher>();
Bind<ISickRageCacher>().To<SickRageCacher>(); Bind<ISickRageCacher>().To<SickRageCacher>();
Bind<IRecentlyAdded>().To<RecentlyAdded>(); Bind<IRecentlyAdded>().To<RecentlyAddedNewsletter>();
Bind<IMassEmail>().To<RecentlyAdded>(); Bind<IMassEmail>().To<RecentlyAddedNewsletter>();
Bind<IRadarrCacher>().To<RadarrCacher>(); Bind<IRadarrCacher>().To<RadarrCacher>();
Bind<IPlexContentCacher>().To<PlexContentCacher>(); Bind<IPlexContentCacher>().To<PlexContentCacher>();
Bind<IJobFactory>().To<CustomJobFactory>(); Bind<IJobFactory>().To<CustomJobFactory>();
@ -65,6 +66,7 @@ namespace Ombi.UI.NinjectModules
Bind<IEmbyContentCacher>().To<EmbyContentCacher>(); Bind<IEmbyContentCacher>().To<EmbyContentCacher>();
Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>(); Bind<IEmbyEpisodeCacher>().To<EmbyEpisodeCacher>();
Bind<IEmbyUserChecker>().To<EmbyUserChecker>(); Bind<IEmbyUserChecker>().To<EmbyUserChecker>();
Bind<IEmbyAddedNewsletter>().To<EmbyAddedNewsletter>();
Bind<IAnalytics>().To<Analytics>(); Bind<IAnalytics>().To<Analytics>();