mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-14 01:02:57 -07:00
Pretty much finished the actual newsletter. Still need to work on the UI !wip
This commit is contained in:
parent
9767380f83
commit
1528cdfc03
15 changed files with 463 additions and 24 deletions
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Models;
|
using Ombi.Core.Models;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
|
@ -10,5 +11,6 @@ namespace Ombi.Core.Engine
|
||||||
IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to);
|
IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to);
|
||||||
IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason);
|
IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason);
|
||||||
IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(bool groupBySeason);
|
IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(bool groupBySeason);
|
||||||
|
Task<bool> UpdateRecentlyAddedDatabase();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,27 +1,28 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
||||||
using Ombi.Core.Models;
|
using Ombi.Core.Models;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
using RecentlyAddedType = Ombi.Store.Entities.RecentlyAddedType;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
||||||
{
|
{
|
||||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby)
|
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||||
{
|
{
|
||||||
_plex = plex;
|
_plex = plex;
|
||||||
_emby = emby;
|
_emby = emby;
|
||||||
|
_recentlyAddedLog = recentlyAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexContentRepository _plex;
|
private readonly IPlexContentRepository _plex;
|
||||||
private readonly IEmbyContentRepository _emby;
|
private readonly IEmbyContentRepository _emby;
|
||||||
|
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||||
|
|
||||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
|
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(DateTime from, DateTime to)
|
||||||
{
|
{
|
||||||
|
@ -55,6 +56,67 @@ namespace Ombi.Core.Engine
|
||||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason);
|
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateRecentlyAddedDatabase()
|
||||||
|
{
|
||||||
|
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||||
|
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
||||||
|
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||||
|
foreach (var p in plexContent)
|
||||||
|
{
|
||||||
|
if (p.Type == PlexMediaTypeEntity.Movie)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = p.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the episodes
|
||||||
|
foreach (var ep in p.Episodes)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = ep.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var e in embyContent)
|
||||||
|
{
|
||||||
|
if (e.Type == EmbyMediaType.Movie)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Emby,
|
||||||
|
ContentId = e.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the episodes
|
||||||
|
foreach (var ep in e.Episodes)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = ep.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv,
|
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv,
|
||||||
bool groupBySeason)
|
bool groupBySeason)
|
||||||
{
|
{
|
||||||
|
|
23
src/Ombi.Core/Models/UI/NewsletterNotificationViewModel.cs
Normal file
23
src/Ombi.Core/Models/UI/NewsletterNotificationViewModel.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Models.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The view model for the notification settings page
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="NewsletterNotificationViewModel" />
|
||||||
|
public class NewsletterNotificationViewModel : NewsletterSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the notification templates.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The notification templates.
|
||||||
|
/// </value>
|
||||||
|
public NotificationTemplates NotificationTemplate { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,5 +9,6 @@
|
||||||
public const string RequestTv = nameof(RequestTv);
|
public const string RequestTv = nameof(RequestTv);
|
||||||
public const string RequestMovie = nameof(RequestMovie);
|
public const string RequestMovie = nameof(RequestMovie);
|
||||||
public const string Disabled = nameof(Disabled);
|
public const string Disabled = nameof(Disabled);
|
||||||
|
public const string RecievesNewsletter = nameof(RecievesNewsletter);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -38,6 +38,13 @@ namespace Ombi.Notifications
|
||||||
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetupNewsletter(CustomizationSettings s, string username)
|
||||||
|
{
|
||||||
|
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
||||||
|
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
||||||
|
RequestedUser = username;
|
||||||
|
}
|
||||||
|
|
||||||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s)
|
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Ombi.Api.TheMovieDb;
|
using Ombi.Api.TheMovieDb;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
@ -22,7 +24,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> addedLog,
|
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> addedLog,
|
||||||
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
||||||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo)
|
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||||
|
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter)
|
||||||
{
|
{
|
||||||
_plex = plex;
|
_plex = plex;
|
||||||
_emby = emby;
|
_emby = emby;
|
||||||
|
@ -33,6 +36,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
_customizationSettings = custom;
|
_customizationSettings = custom;
|
||||||
_templateRepo = templateRepo;
|
_templateRepo = templateRepo;
|
||||||
_emailSettings = emailSettings;
|
_emailSettings = emailSettings;
|
||||||
|
_newsletterSettings = newsletter;
|
||||||
|
_userManager = um;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IPlexContentRepository _plex;
|
private readonly IPlexContentRepository _plex;
|
||||||
|
@ -44,9 +49,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
private readonly ISettingsService<CustomizationSettings> _customizationSettings;
|
||||||
private readonly INotificationTemplatesRepository _templateRepo;
|
private readonly INotificationTemplatesRepository _templateRepo;
|
||||||
private readonly ISettingsService<EmailNotificationSettings> _emailSettings;
|
private readonly ISettingsService<EmailNotificationSettings> _emailSettings;
|
||||||
|
private readonly ISettingsService<NewsletterSettings> _newsletterSettings;
|
||||||
|
private readonly UserManager<OmbiUser> _userManager;
|
||||||
|
|
||||||
public async Task Start()
|
public async Task Start()
|
||||||
{
|
{
|
||||||
|
var newsletterSettings = await _newsletterSettings.GetSettingsAsync();
|
||||||
|
if (!newsletterSettings.Enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var template = await _templateRepo.GetTemplate(NotificationAgent.Email, NotificationType.Newsletter);
|
var template = await _templateRepo.GetTemplate(NotificationAgent.Email, NotificationType.Newsletter);
|
||||||
if (!template.Enabled)
|
if (!template.Enabled)
|
||||||
{
|
{
|
||||||
|
@ -59,40 +71,146 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var customization = await _customizationSettings.GetSettingsAsync();
|
||||||
|
|
||||||
// Get the Content
|
// Get the Content
|
||||||
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||||
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
||||||
|
|
||||||
var addedLog = _recentlyAddedLog.GetAll();
|
var addedLog = _recentlyAddedLog.GetAll();
|
||||||
var addedPlexLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex).Select(x => x.ContentId);
|
var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
|
||||||
var addedEmbyLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby).Select(x => x.ContentId);
|
var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
|
||||||
|
|
||||||
|
var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode).Select(x => x.ContentId);
|
||||||
|
var addedEmbyEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode).Select(x => x.ContentId);
|
||||||
|
|
||||||
// Filter out the ones that we haven't sent yet
|
// Filter out the ones that we haven't sent yet
|
||||||
var plexContentToSend = plexContent.Where(x => !addedPlexLogIds.Contains(x.Id));
|
var plexContentMoviesToSend = plexContent.Where(x => !addedPlexMovieLogIds.Contains(x.Id));
|
||||||
var embyContentToSend = embyContent.Where(x => !addedEmbyLogIds.Contains(x.Id));
|
var embyContentMoviesToSend = embyContent.Where(x => !addedEmbyMoviesLogIds.Contains(x.Id));
|
||||||
|
|
||||||
|
var plexContentTvToSend = plexContent.Where(x => x.Episodes.Any(e => !addedPlexEpisodesLogIds.Contains(e.Id)));
|
||||||
|
var embyContentTvToSend = embyContent.Where(x => x.Episodes.Any(e => !addedEmbyEpisodesLogIds.Contains(e.Id)));
|
||||||
|
|
||||||
|
var plexContentToSend = plexContentMoviesToSend.Union(plexContentTvToSend);
|
||||||
|
var embyContentToSend = embyContentMoviesToSend.Union(embyContentTvToSend);
|
||||||
|
|
||||||
|
|
||||||
var body = await BuildHtml(plexContentToSend, embyContentToSend);
|
var body = await BuildHtml(plexContentToSend, embyContentToSend);
|
||||||
|
|
||||||
|
// Get the users to send it to
|
||||||
|
var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter);
|
||||||
|
if (!users.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var emailTasks = new List<Task>();
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
if (user.Email.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = LoadTemplate(body, template, customization, user.Alias);
|
||||||
|
|
||||||
|
emailTasks.Add(_email.Send(new NotificationMessage { Message = html, Subject = template.Subject, To = user.Email }, emailSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add all of this to the Recently Added log
|
||||||
|
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||||
|
foreach (var p in plexContentMoviesToSend)
|
||||||
|
{
|
||||||
|
if (p.Type == PlexMediaTypeEntity.Movie)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = p.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the episodes
|
||||||
|
foreach (var ep in p.Episodes)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = ep.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var e in embyContentMoviesToSend)
|
||||||
|
{
|
||||||
|
if (e.Type == EmbyMediaType.Movie)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Emby,
|
||||||
|
ContentId = e.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add the episodes
|
||||||
|
foreach (var ep in e.Episodes)
|
||||||
|
{
|
||||||
|
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||||
|
{
|
||||||
|
AddedAt = DateTime.Now,
|
||||||
|
Type = RecentlyAddedType.Plex,
|
||||||
|
ContentId = ep.Id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||||
|
|
||||||
|
await Task.WhenAll(emailTasks.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string LoadTemplate(string body, NotificationTemplates template, CustomizationSettings settings, string username)
|
||||||
|
{
|
||||||
var email = new NewsletterTemplate();
|
var email = new NewsletterTemplate();
|
||||||
|
|
||||||
var customization = await _customizationSettings.GetSettingsAsync();
|
var resolver = new NotificationMessageResolver();
|
||||||
|
var curlys = new NotificationMessageCurlys();
|
||||||
|
|
||||||
var html = email.LoadTemplate(template.Subject, template.Message, body, customization.Logo);
|
curlys.SetupNewsletter(settings, username);
|
||||||
|
|
||||||
await _email.Send(new NotificationMessage {Message = html, Subject = template.Subject, To = "tidusjar@gmail.com"}, emailSettings);
|
var parsed = resolver.ParseMessage(template, curlys);
|
||||||
|
|
||||||
|
var html = email.LoadTemplate(parsed.Subject, parsed.Message, body, settings.Logo);
|
||||||
|
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend)
|
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
sb.Append("<h1>New Movies:</h1><br /><br />");
|
var plexMovies = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||||
await ProcessPlexMovies(plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Movie), sb);
|
var embyMovies = embyContentToSend.Where(x => x.Type == EmbyMediaType.Movie);
|
||||||
await ProcessEmbyMovies(embyContentToSend.Where(x => x.Type == EmbyMediaType.Movie), sb);
|
if (plexMovies.Any() || embyMovies.Any())
|
||||||
|
{
|
||||||
|
sb.Append("<h1>New Movies:</h1><br /><br />");
|
||||||
|
await ProcessPlexMovies(plexMovies, sb);
|
||||||
|
await ProcessEmbyMovies(embyMovies, sb);
|
||||||
|
}
|
||||||
|
|
||||||
sb.Append("<h1>New Episodes:</h1><br /><br />");
|
var plexTv = plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Show);
|
||||||
await ProcessPlexTv(plexContentToSend.Where(x => x.Type == PlexMediaTypeEntity.Show), sb);
|
var embyTv = embyContentToSend.Where(x => x.Type == EmbyMediaType.Series);
|
||||||
await ProcessEmbyMovies(embyContentToSend.Where(x => x.Type == EmbyMediaType.Series), sb);
|
if (plexTv.Any() || embyTv.Any())
|
||||||
|
{
|
||||||
|
sb.Append("<h1>New Episodes:</h1><br /><br />");
|
||||||
|
await ProcessPlexTv(plexTv, sb);
|
||||||
|
await ProcessEmbyMovies(embyTv, sb);
|
||||||
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
@ -330,7 +448,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
sb.Append("<tr>");
|
sb.Append("<tr>");
|
||||||
sb.Append(
|
sb.Append(
|
||||||
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||||
|
|
||||||
Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/");
|
Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/");
|
||||||
Header(sb, 3, t.Title);
|
Header(sb, 3, t.Title);
|
||||||
EndTag(sb, "a");
|
EndTag(sb, "a");
|
||||||
|
@ -426,6 +544,12 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
_plex?.Dispose();
|
_plex?.Dispose();
|
||||||
_emby?.Dispose();
|
_emby?.Dispose();
|
||||||
|
_newsletterSettings?.Dispose();
|
||||||
|
_customizationSettings?.Dispose();
|
||||||
|
_emailSettings.Dispose();
|
||||||
|
_recentlyAddedLog.Dispose();
|
||||||
|
_templateRepo?.Dispose();
|
||||||
|
_userManager?.Dispose();
|
||||||
}
|
}
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Settings.Settings.Models.Notifications
|
||||||
|
{
|
||||||
|
public class NewsletterSettings : Settings
|
||||||
|
{
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,20 @@ namespace Ombi.Store.Entities
|
||||||
public class RecentlyAddedLog : Entity
|
public class RecentlyAddedLog : Entity
|
||||||
{
|
{
|
||||||
public RecentlyAddedType Type { get; set; }
|
public RecentlyAddedType Type { get; set; }
|
||||||
|
public ContentType ContentType { get; set; }
|
||||||
public int ContentId { get; set; } // This is dependant on the type
|
public int ContentId { get; set; } // This is dependant on the type
|
||||||
public DateTime AddedAt { get; set; }
|
public DateTime AddedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RecentlyAddedType
|
public enum RecentlyAddedType
|
||||||
{
|
{
|
||||||
Plex,
|
Plex = 0,
|
||||||
Emby
|
Emby = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ContentType
|
||||||
|
{
|
||||||
|
Parent = 0,
|
||||||
|
Episode = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
|
@ -6,7 +7,7 @@ using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Store.Repository
|
namespace Ombi.Store.Repository
|
||||||
{
|
{
|
||||||
public interface INotificationTemplatesRepository
|
public interface INotificationTemplatesRepository : IDisposable
|
||||||
{
|
{
|
||||||
IQueryable<NotificationTemplates> All();
|
IQueryable<NotificationTemplates> All();
|
||||||
Task<IEnumerable<NotificationTemplates>> GetAllTemplates();
|
Task<IEnumerable<NotificationTemplates>> GetAllTemplates();
|
||||||
|
|
|
@ -60,5 +60,26 @@ namespace Ombi.Store.Repository
|
||||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||||
return settings.Entity;
|
return settings.Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
// Protected implementation of Dispose pattern.
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Db?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -54,6 +54,10 @@ export interface IDiscordNotifcationSettings extends INotificationSettings {
|
||||||
notificationTemplates: INotificationTemplates[];
|
notificationTemplates: INotificationTemplates[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface INewsletterNotificationSettings extends INotificationSettings {
|
||||||
|
notificationTemplate: INotificationTemplates;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITelegramNotifcationSettings extends INotificationSettings {
|
export interface ITelegramNotifcationSettings extends INotificationSettings {
|
||||||
botApi: string;
|
botApi: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
ILandingPageSettings,
|
ILandingPageSettings,
|
||||||
IMattermostNotifcationSettings,
|
IMattermostNotifcationSettings,
|
||||||
IMobileNotifcationSettings,
|
IMobileNotifcationSettings,
|
||||||
|
INewsletterNotificationSettings,
|
||||||
IOmbiSettings,
|
IOmbiSettings,
|
||||||
IPlexSettings,
|
IPlexSettings,
|
||||||
IPushbulletNotificationSettings,
|
IPushbulletNotificationSettings,
|
||||||
|
@ -265,4 +266,17 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http
|
return this.http
|
||||||
.post<boolean>(`${this.url}/issues`, JSON.stringify(settings), {headers: this.headers});
|
.post<boolean>(`${this.url}/issues`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getNewsletterSettings(): Observable<INewsletterNotificationSettings> {
|
||||||
|
return this.http.get<INewsletterNotificationSettings>(`${this.url}/notifications/newsletter`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateNewsletterDatabase(): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}/notifications/newsletterdatabase`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveNewsletterSettings(settings: INewsletterNotificationSettings): Observable<boolean> {
|
||||||
|
return this.http
|
||||||
|
.post<boolean>(`${this.url}/notifications/newsletter`, JSON.stringify(settings), {headers: this.headers});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<settings-menu></settings-menu>
|
||||||
|
|
||||||
|
<div *ngIf="form">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Newsletter</legend>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="enable" formControlName="enabled">
|
||||||
|
<label for="enable">Enabled</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="enabled" [(ngModel)]="template.enabled" ng-checked="template.enabled">
|
||||||
|
<label for="enabled">Enable</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngIf="showSubject">
|
||||||
|
<label class="control-label">Subject</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="form-control form-control-custom" [(ngModel)]="template.subject" value="{{template.subject}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">Message</label>
|
||||||
|
<div>
|
||||||
|
<textarea type="text" class="form-control form-control-custom" [(ngModel)]="template.message" value="{{template.message}}"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button [disabled]="form.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||||
|
<button [disabled]="form.invalid" type="button" (click)="updateDatabase()" class="btn btn-info-outline" tooltipPosition="top" pTooltip="I reccomend running this with a fresh Ombi install, this will set all the current *found* content to have been sent via Newsletter,
|
||||||
|
if you do not do this then eventhing that Ombi has found in your libraries will go out on the first email!">Update Database</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
import { INewsletterNotificationSettings, INotificationTemplates, NotificationType } from "../../interfaces";
|
||||||
|
import { TesterService } from "../../services";
|
||||||
|
import { NotificationService } from "../../services";
|
||||||
|
import { SettingsService } from "../../services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./newsletter.component.html",
|
||||||
|
})
|
||||||
|
export class NewsletterComponent implements OnInit {
|
||||||
|
|
||||||
|
public NotificationType = NotificationType;
|
||||||
|
public template: INotificationTemplates;
|
||||||
|
public form: FormGroup;
|
||||||
|
|
||||||
|
constructor(private settingsService: SettingsService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private testerService: TesterService) { }
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.settingsService.getNewsletterSettings().subscribe(x => {
|
||||||
|
this.template = x.notificationTemplate;
|
||||||
|
|
||||||
|
this.form = this.fb.group({
|
||||||
|
enabled: [x.enabled],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateDatabase() {
|
||||||
|
this.settingsService.updateNewsletterDatabase().subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSubmit(form: FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = <INewsletterNotificationSettings>form.value;
|
||||||
|
settings.notificationTemplate = this.template;
|
||||||
|
|
||||||
|
this.settingsService.saveNewsletterSettings(settings).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.notificationService.success("Successfully saved the Newsletter settings");
|
||||||
|
} else {
|
||||||
|
this.notificationService.error("There was an error when saving the Newsletter settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public test(form: FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Please check your entered values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.testerService.discordTest(form.value).subscribe(x => {
|
||||||
|
if (x) {
|
||||||
|
this.notificationService.success("Successfully sent a Discord message, please check the discord channel");
|
||||||
|
} else {
|
||||||
|
this.notificationService.error("There was an error when sending the Discord message. Please check your settings");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ using Ombi.Settings.Settings.Models.Notifications;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Ombi.Api.Github;
|
using Ombi.Api.Github;
|
||||||
|
using Ombi.Core.Engine;
|
||||||
|
|
||||||
namespace Ombi.Controllers
|
namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
|
@ -60,7 +61,8 @@ namespace Ombi.Controllers
|
||||||
IEmbyApi embyApi,
|
IEmbyApi embyApi,
|
||||||
IRadarrSync radarrSync,
|
IRadarrSync radarrSync,
|
||||||
ICacheService memCache,
|
ICacheService memCache,
|
||||||
IGithubApi githubApi)
|
IGithubApi githubApi,
|
||||||
|
IRecentlyAddedEngine engine)
|
||||||
{
|
{
|
||||||
SettingsResolver = resolver;
|
SettingsResolver = resolver;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
|
@ -78,6 +80,7 @@ namespace Ombi.Controllers
|
||||||
private readonly IRadarrSync _radarrSync;
|
private readonly IRadarrSync _radarrSync;
|
||||||
private readonly ICacheService _cache;
|
private readonly ICacheService _cache;
|
||||||
private readonly IGithubApi _githubApi;
|
private readonly IGithubApi _githubApi;
|
||||||
|
private readonly IRecentlyAddedEngine _recentlyAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Ombi settings.
|
/// Gets the Ombi settings.
|
||||||
|
@ -865,13 +868,53 @@ namespace Ombi.Controllers
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the Newsletter notification settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("notifications/newsletter")]
|
||||||
|
public async Task<bool> NewsletterSettings([FromBody] NewsletterNotificationViewModel model)
|
||||||
|
{
|
||||||
|
// Save the email settings
|
||||||
|
var settings = Mapper.Map<NewsletterSettings>(model);
|
||||||
|
var result = await Save(settings);
|
||||||
|
|
||||||
|
// Save the templates
|
||||||
|
await TemplateRepository.Update(model.NotificationTemplate);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
[HttpPost("notifications/newsletterdatabase")]
|
||||||
|
public async Task<bool> UpdateNewsletterDatabase()
|
||||||
|
{
|
||||||
|
return await _recentlyAdded.UpdateRecentlyAddedDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Newsletter Notification Settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("notifications/newsletter")]
|
||||||
|
public async Task<NewsletterNotificationViewModel> NewsletterSettings()
|
||||||
|
{
|
||||||
|
var settings = await Get<NewsletterSettings>();
|
||||||
|
var model = Mapper.Map<NewsletterNotificationViewModel>(settings);
|
||||||
|
|
||||||
|
// Lookup to see if we have any templates saved
|
||||||
|
var templates = await BuildTemplates(NotificationAgent.Email);
|
||||||
|
model.NotificationTemplate = templates.FirstOrDefault(x => x.NotificationType == NotificationType.Newsletter);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
|
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
|
||||||
{
|
{
|
||||||
var templates = await TemplateRepository.GetAllTemplates(agent);
|
var templates = await TemplateRepository.GetAllTemplates(agent);
|
||||||
return templates.OrderBy(x => x.NotificationType.ToString()).ToList();
|
return templates.OrderBy(x => x.NotificationType.ToString()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<T> Get<T>()
|
private async Task<T> Get<T>()
|
||||||
{
|
{
|
||||||
var settings = SettingsResolver.Resolve<T>();
|
var settings = SettingsResolver.Resolve<T>();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue