Merge pull request #1061 from tidusjar/dev

Dev
This commit is contained in:
Jamie 2017-02-03 20:46:14 +00:00 committed by GitHub
commit 6eab8c09cc
26 changed files with 737 additions and 168 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

@ -100,9 +100,9 @@ namespace Ombi.Api
var obj = RetryHandler.Execute<CouchPotatoStatus>(() => Api.Execute<CouchPotatoStatus>(request, url), var obj = RetryHandler.Execute<CouchPotatoStatus>(() => Api.Execute<CouchPotatoStatus>(request, url),
(exception, timespan) => Log.Error(exception, "Exception when calling GetStatus for CP, Retrying {0}", timespan), new TimeSpan[] { (exception, timespan) => Log.Error(exception, "Exception when calling GetStatus for CP, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (2), TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(10)}); TimeSpan.FromSeconds(3)});
return obj; return obj;
} }
@ -140,9 +140,9 @@ namespace Ombi.Api
{ {
var obj = RetryHandler.Execute(() => Api.Execute<CouchPotatoMovies>(request, baseUrl), var obj = RetryHandler.Execute(() => Api.Execute<CouchPotatoMovies>(request, baseUrl),
(exception, timespan) => Log.Error(exception, "Exception when calling GetMovies for CP, Retrying {0}", timespan), new[] { (exception, timespan) => Log.Error(exception, "Exception when calling GetMovies for CP, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (5), TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(30) TimeSpan.FromSeconds(5)
}); });
return obj; return obj;

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
{ {
@ -52,8 +53,15 @@ namespace Ombi.Core.Migration.Migrations
public void Start(IDbConnection con) public void Start(IDbConnection con)
{ {
UpdatePlexSettings(); UpdatePlexSettings();
//UpdateCustomSettings(); Turned off the migration for now until the search has been improved on. UpdateCustomSettings();
//UpdateSchema(con, Version); AddNewColumns(con);
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()
@ -68,7 +76,7 @@ namespace Ombi.Core.Migration.Migrations
{ {
var settings = Customization.GetSettings(); var settings = Customization.GetSettings();
settings.NewSearch = true; // Use the new search settings.EnableIssues = true;
Customization.SaveSettings(settings); Customization.SaveSettings(settings);

View file

@ -144,7 +144,7 @@
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr> <tr>
<td align="center"> <td align="center">
<img src="http://i.imgur.com/ROTp8mn.png" text-align="center" /> <img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -54,6 +54,7 @@ namespace Ombi.Core.SettingModels
public int DefaultLang { get; set; } public int DefaultLang { get; set; }
public bool NewSearch { get; set; } public bool NewSearch { get; set; }
public bool EnableIssues { get; set; }
} }
} }

View file

@ -6,6 +6,6 @@ namespace Ombi.Services.Jobs
{ {
void Execute(IJobExecutionContext context); void Execute(IJobExecutionContext context);
void RecentlyAddedAdminTest(); void RecentlyAddedAdminTest();
void Start(); void StartNewsLetter();
} }
} }

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;
@ -108,35 +108,23 @@ namespace Ombi.Services.Jobs
foreach (var m in movies) foreach (var m in movies)
{ {
var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey, if (m.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase))
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
if (string.IsNullOrEmpty(movieInfo.ProviderIds.Imdb))
{ {
Log.Error("Provider Id on movie {0} is null", movieInfo.Name); var info = EmbyApi.GetCollection(m.Id, embySettings.ApiKey,
continue; embySettings.AdministratorId, embySettings.FullUri);
} foreach (var item in info.Items)
// 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, var movieInfo = EmbyApi.GetInformation(item.Id, EmbyMediaType.Movie, embySettings.ApiKey,
PremierDate = movieInfo.PremiereDate, embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
Title = movieInfo.Name, ProcessMovies(movieInfo);
Type = Store.Models.Plex.EmbyMediaType.Movie, }
EmbyId = m.Id }
}); else
{
var movieInfo = EmbyApi.GetInformation(m.Id, EmbyMediaType.Movie, embySettings.ApiKey,
embySettings.AdministratorId, embySettings.FullUri).MovieInformation;
ProcessMovies(movieInfo);
} }
} }
@ -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
}); });
} }
} }
@ -216,7 +205,7 @@ namespace Ombi.Services.Jobs
} }
} }
private bool ValidateSettings(EmbySettings emby) private bool ValidateSettings(EmbySettings emby)
{ {
@ -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);
@ -74,25 +76,40 @@ namespace Ombi.Services.Jobs
{ {
var epInfo = EmbyApi.GetInformation(ep.Id, EmbyMediaType.Episode, settings.ApiKey, var epInfo = EmbyApi.GetInformation(ep.Id, EmbyMediaType.Episode, settings.ApiKey,
settings.AdministratorId, settings.FullUri); settings.AdministratorId, settings.FullUri);
if (epInfo.EpisodeInformation?.ProviderIds?.Tvdb == null) if (epInfo.EpisodeInformation?.ProviderIds?.Tvdb == null)
{
continue;
}
model.Add(new EmbyEpisodes
{ {
EmbyId = ep.Id, continue;
EpisodeNumber = ep.IndexNumber, }
SeasonNumber = ep.ParentIndexNumber,
EpisodeTitle = ep.Name, // Check it this episode exists
ParentId = ep.SeriesId, var item = Repo.Custom(connection =>
ShowTitle = ep.SeriesName, {
ProviderId = epInfo.EpisodeInformation.ProviderIds.Tvdb 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
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumber,
SeasonNumber = ep.ParentIndexNumber,
EpisodeTitle = ep.Name,
ParentId = ep.SeriesId,
ShowTitle = ep.SeriesName,
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,343 @@
#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().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<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 || 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<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,23 +63,25 @@ 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 const int MetadataTypeTv = 4;
private const int MetadataTypeMovie = 1;
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();
public void Start() public void StartNewsLetter()
{ {
try try
{ {
@ -88,7 +91,7 @@ namespace Ombi.Services.Jobs
return; return;
} }
JobRecord.SetRunning(true, JobNames.RecentlyAddedEmail); JobRecord.SetRunning(true, JobNames.RecentlyAddedEmail);
Start(settings); StartNewsLetter(settings);
} }
catch (Exception e) catch (Exception e)
{ {
@ -102,127 +105,135 @@ namespace Ombi.Services.Jobs
} }
public void Execute(IJobExecutionContext context) public void Execute(IJobExecutionContext context)
{ {
Start(); StartNewsLetter();
} }
public void RecentlyAddedAdminTest() public void RecentlyAddedAdminTest()
{ {
Log.Debug("Starting Recently Added Newsletter Test"); Log.Debug("Starting Recently Added Newsletter Test");
var settings = NewsletterSettings.GetSettings(); var settings = NewsletterSettings.GetSettings();
Start(settings, true); StartNewsLetter(settings, true);
} }
public void MassEmailAdminTest(string html, string subject) public void MassEmailAdminTest(string html, string subject)
{ {
Log.Debug("Starting Mass Email Test"); Log.Debug("Starting Mass Email Test");
var settings = NewsletterSettings.GetSettings();
var plexSettings = PlexSettings.GetSettings();
var template = new MassEmailTemplate(); var template = new MassEmailTemplate();
var body = template.LoadTemplate(html); var body = template.LoadTemplate(html);
Send(settings, body, plexSettings, true, subject); SendMassEmail(body, subject, true);
} }
public void SendMassEmail(string html, string subject) public void SendMassEmail(string html, string subject)
{ {
Log.Debug("Starting Mass Email Test"); Log.Debug("Starting Mass Email Test");
var settings = NewsletterSettings.GetSettings();
var plexSettings = PlexSettings.GetSettings();
var template = new MassEmailTemplate(); var template = new MassEmailTemplate();
var body = template.LoadTemplate(html); var body = template.LoadTemplate(html);
Send(settings, body, plexSettings, false, subject); SendMassEmail(body, subject, false);
} }
private void Start(NewletterSettings newletterSettings, bool testEmail = false) private void StartNewsLetter(NewletterSettings newletterSettings, bool testEmail = false)
{ {
var sb = new StringBuilder(); var embySettings = EmbySettings.GetSettings();
var plexSettings = PlexSettings.GetSettings(); if (embySettings.Enable)
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<Metadata>(); var html = EmbyNewsletter.GetNewsletterHtml(testEmail);
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"); var escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
GenerateMovieHtml(movieMetadata, plexSettings, sb); Log.Debug(escapedHtml);
Log.Debug("Finished Generating Movie HTML"); SendNewsletter(newletterSettings, escapedHtml, testEmail);
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 else
{ {
// Old API var sb = new StringBuilder();
var tvChild = new List<RecentlyAddedChild>(); var plexSettings = PlexSettings.GetSettings();
var movieChild = new List<RecentlyAddedChild>(); Log.Debug("Got Plex Settings");
foreach (var tvSection in tvSections)
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 recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key); var tvMetadata = new List<Metadata>();
if (recentlyAddedTv?._children != null) var movieMetadata = new List<Metadata>();
foreach (var tvSection in tvSections)
{ {
tvChild.AddRange(recentlyAddedTv?._children); 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("Got RecentlyAdded TV Shows"); Log.Debug("Started Generating Movie HTML");
foreach (var movie in movieSection) 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
{ {
var recentlyAddedMovies = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key); // Old API
if (recentlyAddedMovies?._children != null) var tvChild = new List<RecentlyAddedChild>();
var movieChild = new List<RecentlyAddedChild>();
foreach (var tvSection in tvSections)
{ {
tvChild.AddRange(recentlyAddedMovies?._children); 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");
} }
Log.Debug("Got RecentlyAdded Movies"); string escapedHtml = new string(html.Where(c => !char.IsControl(c)).ToArray());
Log.Debug(escapedHtml);
Log.Debug("Started Generating Movie HTML"); SendNewsletter(newletterSettings, escapedHtml, testEmail);
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);
Send(newletterSettings, escapedHtml, plexSettings, testEmail);
} }
private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb) private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb)
@ -457,9 +468,49 @@ namespace Ombi.Services.Jobs
sb.Append("</table><br /><br />"); sb.Append("</table><br /><br />");
} }
private void Send(NewletterSettings newletterSettings, string html, PlexSettings plexSettings, bool testEmail = false, string subject = "New Content on Plex!")
private void SendMassEmail(string html, string subject, bool testEmail)
{ {
Log.Debug("Entering Send"); var settings = EmailSettings.GetSettings();
if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost))
{
return;
}
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = subject
};
Log.Debug("Created Plain/HTML MIME body");
if (!testEmail)
{
var users = UserHelper.GetUsers(); // Get all users
if (users != null)
{
foreach (var user in users)
{
if (!string.IsNullOrEmpty(user.EmailAddress))
{
message.Bcc.Add(new MailboxAddress(user.Username, user.EmailAddress)); // BCC everyone
}
}
}
}
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
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");
var settings = EmailSettings.GetSettings(); var settings = EmailSettings.GetSettings();
if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost)) if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost))
@ -506,6 +557,11 @@ namespace Ombi.Services.Jobs
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender)); message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
SendMail(settings, message);
}
private void SendMail(EmailNotificationSettings settings, MimeMessage message)
{
try try
{ {
using (var client = new SmtpClient()) using (var client = new SmtpClient())

View file

@ -144,7 +144,7 @@
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr> <tr>
<td align="center"> <td align="center">
<img src="http://i.imgur.com/ROTp8mn.png" text-align="center" /> <img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -144,7 +144,7 @@
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr> <tr>
<td align="center"> <td align="center">
<img src="http://i.imgur.com/ROTp8mn.png" text-align="center" /> <img src="http://i.imgur.com/qQsN78U.png" width="400px" text-align="center" />
</td> </td>
</tr> </tr>
<tr> <tr>

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

@ -314,6 +314,7 @@ namespace Ombi.UI.Helpers
{ {
url = $"/{content}{url}"; url = $"/{content}{url}";
} }
var returnString = context.Request.Path == url ? var returnString = context.Request.Path == url ?
$"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>" $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>"
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>"; : $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>";
@ -328,7 +329,14 @@ namespace Ombi.UI.Helpers
{ {
url = $"/{content}{url}"; url = $"/{content}{url}";
} }
if (url.Contains("issues"))
{
var custom = GetCustomizationSettings();
if (!custom.EnableIssues)
{
return helper.Raw(string.Empty);
}
}
var returnString = context.Request.Path == url var returnString = context.Request.Path == url
? $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>" ? $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>"
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>"; : $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>";

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(),
@ -304,8 +305,8 @@ namespace Ombi.UI.Jobs
var embyEpisode = var embyEpisode =
TriggerBuilder.Create() TriggerBuilder.Create()
.WithIdentity("EmbyEpisodeCacher", "Emby") .WithIdentity("EmbyEpisodeCacher", "Emby")
.StartNow() //.StartNow()
//.StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute)) .StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute))
.WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyEpisodeCacher).RepeatForever()) .WithSimpleSchedule(x => x.WithIntervalInHours(s.EmbyEpisodeCacher).RepeatForever())
.Build(); .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
@ -1141,6 +1146,8 @@ namespace Ombi.UI.Modules.Admin
var emby = await EmbySettings.GetSettingsAsync(); var emby = await EmbySettings.GetSettingsAsync();
var plex = await PlexService.GetSettingsAsync(); var plex = await PlexService.GetSettingsAsync();
var dict = new Dictionary<string, DateTime>(); var dict = new Dictionary<string, DateTime>();
@ -1153,7 +1160,24 @@ namespace Ombi.UI.Modules.Admin
} }
else else
{ {
dict.Add(j.Name,j.LastRun); if (j.Name.Contains("Plex"))
{
if (plex.Enable)
{
dict.Add(j.Name, j.LastRun);
}
}
else if (j.Name.Contains("Emby"))
{
if (emby.Enable)
{
dict.Add(j.Name, j.LastRun);
}
}
else
{
dict.Add(j.Name, j.LastRun);
}
} }
} }

View file

@ -142,7 +142,7 @@ namespace Ombi.UI.Modules.Admin
} }
if (key.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase)) if (key.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase))
{ {
RecentlyAdded.Start(); RecentlyAdded.StartNewsLetter();
} }
if (key.Equals(JobNames.FaultQueueHandler, StringComparison.CurrentCultureIgnoreCase)) if (key.Equals(JobNames.FaultQueueHandler, StringComparison.CurrentCultureIgnoreCase))
{ {

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>();

View file

@ -104,7 +104,8 @@
</div> </div>
</div> </div>
@Html.Checkbox(Model.Settings.NewSearch, "NewSearch", "Use New Search") @*@Html.Checkbox(Model.Settings.NewSearch, "NewSearch", "Use New Search")*@
@Html.Checkbox(Model.Settings.EnableIssues, "EnableIssues", "Enable Issues")
<div class="form-group"> <div class="form-group">
<div> <div>
<button type="submit" id="save" class="btn btn-primary-outline">Submit</button> <button type="submit" id="save" class="btn btn-primary-outline">Submit</button>