!wip added music into the newsletter and also added issue reporting to the albums

pull/2473/head
TidusJar 6 years ago
parent fd8bfeb1e4
commit 82c353a727

@ -1,4 +1,5 @@
using System.Text;
using Ombi.Helpers;
namespace Ombi.Schedule.Jobs.Ombi
{
@ -22,13 +23,20 @@ namespace Ombi.Schedule.Jobs.Ombi
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url)
{
sb.Append("<tr>");
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
sb.AppendFormat("<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">", url);
sb.Append("</a>");
sb.Append("</td>");
sb.Append("</tr>");
if (url.HasValue())
{
sb.Append("<tr>");
sb.Append(
"<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
sb.AppendFormat(
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">",
url);
sb.Append("</a>");
sb.Append("</td>");
sb.Append("</tr>");
}
sb.Append("</table>");
sb.Append("</td>");
}
@ -44,9 +52,9 @@ namespace Ombi.Schedule.Jobs.Ombi
{
sb.Append("<tr>");
sb.Append("<td class=\"title\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.9rem; vertical-align: top; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; line-height: 1.2rem; padding: 5px; \">");
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
sb.AppendFormat("<h1 style=\"white-space: normal; line-height: 1;\" >{0}</h1>", title);
sb.Append("</a>");
if (url.HasValue()) sb.Append("</a>");
sb.Append("</td>");
sb.Append("</tr>");
}

@ -9,6 +9,8 @@ using MailKit;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Api.Lidarr;
using Ombi.Api.Lidarr.Models;
using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Api.TvMaze;
@ -18,6 +20,7 @@ using Ombi.Notifications;
using Ombi.Notifications.Models;
using Ombi.Notifications.Templates;
using Ombi.Settings.Settings.Models;
using Ombi.Settings.Settings.Models.External;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
@ -29,7 +32,8 @@ namespace Ombi.Schedule.Jobs.Ombi
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> addedLog,
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log)
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
ILidarrApi lidarrApi, IRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings)
{
_plex = plex;
_emby = emby;
@ -46,6 +50,10 @@ namespace Ombi.Schedule.Jobs.Ombi
_customizationSettings.ClearCache();
_newsletterSettings.ClearCache();
_log = log;
_lidarrApi = lidarrApi;
_lidarrAlbumRepository = albumCache;
_lidarrSettings = lidarrSettings;
_lidarrSettings.ClearCache();
}
private readonly IPlexContentRepository _plex;
@ -60,6 +68,9 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly ISettingsService<NewsletterSettings> _newsletterSettings;
private readonly UserManager<OmbiUser> _userManager;
private readonly ILogger _log;
private readonly ILidarrApi _lidarrApi;
private readonly IRepository<LidarrAlbumCache> _lidarrAlbumRepository;
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
public async Task Start(NewsletterSettings settings, bool test)
{
@ -87,21 +98,26 @@ namespace Ombi.Schedule.Jobs.Ombi
// Get the Content
var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking();
var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking();
var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking();
var addedLog = _recentlyAddedLog.GetAll();
var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId);
var addedPlexEpisodesLogIds =
addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode);
var addedEmbyEpisodesLogIds =
addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode);
// Filter out the ones that we haven't sent yet
var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && x.HasTheMovieDb && !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId)));
var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && x.HasTheMovieDb && !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId)));
var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet();
_log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count());
_log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count());
_log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count());
var plexEpisodesToSend =
FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), addedPlexEpisodesLogIds);
@ -117,11 +133,12 @@ namespace Ombi.Schedule.Jobs.Ombi
var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie ).OrderByDescending(x => x.AddedAt).Take(10);
var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet();
var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
body = await BuildHtml(plexm, embym, plext, embyt, settings);
var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings);
}
else
{
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings);
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings);
if (body.IsNullOrEmpty())
{
return;
@ -298,7 +315,8 @@ namespace Ombi.Schedule.Jobs.Ombi
return resolver.ParseMessage(template, curlys);
}
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend, HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, NewsletterSettings settings)
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings)
{
var sb = new StringBuilder();
@ -340,6 +358,24 @@ namespace Ombi.Schedule.Jobs.Ombi
sb.Append("</table>");
}
if (albums.Any() && !settings.DisableMusic)
{
sb.Append("<h1 style=\"text-align: center; max-width: 1042px;\">New Albums</h1><br /><br />");
sb.Append(
"<table class=\"movies-table\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
sb.Append("<tr>");
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
sb.Append("<tr>");
await ProcessAlbums(albums, sb);
sb.Append("</tr>");
sb.Append("</table>");
sb.Append("</td>");
sb.Append("</tr>");
sb.Append("</table>");
}
return sb.ToString();
}
@ -382,6 +418,40 @@ namespace Ombi.Schedule.Jobs.Ombi
}
}
}
private async Task ProcessAlbums(HashSet<LidarrAlbumCache> albumsToSend, StringBuilder sb)
{
var settings = await _lidarrSettings.GetSettingsAsync();
int count = 0;
var ordered = albumsToSend.OrderByDescending(x => x.AddedAt);
foreach (var content in ordered)
{
var info = await _lidarrApi.GetAlbumByForeignId(content.ForeignAlbumId, settings.ApiKey, settings.FullUri);
if (info == null)
{
continue;
}
try
{
CreateAlbumHtmlContent(sb, info);
count += 1;
}
catch (Exception e)
{
_log.LogError(e, "Error when Processing Lidarr Album {0}", info.title);
}
finally
{
EndLoopHtml(sb);
}
if (count == 2)
{
count = 0;
sb.Append("</tr>");
sb.Append("<tr>");
}
}
}
private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb)
{
@ -467,6 +537,41 @@ namespace Ombi.Schedule.Jobs.Ombi
}
}
private void CreateAlbumHtmlContent(StringBuilder sb, AlbumLookup info)
{
var cover = info.images
.FirstOrDefault(x => x.coverType.Equals("cover", StringComparison.InvariantCultureIgnoreCase))?.url;
if (cover.IsNullOrEmpty())
{
cover = info.remoteCover;
}
AddBackgroundInsideTable(sb, cover);
var disk = info.images
.FirstOrDefault(x => x.coverType.Equals("disc", StringComparison.InvariantCultureIgnoreCase))?.url;
if (disk.IsNullOrEmpty())
{
disk = info.remoteCover;
}
AddPosterInsideTable(sb, disk);
AddMediaServerUrl(sb, string.Empty, string.Empty);
AddInfoTable(sb);
var releaseDate = $"({info.releaseDate.Year})";
AddTitle(sb, string.Empty, $"{info.title} {releaseDate}");
var summary = info.artist?.artistName ?? string.Empty;
if (summary.Length > 280)
{
summary = summary.Remove(280);
summary = summary + "...</p>";
}
AddParagraph(sb, summary);
AddGenres(sb, $"Type: {info.albumType}");
}
private async Task ProcessPlexTv(HashSet<PlexEpisode> plexContent, StringBuilder sb)
{
var series = new List<PlexServerContent>();

@ -6,6 +6,7 @@ namespace Ombi.Settings.Settings.Models.Notifications
{
public bool DisableTv { get; set; }
public bool DisableMovies { get; set; }
public bool DisableMusic { get; set; }
public bool Enabled { get; set; }
public List<string> ExternalEmails { get; set; } = new List<string>();
}

@ -13,6 +13,7 @@ namespace Ombi.Store.Entities
public bool Monitored { get; set; }
public string Title { get; set; }
public decimal PercentOfTracks { get; set; }
public DateTime AddedAt { get; set; }
[NotMapped]
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;

@ -11,18 +11,21 @@ namespace Ombi.Store.Entities
public int ContentId { get; set; } // This is dependant on the type, it's either TMDBID or TVDBID
public int? EpisodeNumber { get; set; }
public int? SeasonNumber { get; set; }
public string AlbumId { get; set; }
public DateTime AddedAt { get; set; }
}
public enum RecentlyAddedType
{
Plex = 0,
Emby = 1
Emby = 1,
Lidarr = 2
}
public enum ContentType
{
Parent = 0,
Episode = 1
Episode = 1,
Album = 2,
}
}

@ -189,7 +189,7 @@
</div>
<!-- <div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
@ -200,7 +200,7 @@
<a [routerLink]="" (click)="reportIssue(cat, request)">{{cat.value}}</a>
</li>
</ul>
</div> -->
</div>
</div>
</div>

@ -68,13 +68,13 @@
<!--Buttons-->
<div class="col-sm-12 small-padding">
<div class="row" *ngIf="result.requested">
<!-- <div class="row" *ngIf="result.requested">
<div class="col-md-2 col-md-push-10">
<!-- <a *ngIf="result.showSubscribe && !result.subscribed" style="color:white" (click)="subscribe(result)" pTooltip="Subscribe for notifications"> <i class="fa fa-rss"></i></a>
<a *ngIf="result.showSubscribe && result.subscribed" style="color:red" (click)="unSubscribe(result)" pTooltip="Unsubscribe notification"> <i class="fa fa-rss"></i></a> -->
<a *ngIf="result.showSubscribe && !result.subscribed" style="color:white" (click)="subscribe(result)" pTooltip="Subscribe for notifications"> <i class="fa fa-rss"></i></a>
<a *ngIf="result.showSubscribe && result.subscribed" style="color:red" (click)="unSubscribe(result)" pTooltip="Unsubscribe notification"> <i class="fa fa-rss"></i></a>
</div>
</div>
</div> -->
<div *ngIf="result.fullyAvailable">
<button style="text-align: right" class="btn btn-success-outline disabled" disabled>
<i class="fa fa-check"></i> {{ 'Common.Available' | translate }}</button>
@ -93,11 +93,9 @@
| translate }}</button>
</ng-template>
</div>
<!-- <button style="text-align: right" class="btn btn-sm btn-info-outline" (click)="similarMovies(result.id)"> <i class="fa fa-eye"></i> {{ 'Search.Similar' | translate }}</button> -->
<br/>
<!-- <div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
<div class="dropdown" *ngIf="(result.partiallyAvailable || result.fullyAvailable) && issueCategories && issuesEnabled">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="true">
<i class="fa fa-plus"></i> Report Issue
@ -106,8 +104,13 @@
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li *ngFor="let cat of issueCategories"><a [routerLink]="" (click)="reportIssue(cat, result)">{{cat.value}}</a></li>
</ul>
</div> -->
</div>
</div>
</div>
</div>
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequestTitle"
[issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"></issue-report>

@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { AuthService } from "../../auth/auth.service";
import { IRequestEngineResult } from "../../interfaces";
import { IIssueCategory, IRequestEngineResult } from "../../interfaces";
import { ISearchAlbumResult } from "../../interfaces/ISearchMusicResult";
import { NotificationService, RequestService } from "../../services";
@ -14,7 +14,15 @@ export class AlbumSearchComponent {
@Input() public result: ISearchAlbumResult;
public engineResult: IRequestEngineResult;
@Input() public defaultPoster: string;
@Input() public defaultPoster: string;
@Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean;
public issuesBarVisible = false;
public issueRequestTitle: string;
public issueRequestId: number;
public issueProviderId: string;
public issueCategorySelected: IIssueCategory;
@Output() public setSearch = new EventEmitter<string>();
@ -29,6 +37,14 @@ export class AlbumSearchComponent {
this.setSearch.emit(artistId);
}
public reportIssue(catId: IIssueCategory, req: ISearchAlbumResult) {
this.issueRequestId = req.id;
this.issueRequestTitle = req.title + `(${req.releaseDate.getFullYear})`;
this.issueCategorySelected = catId;
this.issuesBarVisible = true;
this.issueProviderId = req.id.toString();
}
public request(searchResult: ISearchAlbumResult) {
searchResult.requested = true;
searchResult.requestProcessing = true;

Loading…
Cancel
Save