Merge branch 'develop' into master

pull/2973/head
Jamie 5 years ago committed by GitHub
commit 82a24d5393
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,7 +15,7 @@ ___
Follow me developing Ombi! Follow me developing Ombi!
[![Twitch](https://img.shields.io/badge/Twitch-Watch-blue.svg?style=flat-square&logo=twitch)](https://twitch.tv/tiusjar) [![Twitch](https://img.shields.io/badge/Twitch-Watch-blue.svg?style=flat-square&logo=twitch)](https://www.twitch.tv/tidusjar)
___ ___
@ -68,6 +68,7 @@ We integrate with the following applications:
Supported notifications: Supported notifications:
* SMTP Notifications (Email) * SMTP Notifications (Email)
* Discord * Discord
* Gotify
* Slack * Slack
* Pushbullet * Pushbullet
* Pushover * Pushover

@ -3,7 +3,7 @@
#addin "Cake.Gulp" #addin "Cake.Gulp"
#addin "SharpZipLib" #addin "SharpZipLib"
#addin nuget:?package=Cake.Compression&version=0.1.4 #addin nuget:?package=Cake.Compression&version=0.1.4
#addin "Cake.Incubator" #addin "Cake.Incubator&version=3.1.0"
#addin "Cake.Yarn" #addin "Cake.Yarn"
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -81,9 +81,9 @@ Task("SetVersionInfo")
versionInfo = GitVersion(settings); versionInfo = GitVersion(settings);
Information("GitResults -> {0}", versionInfo.Dump()); // Information("GitResults -> {0}", versionInfo.Dump());
Information(@"Build:{0}",AppVeyor.Environment.Build.Dump()); //Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
var buildVersion = string.Empty; var buildVersion = string.Empty;
if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version)) if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version))

@ -16,6 +16,7 @@ namespace Ombi.Api.Emby.Models.Media.Tv
public int ProductionYear { get; set; } public int ProductionYear { get; set; }
public bool IsPlaceHolder { get; set; } public bool IsPlaceHolder { get; set; }
public int IndexNumber { get; set; } public int IndexNumber { get; set; }
public int? IndexNumberEnd { get; set; }
public int ParentIndexNumber { get; set; } public int ParentIndexNumber { get; set; }
public bool IsHD { get; set; } public bool IsHD { get; set; }
public bool IsFolder { get; set; } public bool IsFolder { get; set; }

@ -0,0 +1,36 @@
using System.Net.Http;
using System.Threading.Tasks;
namespace Ombi.Api.Gotify
{
public class GotifyApi : IGotifyApi
{
public GotifyApi(IApi api)
{
_api = api;
}
private readonly IApi _api;
public async Task PushAsync(string baseUrl, string accessToken, string subject, string body, sbyte priority)
{
var request = new Request("/message", baseUrl, HttpMethod.Post);
request.AddQueryString("token", accessToken);
request.AddHeader("Access-Token", accessToken);
request.ApplicationJsonContentType();
var jsonBody = new
{
message = body,
title = subject,
priority = priority
};
request.AddJsonBody(jsonBody);
await _api.Request(request);
}
}
}

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Api.Gotify
{
public interface IGotifyApi
{
Task PushAsync(string endpoint, string accessToken, string subject, string body, sbyte priority);
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup>
</Project>

@ -23,5 +23,6 @@ namespace Ombi.Api.Lidarr
Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl); Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl);
Task<LidarrStatus> Status(string apiKey, string baseUrl); Task<LidarrStatus> Status(string apiKey, string baseUrl);
Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl); Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl);
Task<AlbumResponse> AlbumInformation(string albumId, string apiKey, string baseUrl);
} }
} }

@ -105,6 +105,32 @@ namespace Ombi.Api.Lidarr
return Api.Request<List<AlbumResponse>>(request); return Api.Request<List<AlbumResponse>>(request);
} }
public async Task<AlbumResponse> AlbumInformation(string albumId, string apiKey, string baseUrl)
{
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
request.AddQueryString("foreignAlbumId", albumId);
AddHeaders(request, apiKey);
var albums = await Api.Request<List<AlbumResponse>>(request);
return albums.Where(x => x.foreignAlbumId.Equals(albumId, StringComparison.InvariantCultureIgnoreCase))
.FirstOrDefault();
}
/// <summary>
/// THIS ONLY SUPPORTS ALBUMS THAT THE ARTIST IS IN LIDARR
/// </summary>
/// <param name="albumId"></param>
/// <param name="apiKey"></param>
/// <param name="baseUrl"></param>
/// <returns></returns>
public Task<List<LidarrTrack>> GetTracksForAlbum(int albumId, string apiKey, string baseUrl)
{
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
request.AddQueryString("albumId", albumId.ToString());
AddHeaders(request, apiKey);
return Api.Request<List<LidarrTrack>>(request);
}
public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl) public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
{ {
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post); var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);

@ -1,10 +1,15 @@
using System; using System;
using System.Collections.Generic;
namespace Ombi.Api.Lidarr.Models namespace Ombi.Api.Lidarr.Models
{ {
public class AlbumLookup public class AlbumLookup
{ {
public string title { get; set; } public string title { get; set; }
public string status { get; set; }
public string artistType { get; set; }
public string disambiguation { get; set; }
public List<LidarrLinks> links { get; set; }
public int artistId { get; set; } public int artistId { get; set; }
public string foreignAlbumId { get; set; } public string foreignAlbumId { get; set; }
public bool monitored { get; set; } public bool monitored { get; set; }

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ombi.Api.Lidarr.Models
{
public class LidarrLinks
{
public string url { get; set; }
public string name { get; set; }
}
}

@ -0,0 +1,8 @@
namespace Ombi.Api.Lidarr.Models
{
public class LidarrRatings
{
public int votes { get; set; }
public decimal value { get; set; }
}
}

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ombi.Api.Lidarr.Models
{
public class LidarrTrack
{
public int artistId { get; set; }
public int trackFileId { get; set; }
public int albumId { get; set; }
public bool _explicit { get; set; }
public int absoluteTrackNumber { get; set; }
public string trackNumber { get; set; }
public string title { get; set; }
public int duration { get; set; }
public int mediumNumber { get; set; }
public bool hasFile { get; set; }
public bool monitored { get; set; }
public int id { get; set; }
}
}

@ -2,6 +2,7 @@
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Ombi.Api.Pushover.Models; using Ombi.Api.Pushover.Models;
namespace Ombi.Api.Pushover namespace Ombi.Api.Pushover
@ -18,11 +19,7 @@ namespace Ombi.Api.Pushover
public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound) public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound)
{ {
if (message.Contains("'")) var request = new Request($"messages.json?token={accessToken}&user={userToken}&priority={priority}&sound={sound}&message={WebUtility.UrlEncode(message)}", PushoverEndpoint, HttpMethod.Post);
{
message = message.Replace("'", "&#39;");
}
var request = new Request($"messages.json?token={accessToken}&user={userToken}&priority={priority}&sound={sound}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post);
var result = await _api.Request<PushoverResponse>(request); var result = await _api.Request<PushoverResponse>(request);
return result; return result;

@ -4,6 +4,7 @@ using Moq;
using Ombi.Core.Rule.Rules.Request; using Ombi.Core.Rule.Rules.Request;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Helpers; using Ombi.Helpers;
namespace Ombi.Core.Tests.Rule.Request namespace Ombi.Core.Tests.Rule.Request
@ -16,7 +17,7 @@ namespace Ombi.Core.Tests.Rule.Request
{ {
PrincipalMock = new Mock<IPrincipal>(); PrincipalMock = new Mock<IPrincipal>();
Rule = new AutoApproveRule(PrincipalMock.Object); Rule = new AutoApproveRule(PrincipalMock.Object, null);
} }

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Rule.Rules; using Ombi.Core.Rule.Rules;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
@ -15,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Request
{ {
PrincipalMock = new Mock<IPrincipal>(); PrincipalMock = new Mock<IPrincipal>();
Rule = new CanRequestRule(PrincipalMock.Object); Rule = new CanRequestRule(PrincipalMock.Object, null);
} }

@ -16,7 +16,7 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup() public void Setup()
{ {
ContextMock = new Mock<IEmbyContentRepository>(); ContextMock = new Mock<IEmbyContentRepository>();
Rule = new EmbyAvailabilityRule(ContextMock.Object); Rule = new EmbyAvailabilityRule(ContextMock.Object, null);
} }
private EmbyAvailabilityRule Rule { get; set; } private EmbyAvailabilityRule Rule { get; set; }

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
@ -14,7 +15,7 @@ namespace Ombi.Core.Tests.Rule.Search
public void Setup() public void Setup()
{ {
ContextMock = new Mock<IPlexContentRepository>(); ContextMock = new Mock<IPlexContentRepository>();
Rule = new PlexAvailabilityRule(ContextMock.Object); Rule = new PlexAvailabilityRule(ContextMock.Object, new Mock<ILogger<PlexAvailabilityRule>>().Object);
} }
private PlexAvailabilityRule Rule { get; set; } private PlexAvailabilityRule Rule { get; set; }

@ -12,5 +12,6 @@ namespace Ombi.Core.Engine
Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId); Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId);
Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search); Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search);
Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search); Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search);
Task<SearchAlbumViewModel> GetAlbumInformation(string foreignAlbumId);
} }
} }

@ -227,7 +227,7 @@ namespace Ombi.Core.Engine
} }
var request = await RequestService.MovieRequestService.GetAll() var request = await RequestService.MovieRequestService.GetAll()
.AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id); .AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id);
if (request) if (request || viewModel.Available)
{ {
viewModel.ShowSubscribe = false; viewModel.ShowSubscribe = false;
} }

@ -60,6 +60,18 @@ namespace Ombi.Core.Engine
return vm; return vm;
} }
public async Task<SearchAlbumViewModel> GetAlbumInformation(string foreignAlbumId)
{
var settings = await GetSettings();
var result = await _lidarrApi.AlbumInformation(foreignAlbumId, settings.ApiKey, settings.FullUri);
var vm = await MapIntoAlbumVm(result, settings);
return vm;
}
/// <summary> /// <summary>
/// Searches the specified artist /// Searches the specified artist
/// </summary> /// </summary>
@ -143,6 +155,48 @@ namespace Ombi.Core.Engine
return vm; return vm;
} }
// TODO
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumResponse a, LidarrSettings settings)
{
var vm = new SearchAlbumViewModel
{
ForeignAlbumId = a.foreignAlbumId,
Monitored = a.monitored,
Rating = a.ratings?.value ?? 0m,
ReleaseDate = a.releaseDate,
Title = a.title,
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
Genres = a.genres
};
if (a.artistId > 0)
{
//TODO THEY HAVE FIXED THIS IN DEV
// The JSON is different for some stupid reason
// Need to lookup the artist now and all the images -.-"
var artist = await _lidarrApi.GetArtist(a.artistId, settings.ApiKey, settings.FullUri);
vm.ArtistName = artist.artistName;
vm.ForeignArtistId = artist.foreignArtistId;
}
else
{
//vm.ForeignArtistId = a.artistId?.foreignArtistId;
//vm.ArtistName = a.artist?.artistName;
}
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
if (vm.Cover.IsNullOrEmpty())
{
//vm.Cover = a.remoteCover;
}
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
await RunSearchRules(vm);
return vm;
}
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings) private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings)
{ {
var vm = new SearchAlbumViewModel var vm = new SearchAlbumViewModel
@ -152,7 +206,8 @@ namespace Ombi.Core.Engine
Rating = a.ratings?.value ?? 0m, Rating = a.ratings?.value ?? 0m,
ReleaseDate = a.releaseDate, ReleaseDate = a.releaseDate,
Title = a.title, Title = a.title,
Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url Disk = a.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url?.Replace("http", "https"),
Genres = a.genres
}; };
if (a.artistId > 0) if (a.artistId > 0)
{ {
@ -169,7 +224,7 @@ namespace Ombi.Core.Engine
vm.ArtistName = a.artist?.artistName; vm.ArtistName = a.artist?.artistName;
} }
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url; vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.Replace("http", "https");
if (vm.Cover.IsNullOrEmpty()) if (vm.Cover.IsNullOrEmpty())
{ {
vm.Cover = a.remoteCover; vm.Cover = a.remoteCover;

@ -31,14 +31,13 @@ namespace Ombi.Core.Engine
{ {
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user, public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager,
ITvSender sender, IAuditRepository audit, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache, ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub) IRepository<RequestSubscription> sub) : base(user, requestService, rule, manager, cache, settings, sub)
{ {
TvApi = tvApi; TvApi = tvApi;
MovieDbApi = movApi; MovieDbApi = movApi;
NotificationHelper = helper; NotificationHelper = helper;
TvSender = sender; TvSender = sender;
Audit = audit;
_requestLog = rl; _requestLog = rl;
} }
@ -46,7 +45,6 @@ namespace Ombi.Core.Engine
private ITvMazeApi TvApi { get; } private ITvMazeApi TvApi { get; }
private IMovieDbApi MovieDbApi { get; } private IMovieDbApi MovieDbApi { get; }
private ITvSender TvSender { get; } private ITvSender TvSender { get; }
private IAuditRepository Audit { get; }
private readonly IRepository<RequestLog> _requestLog; private readonly IRepository<RequestLog> _requestLog;
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv) public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
@ -84,8 +82,6 @@ namespace Ombi.Core.Engine
} }
} }
await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tvBuilder.ChildRequest.Title}", Username);
var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId); var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId);
if (existingRequest != null) if (existingRequest != null)
{ {
@ -351,7 +347,6 @@ namespace Ombi.Core.Engine
public async Task<TvRequests> UpdateTvRequest(TvRequests request) public async Task<TvRequests> UpdateTvRequest(TvRequests request)
{ {
await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username);
var allRequests = TvRepository.Get(); var allRequests = TvRepository.Get();
var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id);
@ -394,7 +389,6 @@ namespace Ombi.Core.Engine
if (request.Approved) if (request.Approved)
{ {
NotificationHelper.Notify(request, NotificationType.RequestApproved); NotificationHelper.Notify(request, NotificationType.RequestApproved);
await Audit.Record(AuditType.Approved, AuditArea.TvRequest, $"Approved Request {request.Title}", Username);
// Autosend // Autosend
await TvSender.Send(request); await TvSender.Send(request);
} }
@ -426,9 +420,7 @@ namespace Ombi.Core.Engine
public async Task<ChildRequests> UpdateChildRequest(ChildRequests request) public async Task<ChildRequests> UpdateChildRequest(ChildRequests request)
{ {
await Audit.Record(AuditType.Updated, AuditArea.TvRequest, $"Updated Request {request.Title}", Username); await TvRepository.UpdateChild(request);
await TvRepository.UpdateChild(request);
return request; return request;
} }
@ -446,16 +438,14 @@ namespace Ombi.Core.Engine
// Delete the parent // Delete the parent
TvRepository.Db.TvRequests.Remove(parent); TvRepository.Db.TvRequests.Remove(parent);
} }
await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username);
await TvRepository.Db.SaveChangesAsync(); await TvRepository.Db.SaveChangesAsync();
} }
public async Task RemoveTvRequest(int requestId) public async Task RemoveTvRequest(int requestId)
{ {
var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId); var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId);
await Audit.Record(AuditType.Deleted, AuditArea.TvRequest, $"Deleting Request {request.Title}", Username); await TvRepository.Delete(request);
await TvRepository.Delete(request);
} }
public async Task<bool> UserHasRequest(string userId) public async Task<bool> UserHasRequest(string userId)

@ -16,8 +16,13 @@ namespace Ombi.Core.Models.Search
public string Cover { get; set; } public string Cover { get; set; }
public string Disk { get; set; } public string Disk { get; set; }
public decimal PercentOfTracks { get; set; } public decimal PercentOfTracks { get; set; }
public object[] Genres { get; set; }
public override RequestType Type => RequestType.Album; public override RequestType Type => RequestType.Album;
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0; public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
public bool FullyAvailable => PercentOfTracks == 100; public bool FullyAvailable => PercentOfTracks == 100;
// Below is from the INFO call NEED A SEPERATE VM FOR THIS IN V4 TODO
// TODO ADD TRACK COUNT
} }
} }

@ -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="GotifyNotificationSettings" />
public class GotifyNotificationViewModel : GotifySettings
{
/// <summary>
/// Gets or sets the notification templates.
/// </summary>
/// <value>
/// The notification templates.
/// </value>
public List<NotificationTemplates> NotificationTemplates { get; set; }
}
}

@ -11,9 +11,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="6.1.1" /> <PackageReference Include="AutoMapper" Version="6.1.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
<PackageReference Include="Hangfire" Version="1.6.21" /> <PackageReference Include="Hangfire" Version="1.6.22" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.2" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" /> <PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

@ -1,5 +1,7 @@
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers; using Ombi.Helpers;
@ -10,28 +12,31 @@ namespace Ombi.Core.Rule.Rules.Request
{ {
public class AutoApproveRule : BaseRequestRule, IRules<BaseRequest> public class AutoApproveRule : BaseRequestRule, IRules<BaseRequest>
{ {
public AutoApproveRule(IPrincipal principal) public AutoApproveRule(IPrincipal principal, OmbiUserManager um)
{ {
User = principal; User = principal;
_manager = um;
} }
private IPrincipal User { get; } private IPrincipal User { get; }
private readonly OmbiUserManager _manager;
public Task<RuleResult> Execute(BaseRequest obj) public async Task<RuleResult> Execute(BaseRequest obj)
{ {
if (User.IsInRole(OmbiRoles.Admin)) var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
{ {
obj.Approved = true; obj.Approved = true;
return Task.FromResult(Success()); return Success();
} }
if (obj.RequestType == RequestType.Movie && User.IsInRole(OmbiRoles.AutoApproveMovie)) if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
obj.Approved = true; obj.Approved = true;
if (obj.RequestType == RequestType.TvShow && User.IsInRole(OmbiRoles.AutoApproveTv)) if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
obj.Approved = true; obj.Approved = true;
if (obj.RequestType == RequestType.Album && User.IsInRole(OmbiRoles.AutoApproveMusic)) if (obj.RequestType == RequestType.Album && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
obj.Approved = true; obj.Approved = true;
return Task.FromResult(Success()); // We don't really care, we just don't set the obj to approve return Success(); // We don't really care, we just don't set the obj to approve
} }
} }
} }

@ -1,57 +1,59 @@
using Ombi.Store.Entities; using Ombi.Store.Entities;
using System.IO; using System.IO;
using System.Security.Claims;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
namespace Ombi.Core.Rule.Rules namespace Ombi.Core.Rule.Rules.Request
{ {
public class CanRequestRule : BaseRequestRule, IRules<BaseRequest> public class CanRequestRule : BaseRequestRule, IRules<BaseRequest>
{ {
public CanRequestRule(IPrincipal principal) public CanRequestRule(IPrincipal principal, OmbiUserManager manager)
{ {
User = principal; User = principal;
_manager = manager;
} }
private IPrincipal User { get; } private IPrincipal User { get; }
private readonly OmbiUserManager _manager;
public Task<RuleResult> Execute(BaseRequest obj) public async Task<RuleResult> Execute(BaseRequest obj)
{ {
if (User.IsInRole(OmbiRoles.Admin)) var user = await _manager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
{ if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin))
return Task.FromResult(Success()); return Success();
}
if (obj.RequestType == RequestType.Movie) if (obj.RequestType == RequestType.Movie)
{ {
if (User.IsInRole(OmbiRoles.RequestMovie) || User.IsInRole(OmbiRoles.AutoApproveMovie)) if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMovie) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
{ return Success();
return Task.FromResult(Success()); return Fail("You do not have permissions to Request a Movie");
}
return Task.FromResult(Fail("You do not have permissions to Request a Movie"));
} }
if (obj.RequestType == RequestType.TvShow) if (obj.RequestType == RequestType.TvShow)
{ {
if (User.IsInRole(OmbiRoles.RequestTv) || User.IsInRole(OmbiRoles.AutoApproveTv)) if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestTv) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
{ {
return Task.FromResult(Success()); return TSuccess();
} }
return Task.FromResult(Fail("You do not have permissions to Request a TV Show")); return Fail("You do not have permissions to Request a TV Show");
} }
if (obj.RequestType == RequestType.Album) if (obj.RequestType == RequestType.Album)
{ {
if (User.IsInRole(OmbiRoles.RequestMusic) || User.IsInRole(OmbiRoles.AutoApproveMusic)) if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMusic) || await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))
{ {
return Task.FromResult(Success()); return Success();
} }
return Task.FromResult(Fail("You do not have permissions to Request an Album")); return Fail("You do not have permissions to Request an Album");
} }
throw new InvalidDataException("Permission check failed: unknown RequestType"); throw new InvalidDataException("Permission check failed: unknown RequestType");

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.Extensions.Logging;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository.Requests; using Ombi.Store.Repository.Requests;
@ -24,7 +24,7 @@ namespace Ombi.Core.Rule.Rules.Search
if (!airedButNotAvailable) if (!airedButNotAvailable)
{ {
var unairedEpisodes = search.SeasonRequests.Any(x => var unairedEpisodes = search.SeasonRequests.Any(x =>
x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date)); x.Episodes.Any(c => !c.Available && c.AirDate > DateTime.Now.Date || c.AirDate != DateTime.MinValue));
if (unairedEpisodes) if (unairedEpisodes)
{ {
search.FullyAvailable = true; search.FullyAvailable = true;
@ -34,28 +34,36 @@ namespace Ombi.Core.Rule.Rules.Search
} }
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<PlexEpisode> allEpisodes, EpisodeRequests episode, public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<PlexEpisode> allEpisodes, EpisodeRequests episode,
SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb) SeasonRequests season, PlexServerContent item, bool useTheMovieDb, bool useTvDb, ILogger log)
{ {
PlexEpisode epExists = null; PlexEpisode epExists = null;
if (useImdb) try
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.ImdbId == item.ImdbId.ToString());
}
if (useTheMovieDb) if (useImdb)
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x => epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TheMovieDbId == item.TheMovieDbId.ToString()); x.Series.ImdbId == item.ImdbId);
} }
if (useTvDb) if (useTheMovieDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TheMovieDbId == item.TheMovieDbId);
}
if (useTvDb)
{
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TvDbId == item.TvDbId);
}
}
catch (Exception e)
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x => log.LogError(e, "Exception thrown when attempting to check if something is available");
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TvDbId == item.TvDbId.ToString());
} }
if (epExists != null) if (epExists != null)
@ -71,21 +79,21 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x => epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.ImdbId == item.ImdbId.ToString()); x.Series.ImdbId == item.ImdbId);
} }
if (useTheMovieDb) if (useTheMovieDb)
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x => epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TheMovieDbId == item.TheMovieDbId.ToString()); x.Series.TheMovieDbId == item.TheMovieDbId);
} }
if (useTvDb) if (useTvDb)
{ {
epExists = await allEpisodes.FirstOrDefaultAsync(x => epExists = await allEpisodes.FirstOrDefaultAsync(x =>
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
x.Series.TvDbId == item.TvDbId.ToString()); x.Series.TvDbId == item.TvDbId);
} }
if (epExists != null) if (epExists != null)

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
@ -11,12 +13,14 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
public class EmbyAvailabilityRule : BaseSearchRule, IRules<SearchViewModel> public class EmbyAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
{ {
public EmbyAvailabilityRule(IEmbyContentRepository repo) public EmbyAvailabilityRule(IEmbyContentRepository repo, ISettingsService<EmbySettings> s)
{ {
EmbyContentRepository = repo; EmbyContentRepository = repo;
EmbySettings = s;
} }
private IEmbyContentRepository EmbyContentRepository { get; } private IEmbyContentRepository EmbyContentRepository { get; }
private ISettingsService<EmbySettings> EmbySettings { get; }
public async Task<RuleResult> Execute(SearchViewModel obj) public async Task<RuleResult> Execute(SearchViewModel obj)
{ {
@ -60,7 +64,16 @@ namespace Ombi.Core.Rule.Rules.Search
if (item != null) if (item != null)
{ {
obj.Available = true; obj.Available = true;
obj.EmbyUrl = item.Url; var s = await EmbySettings.GetSettingsAsync();
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
if ((server?.ServerHostname ?? string.Empty).HasValue())
{
obj.EmbyUrl = $"{server.ServerHostname}#!/itemdetails.html?id={item.EmbyId}";
}
else
{
obj.EmbyUrl = $"https://app.emby.media/#!/itemdetails.html?id={item.EmbyId}";
}
if (obj.Type == RequestType.TvShow) if (obj.Type == RequestType.TvShow)
{ {

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers; using Ombi.Helpers;
@ -10,12 +11,14 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
public class PlexAvailabilityRule : BaseSearchRule, IRules<SearchViewModel> public class PlexAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
{ {
public PlexAvailabilityRule(IPlexContentRepository repo) public PlexAvailabilityRule(IPlexContentRepository repo, ILogger<PlexAvailabilityRule> log)
{ {
PlexContentRepository = repo; PlexContentRepository = repo;
Log = log;
} }
private IPlexContentRepository PlexContentRepository { get; } private IPlexContentRepository PlexContentRepository { get; }
private ILogger Log { get; }
public async Task<RuleResult> Execute(SearchViewModel obj) public async Task<RuleResult> Execute(SearchViewModel obj)
{ {
@ -72,7 +75,7 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
foreach (var episode in season.Episodes) foreach (var episode in season.Episodes)
{ {
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb); await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log);
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EnsureThat;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Api.Lidarr; using Ombi.Api.Lidarr;
using Ombi.Api.Lidarr.Models; using Ombi.Api.Lidarr.Models;
@ -87,6 +88,11 @@ namespace Ombi.Core.Senders
if (artist == null || artist.id <= 0) if (artist == null || artist.id <= 0)
{ {
EnsureArg.IsNotNullOrEmpty(model.ForeignArtistId, nameof(model.ForeignArtistId));
EnsureArg.IsNotNullOrEmpty(model.ForeignAlbumId, nameof(model.ForeignAlbumId));
EnsureArg.IsNotNullOrEmpty(model.ArtistName, nameof(model.ArtistName));
EnsureArg.IsNotNullOrEmpty(rootFolderPath, nameof(rootFolderPath));
// Create artist // Create artist
var newArtist = new ArtistAdd var newArtist = new ArtistAdd
{ {

@ -32,6 +32,7 @@ using Ombi.Api.CouchPotato;
using Ombi.Api.DogNzb; using Ombi.Api.DogNzb;
using Ombi.Api.FanartTv; using Ombi.Api.FanartTv;
using Ombi.Api.Github; using Ombi.Api.Github;
using Ombi.Api.Gotify;
using Ombi.Api.Lidarr; using Ombi.Api.Lidarr;
using Ombi.Api.Mattermost; using Ombi.Api.Mattermost;
using Ombi.Api.Notifications; using Ombi.Api.Notifications;
@ -60,6 +61,7 @@ using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Jobs.SickRage;
using Ombi.Schedule.Processor; using Ombi.Schedule.Processor;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz.Spi;
namespace Ombi.DependencyInjection namespace Ombi.DependencyInjection
{ {
@ -120,6 +122,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IOmbiService, OmbiService>(); services.AddTransient<IOmbiService, OmbiService>();
services.AddTransient<IFanartTvApi, FanartTvApi>(); services.AddTransient<IFanartTvApi, FanartTvApi>();
services.AddTransient<IPushoverApi, PushoverApi>(); services.AddTransient<IPushoverApi, PushoverApi>();
services.AddTransient<IGotifyApi, GotifyApi>();
services.AddTransient<IMattermostApi, MattermostApi>(); services.AddTransient<IMattermostApi, MattermostApi>();
services.AddTransient<ICouchPotatoApi, CouchPotatoApi>(); services.AddTransient<ICouchPotatoApi, CouchPotatoApi>();
services.AddTransient<IDogNzbApi, DogNzbApi>(); services.AddTransient<IDogNzbApi, DogNzbApi>();
@ -132,28 +135,28 @@ namespace Ombi.DependencyInjection
} }
public static void RegisterStore(this IServiceCollection services) { public static void RegisterStore(this IServiceCollection services) {
services.AddEntityFrameworkSqlite().AddDbContext<OmbiContext>(); services.AddDbContext<OmbiContext>();
services.AddEntityFrameworkSqlite().AddDbContext<SettingsContext>(); services.AddDbContext<SettingsContext>();
services.AddEntityFrameworkSqlite().AddDbContext<ExternalContext>(); services.AddDbContext<ExternalContext>();
services.AddScoped<IOmbiContext, OmbiContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 services.AddScoped<IOmbiContext, OmbiContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
services.AddScoped<ISettingsContext, SettingsContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 services.AddScoped<ISettingsContext, SettingsContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
services.AddScoped<IExternalContext, ExternalContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 services.AddScoped<IExternalContext, ExternalContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
services.AddTransient<ISettingsRepository, SettingsJsonRepository>(); services.AddScoped<ISettingsRepository, SettingsJsonRepository>();
services.AddTransient<ISettingsResolver, SettingsResolver>(); services.AddScoped<ISettingsResolver, SettingsResolver>();
services.AddTransient<IPlexContentRepository, PlexServerContentRepository>(); services.AddScoped<IPlexContentRepository, PlexServerContentRepository>();
services.AddTransient<IEmbyContentRepository, EmbyContentRepository>(); services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>(); services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
services.AddTransient<ITvRequestRepository, TvRequestRepository>(); services.AddScoped<ITvRequestRepository, TvRequestRepository>();
services.AddTransient<IMovieRequestRepository, MovieRequestRepository>(); services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
services.AddTransient<IMusicRequestRepository, MusicRequestRepository>(); services.AddScoped<IMusicRequestRepository, MusicRequestRepository>();
services.AddTransient<IAuditRepository, AuditRepository>(); services.AddScoped<IAuditRepository, AuditRepository>();
services.AddTransient<IApplicationConfigRepository, ApplicationConfigRepository>(); services.AddScoped<IApplicationConfigRepository, ApplicationConfigRepository>();
services.AddTransient<ITokenRepository, TokenRepository>(); services.AddScoped<ITokenRepository, TokenRepository>();
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>)); services.AddScoped(typeof(ISettingsService<>), typeof(SettingsService<>));
services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddTransient(typeof(IExternalRepository<>), typeof(ExternalRepository<>)); services.AddScoped(typeof(IExternalRepository<>), typeof(ExternalRepository<>));
} }
public static void RegisterServices(this IServiceCollection services) public static void RegisterServices(this IServiceCollection services)
{ {
@ -161,7 +164,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<INotificationService, NotificationService>(); services.AddTransient<INotificationService, NotificationService>();
services.AddTransient<IEmailProvider, GenericEmailProvider>(); services.AddTransient<IEmailProvider, GenericEmailProvider>();
services.AddTransient<INotificationHelper, NotificationHelper>(); services.AddTransient<INotificationHelper, NotificationHelper>();
services.AddTransient<ICacheService, CacheService>(); services.AddSingleton<ICacheService, CacheService>();
services.AddTransient<IDiscordNotification, DiscordNotification>(); services.AddTransient<IDiscordNotification, DiscordNotification>();
services.AddTransient<IEmailNotification, EmailNotification>(); services.AddTransient<IEmailNotification, EmailNotification>();
@ -170,6 +173,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<ISlackNotification, SlackNotification>(); services.AddTransient<ISlackNotification, SlackNotification>();
services.AddTransient<IMattermostNotification, MattermostNotification>(); services.AddTransient<IMattermostNotification, MattermostNotification>();
services.AddTransient<IPushoverNotification, PushoverNotification>(); services.AddTransient<IPushoverNotification, PushoverNotification>();
services.AddTransient<IGotifyNotification, GotifyNotification>();
services.AddTransient<ITelegramNotification, TelegramNotification>(); services.AddTransient<ITelegramNotification, TelegramNotification>();
services.AddTransient<IMobileNotification, MobileNotification>(); services.AddTransient<IMobileNotification, MobileNotification>();
services.AddTransient<IChangeLogProcessor, ChangeLogProcessor>(); services.AddTransient<IChangeLogProcessor, ChangeLogProcessor>();
@ -177,6 +181,7 @@ namespace Ombi.DependencyInjection
public static void RegisterJobs(this IServiceCollection services) public static void RegisterJobs(this IServiceCollection services)
{ {
services.AddSingleton<IJobFactory, IoCJobFactory>(provider => new IoCJobFactory(provider));
services.AddTransient<IBackgroundJobClient, BackgroundJobClient>(); services.AddTransient<IBackgroundJobClient, BackgroundJobClient>();
services.AddTransient<IPlexContentSync, PlexContentSync>(); services.AddTransient<IPlexContentSync, PlexContentSync>();
@ -197,7 +202,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<ISickRageSync, SickRageSync>(); services.AddTransient<ISickRageSync, SickRageSync>();
services.AddTransient<IRefreshMetadata, RefreshMetadata>(); services.AddTransient<IRefreshMetadata, RefreshMetadata>();
services.AddTransient<INewsletterJob, NewsletterJob>(); services.AddTransient<INewsletterJob, NewsletterJob>();
services.AddTransient<IPlexRecentlyAddedSync, PlexRecentlyAddedSync>(); //services.AddTransient<IPlexRecentlyAddedSync, PlexRecentlyAddedSync>();
services.AddTransient<ILidarrAlbumSync, LidarrAlbumSync>(); services.AddTransient<ILidarrAlbumSync, LidarrAlbumSync>();
services.AddTransient<ILidarrArtistSync, LidarrArtistSync>(); services.AddTransient<ILidarrArtistSync, LidarrArtistSync>();
services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>(); services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>();

@ -28,18 +28,15 @@ namespace Ombi.Helpers
return result; return result;
} }
using (await _mutex.LockAsync()) if (_memoryCache.TryGetValue(cacheKey, out result))
{ {
if (_memoryCache.TryGetValue(cacheKey, out result))
{
return result;
}
result = await factory();
_memoryCache.Set(cacheKey, result, absoluteExpiration);
return result; return result;
} }
result = await factory();
_memoryCache.Set(cacheKey, result, absoluteExpiration);
return result;
} }
public void Remove(string key) public void Remove(string key)
@ -47,34 +44,34 @@ namespace Ombi.Helpers
_memoryCache.Remove(key); _memoryCache.Remove(key);
} }
public T GetOrAdd<T>(string cacheKey, Func<T> factory, DateTime absoluteExpiration) public T GetOrAdd<T>(string cacheKey, Func<T> factory, DateTime absoluteExpiration)
{
// locks get and set internally
if (_memoryCache.TryGetValue<T>(cacheKey, out var result))
{ {
// locks get and set internally return result;
if (_memoryCache.TryGetValue<T>(cacheKey, out var result)) }
lock (TypeLock<T>.Lock)
{
if (_memoryCache.TryGetValue(cacheKey, out result))
{ {
return result; return result;
} }
lock (TypeLock<T>.Lock) result = factory();
{ _memoryCache.Set(cacheKey, result, absoluteExpiration);
if (_memoryCache.TryGetValue(cacheKey, out result))
{
return result;
}
result = factory();
_memoryCache.Set(cacheKey, result, absoluteExpiration);
return result; return result;
}
} }
}
private static class TypeLock<T>
{
public static object Lock { get; } = new object();
}
private static class TypeLock<T>
{
public static object Lock { get; } = new object();
}
} }
} }

@ -16,7 +16,6 @@
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>. // License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
/// <summary> /// <summary>
/// Helper class that provides common values for the cron expressions. /// Helper class that provides common values for the cron expressions.
/// </summary> /// </summary>
@ -44,7 +43,7 @@
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> /// <param name="minute">The minute in which the schedule will be activated (0-59).</param>
public static string Hourly(int minute) public static string Hourly(int minute)
{ {
return $"{minute} * * * *"; return $"0 {minute} 0/1 1/1 * ? *";
} }
/// <summary> /// <summary>
@ -73,7 +72,7 @@
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> /// <param name="minute">The minute in which the schedule will be activated (0-59).</param>
public static string Daily(int hour, int minute) public static string Daily(int hour, int minute)
{ {
return $"{minute} {hour} * * *"; return $"0 {minute} {hour} 1/1 * ? *";
} }
/// <summary> /// <summary>
@ -114,7 +113,7 @@
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> /// <param name="minute">The minute in which the schedule will be activated (0-59).</param>
public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute) public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute)
{ {
return $"{minute} {hour} * * {(int)dayOfWeek}"; return $"0 {minute} {hour} ? * {(int)dayOfWeek} *";
} }
/// <summary> /// <summary>
@ -219,7 +218,7 @@
/// <param name="interval">The number of minutes to wait between every activation.</param> /// <param name="interval">The number of minutes to wait between every activation.</param>
public static string MinuteInterval(int interval) public static string MinuteInterval(int interval)
{ {
return $"*/{interval} * * * *"; return $"0 0/{interval} * 1/1 * ? *";
} }
/// <summary> /// <summary>
@ -228,7 +227,7 @@
/// <param name="interval">The number of hours to wait between every activation.</param> /// <param name="interval">The number of hours to wait between every activation.</param>
public static string HourInterval(int interval) public static string HourInterval(int interval)
{ {
return $"0 */{interval} * * *"; return $"0 0 0/{interval} 1/1 * ? *";
} }
/// <summary> /// <summary>
@ -237,7 +236,7 @@
/// <param name="interval">The number of days to wait between every activation.</param> /// <param name="interval">The number of days to wait between every activation.</param>
public static string DayInterval(int interval) public static string DayInterval(int interval)
{ {
return $"0 0 */{interval} * *"; return $"0 0 12 1/{interval} * ? *";
} }
/// <summary> /// <summary>
@ -249,4 +248,39 @@
return $"0 0 1 */{interval} *"; return $"0 0 1 */{interval} *";
} }
} }
//
// Summary:
// Specifies the day of the week.
public enum DayOfWeek
{
//
// Summary:
// Indicates Sunday.
Sunday = 1,
//
// Summary:
// Indicates Monday.
Monday = 2,
//
// Summary:
// Indicates Tuesday.
Tuesday = 3,
//
// Summary:
// Indicates Wednesday.
Wednesday = 4,
//
// Summary:
// Indicates Thursday.
Thursday = 5,
//
// Summary:
// Indicates Friday.
Friday = 6,
//
// Summary:
// Indicates Saturday.
Saturday = 7
}
} }

@ -32,6 +32,7 @@ namespace Ombi.Helpers
public static EventId MattermostNotification => new EventId(4004); public static EventId MattermostNotification => new EventId(4004);
public static EventId PushoverNotification => new EventId(4005); public static EventId PushoverNotification => new EventId(4005);
public static EventId TelegramNotifcation => new EventId(4006); public static EventId TelegramNotifcation => new EventId(4006);
public static EventId GotifyNotification => new EventId(4007);
public static EventId TvSender => new EventId(5000); public static EventId TvSender => new EventId(5000);
public static EventId SonarrSender => new EventId(5001); public static EventId SonarrSender => new EventId(5001);

@ -10,5 +10,6 @@
Slack = 5, Slack = 5,
Mattermost = 6, Mattermost = 6,
Mobile = 7, Mobile = 7,
Gotify = 8,
} }
} }

@ -19,6 +19,7 @@ namespace Ombi.Mapping.Profiles
CreateMap<UpdateSettingsViewModel, UpdateSettings>().ReverseMap(); CreateMap<UpdateSettingsViewModel, UpdateSettings>().ReverseMap();
CreateMap<MobileNotificationsViewModel, MobileNotificationSettings>().ReverseMap(); CreateMap<MobileNotificationsViewModel, MobileNotificationSettings>().ReverseMap();
CreateMap<NewsletterNotificationViewModel, NewsletterSettings>().ReverseMap(); CreateMap<NewsletterNotificationViewModel, NewsletterSettings>().ReverseMap();
CreateMap<GotifyNotificationViewModel, GotifySettings>().ReverseMap();
} }
} }
} }

@ -0,0 +1,116 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Gotify;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
namespace Ombi.Notifications.Agents
{
public class GotifyNotification : BaseNotification<GotifySettings>, IGotifyNotification
{
public GotifyNotification(IGotifyApi api, ISettingsService<GotifySettings> sn, ILogger<GotifyNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
{
Api = api;
Logger = log;
}
public override string NotificationName => "GotifyNotification";
private IGotifyApi Api { get; }
private ILogger<GotifyNotification> Logger { get; }
protected override bool ValidateConfiguration(GotifySettings settings)
{
return settings.Enabled && !string.IsNullOrEmpty(settings.BaseUrl) && !string.IsNullOrEmpty(settings.ApplicationToken);
}
protected override async Task NewRequest(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.NewRequest);
}
protected override async Task NewIssue(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.Issue);
}
protected override async Task IssueComment(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.IssueComment);
}
protected override async Task IssueResolved(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.IssueResolved);
}
protected override async Task AddedToRequestQueue(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.ItemAddedToFaultQueue);
}
protected override async Task RequestDeclined(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.RequestDeclined);
}
protected override async Task RequestApproved(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.RequestApproved);
}
protected override async Task AvailableRequest(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.RequestAvailable);
}
protected override async Task Send(NotificationMessage model, GotifySettings settings)
{
try
{
await Api.PushAsync(settings.BaseUrl, settings.ApplicationToken, model.Subject, model.Message, settings.Priority);
}
catch (Exception e)
{
Logger.LogError(LoggingEvents.GotifyNotification, e, "Failed to send Gotify notification");
}
}
protected override async Task Test(NotificationOptions model, GotifySettings settings)
{
var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!";
var notification = new NotificationMessage
{
Message = message,
};
await Send(notification, settings);
}
private async Task Run(NotificationOptions model, GotifySettings settings, NotificationType type)
{
var parsed = await LoadTemplate(NotificationAgent.Gotify, type, model);
if (parsed.Disabled)
{
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Gotify}");
return;
}
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
}
}

@ -0,0 +1,6 @@
namespace Ombi.Notifications.Agents
{
public interface IGotifyNotification : INotification
{
}
}

@ -52,7 +52,7 @@ namespace Ombi.Notifications.Agents
private void AddOtherInformation(NotificationOptions model, NotificationMessage notification, private void AddOtherInformation(NotificationOptions model, NotificationMessage notification,
NotificationMessageContent parsed) NotificationMessageContent parsed)
{ {
notification.Other.Add("image", parsed.Image); notification.Other.Add("image", parsed?.Image ?? string.Empty);
notification.Other.Add("title", model.RequestType == RequestType.Movie ? MovieRequest.Title : TvRequest.Title); notification.Other.Add("title", model.RequestType == RequestType.Movie ? MovieRequest.Title : TvRequest.Title);
} }

@ -30,6 +30,7 @@ namespace Ombi.Notifications
_log = log; _log = log;
AlbumRepository = album; AlbumRepository = album;
UserNotificationPreferences = notificationUserPreferences; UserNotificationPreferences = notificationUserPreferences;
Settings.ClearCache();
} }
protected ISettingsService<T> Settings { get; } protected ISettingsService<T> Settings { get; }

@ -4,6 +4,7 @@ using EnsureThat;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MimeKit; using MimeKit;
using MimeKit.Utils;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Notifications.Models; using Ombi.Notifications.Models;
@ -37,6 +38,15 @@ namespace Ombi.Notifications
var customization = await CustomizationSettings.GetSettingsAsync(); var customization = await CustomizationSettings.GetSettingsAsync();
var html = email.LoadTemplate(model.Subject, model.Message, null, customization.Logo); var html = email.LoadTemplate(model.Subject, model.Message, null, customization.Logo);
var messageId = MimeUtils.GenerateMessageId();
if (customization.ApplicationUrl.HasValue())
{
if (Uri.TryCreate(customization.ApplicationUrl, UriKind.RelativeOrAbsolute, out var url))
{
messageId = MimeUtils.GenerateMessageId(url.IdnHost);
}
}
var textBody = string.Empty; var textBody = string.Empty;
@ -50,7 +60,8 @@ namespace Ombi.Notifications
var message = new MimeMessage var message = new MimeMessage
{ {
Body = body.ToMessageBody(), Body = body.ToMessageBody(),
Subject = model.Subject Subject = model.Subject,
MessageId = messageId
}; };
message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress));
message.To.Add(new MailboxAddress(model.To, model.To)); message.To.Add(new MailboxAddress(model.To, model.To));

@ -15,6 +15,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" /> <ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
<ProjectReference Include="..\Ombi.Api.Gotify\Ombi.Api.Gotify.csproj" />
<ProjectReference Include="..\Ombi.Api.Mattermost\Ombi.Api.Mattermost.csproj" /> <ProjectReference Include="..\Ombi.Api.Mattermost\Ombi.Api.Mattermost.csproj" />
<ProjectReference Include="..\Ombi.Api.Notifications\Ombi.Api.Notifications.csproj" /> <ProjectReference Include="..\Ombi.Api.Notifications\Ombi.Api.Notifications.csproj" />
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" /> <ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />

@ -32,7 +32,7 @@ namespace Ombi.Schedule.Tests
[Test] [Test]
public async Task DoesNotRun_WhenDisabled() public async Task DoesNotRun_WhenDisabled()
{ {
await Job.Start(); await Job.Execute(null);
Repo.Verify(x => x.GetAll(),Times.Never); Repo.Verify(x => x.GetAll(),Times.Never);
} }
@ -50,7 +50,7 @@ namespace Ombi.Schedule.Tests
Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 });
Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery<Issues>(issues)); Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery<Issues>(issues));
await Job.Start(); await Job.Execute(null);
Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted)); Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted));
Repo.Verify(x => x.SaveChangesAsync(), Times.Once); Repo.Verify(x => x.SaveChangesAsync(), Times.Once);
@ -75,7 +75,7 @@ namespace Ombi.Schedule.Tests
Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 });
Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery<Issues>(issues)); Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery<Issues>(issues));
await Job.Start(); await Job.Execute(null);
Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted));
Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted)); Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted));

@ -0,0 +1,36 @@
using Moq;
using NUnit.Framework;
using Quartz;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Schedule.Tests
{
[TestFixture]
public class OmbiQuartzTests
{
[Test]
[Ignore("Cannot get this to work")]
public async Task Test()
{
var scheduleMock = new Mock<IScheduler>();
scheduleMock.Setup(x => x.TriggerJob(It.IsAny<JobKey>(),
It.IsAny<CancellationToken>()));
var sut = new QuartzMock(scheduleMock);
//await QuartzMock.TriggerJob("ABC");
scheduleMock.Verify(x => x.TriggerJob(It.Is<JobKey>(j => j.Name == "ABC"),
default(CancellationToken)), Times.Once);
}
}
public class QuartzMock : OmbiQuartz
{
public QuartzMock(Mock<IScheduler> mock)
{
_instance = this;
_scheduler = mock.Object;
}
}
}

@ -46,7 +46,7 @@ namespace Ombi.Schedule.Tests
_movie.Setup(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable()); _movie.Setup(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable());
_repo.Setup(x => x.Get("test")).ReturnsAsync(new PlexServerContent()); _repo.Setup(x => x.Get("test")).ReturnsAsync(new PlexServerContent());
await Checker.Start(); await Checker.Execute(null);
_movie.Verify(x => x.Save(), Times.Once); _movie.Verify(x => x.Save(), Times.Once);
@ -62,8 +62,8 @@ namespace Ombi.Schedule.Tests
}; };
_movie.Setup(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable()); _movie.Setup(x => x.GetAll()).Returns(new List<MovieRequests> { request }.AsQueryable());
await Checker.Start(); await Checker.Execute(null);
Assert.False(request.Available); Assert.False(request.Available);
} }
@ -107,7 +107,7 @@ namespace Ombi.Schedule.Tests
}.AsQueryable); }.AsQueryable);
_repo.Setup(x => x.Include(It.IsAny<IQueryable<PlexEpisode>>(),It.IsAny<Expression<Func<PlexEpisode, PlexServerContent>>>())); _repo.Setup(x => x.Include(It.IsAny<IQueryable<PlexEpisode>>(),It.IsAny<Expression<Func<PlexEpisode, PlexServerContent>>>()));
await Checker.Start(); await Checker.Execute(null);
_tv.Verify(x => x.Save(), Times.Once); _tv.Verify(x => x.Save(), Times.Once);

@ -0,0 +1,32 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Spi;
namespace Ombi.Schedule
{
public class IoCJobFactory : IJobFactory
{
private readonly IServiceProvider _factory;
public IoCJobFactory(IServiceProvider factory)
{
_factory = factory;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var scopeFactory = _factory.GetService<IServiceScopeFactory>();
var scope = scopeFactory.CreateScope();
var scopedContainer = scope.ServiceProvider;
var implementation = scopedContainer.GetRequiredService(bundle.JobDetail.JobType) as IJob;
return implementation;
}
public void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
}

@ -65,25 +65,24 @@ namespace Ombi.Schedule
{ {
var s = _jobSettings.GetSettings(); var s = _jobSettings.GetSettings();
RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s)); // RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s));
RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s)); // RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s));
RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s)); // RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s));
RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(false), JobSettingsHelper.PlexContent(s)); // //RecurringJob.AddOrUpdate(() => _plexContentSync.Execute(null), JobSettingsHelper.PlexContent(s));
RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s)); // //RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s));
RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s)); // RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s));
RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s)); // RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s));
RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s)); // RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s));
RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(s)); // RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(s));
RecurringJob.AddOrUpdate(() => _issuesPurge.Start(), JobSettingsHelper.IssuePurge(s)); // RecurringJob.AddOrUpdate(() => _issuesPurge.Start(), JobSettingsHelper.IssuePurge(s));
RecurringJob.AddOrUpdate(() => _updater.Update(null), JobSettingsHelper.Updater(s)); // RecurringJob.AddOrUpdate(() => _updater.Update(null), JobSettingsHelper.Updater(s));
RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); // RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s));
RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); // RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s));
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); // RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s));
RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); //// RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s));
RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); // RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s));
RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s));
} }
private bool _disposed; private bool _disposed;
@ -94,7 +93,6 @@ namespace Ombi.Schedule
if (disposing) if (disposing)
{ {
_plexContentSync?.Dispose();
_radarrSync?.Dispose(); _radarrSync?.Dispose();
_updater?.Dispose(); _updater?.Dispose();
_plexUserImporter?.Dispose(); _plexUserImporter?.Dispose();

@ -36,6 +36,7 @@ using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.Couchpotato namespace Ombi.Schedule.Jobs.Couchpotato
{ {
@ -56,7 +57,7 @@ namespace Ombi.Schedule.Jobs.Couchpotato
private readonly ILogger<CouchPotatoSync> _log; private readonly ILogger<CouchPotatoSync> _log;
private readonly IExternalContext _ctx; private readonly IExternalContext _ctx;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var settings = await _settings.GetSettingsAsync(); var settings = await _settings.GetSettingsAsync();
if (!settings.Enabled) if (!settings.Enabled)

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Couchpotato
{ {
public interface ICouchPotatoSync : IBaseJob public interface ICouchPotatoSync : IBaseJob
{ {
Task Start();
} }
} }

@ -37,6 +37,7 @@ using Ombi.Notifications.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests; using Ombi.Store.Repository.Requests;
using Quartz;
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
@ -58,7 +59,7 @@ namespace Ombi.Schedule.Jobs.Emby
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
private readonly ILogger<EmbyAvaliabilityChecker> _log; private readonly ILogger<EmbyAvaliabilityChecker> _log;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
await ProcessMovies(); await ProcessMovies();
await ProcessTv(); await ProcessTv();

@ -12,6 +12,7 @@ using Ombi.Helpers;
using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Ombi;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
using Serilog; using Serilog;
using EmbyMediaType = Ombi.Store.Entities.EmbyMediaType; using EmbyMediaType = Ombi.Store.Entities.EmbyMediaType;
@ -20,25 +21,21 @@ namespace Ombi.Schedule.Jobs.Emby
public class EmbyContentSync : IEmbyContentSync public class EmbyContentSync : IEmbyContentSync
{ {
public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApi api, ILogger<EmbyContentSync> logger, public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApi api, ILogger<EmbyContentSync> logger,
IEmbyContentRepository repo, IEmbyEpisodeSync epSync, IRefreshMetadata metadata) IEmbyContentRepository repo)
{ {
_logger = logger; _logger = logger;
_settings = settings; _settings = settings;
_api = api; _api = api;
_repo = repo; _repo = repo;
_episodeSync = epSync;
_metadata = metadata;
} }
private readonly ILogger<EmbyContentSync> _logger; private readonly ILogger<EmbyContentSync> _logger;
private readonly ISettingsService<EmbySettings> _settings; private readonly ISettingsService<EmbySettings> _settings;
private readonly IEmbyApi _api; private readonly IEmbyApi _api;
private readonly IEmbyContentRepository _repo; private readonly IEmbyContentRepository _repo;
private readonly IEmbyEpisodeSync _episodeSync;
private readonly IRefreshMetadata _metadata;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var embySettings = await _settings.GetSettingsAsync(); var embySettings = await _settings.GetSettingsAsync();
if (!embySettings.Enable) if (!embySettings.Enable)
@ -57,8 +54,9 @@ namespace Ombi.Schedule.Jobs.Emby
} }
// Episodes // Episodes
BackgroundJob.Enqueue(() => _episodeSync.Start());
BackgroundJob.Enqueue(() => _metadata.Start()); await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby");
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
} }

@ -36,29 +36,27 @@ using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
public class EmbyEpisodeSync : IEmbyEpisodeSync public class EmbyEpisodeSync : IEmbyEpisodeSync
{ {
public EmbyEpisodeSync(ISettingsService<EmbySettings> s, IEmbyApi api, ILogger<EmbyEpisodeSync> l, IEmbyContentRepository repo, public EmbyEpisodeSync(ISettingsService<EmbySettings> s, IEmbyApi api, ILogger<EmbyEpisodeSync> l, IEmbyContentRepository repo)
IEmbyAvaliabilityChecker checker)
{ {
_api = api; _api = api;
_logger = l; _logger = l;
_settings = s; _settings = s;
_repo = repo; _repo = repo;
_avaliabilityChecker = checker;
} }
private readonly ISettingsService<EmbySettings> _settings; private readonly ISettingsService<EmbySettings> _settings;
private readonly IEmbyApi _api; private readonly IEmbyApi _api;
private readonly ILogger<EmbyEpisodeSync> _logger; private readonly ILogger<EmbyEpisodeSync> _logger;
private readonly IEmbyContentRepository _repo; private readonly IEmbyContentRepository _repo;
private readonly IEmbyAvaliabilityChecker _avaliabilityChecker;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var settings = await _settings.GetSettingsAsync(); var settings = await _settings.GetSettingsAsync();
@ -67,7 +65,8 @@ namespace Ombi.Schedule.Jobs.Emby
await CacheEpisodes(server); await CacheEpisodes(server);
} }
BackgroundJob.Enqueue(() => _avaliabilityChecker.Start());
await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby");
} }
private async Task CacheEpisodes(EmbyServers server) private async Task CacheEpisodes(EmbyServers server)
@ -118,6 +117,22 @@ namespace Ombi.Schedule.Jobs.Emby
Title = ep.Name, Title = ep.Name,
AddedAt = DateTime.UtcNow AddedAt = DateTime.UtcNow
}); });
if (ep.IndexNumberEnd.HasValue && ep.IndexNumberEnd.Value != ep.IndexNumber)
{
epToAdd.Add(new EmbyEpisode
{
EmbyId = ep.Id,
EpisodeNumber = ep.IndexNumberEnd.Value,
SeasonNumber = ep.ParentIndexNumber,
ParentId = ep.SeriesId,
TvDbId = ep.ProviderIds.Tvdb,
TheMovieDbId = ep.ProviderIds.Tmdb,
ImdbId = ep.ProviderIds.Imdb,
Title = ep.Name,
AddedAt = DateTime.UtcNow
});
}
} }
} }
@ -142,7 +157,6 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
_settings?.Dispose(); _settings?.Dispose();
_repo?.Dispose(); _repo?.Dispose();
_avaliabilityChecker?.Dispose();
} }
_disposed = true; _disposed = true;
} }

@ -37,6 +37,7 @@ using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
@ -58,7 +59,7 @@ namespace Ombi.Schedule.Jobs.Emby
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<UserManagementSettings> _userManagementSettings; private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var userManagementSettings = await _userManagementSettings.GetSettingsAsync(); var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
if (!userManagementSettings.ImportEmbyUsers) if (!userManagementSettings.ImportEmbyUsers)

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyAvaliabilityChecker : IBaseJob public interface IEmbyAvaliabilityChecker : IBaseJob
{ {
Task Start();
} }
} }

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyContentSync : IBaseJob public interface IEmbyContentSync : IBaseJob
{ {
Task Start();
} }
} }

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyEpisodeSync : IBaseJob public interface IEmbyEpisodeSync : IBaseJob
{ {
Task Start();
} }
} }

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyUserImporter : IBaseJob public interface IEmbyUserImporter : IBaseJob
{ {
Task Start();
} }
} }

@ -25,11 +25,12 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using Quartz;
using System; using System;
namespace Ombi.Schedule namespace Ombi.Schedule
{ {
public interface IBaseJob : IDisposable public interface IBaseJob : IJob, IDisposable
{ {
} }

@ -1,12 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.Lidarr namespace Ombi.Schedule.Jobs.Lidarr
{ {
public interface ILidarrArtistSync public interface ILidarrArtistSync : IJob
{ {
Task CacheContent();
void Dispose(); void Dispose();
Task<IEnumerable<LidarrArtistCache>> GetCachedContent(); Task<IEnumerable<LidarrArtistCache>> GetCachedContent();
} }

@ -11,6 +11,7 @@ using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
using ILogger = Microsoft.Extensions.Logging.ILogger; using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Ombi.Schedule.Jobs.Lidarr namespace Ombi.Schedule.Jobs.Lidarr
@ -35,7 +36,7 @@ namespace Ombi.Schedule.Jobs.Lidarr
private readonly IBackgroundJobClient _job; private readonly IBackgroundJobClient _job;
private readonly ILidarrAlbumSync _albumSync; private readonly ILidarrAlbumSync _albumSync;
public async Task CacheContent() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {

@ -3,7 +3,6 @@
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface IIssuesPurge : IBaseJob public interface IIssuesPurge : IBaseJob
{ {
Task Start();
} }
} }

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Plex.Interfaces
{ {
public interface IMediaDatabaseRefresh : IBaseJob public interface IMediaDatabaseRefresh : IBaseJob
{ {
Task Start();
} }
} }

@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface INewsletterJob : IBaseJob public interface INewsletterJob : IBaseJob
{ {
Task Start();
Task Start(NewsletterSettings settings, bool test); Task Start(NewsletterSettings settings, bool test);
} }
} }

@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface IOmbiAutomaticUpdater : IBaseJob public interface IOmbiAutomaticUpdater : IBaseJob
{ {
Task Update(PerformContext context);
string[] GetVersion(); string[] GetVersion();
Task<bool> UpdateAvailable(string branch, string currentVersion); Task<bool> UpdateAvailable(string branch, string currentVersion);
} }

@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface IRefreshMetadata : IBaseJob public interface IRefreshMetadata : IBaseJob
{ {
Task Start();
Task ProcessPlexServerContent(IEnumerable<int> contentIds); Task ProcessPlexServerContent(IEnumerable<int> contentIds);
} }
} }

@ -1,9 +1,9 @@
using System.Threading.Tasks; using Quartz;
using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface IResendFailedRequests public interface IResendFailedRequests : IJob
{ {
Task Start();
} }
} }

@ -3,7 +3,7 @@ using Ombi.Store.Entities;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
public interface IWelcomeEmail : IBaseJob public interface IWelcomeEmail
{ {
Task SendEmail(OmbiUser user); Task SendEmail(OmbiUser user);
} }

@ -5,6 +5,7 @@ using Ombi.Core.Settings;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
@ -20,7 +21,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly IRepository<Issues> _issuesRepository; private readonly IRepository<Issues> _issuesRepository;
private readonly ISettingsService<IssueSettings> _settings; private readonly ISettingsService<IssueSettings> _settings;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var settings = await _settings.GetSettingsAsync(); var settings = await _settings.GetSettingsAsync();
if (!settings.DeleteIssues) if (!settings.DeleteIssues)

@ -9,28 +9,28 @@ using Ombi.Helpers;
using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
public class MediaDatabaseRefresh : IMediaDatabaseRefresh public class MediaDatabaseRefresh : IMediaDatabaseRefresh
{ {
public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log, public MediaDatabaseRefresh(ISettingsService<PlexSettings> s, ILogger<MediaDatabaseRefresh> log,
IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IEmbyContentSync embySync) IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo)
{ {
_settings = s; _settings = s;
_log = log; _log = log;
_plexRepo = plexRepo; _plexRepo = plexRepo;
_embyRepo = embyRepo; _embyRepo = embyRepo;
_embyContentSync = embySync; _settings.ClearCache();
} }
private readonly ISettingsService<PlexSettings> _settings; private readonly ISettingsService<PlexSettings> _settings;
private readonly ILogger _log; private readonly ILogger _log;
private readonly IPlexContentRepository _plexRepo; private readonly IPlexContentRepository _plexRepo;
private readonly IEmbyContentRepository _embyRepo; private readonly IEmbyContentRepository _embyRepo;
private readonly IEmbyContentSync _embyContentSync;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {
@ -59,7 +59,7 @@ namespace Ombi.Schedule.Jobs.Ombi
await _embyRepo.ExecuteSql(episodeSQL); await _embyRepo.ExecuteSql(episodeSQL);
await _embyRepo.ExecuteSql(mainSql); await _embyRepo.ExecuteSql(mainSql);
BackgroundJob.Enqueue(() => _embyContentSync.Start()); await OmbiQuartz.TriggerJob(nameof(IEmbyContentSync), "Emby");
} }
catch (Exception e) catch (Exception e)
{ {

@ -26,6 +26,7 @@ using Ombi.Settings.Settings.Models.External;
using Ombi.Settings.Settings.Models.Notifications; using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
using ContentType = Ombi.Store.Entities.ContentType; using ContentType = Ombi.Store.Entities.ContentType;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
@ -57,6 +58,10 @@ namespace Ombi.Schedule.Jobs.Ombi
_ombiSettings = ombiSettings; _ombiSettings = ombiSettings;
_plexSettings = plexSettings; _plexSettings = plexSettings;
_embySettings = embySettings; _embySettings = embySettings;
_ombiSettings.ClearCache();
_plexSettings.ClearCache();
_emailSettings.ClearCache();
_customizationSettings.ClearCache();
} }
private readonly IPlexContentRepository _plex; private readonly IPlexContentRepository _plex;
@ -284,7 +289,7 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
} }
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var newsletterSettings = await _newsletterSettings.GetSettingsAsync(); var newsletterSettings = await _newsletterSettings.GetSettingsAsync();
await Start(newsletterSettings, false); await Start(newsletterSettings, false);
@ -359,7 +364,7 @@ namespace Ombi.Schedule.Jobs.Ombi
if (embySettings.Enable) if (embySettings.Enable)
{ {
await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode); await ProcessEmbyMovies(embyMovies, sb, ombiSettings.DefaultLanguageCode, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
} }
sb.Append("</tr>"); sb.Append("</tr>");
@ -385,7 +390,7 @@ namespace Ombi.Schedule.Jobs.Ombi
if (embySettings.Enable) if (embySettings.Enable)
{ {
await ProcessEmbyTv(embyEp, sb); await ProcessEmbyTv(embyEp, sb, embySettings.Servers.FirstOrDefault()?.ServerHostname ?? string.Empty);
} }
sb.Append("</tr>"); sb.Append("</tr>");
@ -490,7 +495,7 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
} }
private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb, string defaultLangaugeCode) private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb, string defaultLangaugeCode, string customUrl)
{ {
int count = 0; int count = 0;
var ordered = embyContent.OrderByDescending(x => x.AddedAt); var ordered = embyContent.OrderByDescending(x => x.AddedAt);
@ -511,6 +516,10 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
var mediaurl = content.Url; var mediaurl = content.Url;
if (customUrl.HasValue())
{
mediaurl = customUrl;
}
var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId), defaultLangaugeCode); var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId), defaultLangaugeCode);
if (info == null) if (info == null)
{ {
@ -754,7 +763,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private async Task ProcessEmbyTv(HashSet<EmbyEpisode> embyContent, StringBuilder sb) private async Task ProcessEmbyTv(HashSet<EmbyEpisode> embyContent, StringBuilder sb, string serverUrl)
{ {
var series = new List<EmbyContent>(); var series = new List<EmbyContent>();
foreach (var episode in embyContent) foreach (var episode in embyContent)
@ -809,7 +818,7 @@ namespace Ombi.Schedule.Jobs.Ombi
AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/"); AddBackgroundInsideTable(sb, $"https://image.tmdb.org/t/p/w1280/");
} }
AddPosterInsideTable(sb, banner); AddPosterInsideTable(sb, banner);
AddMediaServerUrl(sb, t.Url, banner); AddMediaServerUrl(sb, serverUrl.HasValue() ? serverUrl : t.Url, banner);
AddInfoTable(sb); AddInfoTable(sb);
var title = ""; var title = "";

@ -20,6 +20,7 @@ using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Updater; using Ombi.Updater;
using Quartz;
using SharpCompress.Readers; using SharpCompress.Readers;
using SharpCompress.Readers.Tar; using SharpCompress.Readers.Tar;
@ -41,7 +42,6 @@ namespace Ombi.Schedule.Jobs.Ombi
private IChangeLogProcessor Processor { get; } private IChangeLogProcessor Processor { get; }
private ISettingsService<UpdateSettings> Settings { get; } private ISettingsService<UpdateSettings> Settings { get; }
private readonly IProcessProvider _processProvider; private readonly IProcessProvider _processProvider;
private static PerformContext Ctx { get; set; }
private readonly IApplicationConfigRepository _appConfig; private readonly IApplicationConfigRepository _appConfig;
public string[] GetVersion() public string[] GetVersion()
@ -59,10 +59,8 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
[AutomaticRetry(Attempts = 1)] public async Task Execute(IJobExecutionContext job)
public async Task Update(PerformContext c)
{ {
Ctx = c;
Logger.LogDebug(LoggingEvents.Updater, "Starting Update job"); Logger.LogDebug(LoggingEvents.Updater, "Starting Update job");
var settings = await Settings.GetSettingsAsync(); var settings = await Settings.GetSettingsAsync();
@ -182,7 +180,7 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
Logger.LogDebug(LoggingEvents.Updater, "Starting Download"); Logger.LogDebug(LoggingEvents.Updater, "Starting Download");
await DownloadAsync(download.Url, zipDir, c); await DownloadAsync(download.Url, zipDir);
Logger.LogDebug(LoggingEvents.Updater, "Finished Download"); Logger.LogDebug(LoggingEvents.Updater, "Finished Download");
} }
catch (Exception e) catch (Exception e)
@ -321,7 +319,7 @@ namespace Ombi.Schedule.Jobs.Ombi
} }
} }
public async Task DownloadAsync(string requestUri, string filename, PerformContext ctx) public async Task DownloadAsync(string requestUri, string filename)
{ {
Logger.LogDebug(LoggingEvents.Updater, "Starting the DownloadAsync"); Logger.LogDebug(LoggingEvents.Updater, "Starting the DownloadAsync");
using (var client = new WebClient()) using (var client = new WebClient())

@ -15,6 +15,7 @@ using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Plex; using Ombi.Schedule.Jobs.Plex;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
@ -22,8 +23,7 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo,
ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings, ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings,
IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability, IMovieDbApi movieApi, ISettingsService<EmbySettings> embySettings, IEmbyApi embyApi)
IEmbyApi embyApi)
{ {
_plexRepo = plexRepo; _plexRepo = plexRepo;
_embyRepo = embyRepo; _embyRepo = embyRepo;
@ -32,15 +32,11 @@ namespace Ombi.Schedule.Jobs.Ombi
_tvApi = tvApi; _tvApi = tvApi;
_plexSettings = plexSettings; _plexSettings = plexSettings;
_embySettings = embySettings; _embySettings = embySettings;
_plexAvailabilityChecker = plexAvailability;
_embyAvaliabilityChecker = embyAvaliability;
_embyApi = embyApi; _embyApi = embyApi;
} }
private readonly IPlexContentRepository _plexRepo; private readonly IPlexContentRepository _plexRepo;
private readonly IEmbyContentRepository _embyRepo; private readonly IEmbyContentRepository _embyRepo;
private readonly IPlexAvailabilityChecker _plexAvailabilityChecker;
private readonly IEmbyAvaliabilityChecker _embyAvaliabilityChecker;
private readonly ILogger _log; private readonly ILogger _log;
private readonly IMovieDbApi _movieApi; private readonly IMovieDbApi _movieApi;
private readonly ITvMazeApi _tvApi; private readonly ITvMazeApi _tvApi;
@ -48,7 +44,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly IEmbyApi _embyApi; private readonly IEmbyApi _embyApi;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
_log.LogInformation("Starting the Metadata refresh"); _log.LogInformation("Starting the Metadata refresh");
try try
@ -93,12 +89,12 @@ namespace Ombi.Schedule.Jobs.Ombi
{ {
if (plexSettings.Enable) if (plexSettings.Enable)
{ {
BackgroundJob.Enqueue(() => _plexAvailabilityChecker.Start()); await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
} }
if (embySettings.Enable) if (embySettings.Enable)
{ {
BackgroundJob.Enqueue(() => _embyAvaliabilityChecker.Start()); await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby");
} }
} }

@ -7,6 +7,7 @@ using Ombi.Core.Senders;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests; using Ombi.Store.Repository.Requests;
using Quartz;
namespace Ombi.Schedule.Jobs.Ombi namespace Ombi.Schedule.Jobs.Ombi
{ {
@ -32,7 +33,7 @@ namespace Ombi.Schedule.Jobs.Ombi
private readonly ITvRequestRepository _tvRequestRepository; private readonly ITvRequestRepository _tvRequestRepository;
private readonly IMusicRequestRepository _musicRequestRepository; private readonly IMusicRequestRepository _musicRequestRepository;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
// Get all the failed ones! // Get all the failed ones!
var failedRequests = _requestQueue.GetAll().Where(x => !x.Completed.HasValue); var failedRequests = _requestQueue.GetAll().Where(x => !x.Completed.HasValue);

@ -5,6 +5,5 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
public interface IPlexAvailabilityChecker : IBaseJob public interface IPlexAvailabilityChecker : IBaseJob
{ {
Task Start();
} }
} }

@ -1,9 +1,9 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Quartz;
namespace Ombi.Schedule.Jobs namespace Ombi.Schedule.Jobs
{ {
public interface IPlexContentSync : IBaseJob public interface IPlexContentSync : IJob
{ {
Task CacheContent(bool recentlyAddedSearch = false);
} }
} }

@ -9,7 +9,6 @@ namespace Ombi.Schedule.Jobs.Plex.Interfaces
{ {
public interface IPlexEpisodeSync : IBaseJob public interface IPlexEpisodeSync : IBaseJob
{ {
Task Start();
Task<HashSet<PlexEpisode>> ProcessEpsiodes(Metadata[] episodes, IQueryable<PlexEpisode> currentEpisodes); Task<HashSet<PlexEpisode>> ProcessEpsiodes(Metadata[] episodes, IQueryable<PlexEpisode> currentEpisodes);
} }
} }

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
public interface IPlexUserImporter : IBaseJob public interface IPlexUserImporter : IBaseJob
{ {
Task Start();
} }
} }

@ -12,6 +12,7 @@ using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests; using Ombi.Store.Repository.Requests;
using Quartz;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
@ -35,7 +36,7 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly IBackgroundJobClient _backgroundJobClient; private readonly IBackgroundJobClient _backgroundJobClient;
private readonly ILogger _log; private readonly ILogger _log;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {

@ -42,13 +42,14 @@ using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Schedule.Jobs.Plex.Models; using Ombi.Schedule.Jobs.Plex.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
public class PlexContentSync : IPlexContentSync public class PlexContentSync : IPlexContentSync
{ {
public PlexContentSync(ISettingsService<PlexSettings> plex, IPlexApi plexApi, ILogger<PlexContentSync> logger, IPlexContentRepository repo, public PlexContentSync(ISettingsService<PlexSettings> plex, IPlexApi plexApi, ILogger<PlexContentSync> logger, IPlexContentRepository repo,
IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh, IPlexAvailabilityChecker checker) IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh)
{ {
Plex = plex; Plex = plex;
PlexApi = plexApi; PlexApi = plexApi;
@ -56,7 +57,7 @@ namespace Ombi.Schedule.Jobs.Plex
Repo = repo; Repo = repo;
EpisodeSync = epsiodeSync; EpisodeSync = epsiodeSync;
Metadata = metadataRefresh; Metadata = metadataRefresh;
Checker = checker; Plex.ClearCache();
} }
private ISettingsService<PlexSettings> Plex { get; } private ISettingsService<PlexSettings> Plex { get; }
@ -65,10 +66,12 @@ namespace Ombi.Schedule.Jobs.Plex
private IPlexContentRepository Repo { get; } private IPlexContentRepository Repo { get; }
private IPlexEpisodeSync EpisodeSync { get; } private IPlexEpisodeSync EpisodeSync { get; }
private IRefreshMetadata Metadata { get; } private IRefreshMetadata Metadata { get; }
private IPlexAvailabilityChecker Checker { get; }
public async Task CacheContent(bool recentlyAddedSearch = false) public async Task Execute(IJobExecutionContext context)
{ {
JobDataMap dataMap = context.JobDetail.JobDataMap;
var recentlyAddedSearch = dataMap.GetBooleanValueFromString("recentlyAddedSearch");
var plexSettings = await Plex.GetSettingsAsync(); var plexSettings = await Plex.GetSettingsAsync();
if (!plexSettings.Enable) if (!plexSettings.Enable)
{ {
@ -100,19 +103,23 @@ namespace Ombi.Schedule.Jobs.Plex
if (!recentlyAddedSearch) if (!recentlyAddedSearch)
{ {
Logger.LogInformation("Starting EP Cacher"); Logger.LogInformation("Starting EP Cacher");
BackgroundJob.Enqueue(() => EpisodeSync.Start());
await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex");
} }
if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch) if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch)
{ {
// Just check what we send it // Just check what we send it
BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); await OmbiQuartz.TriggerJob(nameof(IMediaDatabaseRefresh), "System");
} }
if ((processedContent?.HasProcessedEpisodes ?? false) && recentlyAddedSearch) if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch)
{ {
BackgroundJob.Enqueue(() => Checker.Start());
await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
} }
Logger.LogInformation("Finished Plex Content Cacher, with processed content: {0}, episodes: {0}", processedContent.Content.Count(), processedContent.Episodes.Count());
} }
private async Task<ProcessedContent> StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch) private async Task<ProcessedContent> StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch)

@ -13,28 +13,28 @@ using Ombi.Helpers;
using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Quartz;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
public class PlexEpisodeSync : IPlexEpisodeSync public class PlexEpisodeSync : IPlexEpisodeSync
{ {
public PlexEpisodeSync(ISettingsService<PlexSettings> s, ILogger<PlexEpisodeSync> log, IPlexApi plexApi, public PlexEpisodeSync(ISettingsService<PlexSettings> s, ILogger<PlexEpisodeSync> log, IPlexApi plexApi,
IPlexContentRepository repo, IPlexAvailabilityChecker a) IPlexContentRepository repo)
{ {
_settings = s; _settings = s;
_log = log; _log = log;
_api = plexApi; _api = plexApi;
_repo = repo; _repo = repo;
_availabilityChecker = a; _settings.ClearCache();
} }
private readonly ISettingsService<PlexSettings> _settings; private readonly ISettingsService<PlexSettings> _settings;
private readonly ILogger<PlexEpisodeSync> _log; private readonly ILogger<PlexEpisodeSync> _log;
private readonly IPlexApi _api; private readonly IPlexApi _api;
private readonly IPlexContentRepository _repo; private readonly IPlexContentRepository _repo;
private readonly IPlexAvailabilityChecker _availabilityChecker;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {
@ -55,7 +55,8 @@ namespace Ombi.Schedule.Jobs.Plex
_log.LogError(LoggingEvents.Cacher, e, "Caching Episodes Failed"); _log.LogError(LoggingEvents.Cacher, e, "Caching Episodes Failed");
} }
BackgroundJob.Enqueue(() => _availabilityChecker.Start());
await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
} }
private async Task Cache(PlexServers settings) private async Task Cache(PlexServers settings)

@ -1,40 +1,40 @@
using System; //using System;
using System.Threading.Tasks; //using System.Threading.Tasks;
using Hangfire; //using Hangfire;
namespace Ombi.Schedule.Jobs.Plex //namespace Ombi.Schedule.Jobs.Plex
{ //{
public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync // public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync
{ // {
public PlexRecentlyAddedSync(IPlexContentSync sync) // public PlexRecentlyAddedSync(IPlexContentSync sync)
{ // {
_sync = sync; // _sync = sync;
} // }
private readonly IPlexContentSync _sync; // private readonly IPlexContentSync _sync;
public void Start() // public void Start()
{ // {
BackgroundJob.Enqueue(() => _sync.CacheContent(true)); // BackgroundJob.Enqueue(() => _sync.CacheContent(true));
} // }
private bool _disposed; // private bool _disposed;
protected virtual void Dispose(bool disposing) // protected virtual void Dispose(bool disposing)
{ // {
if (_disposed) // if (_disposed)
return; // return;
if (disposing) // if (disposing)
{ // {
_sync?.Dispose(); // _sync?.Dispose();
} // }
_disposed = true; // _disposed = true;
} // }
public void Dispose() // public void Dispose()
{ // {
Dispose(true); // Dispose(true);
GC.SuppressFinalize(this); // GC.SuppressFinalize(this);
} // }
} // }
} //}

@ -11,6 +11,7 @@ using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
@ -24,6 +25,8 @@ namespace Ombi.Schedule.Jobs.Plex
_log = log; _log = log;
_plexSettings = plexSettings; _plexSettings = plexSettings;
_userManagementSettings = ums; _userManagementSettings = ums;
_plexSettings.ClearCache();
_userManagementSettings.ClearCache();
} }
private readonly IPlexApi _api; private readonly IPlexApi _api;
@ -33,7 +36,7 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly ISettingsService<UserManagementSettings> _userManagementSettings; private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
var userManagementSettings = await _userManagementSettings.GetSettingsAsync(); var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
if (!userManagementSettings.ImportPlexUsers) if (!userManagementSettings.ImportPlexUsers)

@ -6,7 +6,6 @@ namespace Ombi.Schedule.Jobs.Radarr
{ {
public interface IRadarrSync : IBaseJob public interface IRadarrSync : IBaseJob
{ {
Task CacheContent();
Task<IEnumerable<RadarrCache>> GetCachedContent(); Task<IEnumerable<RadarrCache>> GetCachedContent();
} }
} }

@ -10,6 +10,7 @@ using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
using Serilog; using Serilog;
namespace Ombi.Schedule.Jobs.Radarr namespace Ombi.Schedule.Jobs.Radarr
@ -22,6 +23,7 @@ namespace Ombi.Schedule.Jobs.Radarr
RadarrApi = radarrApi; RadarrApi = radarrApi;
Logger = log; Logger = log;
_ctx = ctx; _ctx = ctx;
RadarrSettings.ClearCache();
} }
private ISettingsService<RadarrSettings> RadarrSettings { get; } private ISettingsService<RadarrSettings> RadarrSettings { get; }
@ -31,7 +33,7 @@ namespace Ombi.Schedule.Jobs.Radarr
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
public async Task CacheContent() public async Task Execute(IJobExecutionContext job)
{ {
await SemaphoreSlim.WaitAsync(); await SemaphoreSlim.WaitAsync();
try try

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.SickRage
{ {
public interface ISickRageSync : IBaseJob public interface ISickRageSync : IBaseJob
{ {
Task Start();
} }
} }

@ -11,6 +11,7 @@ using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.SickRage namespace Ombi.Schedule.Jobs.SickRage
{ {
@ -22,6 +23,7 @@ namespace Ombi.Schedule.Jobs.SickRage
_api = api; _api = api;
_log = l; _log = l;
_ctx = ctx; _ctx = ctx;
_settings.ClearCache();
} }
private readonly ISettingsService<SickRageSettings> _settings; private readonly ISettingsService<SickRageSettings> _settings;
@ -29,7 +31,7 @@ namespace Ombi.Schedule.Jobs.SickRage
private readonly ILogger<SickRageSync> _log; private readonly ILogger<SickRageSync> _log;
private readonly IExternalContext _ctx; private readonly IExternalContext _ctx;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {

@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Sonarr
{ {
public interface ISonarrSync : IBaseJob public interface ISonarrSync : IBaseJob
{ {
Task Start();
} }
} }

@ -14,6 +14,7 @@ using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Quartz;
namespace Ombi.Schedule.Jobs.Sonarr namespace Ombi.Schedule.Jobs.Sonarr
{ {
@ -25,6 +26,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
_api = api; _api = api;
_log = l; _log = l;
_ctx = ctx; _ctx = ctx;
_settings.ClearCache();
} }
private readonly ISettingsService<SonarrSettings> _settings; private readonly ISettingsService<SonarrSettings> _settings;
@ -32,7 +34,7 @@ namespace Ombi.Schedule.Jobs.Sonarr
private readonly ILogger<SonarrSync> _log; private readonly ILogger<SonarrSync> _log;
private readonly IExternalContext _ctx; private readonly IExternalContext _ctx;
public async Task Start() public async Task Execute(IJobExecutionContext job)
{ {
try try
{ {

@ -10,13 +10,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="1.50.2" /> <PackageReference Include="Dapper" Version="1.50.2" />
<PackageReference Include="Hangfire" Version="1.6.21" /> <PackageReference Include="Hangfire" Version="1.6.22" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.21" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.6.22" />
<PackageReference Include="Hangfire.Console" Version="1.3.10" /> <PackageReference Include="Hangfire.Console" Version="1.3.10" />
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" /> <PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" /> <PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
<PackageReference Include="Hangfire.SQLite" Version="1.4.2" /> <PackageReference Include="Hangfire.SQLite" Version="1.4.2" />
<PackageReference Include="Serilog" Version="2.7.1" /> <PackageReference Include="Serilog" Version="2.7.1" />
<PackageReference Include="Quartz" Version="3.0.7" />
<PackageReference Include="SharpCompress" Version="0.18.2" /> <PackageReference Include="SharpCompress" Version="0.18.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.6.13" /> <PackageReference Include="HtmlAgilityPack" Version="1.6.13" />

@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Helpers;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
namespace Ombi.Schedule
{
public class OmbiQuartz
{
protected IScheduler _scheduler { get; set; }
public static IScheduler Scheduler => Instance._scheduler;
// Singleton
protected static OmbiQuartz _instance;
/// <summary>
/// Singleton
/// </summary>
public static OmbiQuartz Instance => _instance ?? (_instance = new OmbiQuartz());
protected OmbiQuartz()
{
Init();
}
private async void Init()
{
_scheduler = await new StdSchedulerFactory().GetScheduler();
}
public IScheduler UseJobFactory(IJobFactory jobFactory)
{
Scheduler.JobFactory = jobFactory;
return Scheduler;
}
public async Task AddJob<T>(string name, string group, string cronExpression, Dictionary<string, string> jobData = null)
where T : IJob
{
var jobBuilder = JobBuilder.Create<T>()
.WithIdentity(new JobKey(name, group));
if (jobData != null)
{
foreach (var o in jobData)
{
jobBuilder.UsingJobData(o.Key, o.Value);
}
}
if(!cronExpression.HasValue())
{
jobBuilder.StoreDurably(true);
}
var job = jobBuilder.Build();
if (cronExpression.HasValue())
{
ITrigger jobTrigger = TriggerBuilder.Create()
.WithIdentity(name + "Trigger", group)
.WithCronSchedule(cronExpression,
x => x.WithMisfireHandlingInstructionFireAndProceed())
.ForJob(name, group)
.StartNow()
.Build();
await Scheduler.ScheduleJob(job, jobTrigger);
}
else
{
await Scheduler.AddJob(job, true);
}
}
public static async Task TriggerJob(string jobName, string group)
{
await Scheduler.TriggerJob(new JobKey(jobName, group));
}
public static async Task Start()
{
await Scheduler.Start();
}
}
}

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Ombi.Core.Settings;
using Ombi.Schedule.Jobs;
using Ombi.Schedule.Jobs.Couchpotato;
using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Lidarr;
using Ombi.Schedule.Jobs.Ombi;
using Ombi.Schedule.Jobs.Plex;
using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Schedule.Jobs.Radarr;
using Ombi.Schedule.Jobs.SickRage;
using Ombi.Schedule.Jobs.Sonarr;
using Ombi.Settings.Settings.Models;
using Quartz;
using Quartz.Spi;
namespace Ombi.Schedule
{
public static class OmbiScheduler
{
//public void Setup()
//{
// CreateJobDefinitions();
//}
//public void CreateJobDefinitions()
//{
// var contentSync = JobBuilder.Create<PlexContentSync>()
// .UsingJobData("recentlyAddedSearch", false)
// .WithIdentity(nameof(PlexContentSync), "Plex")
// .Build();
// var recentlyAdded = JobBuilder.Create<PlexContentSync>()
// .UsingJobData("recentlyAddedSearch", true)
// .WithIdentity("PlexRecentlyAdded", "Plex")
// .Build();
//}
public static async Task UseQuartz(this IApplicationBuilder app)
{
// Job Factory through IOC container
var jobFactory = (IJobFactory)app.ApplicationServices.GetService(typeof(IJobFactory));
var service = (ISettingsService<JobSettings>)app.ApplicationServices.GetService(typeof(ISettingsService<JobSettings>));
var s = service.GetSettings();
// Set job factory
OmbiQuartz.Instance.UseJobFactory(jobFactory);
// Run configuration
await AddPlex(s);
await AddEmby(s);
await AddDvrApps(s);
await AddSystem(s);
// Run Quartz
await OmbiQuartz.Start();
}
private static async Task AddSystem(JobSettings s)
{
await OmbiQuartz.Instance.AddJob<IRefreshMetadata>(nameof(IRefreshMetadata), "System", JobSettingsHelper.RefreshMetadata(s));
await OmbiQuartz.Instance.AddJob<IIssuesPurge>(nameof(IIssuesPurge), "System", JobSettingsHelper.IssuePurge(s));
//OmbiQuartz.Instance.AddJob<IOmbiAutomaticUpdater>(nameof(IOmbiAutomaticUpdater), "System", JobSettingsHelper.Updater(s));
await OmbiQuartz.Instance.AddJob<INewsletterJob>(nameof(INewsletterJob), "System", JobSettingsHelper.Newsletter(s));
await OmbiQuartz.Instance.AddJob<IResendFailedRequests>(nameof(IResendFailedRequests), "System", JobSettingsHelper.ResendFailedRequests(s));
await OmbiQuartz.Instance.AddJob<IMediaDatabaseRefresh>(nameof(IMediaDatabaseRefresh), "System", JobSettingsHelper.MediaDatabaseRefresh(s));
}
private static async Task AddDvrApps(JobSettings s)
{
await OmbiQuartz.Instance.AddJob<ISonarrSync>(nameof(ISonarrSync), "DVR", JobSettingsHelper.Sonarr(s));
await OmbiQuartz.Instance.AddJob<IRadarrSync>(nameof(IRadarrSync), "DVR", JobSettingsHelper.Radarr(s));
await OmbiQuartz.Instance.AddJob<ICouchPotatoSync>(nameof(ICouchPotatoSync), "DVR", JobSettingsHelper.CouchPotato(s));
await OmbiQuartz.Instance.AddJob<ISickRageSync>(nameof(ISickRageSync), "DVR", JobSettingsHelper.SickRageSync(s));
await OmbiQuartz.Instance.AddJob<ILidarrArtistSync>(nameof(ILidarrArtistSync), "DVR", JobSettingsHelper.LidarrArtistSync(s));
}
private static async Task AddPlex(JobSettings s)
{
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync), "Plex", JobSettingsHelper.PlexContent(s), new Dictionary<string, string> { { "recentlyAddedSearch", "false" } });
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary<string, string> { { "recentlyAddedSearch", "true" } });
await OmbiQuartz.Instance.AddJob<IPlexUserImporter>(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s));
await OmbiQuartz.Instance.AddJob<IPlexEpisodeSync>(nameof(IPlexEpisodeSync), "Plex", null);
await OmbiQuartz.Instance.AddJob<IPlexAvailabilityChecker>(nameof(IPlexAvailabilityChecker), "Plex", null);
}
private static async Task AddEmby(JobSettings s)
{
await OmbiQuartz.Instance.AddJob<IEmbyContentSync>(nameof(IEmbyContentSync), "Emby", JobSettingsHelper.EmbyContent(s));
await OmbiQuartz.Instance.AddJob<IEmbyEpisodeSync>(nameof(IEmbyEpisodeSync), "Emby", null);
await OmbiQuartz.Instance.AddJob<IEmbyAvaliabilityChecker>(nameof(IEmbyAvaliabilityChecker), "Emby", null);
await OmbiQuartz.Instance.AddJob<IEmbyUserImporter>(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s));
}
}
}

@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Quartz" Version="3.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup> </ItemGroup>

@ -1,5 +1,6 @@
using System; using System;
using Ombi.Helpers; using Ombi.Helpers;
using Quartz;
namespace Ombi.Settings.Settings.Models namespace Ombi.Settings.Settings.Models
{ {
@ -7,71 +8,93 @@ namespace Ombi.Settings.Settings.Models
{ {
public static string Radarr(JobSettings s) public static string Radarr(JobSettings s)
{ {
return Get(s.RadarrSync, Cron.Hourly(15)); return ValidateCron(Get(s.RadarrSync, Cron.Hourly(15)));
} }
public static string Sonarr(JobSettings s) public static string Sonarr(JobSettings s)
{ {
return Get(s.SonarrSync, Cron.Hourly(10)); return ValidateCron(Get(s.SonarrSync, Cron.Hourly(10)));
} }
public static string EmbyContent(JobSettings s) public static string EmbyContent(JobSettings s)
{ {
return Get(s.EmbyContentSync, Cron.Hourly(5)); return ValidateCron(Get(s.EmbyContentSync, Cron.Hourly(5)));
} }
public static string PlexContent(JobSettings s) public static string PlexContent(JobSettings s)
{ {
return Get(s.PlexContentSync, Cron.Daily(2)); return ValidateCron(Get(s.PlexContentSync, Cron.Daily(2)));
} }
public static string PlexRecentlyAdded(JobSettings s) public static string PlexRecentlyAdded(JobSettings s)
{ {
return Get(s.PlexRecentlyAddedSync, Cron.MinuteInterval(30)); return ValidateCron(Get(s.PlexRecentlyAddedSync, Cron.MinuteInterval(30)));
} }
public static string CouchPotato(JobSettings s) public static string CouchPotato(JobSettings s)
{ {
return Get(s.CouchPotatoSync, Cron.Hourly(30)); return ValidateCron(Get(s.CouchPotatoSync, Cron.Hourly(30)));
} }
public static string Updater(JobSettings s) public static string Updater(JobSettings s)
{ {
return Get(s.AutomaticUpdater, Cron.HourInterval(6)); return ValidateCron(Get(s.AutomaticUpdater, Cron.HourInterval(6)));
} }
public static string UserImporter(JobSettings s) public static string UserImporter(JobSettings s)
{ {
return Get(s.UserImporter, Cron.Daily()); return ValidateCron(Get(s.UserImporter, Cron.Daily()));
} }
public static string Newsletter(JobSettings s) public static string Newsletter(JobSettings s)
{ {
return Get(s.Newsletter, Cron.Weekly(DayOfWeek.Friday, 12)); return ValidateCron(Get(s.Newsletter, Cron.Weekly(Helpers.DayOfWeek.Friday, 12)));
} }
public static string SickRageSync(JobSettings s) public static string SickRageSync(JobSettings s)
{ {
return Get(s.SickRageSync, Cron.Hourly(35)); return ValidateCron(Get(s.SickRageSync, Cron.Hourly(35)));
} }
public static string RefreshMetadata(JobSettings s) public static string RefreshMetadata(JobSettings s)
{ {
return Get(s.RefreshMetadata, Cron.DayInterval(3)); return ValidateCron(Get(s.RefreshMetadata, Cron.DayInterval(3)));
} }
public static string LidarrArtistSync(JobSettings s) public static string LidarrArtistSync(JobSettings s)
{ {
return Get(s.LidarrArtistSync, Cron.Hourly(40)); return ValidateCron(Get(s.LidarrArtistSync, Cron.Hourly(40)));
} }
public static string IssuePurge(JobSettings s) public static string IssuePurge(JobSettings s)
{ {
return Get(s.IssuesPurge, Cron.Daily()); return ValidateCron(Get(s.IssuesPurge, Cron.Daily()));
} }
public static string ResendFailedRequests(JobSettings s) public static string ResendFailedRequests(JobSettings s)
{ {
return Get(s.RetryRequests, Cron.Daily(6)); return ValidateCron(Get(s.RetryRequests, Cron.Daily(6)));
} }
public static string MediaDatabaseRefresh(JobSettings s) public static string MediaDatabaseRefresh(JobSettings s)
{ {
return Get(s.MediaDatabaseRefresh, Cron.DayInterval(5)); return ValidateCron(Get(s.MediaDatabaseRefresh, Cron.DayInterval(5)));
} }
private static string Get(string settings, string defaultCron) private static string Get(string settings, string defaultCron)
{ {
return settings.HasValue() ? settings : defaultCron; return settings.HasValue() ? settings : defaultCron;
} }
private const string _defaultCron = "0 0 12 1/1 * ? *";
private static string ValidateCron(string cron)
{
if (CronExpression.IsValidExpression(cron))
{
return cron;
}
return _defaultCron;
}
} }
} }

@ -0,0 +1,10 @@
namespace Ombi.Settings.Settings.Models.Notifications
{
public class GotifySettings : Settings
{
public bool Enabled { get; set; }
public string BaseUrl { get; set; }
public string ApplicationToken { get; set; }
public sbyte Priority { get; set; } = 4;
}
}

@ -13,6 +13,7 @@ namespace Ombi.Store.Context
if (_created) return; if (_created) return;
_created = true; _created = true;
Database.SetCommandTimeout(60);
Database.Migrate(); Database.Migrate();
} }

@ -17,7 +17,9 @@ namespace Ombi.Store.Context
{ {
if (_created) return; if (_created) return;
_created = true; _created = true;
Database.SetCommandTimeout(60);
Database.Migrate(); Database.Migrate();
} }
@ -107,6 +109,7 @@ namespace Ombi.Store.Context
var allAgents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>().ToList(); var allAgents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>().ToList();
var allTypes = Enum.GetValues(typeof(NotificationType)).Cast<NotificationType>().ToList(); var allTypes = Enum.GetValues(typeof(NotificationType)).Cast<NotificationType>().ToList();
var needToSave = false;
foreach (var agent in allAgents) foreach (var agent in allAgents)
{ {
foreach (var notificationType in allTypes) foreach (var notificationType in allTypes)
@ -116,6 +119,8 @@ namespace Ombi.Store.Context
// We already have this // We already have this
continue; continue;
} }
needToSave = true;
NotificationTemplates notificationToAdd; NotificationTemplates notificationToAdd;
switch (notificationType) switch (notificationType)
{ {
@ -230,7 +235,11 @@ namespace Ombi.Store.Context
NotificationTemplates.Add(notificationToAdd); NotificationTemplates.Add(notificationToAdd);
} }
} }
SaveChanges();
if (needToSave)
{
SaveChanges();
}
} }
} }
} }

@ -14,6 +14,7 @@ namespace Ombi.Store.Context
if (_created) return; if (_created) return;
_created = true; _created = true;
Database.SetCommandTimeout(60);
Database.Migrate(); Database.Migrate();
} }
@ -63,13 +64,6 @@ namespace Ombi.Store.Context
}); });
SaveChanges(); SaveChanges();
} }
SaveChanges();
}
~SettingsContext()
{
} }
} }
} }

@ -0,0 +1,50 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context;
namespace Ombi.Store.Migrations.Settings
{
[DbContext(typeof(SettingsContext))]
[Migration("20190416204533_ResetSchedules")]
partial class ResetSchedules
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Type");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ApplicationConfiguration");
});
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Content");
b.Property<string>("SettingsName");
b.HasKey("Id");
b.ToTable("GlobalSettings");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations.Settings
{
public partial class ResetSchedules : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
DELETE FROM GlobalSettings
WHERE SettingsName = 'JobSettings'
");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

@ -13,7 +13,7 @@ namespace Ombi.Store.Migrations.Settings
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687"); .HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
{ {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save