diff --git a/README.md b/README.md index 0af908f65..4f854cb56 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,15 @@ ____ [![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi) [![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet) +___ + +[![Twitter](https://img.shields.io/twitter/follow/tidusjar.svg?style=social)](https://twitter.com/intent/follow?screen_name=tidusjar) + +Follow me developing Ombi! + +[![Twitch](https://img.shields.io/badge/Twitch-Watch-blue.svg?style=flat-square&logo=twitch)](https://www.twitch.tv/tidusjar) + + ___ Get it on Google Play diff --git a/src/Ombi.Api.Lidarr/ILidarrApi.cs b/src/Ombi.Api.Lidarr/ILidarrApi.cs index 4a23c6200..0eac960d0 100644 --- a/src/Ombi.Api.Lidarr/ILidarrApi.cs +++ b/src/Ombi.Api.Lidarr/ILidarrApi.cs @@ -23,5 +23,6 @@ namespace Ombi.Api.Lidarr Task> GetLanguageProfile(string apiKey, string baseUrl); Task Status(string apiKey, string baseUrl); Task AlbumSearch(int[] albumIds, string apiKey, string baseUrl); + Task AlbumInformation(string albumId, string apiKey, string baseUrl); } } \ No newline at end of file diff --git a/src/Ombi.Api.Lidarr/LidarrApi.cs b/src/Ombi.Api.Lidarr/LidarrApi.cs index a6a283703..cb8db759e 100644 --- a/src/Ombi.Api.Lidarr/LidarrApi.cs +++ b/src/Ombi.Api.Lidarr/LidarrApi.cs @@ -105,6 +105,32 @@ namespace Ombi.Api.Lidarr return Api.Request>(request); } + public async Task 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>(request); + return albums.Where(x => x.foreignAlbumId.Equals(albumId, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(); + } + + + /// + /// THIS ONLY SUPPORTS ALBUMS THAT THE ARTIST IS IN LIDARR + /// + /// + /// + /// + /// + public Task> 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>(request); + } + public Task AddArtist(ArtistAdd artist, string apiKey, string baseUrl) { var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post); diff --git a/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs index b2394eb5f..1e909cf4c 100644 --- a/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs +++ b/src/Ombi.Api.Lidarr/Models/AlbumLookup.cs @@ -1,10 +1,15 @@ using System; +using System.Collections.Generic; namespace Ombi.Api.Lidarr.Models { public class AlbumLookup { public string title { get; set; } + public string status { get; set; } + public string artistType { get; set; } + public string disambiguation { get; set; } + public List links { get; set; } public int artistId { get; set; } public string foreignAlbumId { get; set; } public bool monitored { get; set; } diff --git a/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs b/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs new file mode 100644 index 000000000..e8bdb8975 --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrLinks.cs @@ -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; } + } +} diff --git a/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs b/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs new file mode 100644 index 000000000..7728d58bf --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrRatings.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.Lidarr.Models +{ + public class LidarrRatings + { + public int votes { get; set; } + public decimal value { get; set; } + } +} diff --git a/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs b/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs new file mode 100644 index 000000000..367e8cc7c --- /dev/null +++ b/src/Ombi.Api.Lidarr/Models/LidarrTrack.cs @@ -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; } + } +} diff --git a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs index 03294982a..71c02af9d 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs @@ -12,5 +12,6 @@ namespace Ombi.Core.Engine Task> GetArtistAlbums(string foreignArtistId); Task> SearchAlbum(string search); Task> SearchArtist(string search); + Task GetAlbumInformation(string foreignAlbumId); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MusicSearchEngine.cs b/src/Ombi.Core/Engine/MusicSearchEngine.cs index d0e577801..c8d285766 100644 --- a/src/Ombi.Core/Engine/MusicSearchEngine.cs +++ b/src/Ombi.Core/Engine/MusicSearchEngine.cs @@ -60,6 +60,18 @@ namespace Ombi.Core.Engine return vm; } + public async Task 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; + } + /// /// Searches the specified artist /// @@ -143,6 +155,48 @@ namespace Ombi.Core.Engine return vm; } + + // TODO + private async Task 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 MapIntoAlbumVm(AlbumLookup a, LidarrSettings settings) { var vm = new SearchAlbumViewModel @@ -152,7 +206,8 @@ namespace Ombi.Core.Engine Rating = a.ratings?.value ?? 0m, ReleaseDate = a.releaseDate, 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) { @@ -169,7 +224,7 @@ namespace Ombi.Core.Engine 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()) { vm.Cover = a.remoteCover; diff --git a/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs index a494a3cb5..e9f9c5f9e 100644 --- a/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs @@ -16,8 +16,13 @@ namespace Ombi.Core.Models.Search public string Cover { get; set; } public string Disk { get; set; } public decimal PercentOfTracks { get; set; } + public object[] Genres { get; set; } public override RequestType Type => RequestType.Album; public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0; public bool FullyAvailable => PercentOfTracks == 100; + + + // Below is from the INFO call NEED A SEPERATE VM FOR THIS IN V4 TODO + // TODO ADD TRACK COUNT } } \ No newline at end of file diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index b9edfbaec..837a2ab57 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -36,4 +36,10 @@ + + + ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll + + + \ No newline at end of file diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs index 76a9fc14c..0e9db9192 100644 --- a/src/Ombi.Core/Senders/MusicSender.cs +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using EnsureThat; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; @@ -87,6 +88,11 @@ namespace Ombi.Core.Senders 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 var newArtist = new ArtistAdd { diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index dd4d80f1c..5c8c46739 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -2,6 +2,7 @@ using System.Security.Principal; using Hangfire; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Ombi.Api.Discord; @@ -60,6 +61,8 @@ using Ombi.Schedule.Jobs.Lidarr; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Processor; +using Ombi.Store.Entities; +using Quartz.Spi; namespace Ombi.DependencyInjection { @@ -189,6 +192,7 @@ namespace Ombi.DependencyInjection public static void RegisterJobs(this IServiceCollection services) { + services.AddSingleton(provider => new IoCJobFactory(provider)); services.AddTransient(); services.AddTransient(); @@ -209,7 +213,6 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index ec905e718..8cb6122be 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -41,4 +41,10 @@ + + + ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll + + + \ No newline at end of file diff --git a/src/Ombi.Helpers/CacheService.cs b/src/Ombi.Helpers/CacheService.cs index 3e98e94ce..ae57a9f61 100644 --- a/src/Ombi.Helpers/CacheService.cs +++ b/src/Ombi.Helpers/CacheService.cs @@ -28,18 +28,15 @@ namespace Ombi.Helpers 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; } + + result = await factory(); + _memoryCache.Set(cacheKey, result, absoluteExpiration); + + return result; } public void Remove(string key) @@ -47,34 +44,34 @@ namespace Ombi.Helpers _memoryCache.Remove(key); } - - - public T GetOrAdd(string cacheKey, Func factory, DateTime absoluteExpiration) + + + public T GetOrAdd(string cacheKey, Func factory, DateTime absoluteExpiration) + { + // locks get and set internally + if (_memoryCache.TryGetValue(cacheKey, out var result)) { - // locks get and set internally - if (_memoryCache.TryGetValue(cacheKey, out var result)) + return result; + } + + lock (TypeLock.Lock) + { + if (_memoryCache.TryGetValue(cacheKey, out result)) { return result; } - lock (TypeLock.Lock) - { - if (_memoryCache.TryGetValue(cacheKey, out result)) - { - return result; - } + result = factory(); + _memoryCache.Set(cacheKey, result, absoluteExpiration); - result = factory(); - _memoryCache.Set(cacheKey, result, absoluteExpiration); - - return result; - } + return result; } + } + + private static class TypeLock + { + public static object Lock { get; } = new object(); + } - private static class TypeLock - { - public static object Lock { get; } = new object(); - } - } } diff --git a/src/Ombi.Helpers/Cron.cs b/src/Ombi.Helpers/Cron.cs index 35b141eda..9b6339391 100644 --- a/src/Ombi.Helpers/Cron.cs +++ b/src/Ombi.Helpers/Cron.cs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU Lesser General Public // License along with Hangfire. If not, see . - using System; /// /// Helper class that provides common values for the cron expressions. /// @@ -44,7 +43,7 @@ /// The minute in which the schedule will be activated (0-59). public static string Hourly(int minute) { - return $"{minute} * * * *"; + return $"0 {minute} 0/1 1/1 * ? *"; } /// @@ -73,7 +72,7 @@ /// The minute in which the schedule will be activated (0-59). public static string Daily(int hour, int minute) { - return $"{minute} {hour} * * *"; + return $"0 {minute} {hour} 1/1 * ? *"; } /// @@ -114,7 +113,7 @@ /// The minute in which the schedule will be activated (0-59). public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute) { - return $"{minute} {hour} * * {(int)dayOfWeek}"; + return $"0 {minute} {hour} ? * {(int)dayOfWeek} *"; } /// @@ -219,7 +218,7 @@ /// The number of minutes to wait between every activation. public static string MinuteInterval(int interval) { - return $"*/{interval} * * * *"; + return $"0 0/{interval} * 1/1 * ? *"; } /// @@ -228,7 +227,7 @@ /// The number of hours to wait between every activation. public static string HourInterval(int interval) { - return $"0 */{interval} * * *"; + return $"0 0 0/{interval} 1/1 * ? *"; } /// @@ -237,7 +236,7 @@ /// The number of days to wait between every activation. public static string DayInterval(int interval) { - return $"0 0 */{interval} * *"; + return $"0 0 12 1/{interval} * ? *"; } /// @@ -249,4 +248,39 @@ 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 + } } \ No newline at end of file diff --git a/src/Ombi.Hubs/NotificationHub.cs b/src/Ombi.Hubs/NotificationHub.cs new file mode 100644 index 000000000..da6dee830 --- /dev/null +++ b/src/Ombi.Hubs/NotificationHub.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using System.Linq; +using System.Security.Claims; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Helpers; + +namespace Ombi.Hubs +{ + public class NotificationHub : Hub + { + public NotificationHub(OmbiUserManager um) + { + _userManager = um; + } + + public static ConcurrentDictionary UsersOnline = new ConcurrentDictionary(); + + public static List AdminConnectionIds + { + get + { + return UsersOnline.Where(x => x.Value.Roles.Contains(OmbiRoles.Admin)).Select(x => x.Key).ToList(); + } + } + + public const string NotificationEvent = "Notification"; + + private readonly OmbiUserManager _userManager; + + public override async Task OnConnectedAsync() + { + var identity = (ClaimsIdentity) Context.User.Identity; + var userIdClaim = identity.Claims.FirstOrDefault(x => x.Type.Equals("Id", StringComparison.InvariantCultureIgnoreCase)); + if (userIdClaim == null) + { + await base.OnConnectedAsync(); + return; + } + + var user = await _userManager.Users. + FirstOrDefaultAsync(x => x.Id.Equals(userIdClaim.Value, StringComparison.InvariantCultureIgnoreCase)); + var claims = await _userManager.GetRolesAsync(user); + UsersOnline.TryAdd(Context.ConnectionId, new HubUsers + { + UserId = userIdClaim.Value, + Roles = claims + }); + await base.OnConnectedAsync(); + } + + public override Task OnDisconnectedAsync(Exception exception) + { + UsersOnline.TryRemove(Context.ConnectionId, out _); + return base.OnDisconnectedAsync(exception); + } + + public Task Notification(string data) + { + return Clients.All.SendAsync(NotificationEvent, data); + } + } + + public class HubUsers + { + public string UserId { get; set; } + public IList Roles { get; set; } = new List(); + } +} diff --git a/src/Ombi.Hubs/Ombi.Hubs.csproj b/src/Ombi.Hubs/Ombi.Hubs.csproj new file mode 100644 index 000000000..64d92ec89 --- /dev/null +++ b/src/Ombi.Hubs/Ombi.Hubs.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + + + + + + + + + + + + + ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll + + + + diff --git a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs index 1ddafb55b..068b00b13 100644 --- a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs +++ b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs @@ -32,7 +32,7 @@ namespace Ombi.Schedule.Tests [Test] public async Task DoesNotRun_WhenDisabled() { - await Job.Start(); + await Job.Execute(null); Repo.Verify(x => x.GetAll(),Times.Never); } @@ -49,8 +49,8 @@ namespace Ombi.Schedule.Tests }; Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); - Repo.Setup(x => x.GetAll()).Returns(issues.AsQueryable()); - await Job.Start(); + Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); + await Job.Execute(null); Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted)); 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 }); Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); - await Job.Start(); + await Job.Execute(null); Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted)); @@ -101,7 +101,7 @@ namespace Ombi.Schedule.Tests Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); - await Job.Start(); + await Job.Execute(null); Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); Assert.That(issues[1].Status, Is.Not.EqualTo(IssueStatus.Deleted)); diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index 367221735..56acb9137 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -18,4 +18,10 @@ + + + ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll + + + diff --git a/src/Ombi.Schedule.Tests/OmbiQuartzTests.cs b/src/Ombi.Schedule.Tests/OmbiQuartzTests.cs new file mode 100644 index 000000000..6d00f8ef6 --- /dev/null +++ b/src/Ombi.Schedule.Tests/OmbiQuartzTests.cs @@ -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(); + scheduleMock.Setup(x => x.TriggerJob(It.IsAny(), + It.IsAny())); + var sut = new QuartzMock(scheduleMock); + + //await QuartzMock.TriggerJob("ABC"); + + scheduleMock.Verify(x => x.TriggerJob(It.Is(j => j.Name == "ABC"), + default(CancellationToken)), Times.Once); + } + } + public class QuartzMock : OmbiQuartz + { + public QuartzMock(Mock mock) + { + _instance = this; + _scheduler = mock.Object; + } + } +} diff --git a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs index cd212b1e5..dc5cc06e5 100644 --- a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs +++ b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs @@ -2,13 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Castle.Components.DictionaryAdapter; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Moq; using MockQueryable.Moq; using NUnit.Framework; using Ombi.Core.Notifications; +using Ombi.Hubs; using Ombi.Schedule.Jobs.Plex; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; @@ -18,6 +21,7 @@ using Ombi.Store.Repository.Requests; namespace Ombi.Schedule.Tests { [TestFixture] + [Ignore("Need to work out how to mockout the hub context")] public class PlexAvailabilityCheckerTests { [SetUp] @@ -27,7 +31,11 @@ namespace Ombi.Schedule.Tests _tv = new Mock(); _movie = new Mock(); _notify = new Mock(); - Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object, null); + var hub = new Mock>(); + hub.Setup(x => + x.Clients.Clients(It.IsAny>()).SendCoreAsync(It.IsAny(), It.IsAny(), It.IsAny())); + NotificationHub.UsersOnline.TryAdd("A", new HubUsers()); + Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object, null, hub.Object); } @@ -47,7 +55,7 @@ namespace Ombi.Schedule.Tests _movie.Setup(x => x.GetAll()).Returns(new List { request }.AsQueryable()); _repo.Setup(x => x.Get("test")).ReturnsAsync(new PlexServerContent()); - await Checker.Start(); + await Checker.Execute(null); _movie.Verify(x => x.Save(), Times.Once); @@ -63,8 +71,8 @@ namespace Ombi.Schedule.Tests }; _movie.Setup(x => x.GetAll()).Returns(new List { request }.AsQueryable()); - await Checker.Start(); - + await Checker.Execute(null); + Assert.False(request.Available); } @@ -111,7 +119,7 @@ namespace Ombi.Schedule.Tests }.AsQueryable().BuildMock().Object); _repo.Setup(x => x.Include(It.IsAny>(),It.IsAny>>())); - await Checker.Start(); + await Checker.Execute(null); _tv.Verify(x => x.Save(), Times.Once); diff --git a/src/Ombi.Schedule/IocJobFactory.cs b/src/Ombi.Schedule/IocJobFactory.cs new file mode 100644 index 000000000..83ab5a974 --- /dev/null +++ b/src/Ombi.Schedule/IocJobFactory.cs @@ -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(); + 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(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 38686396f..f0f53f128 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -65,24 +65,24 @@ namespace Ombi.Schedule { var s = _jobSettings.GetSettings(); - RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s)); - RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s)); - RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s)); - RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(false), JobSettingsHelper.PlexContent(s)); - RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s)); - RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s)); - RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s)); - RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s)); - RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(s)); - RecurringJob.AddOrUpdate(() => _issuesPurge.Start(), JobSettingsHelper.IssuePurge(s)); + // RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s)); + // RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s)); + // RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s)); + // //RecurringJob.AddOrUpdate(() => _plexContentSync.Execute(null), JobSettingsHelper.PlexContent(s)); + // //RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s)); + // RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s)); + // RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s)); + // RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s)); + // RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(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(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); - RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); - RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); - RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s)); + // RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); + // RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); + // RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); + //// RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); + // RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s)); } private bool _disposed; @@ -93,7 +93,6 @@ namespace Ombi.Schedule if (disposing) { - _plexContentSync?.Dispose(); _radarrSync?.Dispose(); _updater?.Dispose(); _plexUserImporter?.Dispose(); diff --git a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs index 5e9f13534..f42d910db 100644 --- a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs +++ b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs @@ -28,26 +28,30 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.CouchPotato; using Ombi.Core.Settings; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Couchpotato { public class CouchPotatoSync : ICouchPotatoSync { public CouchPotatoSync(ISettingsService cpSettings, - ICouchPotatoApi api, ILogger log, IExternalContext ctx) + ICouchPotatoApi api, ILogger log, IExternalContext ctx, IHubContext hub) { _settings = cpSettings; _api = api; _log = log; _ctx = ctx; + _notification = hub; _settings.ClearCache(); } @@ -55,8 +59,9 @@ namespace Ombi.Schedule.Jobs.Couchpotato private readonly ICouchPotatoApi _api; private readonly ILogger _log; private readonly IExternalContext _ctx; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var settings = await _settings.GetSettingsAsync(); if (!settings.Enabled) @@ -64,6 +69,8 @@ namespace Ombi.Schedule.Jobs.Couchpotato return; } + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Couch Potato Sync Started"); try { _log.LogInformation(LoggingEvents.CouchPotatoCacher, "Getting all active movies from CP"); @@ -94,10 +101,15 @@ namespace Ombi.Schedule.Jobs.Couchpotato await _ctx.CouchPotatoCache.AddRangeAsync(movieIds); await _ctx.SaveChangesAsync(); + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Couch Potato Sync Finished"); } } catch (Exception e) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Couch Potato Sync Failed"); _log.LogError(LoggingEvents.CouchPotatoCacher, e, "error when trying to get movies from CP"); throw; } diff --git a/src/Ombi.Schedule/Jobs/Couchpotato/ICouchPotatoSync.cs b/src/Ombi.Schedule/Jobs/Couchpotato/ICouchPotatoSync.cs index caa390834..fa23d5030 100644 --- a/src/Ombi.Schedule/Jobs/Couchpotato/ICouchPotatoSync.cs +++ b/src/Ombi.Schedule/Jobs/Couchpotato/ICouchPotatoSync.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Couchpotato { public interface ICouchPotatoSync : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index 7007b3743..632542d34 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -29,27 +29,31 @@ using System; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Notifications.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; +using Quartz; namespace Ombi.Schedule.Jobs.Emby { public class EmbyAvaliabilityChecker : IEmbyAvaliabilityChecker { public EmbyAvaliabilityChecker(IEmbyContentRepository repo, ITvRequestRepository t, IMovieRequestRepository m, - INotificationService n, ILogger log) + INotificationService n, ILogger log, IHubContext notification) { _repo = repo; _tvRepo = t; _movieRepo = m; _notificationService = n; _log = log; + _notification = notification; } private readonly ITvRequestRepository _tvRepo; @@ -57,11 +61,17 @@ namespace Ombi.Schedule.Jobs.Emby private readonly IEmbyContentRepository _repo; private readonly INotificationService _notificationService; private readonly ILogger _log; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Availability Checker Started"); await ProcessMovies(); await ProcessTv(); + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Availability Checker Finished"); } private async Task ProcessMovies() diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index e22c0ca51..0fc24dd81 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -3,15 +3,18 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Ombi.Api.Emby; using Ombi.Api.Emby.Models.Movie; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Schedule.Jobs.Ombi; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; using Serilog; using EmbyMediaType = Ombi.Store.Entities.EmbyMediaType; @@ -20,30 +23,31 @@ namespace Ombi.Schedule.Jobs.Emby public class EmbyContentSync : IEmbyContentSync { public EmbyContentSync(ISettingsService settings, IEmbyApi api, ILogger logger, - IEmbyContentRepository repo, IEmbyEpisodeSync epSync, IRefreshMetadata metadata) + IEmbyContentRepository repo, IHubContext notification) { _logger = logger; _settings = settings; _api = api; _repo = repo; - _episodeSync = epSync; - _metadata = metadata; + _notification = notification; } private readonly ILogger _logger; private readonly ISettingsService _settings; private readonly IEmbyApi _api; private readonly IEmbyContentRepository _repo; - private readonly IEmbyEpisodeSync _episodeSync; - private readonly IRefreshMetadata _metadata; + private readonly IHubContext _notification; - - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var embySettings = await _settings.GetSettingsAsync(); if (!embySettings.Enable) return; + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Started"); + foreach (var server in embySettings.Servers) { try @@ -52,13 +56,18 @@ namespace Ombi.Schedule.Jobs.Emby } catch (Exception e) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Failed"); _logger.LogError(e, "Exception when caching Emby for server {0}", server.Name); } } + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Finished"); // Episodes - BackgroundJob.Enqueue(() => _episodeSync.Start()); - BackgroundJob.Enqueue(() => _metadata.Start()); + + await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby"); + await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "Emby"); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index 962b08cda..2c2944a55 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -30,44 +30,52 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Ombi.Api.Emby; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; +using Ombi.Hubs; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Emby { public class EmbyEpisodeSync : IEmbyEpisodeSync { - public EmbyEpisodeSync(ISettingsService s, IEmbyApi api, ILogger l, IEmbyContentRepository repo, - IEmbyAvaliabilityChecker checker) + public EmbyEpisodeSync(ISettingsService s, IEmbyApi api, ILogger l, IEmbyContentRepository repo + , IHubContext notification) { _api = api; _logger = l; _settings = s; _repo = repo; - _avaliabilityChecker = checker; + _notification = notification; } private readonly ISettingsService _settings; private readonly IEmbyApi _api; private readonly ILogger _logger; private readonly IEmbyContentRepository _repo; - private readonly IEmbyAvaliabilityChecker _avaliabilityChecker; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var settings = await _settings.GetSettingsAsync(); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Started"); foreach (var server in settings.Servers) { await CacheEpisodes(server); } - BackgroundJob.Enqueue(() => _avaliabilityChecker.Start()); + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby Episode Sync Finished"); + await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby"); } private async Task CacheEpisodes(EmbyServers server) @@ -142,7 +150,6 @@ namespace Ombi.Schedule.Jobs.Emby { _settings?.Dispose(); _repo?.Dispose(); - _avaliabilityChecker?.Dispose(); } _disposed = true; } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs index 280a61ab4..cd0bd0b8e 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs @@ -29,27 +29,31 @@ using System; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Emby; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Emby { public class EmbyUserImporter : IEmbyUserImporter { public EmbyUserImporter(IEmbyApi api, UserManager um, ILogger log, - ISettingsService embySettings, ISettingsService ums) + ISettingsService embySettings, ISettingsService ums, IHubContext notification) { _api = api; _userManager = um; _log = log; _embySettings = embySettings; _userManagementSettings = ums; + _notification = notification; } private readonly IEmbyApi _api; @@ -57,8 +61,9 @@ namespace Ombi.Schedule.Jobs.Emby private readonly ILogger _log; private readonly ISettingsService _embySettings; private readonly ISettingsService _userManagementSettings; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var userManagementSettings = await _userManagementSettings.GetSettingsAsync(); if (!userManagementSettings.ImportEmbyUsers) @@ -70,6 +75,9 @@ namespace Ombi.Schedule.Jobs.Emby { return; } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby User Importer Started"); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser).ToListAsync(); foreach (var server in settings.Servers) { @@ -147,6 +155,9 @@ namespace Ombi.Schedule.Jobs.Emby } } } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Emby User Importer Finished"); } private bool _disposed; diff --git a/src/Ombi.Schedule/Jobs/Emby/IEmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/IEmbyAvaliabilityChecker.cs index ce9792f3b..ce2d7dba7 100644 --- a/src/Ombi.Schedule/Jobs/Emby/IEmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/IEmbyAvaliabilityChecker.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby { public interface IEmbyAvaliabilityChecker : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/IEmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/IEmbyContentSync.cs index 5ba8ea6f9..8dd85bab4 100644 --- a/src/Ombi.Schedule/Jobs/Emby/IEmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/IEmbyContentSync.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby { public interface IEmbyContentSync : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/IEmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/IEmbyEpisodeSync.cs index 42d761832..bff623ff8 100644 --- a/src/Ombi.Schedule/Jobs/Emby/IEmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/IEmbyEpisodeSync.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby { public interface IEmbyEpisodeSync : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/IEmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/IEmbyUserImporter.cs index c8bd6fc0e..1c3a51340 100644 --- a/src/Ombi.Schedule/Jobs/Emby/IEmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/IEmbyUserImporter.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Emby { public interface IEmbyUserImporter : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/IBaseJob.cs b/src/Ombi.Schedule/Jobs/IBaseJob.cs index d43d21b9a..bec0db7a0 100644 --- a/src/Ombi.Schedule/Jobs/IBaseJob.cs +++ b/src/Ombi.Schedule/Jobs/IBaseJob.cs @@ -25,11 +25,12 @@ // ************************************************************************/ #endregion +using Quartz; using System; namespace Ombi.Schedule { - public interface IBaseJob : IDisposable + public interface IBaseJob : IJob, IDisposable { } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs index 56444b105..6c43f58c0 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Lidarr { - public interface ILidarrAlbumSync + public interface ILidarrAlbumSync : IJob { - Task CacheContent(); void Dispose(); Task> GetCachedContent(); } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs index 1d3424756..3ba74e7cf 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Lidarr { - public interface ILidarrArtistSync + public interface ILidarrArtistSync : IJob { - Task CacheContent(); void Dispose(); Task> GetCachedContent(); } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs index f0c679229..7f0deb3e8 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/ILidarrAvailabilityChecker.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; +using Quartz; namespace Ombi.Schedule.Jobs.Lidarr { - public interface ILidarrAvailabilityChecker + public interface ILidarrAvailabilityChecker : IJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs index 2a50b5b38..3e75a5fdb 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs @@ -2,15 +2,18 @@ using System.Collections.Generic; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Core.Settings; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Schedule.Jobs.Lidarr @@ -18,7 +21,7 @@ namespace Ombi.Schedule.Jobs.Lidarr public class LidarrAlbumSync : ILidarrAlbumSync { public LidarrAlbumSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IExternalContext ctx, - IBackgroundJobClient job, ILidarrAvailabilityChecker availability) + IBackgroundJobClient job, ILidarrAvailabilityChecker availability, IHubContext notification) { _lidarrSettings = lidarr; _lidarrApi = lidarrApi; @@ -26,6 +29,7 @@ namespace Ombi.Schedule.Jobs.Lidarr _ctx = ctx; _job = job; _availability = availability; + _notification = notification; } private readonly ISettingsService _lidarrSettings; @@ -34,14 +38,18 @@ namespace Ombi.Schedule.Jobs.Lidarr private readonly IExternalContext _ctx; private readonly IBackgroundJobClient _job; private readonly ILidarrAvailabilityChecker _availability; - - public async Task CacheContent() + private readonly IHubContext _notification; + + public async Task Execute(IJobExecutionContext ctx) { try { var settings = await _lidarrSettings.GetSettingsAsync(); if (settings.Enabled) { + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Album Sync Started"); try { var albums = await _lidarrApi.GetAllAlbums(settings.ApiKey, settings.FullUri); @@ -75,10 +83,15 @@ namespace Ombi.Schedule.Jobs.Lidarr } catch (System.Exception ex) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Album Sync Failed"); _logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr Album"); } - _job.Enqueue(() => _availability.Start()); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Album Sync Finished"); + + await OmbiQuartz.TriggerJob(nameof(ILidarrAvailabilityChecker), "DVR"); } } catch (Exception) diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs index e9a64f2a3..3d17df2e8 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs @@ -2,46 +2,50 @@ using System.Collections.Generic; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Core.Settings; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Schedule.Jobs.Lidarr { public class LidarrArtistSync : ILidarrArtistSync { - public LidarrArtistSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IExternalContext ctx, - IBackgroundJobClient background, ILidarrAlbumSync album) + public LidarrArtistSync(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, IExternalContext ctx + , IHubContext notification) { _lidarrSettings = lidarr; _lidarrApi = lidarrApi; _logger = log; _ctx = ctx; - _job = background; - _albumSync = album; + _notification = notification; } private readonly ISettingsService _lidarrSettings; private readonly ILidarrApi _lidarrApi; private readonly ILogger _logger; private readonly IExternalContext _ctx; - private readonly IBackgroundJobClient _job; - private readonly ILidarrAlbumSync _albumSync; - - public async Task CacheContent() + private readonly IHubContext _notification; + + public async Task Execute(IJobExecutionContext job) { try { var settings = await _lidarrSettings.GetSettingsAsync(); if (settings.Enabled) { + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Artist Sync Started"); try { var artists = await _lidarrApi.GetArtists(settings.ApiKey, settings.FullUri); @@ -71,10 +75,15 @@ namespace Ombi.Schedule.Jobs.Lidarr } catch (Exception ex) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Artist Sync Failed"); _logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr"); } - _job.Enqueue(() => _albumSync.CacheContent()); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Artist Sync Finished"); + + await OmbiQuartz.TriggerJob(nameof(ILidarrAlbumSync), "DVR"); } } catch (Exception) diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs index 5708dad6c..4dbf91215 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -3,28 +3,32 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Notifications.Models; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; +using Quartz; namespace Ombi.Schedule.Jobs.Lidarr { public class LidarrAvailabilityChecker : ILidarrAvailabilityChecker { public LidarrAvailabilityChecker(IMusicRequestRepository requests, IRepository albums, ILogger log, - IBackgroundJobClient job, INotificationService notification) + IBackgroundJobClient job, INotificationService notification, IHubContext notificationHub) { _cachedAlbums = albums; _requestRepository = requests; _logger = log; _job = job; _notificationService = notification; + _notification = notificationHub; } private readonly IMusicRequestRepository _requestRepository; @@ -32,9 +36,13 @@ namespace Ombi.Schedule.Jobs.Lidarr private readonly ILogger _logger; private readonly IBackgroundJobClient _job; private readonly INotificationService _notificationService; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext ctx) { + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Availability Check Started"); var allAlbumRequests = _requestRepository.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available); var albumsToUpdate = new List(); foreach (var request in allAlbumRequests) @@ -68,6 +76,9 @@ namespace Ombi.Schedule.Jobs.Lidarr Recipient = recipient, })); } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Lidarr Availability Check Finished"); } } } diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs index fbd1e3aaf..d72b062ce 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IIssuesPurge.cs @@ -3,7 +3,6 @@ namespace Ombi.Schedule.Jobs.Ombi { public interface IIssuesPurge : IBaseJob - { - Task Start(); + { } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs index 11fe7c51a..06ac9421c 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IMediaDatabaseRefresh.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Plex.Interfaces { public interface IMediaDatabaseRefresh : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs index 887508d34..d8b5c3f3f 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/INewsletterJob.cs @@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi { public interface INewsletterJob : IBaseJob { - Task Start(); Task Start(NewsletterSettings settings, bool test); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs index 48f03b65d..484700c85 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs @@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi { public interface IOmbiAutomaticUpdater : IBaseJob { - Task Update(PerformContext context); string[] GetVersion(); Task UpdateAvailable(string branch, string currentVersion); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs index ed13280b0..a7e7d5fdc 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs @@ -5,7 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi { public interface IRefreshMetadata : IBaseJob { - Task Start(); Task ProcessPlexServerContent(IEnumerable contentIds); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs index b55c0f69b..c740b71d9 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IResendFailedRequests.cs @@ -1,9 +1,9 @@ -using System.Threading.Tasks; +using Quartz; +using System.Threading.Tasks; namespace Ombi.Schedule.Jobs.Ombi { - public interface IResendFailedRequests + public interface IResendFailedRequests : IJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs index 6b1270669..b4be7cbef 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IWelcomeEmail.cs @@ -3,7 +3,7 @@ using Ombi.Store.Entities; namespace Ombi.Schedule.Jobs.Ombi { - public interface IWelcomeEmail : IBaseJob + public interface IWelcomeEmail { Task SendEmail(OmbiUser user); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs index b25bc49ed..f82ba71cd 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IssuesPurge.cs @@ -5,6 +5,7 @@ using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Ombi { @@ -20,7 +21,7 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly IRepository _issuesRepository; private readonly ISettingsService _settings; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var settings = await _settings.GetSettingsAsync(); if (!settings.DeleteIssues) diff --git a/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs b/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs index ed0bf227f..cadabba4e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/MediaDatabaseRefresh.cs @@ -9,28 +9,28 @@ using Ombi.Helpers; using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Ombi { public class MediaDatabaseRefresh : IMediaDatabaseRefresh { public MediaDatabaseRefresh(ISettingsService s, ILogger log, - IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, IEmbyContentSync embySync) + IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo) { _settings = s; _log = log; _plexRepo = plexRepo; _embyRepo = embyRepo; - _embyContentSync = embySync; + _settings.ClearCache(); } private readonly ISettingsService _settings; private readonly ILogger _log; private readonly IPlexContentRepository _plexRepo; private readonly IEmbyContentRepository _embyRepo; - private readonly IEmbyContentSync _embyContentSync; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { try { @@ -59,7 +59,7 @@ namespace Ombi.Schedule.Jobs.Ombi await _embyRepo.ExecuteSql(episodeSQL); await _embyRepo.ExecuteSql(mainSql); - BackgroundJob.Enqueue(() => _embyContentSync.Start()); + await OmbiQuartz.TriggerJob(nameof(IEmbyContentSync), "Emby"); } catch (Exception e) { diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 6c59f4c0f..b2faeeff3 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using MailKit; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MimeKit; @@ -18,6 +19,7 @@ using Ombi.Api.TvMaze; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Notifications; using Ombi.Notifications.Models; using Ombi.Notifications.Templates; @@ -26,6 +28,7 @@ using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.Notifications; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; using ContentType = Ombi.Store.Entities.ContentType; namespace Ombi.Schedule.Jobs.Ombi @@ -37,7 +40,8 @@ namespace Ombi.Schedule.Jobs.Ombi ISettingsService emailSettings, INotificationTemplatesRepository templateRepo, UserManager um, ISettingsService newsletter, ILogger log, ILidarrApi lidarrApi, IRepository albumCache, ISettingsService lidarrSettings, - ISettingsService ombiSettings, ISettingsService plexSettings, ISettingsService embySettings) + ISettingsService ombiSettings, ISettingsService plexSettings, ISettingsService embySettings + , IHubContext notification) { _plex = plex; _emby = emby; @@ -57,6 +61,11 @@ namespace Ombi.Schedule.Jobs.Ombi _ombiSettings = ombiSettings; _plexSettings = plexSettings; _embySettings = embySettings; + _notification = notification; + _ombiSettings.ClearCache(); + _plexSettings.ClearCache(); + _emailSettings.ClearCache(); + _customizationSettings.ClearCache(); } private readonly IPlexContentRepository _plex; @@ -77,6 +86,7 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ISettingsService _lidarrSettings; private readonly ISettingsService _plexSettings; private readonly ISettingsService _embySettings; + private readonly IHubContext _notification; public async Task Start(NewsletterSettings settings, bool test) { @@ -90,9 +100,13 @@ namespace Ombi.Schedule.Jobs.Ombi return; } + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Newsletter Started"); var emailSettings = await _emailSettings.GetSettingsAsync(); if (!ValidateConfiguration(emailSettings)) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Newsletter Email Settings Not Configured"); return; } @@ -279,12 +293,17 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Newsletter Failed"); _log.LogError(e, "Error when attempting to create newsletter"); throw; } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Newsletter Finished"); } - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var newsletterSettings = await _newsletterSettings.GetSettingsAsync(); await Start(newsletterSettings, false); diff --git a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs index 783fe5f9d..4c66b2e2f 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs @@ -20,6 +20,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Updater; +using Quartz; using SharpCompress.Readers; using SharpCompress.Readers.Tar; @@ -41,7 +42,6 @@ namespace Ombi.Schedule.Jobs.Ombi private IChangeLogProcessor Processor { get; } private ISettingsService Settings { get; } private readonly IProcessProvider _processProvider; - private static PerformContext Ctx { get; set; } private readonly IApplicationConfigRepository _appConfig; public string[] GetVersion() @@ -59,10 +59,8 @@ namespace Ombi.Schedule.Jobs.Ombi } - [AutomaticRetry(Attempts = 1)] - public async Task Update(PerformContext c) + public async Task Execute(IJobExecutionContext job) { - Ctx = c; Logger.LogDebug(LoggingEvents.Updater, "Starting Update job"); var settings = await Settings.GetSettingsAsync(); @@ -182,7 +180,7 @@ namespace Ombi.Schedule.Jobs.Ombi } Logger.LogDebug(LoggingEvents.Updater, "Starting Download"); - await DownloadAsync(download.Url, zipDir, c); + await DownloadAsync(download.Url, zipDir); Logger.LogDebug(LoggingEvents.Updater, "Finished Download"); } 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"); using (var client = new WebClient()) diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index c9ba5c6b3..67ef028b5 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using Ombi.Api.Emby; using Ombi.Api.TheMovieDb; @@ -11,10 +12,12 @@ using Ombi.Api.TvMaze; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Plex; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Ombi { @@ -22,8 +25,7 @@ namespace Ombi.Schedule.Jobs.Ombi { public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, ILogger log, ITvMazeApi tvApi, ISettingsService plexSettings, - IMovieDbApi movieApi, ISettingsService embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability, - IEmbyApi embyApi) + IMovieDbApi movieApi, ISettingsService embySettings, IEmbyApi embyApi, IHubContext notification) { _plexRepo = plexRepo; _embyRepo = embyRepo; @@ -32,25 +34,26 @@ namespace Ombi.Schedule.Jobs.Ombi _tvApi = tvApi; _plexSettings = plexSettings; _embySettings = embySettings; - _plexAvailabilityChecker = plexAvailability; - _embyAvaliabilityChecker = embyAvaliability; _embyApi = embyApi; + _notification = notification; } private readonly IPlexContentRepository _plexRepo; private readonly IEmbyContentRepository _embyRepo; - private readonly IPlexAvailabilityChecker _plexAvailabilityChecker; - private readonly IEmbyAvaliabilityChecker _embyAvaliabilityChecker; private readonly ILogger _log; private readonly IMovieDbApi _movieApi; private readonly ITvMazeApi _tvApi; private readonly ISettingsService _plexSettings; private readonly ISettingsService _embySettings; private readonly IEmbyApi _embyApi; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { _log.LogInformation("Starting the Metadata refresh"); + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Started"); try { var settings = await _plexSettings.GetSettingsAsync(); @@ -68,8 +71,14 @@ namespace Ombi.Schedule.Jobs.Ombi catch (Exception e) { _log.LogError(e, "Exception when refreshing the Plex Metadata"); + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Failed"); throw; } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Finished"); } public async Task ProcessPlexServerContent(IEnumerable contentIds) @@ -93,12 +102,12 @@ namespace Ombi.Schedule.Jobs.Ombi { if (plexSettings.Enable) { - BackgroundJob.Enqueue(() => _plexAvailabilityChecker.Start()); + await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); } if (embySettings.Enable) { - BackgroundJob.Enqueue(() => _embyAvaliabilityChecker.Start()); + await OmbiQuartz.TriggerJob(nameof(IEmbyAvaliabilityChecker), "Emby"); } } diff --git a/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs index bc8f93a97..344e3a874 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs @@ -7,6 +7,7 @@ using Ombi.Core.Senders; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; +using Quartz; namespace Ombi.Schedule.Jobs.Ombi { @@ -32,7 +33,7 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ITvRequestRepository _tvRequestRepository; private readonly IMusicRequestRepository _musicRequestRepository; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { // Get all the failed ones! var failedRequests = _requestQueue.GetAll().Where(x => !x.Completed.HasValue); diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs index 2d115ab88..112e3be38 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs @@ -5,6 +5,5 @@ namespace Ombi.Schedule.Jobs.Plex { public interface IPlexAvailabilityChecker : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs index 17a8bbb4f..9d2752d85 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexContentSync.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; +using Quartz; namespace Ombi.Schedule.Jobs { - public interface IPlexContentSync : IBaseJob + public interface IPlexContentSync : IJob { - Task CacheContent(bool recentlyAddedSearch = false); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs index 7de7c3c0c..8eed35066 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs @@ -9,7 +9,6 @@ namespace Ombi.Schedule.Jobs.Plex.Interfaces { public interface IPlexEpisodeSync : IBaseJob { - Task Start(); Task> ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexUserImporter.cs index 431ce3ee3..fede60475 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexUserImporter.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Plex { public interface IPlexUserImporter : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index e0278f854..a608f2988 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -3,22 +3,25 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Notifications.Models; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; +using Quartz; namespace Ombi.Schedule.Jobs.Plex { public class PlexAvailabilityChecker : IPlexAvailabilityChecker { public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies, - INotificationService notification, IBackgroundJobClient background, ILogger log) + INotificationService notification, IBackgroundJobClient background, ILogger log, IHubContext hub) { _tvRepo = tvRequest; _repo = repo; @@ -26,6 +29,7 @@ namespace Ombi.Schedule.Jobs.Plex _notificationService = notification; _backgroundJobClient = background; _log = log; + _notification = hub; } private readonly ITvRequestRepository _tvRepo; @@ -34,18 +38,27 @@ namespace Ombi.Schedule.Jobs.Plex private readonly INotificationService _notificationService; private readonly IBackgroundJobClient _backgroundJobClient; private readonly ILogger _log; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { try { + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Started"); await ProcessMovies(); await ProcessTv(); } catch (Exception e) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Failed"); _log.LogError(e, "Exception thrown in Plex availbility checker"); } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Finished"); } private Task ProcessTv() diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 964832b44..14d781064 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Plex; @@ -37,18 +38,20 @@ using Ombi.Api.Plex.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.Plex.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Plex { public class PlexContentSync : IPlexContentSync { public PlexContentSync(ISettingsService plex, IPlexApi plexApi, ILogger logger, IPlexContentRepository repo, - IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh, IPlexAvailabilityChecker checker) + IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh, IPlexAvailabilityChecker checker, IHubContext hub) { Plex = plex; PlexApi = plexApi; @@ -57,6 +60,8 @@ namespace Ombi.Schedule.Jobs.Plex EpisodeSync = epsiodeSync; Metadata = metadataRefresh; Checker = checker; + Notification = hub; + Plex.ClearCache(); } private ISettingsService Plex { get; } @@ -66,17 +71,25 @@ namespace Ombi.Schedule.Jobs.Plex private IPlexEpisodeSync EpisodeSync { get; } private IRefreshMetadata Metadata { get; } private IPlexAvailabilityChecker Checker { get; } + private IHubContext Notification { get; set; } - 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(); if (!plexSettings.Enable) { return; } + await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Started" : "Plex Content Sync Started"); if (!ValidateSettings(plexSettings)) { Logger.LogError("Plex Settings are not valid"); + await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync, Settings Not Valid" : "Plex Content, Settings Not Valid"); return; } var processedContent = new ProcessedContent(); @@ -94,25 +107,35 @@ namespace Ombi.Schedule.Jobs.Plex } catch (Exception e) { + await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Errored" : "Plex Content Sync Errored"); Logger.LogWarning(LoggingEvents.PlexContentCacher, e, "Exception thrown when attempting to cache the Plex Content"); } if (!recentlyAddedSearch) { Logger.LogInformation("Starting EP Cacher"); - BackgroundJob.Enqueue(() => EpisodeSync.Start()); + + await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex"); } if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch) { // Just check what we send it - BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); + await OmbiQuartz.TriggerJob(nameof(IMediaDatabaseRefresh), "System"); } if ((processedContent?.HasProcessedEpisodes ?? 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()); + + await Notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Plex Recently Added Sync Finished" : "Plex Content Sync Finished"); + } private async Task StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs index 6ab5a5941..b6e54849b 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Hangfire; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Plex; @@ -10,31 +11,34 @@ using Ombi.Api.Plex.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Quartz; namespace Ombi.Schedule.Jobs.Plex { public class PlexEpisodeSync : IPlexEpisodeSync { public PlexEpisodeSync(ISettingsService s, ILogger log, IPlexApi plexApi, - IPlexContentRepository repo, IPlexAvailabilityChecker a) + IPlexContentRepository repo, IHubContext hub) { _settings = s; _log = log; _api = plexApi; _repo = repo; - _availabilityChecker = a; + _notification = hub; + _settings.ClearCache(); } private readonly ISettingsService _settings; private readonly ILogger _log; private readonly IPlexApi _api; private readonly IPlexContentRepository _repo; - private readonly IPlexAvailabilityChecker _availabilityChecker; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { try { @@ -43,6 +47,8 @@ namespace Ombi.Schedule.Jobs.Plex { return; } + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Episode Sync Started"); foreach (var server in s.Servers) { @@ -52,10 +58,15 @@ namespace Ombi.Schedule.Jobs.Plex } catch (Exception e) { + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Episode Sync Failed"); _log.LogError(LoggingEvents.Cacher, e, "Caching Episodes Failed"); } - BackgroundJob.Enqueue(() => _availabilityChecker.Start()); + + await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex Episode Sync Finished"); } private async Task Cache(PlexServers settings) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs index 51596f891..e26f07ba6 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs @@ -1,40 +1,40 @@ -using System; -using System.Threading.Tasks; -using Hangfire; +//using System; +//using System.Threading.Tasks; +//using Hangfire; -namespace Ombi.Schedule.Jobs.Plex -{ - public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync - { - public PlexRecentlyAddedSync(IPlexContentSync sync) - { - _sync = sync; - } +//namespace Ombi.Schedule.Jobs.Plex +//{ +// public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync +// { +// public PlexRecentlyAddedSync(IPlexContentSync sync) +// { +// _sync = sync; +// } - private readonly IPlexContentSync _sync; +// private readonly IPlexContentSync _sync; - public void Start() - { - BackgroundJob.Enqueue(() => _sync.CacheContent(true)); - } +// public void Start() +// { +// BackgroundJob.Enqueue(() => _sync.CacheContent(true)); +// } - private bool _disposed; - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; +// private bool _disposed; +// protected virtual void Dispose(bool disposing) +// { +// if (_disposed) +// return; - if (disposing) - { - _sync?.Dispose(); - } - _disposed = true; - } +// if (disposing) +// { +// _sync?.Dispose(); +// } +// _disposed = true; +// } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} \ No newline at end of file +// public void Dispose() +// { +// Dispose(true); +// GC.SuppressFinalize(this); +// } +// } +//} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs index 105e8876d..5451e80a8 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs @@ -3,27 +3,33 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Plex; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Plex { public class PlexUserImporter : IPlexUserImporter { public PlexUserImporter(IPlexApi api, UserManager um, ILogger log, - ISettingsService plexSettings, ISettingsService ums) + ISettingsService plexSettings, ISettingsService ums, IHubContext hub) { _api = api; _userManager = um; _log = log; _plexSettings = plexSettings; _userManagementSettings = ums; + _notification = hub; + _plexSettings.ClearCache(); + _userManagementSettings.ClearCache(); } private readonly IPlexApi _api; @@ -31,9 +37,10 @@ namespace Ombi.Schedule.Jobs.Plex private readonly ILogger _log; private readonly ISettingsService _plexSettings; private readonly ISettingsService _userManagementSettings; + private readonly IHubContext _notification; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { var userManagementSettings = await _userManagementSettings.GetSettingsAsync(); if (!userManagementSettings.ImportPlexUsers) @@ -47,6 +54,8 @@ namespace Ombi.Schedule.Jobs.Plex } + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex User Importer Started"); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.PlexUser).ToListAsync(); foreach (var server in settings.Servers) { @@ -118,6 +127,9 @@ namespace Ombi.Schedule.Jobs.Plex } } } + + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) + .SendAsync(NotificationHub.NotificationEvent, "Plex User Importer Finished"); } private async Task ImportAdmin(UserManagementSettings settings, PlexServers server, List allUsers) diff --git a/src/Ombi.Schedule/Jobs/Radarr/IRadarrSync.cs b/src/Ombi.Schedule/Jobs/Radarr/IRadarrSync.cs index a9f8edfcd..1fb191fd1 100644 --- a/src/Ombi.Schedule/Jobs/Radarr/IRadarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Radarr/IRadarrSync.cs @@ -6,7 +6,6 @@ namespace Ombi.Schedule.Jobs.Radarr { public interface IRadarrSync : IBaseJob { - Task CacheContent(); Task> GetCachedContent(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs b/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs index f61747ac6..8212aad3b 100644 --- a/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Radarr/RadarrSync.cs @@ -10,6 +10,7 @@ using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; using Serilog; namespace Ombi.Schedule.Jobs.Radarr @@ -22,6 +23,7 @@ namespace Ombi.Schedule.Jobs.Radarr RadarrApi = radarrApi; Logger = log; _ctx = ctx; + RadarrSettings.ClearCache(); } private ISettingsService RadarrSettings { get; } @@ -31,7 +33,7 @@ namespace Ombi.Schedule.Jobs.Radarr private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); - public async Task CacheContent() + public async Task Execute(IJobExecutionContext job) { await SemaphoreSlim.WaitAsync(); try diff --git a/src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs b/src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs index df51698fe..2fcd435a8 100644 --- a/src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs +++ b/src/Ombi.Schedule/Jobs/SickRage/ISickRageSync.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.SickRage { public interface ISickRageSync : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs index 2c8d03b1d..8c5652f3a 100644 --- a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs +++ b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs @@ -11,6 +11,7 @@ using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.SickRage { @@ -22,6 +23,7 @@ namespace Ombi.Schedule.Jobs.SickRage _api = api; _log = l; _ctx = ctx; + _settings.ClearCache(); } private readonly ISettingsService _settings; @@ -29,7 +31,7 @@ namespace Ombi.Schedule.Jobs.SickRage private readonly ILogger _log; private readonly IExternalContext _ctx; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { try { diff --git a/src/Ombi.Schedule/Jobs/Sonarr/ISonarrSync.cs b/src/Ombi.Schedule/Jobs/Sonarr/ISonarrSync.cs index a1ba9f3c2..f190be3c5 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/ISonarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/ISonarrSync.cs @@ -4,6 +4,5 @@ namespace Ombi.Schedule.Jobs.Sonarr { public interface ISonarrSync : IBaseJob { - Task Start(); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs index c77e23394..eaa285cc7 100644 --- a/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrSync.cs @@ -14,6 +14,7 @@ using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; using Ombi.Store.Context; using Ombi.Store.Entities; +using Quartz; namespace Ombi.Schedule.Jobs.Sonarr { @@ -25,6 +26,7 @@ namespace Ombi.Schedule.Jobs.Sonarr _api = api; _log = l; _ctx = ctx; + _settings.ClearCache(); } private readonly ISettingsService _settings; @@ -32,7 +34,7 @@ namespace Ombi.Schedule.Jobs.Sonarr private readonly ILogger _log; private readonly IExternalContext _ctx; - public async Task Start() + public async Task Execute(IJobExecutionContext job) { try { diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 827db0d9a..b07f1641c 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -10,12 +10,13 @@ - - - + + + + @@ -35,9 +36,16 @@ + + + + ..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.signalr.core\1.1.0\lib\netstandard2.0\Microsoft.AspNetCore.SignalR.Core.dll + + + \ No newline at end of file diff --git a/src/Ombi.Schedule/OmbiQuartz.cs b/src/Ombi.Schedule/OmbiQuartz.cs new file mode 100644 index 000000000..715bb187b --- /dev/null +++ b/src/Ombi.Schedule/OmbiQuartz.cs @@ -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; + + /// + /// Singleton + /// + 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(string name, string group, string cronExpression, Dictionary jobData = null) + where T : IJob + { + var jobBuilder = JobBuilder.Create() + .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(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/OmbiScheduler.cs b/src/Ombi.Schedule/OmbiScheduler.cs new file mode 100644 index 000000000..a1a7a3765 --- /dev/null +++ b/src/Ombi.Schedule/OmbiScheduler.cs @@ -0,0 +1,99 @@ +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() + // .UsingJobData("recentlyAddedSearch", false) + // .WithIdentity(nameof(PlexContentSync), "Plex") + // .Build(); + + // var recentlyAdded = JobBuilder.Create() + // .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)app.ApplicationServices.GetService(typeof(ISettingsService)); + 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(nameof(IRefreshMetadata), "System", JobSettingsHelper.RefreshMetadata(s)); + await OmbiQuartz.Instance.AddJob(nameof(IIssuesPurge), "System", JobSettingsHelper.IssuePurge(s)); + //OmbiQuartz.Instance.AddJob(nameof(IOmbiAutomaticUpdater), "System", JobSettingsHelper.Updater(s)); + await OmbiQuartz.Instance.AddJob(nameof(INewsletterJob), "System", JobSettingsHelper.Newsletter(s)); + await OmbiQuartz.Instance.AddJob(nameof(IResendFailedRequests), "System", JobSettingsHelper.ResendFailedRequests(s)); + await OmbiQuartz.Instance.AddJob(nameof(IMediaDatabaseRefresh), "System", JobSettingsHelper.MediaDatabaseRefresh(s)); + } + + private static async Task AddDvrApps(JobSettings s) + { + await OmbiQuartz.Instance.AddJob(nameof(ISonarrSync), "DVR", JobSettingsHelper.Sonarr(s)); + await OmbiQuartz.Instance.AddJob(nameof(IRadarrSync), "DVR", JobSettingsHelper.Radarr(s)); + await OmbiQuartz.Instance.AddJob(nameof(ICouchPotatoSync), "DVR", JobSettingsHelper.CouchPotato(s)); + await OmbiQuartz.Instance.AddJob(nameof(ISickRageSync), "DVR", JobSettingsHelper.SickRageSync(s)); + await OmbiQuartz.Instance.AddJob(nameof(ILidarrArtistSync), "DVR", JobSettingsHelper.LidarrArtistSync(s)); + await OmbiQuartz.Instance.AddJob(nameof(ILidarrAlbumSync), "DVR", null); + await OmbiQuartz.Instance.AddJob(nameof(ILidarrAvailabilityChecker), "DVR", null); + } + + private static async Task AddPlex(JobSettings s) + { + await OmbiQuartz.Instance.AddJob(nameof(IPlexContentSync), "Plex", JobSettingsHelper.PlexContent(s), new Dictionary { { "recentlyAddedSearch", "false" } }); + await OmbiQuartz.Instance.AddJob(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary { { "recentlyAddedSearch", "true" } }); + await OmbiQuartz.Instance.AddJob(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s)); + await OmbiQuartz.Instance.AddJob(nameof(IPlexEpisodeSync), "Plex", null); + await OmbiQuartz.Instance.AddJob(nameof(IPlexAvailabilityChecker), "Plex", null); + } + + private static async Task AddEmby(JobSettings s) + { + await OmbiQuartz.Instance.AddJob(nameof(IEmbyContentSync), "Emby", JobSettingsHelper.EmbyContent(s)); + await OmbiQuartz.Instance.AddJob(nameof(IEmbyEpisodeSync), "Emby", null); + await OmbiQuartz.Instance.AddJob(nameof(IEmbyAvaliabilityChecker), "Emby", null); + await OmbiQuartz.Instance.AddJob(nameof(IEmbyUserImporter), "Emby", JobSettingsHelper.UserImporter(s)); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 6db0768aa..39e53c9ff 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index e2080e3cb..272ad3444 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -1,5 +1,6 @@ using System; using Ombi.Helpers; +using Quartz; namespace Ombi.Settings.Settings.Models { @@ -7,71 +8,93 @@ namespace Ombi.Settings.Settings.Models { 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) { - return Get(s.SonarrSync, Cron.Hourly(10)); + return ValidateCron(Get(s.SonarrSync, Cron.Hourly(10))); } 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) { - return Get(s.PlexContentSync, Cron.Daily(2)); + return ValidateCron(Get(s.PlexContentSync, Cron.Daily(2))); } + 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) { - return Get(s.CouchPotatoSync, Cron.Hourly(30)); + return ValidateCron(Get(s.CouchPotatoSync, Cron.Hourly(30))); } 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) { - return Get(s.UserImporter, Cron.Daily()); + return ValidateCron(Get(s.UserImporter, Cron.Daily())); } + 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) { - return Get(s.SickRageSync, Cron.Hourly(35)); + return ValidateCron(Get(s.SickRageSync, Cron.Hourly(35))); } + 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) { - return Get(s.LidarrArtistSync, Cron.Hourly(40)); + return ValidateCron(Get(s.LidarrArtistSync, Cron.Hourly(40))); } 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) { - return Get(s.RetryRequests, Cron.Daily(6)); + return ValidateCron(Get(s.RetryRequests, Cron.Daily(6))); } + 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) { 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; + } } } \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.Designer.cs b/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.Designer.cs new file mode 100644 index 000000000..205a98b11 --- /dev/null +++ b/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.Designer.cs @@ -0,0 +1,50 @@ +// +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("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.cs b/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.cs new file mode 100644 index 000000000..0a3b9312c --- /dev/null +++ b/src/Ombi.Store/Migrations/Settings/20190416204533_ResetSchedules.cs @@ -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) + { + + } + } +} diff --git a/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs b/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs index fe063ef8b..7a605792d 100644 --- a/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/Settings/SettingsContextModelSnapshot.cs @@ -13,7 +13,7 @@ namespace Ombi.Store.Migrations.Settings { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687"); + .HasAnnotation("ProductVersion", "2.2.2-servicing-10034"); modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => { diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 4eb65deeb..f8fd0bf24 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -10,6 +10,7 @@ + diff --git a/src/Ombi.sln b/src/Ombi.sln index 17a4ea32c..af8171bec 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -104,6 +104,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Settings.Tests", "Ombi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Test.Common", "Ombi.Test.Common\Ombi.Test.Common.csproj", "{27111E7C-748E-4996-BD71-2117027C6460}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Hubs", "Ombi.Hubs\Ombi.Hubs.csproj", "{67416CC5-13B2-44BB-98CE-39DA93D6F70E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -274,6 +276,10 @@ Global {27111E7C-748E-4996-BD71-2117027C6460}.Debug|Any CPU.Build.0 = Debug|Any CPU {27111E7C-748E-4996-BD71-2117027C6460}.Release|Any CPU.ActiveCfg = Release|Any CPU {27111E7C-748E-4996-BD71-2117027C6460}.Release|Any CPU.Build.0 = Release|Any CPU + {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67416CC5-13B2-44BB-98CE-39DA93D6F70E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 6f022eac3..456cc42e8 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -7,10 +7,14 @@ import { AuthService } from "./auth/auth.service"; import { ILocalUser } from "./auth/IUserLogin"; import { IdentityService, NotificationService, CustomPageService } from "./services"; import { JobService, SettingsService } from "./services"; +import { MatSnackBar } from '@angular/material'; import { ICustomizationSettings, ICustomPage } from "./interfaces"; import { StorageService } from './shared/storage/storage-service'; +import { SignalRNotificationService } from './services/signlarnotification.service'; + + @Component({ selector: "app-ombi", templateUrl: "./app.component.html", @@ -32,6 +36,7 @@ export class AppComponent implements OnInit { public username: string; private checkedForUpdate: boolean; + private hubConnected: boolean; @HostBinding('class') public componentCssClass; @@ -45,7 +50,9 @@ export class AppComponent implements OnInit { private readonly identityService: IdentityService, private readonly customPageService: CustomPageService, public overlayContainer: OverlayContainer, - private storage: StorageService) { + private storage: StorageService, + private signalrNotification: SignalRNotificationService, + private readonly snackBar: MatSnackBar) { // const base = this.platformLocation.getBaseHrefFromDOM(); // if (base.length > 1) { @@ -109,6 +116,17 @@ export class AppComponent implements OnInit { }, err => this.checkedForUpdate = true); } + + if (this.authService.loggedIn() && !this.hubConnected) { + this.signalrNotification.initialize(); + this.hubConnected = true; + + this.signalrNotification.Notification.subscribe(data => { + this.snackBar.open(data, "OK", { + duration: 3000 + }); + }); + } } }); } diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index cb9a3d25a..1526e0295 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -8,7 +8,6 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { RouterModule, Routes } from "@angular/router"; import { JwtModule } from "@auth0/angular-jwt"; - import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; import { TranslateHttpLoader } from "@ngx-translate/http-loader"; @@ -53,6 +52,7 @@ import { NavSearchComponent } from "./my-nav/nav-search.component"; import { OverlayModule } from "@angular/cdk/overlay"; import { getBaseLocation } from "./shared/functions/common-functions"; import { StorageService } from "./shared/storage/storage-service"; +import { SignalRNotificationService } from "./services/signlarnotification.service"; const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, @@ -114,6 +114,7 @@ export function JwtTokenGetter() { MatSnackBarModule, SharedModule, NgxEditorModule, + MatSnackBarModule, DialogModule, MatButtonModule, NavbarModule, @@ -179,6 +180,7 @@ export function JwtTokenGetter() { SearchV2Service, MessageService, StorageService, + SignalRNotificationService, { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' } // { // provide: APP_BASE_HREF, diff --git a/src/Ombi/ClientApp/src/app/auth/auth.service.ts b/src/Ombi/ClientApp/src/app/auth/auth.service.ts index cdc626745..8f3c6af29 100644 --- a/src/Ombi/ClientApp/src/app/auth/auth.service.ts +++ b/src/Ombi/ClientApp/src/app/auth/auth.service.ts @@ -28,6 +28,10 @@ export class AuthService extends ServiceHelpers { return this.http.post(`${this.url}/requirePassword`, JSON.stringify(login), { headers: this.headers }); } + public getToken() { + return this.jwtHelperService.tokenGetter(); + } + public loggedIn() { const token: string = this.jwtHelperService.tokenGetter(); diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index 070dabb8d..2145ba9e6 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -222,7 +222,6 @@ export interface IIssueCategory extends ISettings { export interface ICronTestModel { success: boolean; message: string; - schedule: Date[]; } export interface ICronViewModelBody { diff --git a/src/Ombi/ClientApp/src/app/requests/music/musicrequests.component.ts b/src/Ombi/ClientApp/src/app/requests/music/musicrequests.component.ts index 8d8336dcb..54d0ca0c4 100644 --- a/src/Ombi/ClientApp/src/app/requests/music/musicrequests.component.ts +++ b/src/Ombi/ClientApp/src/app/requests/music/musicrequests.component.ts @@ -92,11 +92,10 @@ export class MusicRequestsComponent implements OnInit { this.searchChanged.next(text.target.value); } - public removeRequest(request: IAlbumRequest) { - this.requestService.removeAlbumRequest(request).subscribe(x => { - this.removeRequestFromUi(request); - this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); - }); + public async removeRequest(request: IAlbumRequest) { + await this.requestService.removeAlbumRequest(request).toPromise(); + this.removeRequestFromUi(request); + this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); } public changeAvailability(request: IAlbumRequest, available: boolean) { @@ -337,7 +336,7 @@ export class MusicRequestsComponent implements OnInit { } private setAlbumBackground(req: IAlbumRequest) { if (req.disk === null) { - if(req.cover === null) { + if (req.cover === null) { req.disk = this.defaultPoster; } else { req.disk = req.cover; diff --git a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html index f28650932..ed669e0f5 100644 --- a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html @@ -1,10 +1,8 @@ - - - +
-
@@ -27,7 +25,7 @@
- +
@@ -37,6 +35,7 @@
+
@@ -64,6 +63,7 @@ +
@@ -101,10 +101,10 @@ class="label label-info" [translate]="'Search.Movies.Trailer'"> {{result.quality}}p - - - - + + \ No newline at end of file + [issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"> diff --git a/src/Ombi/ClientApp/src/app/search/music/musicsearch.component.html b/src/Ombi/ClientApp/src/app/search/music/musicsearch.component.html index e0d95203b..89af8de01 100644 --- a/src/Ombi/ClientApp/src/app/search/music/musicsearch.component.html +++ b/src/Ombi/ClientApp/src/app/search/music/musicsearch.component.html @@ -1,7 +1,7 @@ 
- +
@@ -49,4 +49,4 @@ \ No newline at end of file + [issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"> diff --git a/src/Ombi/ClientApp/src/app/search/search.component.html b/src/Ombi/ClientApp/src/app/search/search.component.html index 046635812..7e1d280d2 100644 --- a/src/Ombi/ClientApp/src/app/search/search.component.html +++ b/src/Ombi/ClientApp/src/app/search/search.component.html @@ -4,7 +4,7 @@ -
/// [HttpPost("plexuserimporter")] - public bool PlexUserImporter() + public async Task PlexUserImporter() { - BackgroundJob.Enqueue(() => _plexUserImporter.Start()); + await OmbiQuartz.TriggerJob(nameof(IPlexUserImporter), "Plex"); return true; } @@ -103,9 +94,9 @@ namespace Ombi.Controllers.V1 ///
/// [HttpPost("embyuserimporter")] - public bool EmbyUserImporter() + public async Task EmbyUserImporter() { - BackgroundJob.Enqueue(() => _embyUserImporter.Start()); + await OmbiQuartz.TriggerJob(nameof(IEmbyUserImporter), "Emby"); return true; } @@ -116,7 +107,7 @@ namespace Ombi.Controllers.V1 [HttpPost("plexcontentcacher")] public bool StartPlexContentCacher() { - BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(false)); + OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync), "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "false" } })); return true; } @@ -127,7 +118,7 @@ namespace Ombi.Controllers.V1 [HttpPost("plexrecentlyadded")] public bool StartRecentlyAdded() { - BackgroundJob.Enqueue(() => _plexContentSync.CacheContent(true)); + OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IPlexContentSync), "Plex"), new JobDataMap(new Dictionary { { "recentlyAddedSearch", "true" } })); return true; } @@ -136,9 +127,9 @@ namespace Ombi.Controllers.V1 ///
/// [HttpPost("embycontentcacher")] - public bool StartEmbyContentCacher() + public async Task StartEmbyContentCacher() { - BackgroundJob.Enqueue(() => _embyContentSync.Start()); + await OmbiQuartz.TriggerJob(nameof(IEmbyContentSync), "Emby"); return true; } @@ -147,9 +138,9 @@ namespace Ombi.Controllers.V1 ///
/// [HttpPost("newsletter")] - public bool StartNewsletter() + public async Task StartNewsletter() { - BackgroundJob.Enqueue(() => _newsletterJob.Start()); + await OmbiQuartz.TriggerJob(nameof(INewsletterJob), "System"); return true; } } diff --git a/src/Ombi/Controllers/V1/SearchController.cs b/src/Ombi/Controllers/V1/SearchController.cs index 4bac34964..d969c5102 100644 --- a/src/Ombi/Controllers/V1/SearchController.cs +++ b/src/Ombi/Controllers/V1/SearchController.cs @@ -361,6 +361,19 @@ namespace Ombi.Controllers.V1 return await MusicEngine.SearchAlbum(searchTerm); } + /// + /// Returns the album information specified by the foreignAlbumId passed in + /// + /// We use Lidarr as the Provider + /// + [HttpGet("music/album/info/{foreignAlbumId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public async Task GetAlbumInformation(string foreignAlbumId) + { + return await MusicEngine.GetAlbumInformation(foreignAlbumId); + } + /// /// Returns all albums for the artist using the ForeignArtistId /// diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index d7edc2278..c5916a078 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -27,6 +27,10 @@ using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.Notifications; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Ombi.Api.Github; +using Ombi.Core.Engine; +using Ombi.Schedule; +using Quartz; namespace Ombi.Controllers.V1 { @@ -44,7 +48,6 @@ namespace Ombi.Controllers.V1 IMapper mapper, INotificationTemplatesRepository templateRepo, IEmbyApi embyApi, - IRadarrSync radarrSync, ICacheService memCache, IGithubApi githubApi, IRecentlyAddedEngine engine) @@ -53,7 +56,6 @@ namespace Ombi.Controllers.V1 Mapper = mapper; TemplateRepository = templateRepo; _embyApi = embyApi; - _radarrSync = radarrSync; _cache = memCache; _githubApi = githubApi; _recentlyAdded = engine; @@ -63,7 +65,6 @@ namespace Ombi.Controllers.V1 private IMapper Mapper { get; } private INotificationTemplatesRepository TemplateRepository { get; } private readonly IEmbyApi _embyApi; - private readonly IRadarrSync _radarrSync; private readonly ICacheService _cache; private readonly IGithubApi _githubApi; private readonly IRecentlyAddedEngine _recentlyAdded; @@ -408,7 +409,8 @@ namespace Ombi.Controllers.V1 { _cache.Remove(CacheKeys.RadarrRootProfiles); _cache.Remove(CacheKeys.RadarrQualityProfiles); - BackgroundJob.Enqueue(() => _radarrSync.CacheContent()); + + await OmbiQuartz.TriggerJob(nameof(IRadarrSync), "DVR"); } return result; } @@ -568,8 +570,8 @@ namespace Ombi.Controllers.V1 try { - var r = CrontabSchedule.TryParse(expression); - if (r == null) + var isValid = CronExpression.IsValidExpression(expression); + if (!isValid) { return new JobSettingsViewModel { @@ -599,14 +601,15 @@ namespace Ombi.Controllers.V1 var model = new CronTestModel(); try { - var time = DateTime.UtcNow; - var result = CrontabSchedule.TryParse(body.Expression); - for (int i = 0; i < 10; i++) + var isValid = CronExpression.IsValidExpression(body.Expression); + if (!isValid) { - var next = result.GetNextOccurrence(time); - model.Schedule.Add(next); - time = next; + return new CronTestModel + { + Message = $"CRON Expression {body.Expression} is not valid" + }; } + model.Success = true; return model; } @@ -617,8 +620,6 @@ namespace Ombi.Controllers.V1 Message = $"CRON Expression {body.Expression} is not valid" }; } - - } diff --git a/src/Ombi/Controllers/V1/TokenController.cs b/src/Ombi/Controllers/V1/TokenController.cs index eea38dcd1..723431968 100644 --- a/src/Ombi/Controllers/V1/TokenController.cs +++ b/src/Ombi/Controllers/V1/TokenController.cs @@ -126,7 +126,8 @@ namespace Ombi.Controllers.V1 new Claim(JwtRegisteredClaimNames.Sub, user.UserName), new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.UserName), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim("Id", user.Id) }; claims.AddRange(roles.Select(role => new Claim("role", role))); diff --git a/src/Ombi/Controllers/V2/HubController.cs b/src/Ombi/Controllers/V2/HubController.cs new file mode 100644 index 000000000..8fb5f864b --- /dev/null +++ b/src/Ombi/Controllers/V2/HubController.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Engine.V2; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; +using Ombi.Core; +using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.Search; +using Ombi.Core.Models.Search.V2; +using Ombi.Helpers; +using Ombi.Hubs; +using Ombi.Models; + +namespace Ombi.Controllers.V2 +{ + [ApiV2] + [Authorize] + [ApiController] + public class HubController : ControllerBase + { + public HubController(IHubContext hub) + { + _hub = hub; + } + + private readonly IHubContext _hub; + + /// + /// Returns search results for both TV and Movies + /// + /// + [HttpGet("{searchTerm}")] + public async Task MultiSearch(string searchTerm) + { + await _hub.Clients.All.SendAsync("Notification", searchTerm); + } + + /// + /// Returns search results for both TV and Movies + /// + /// + [HttpGet("admin/{searchTerm}")] + public async Task Admin(string searchTerm) + { + var admins = NotificationHub.UsersOnline.Where(x => x.Value.Roles.Contains(OmbiRoles.Admin)).Select(x => x.Key).ToList(); + await _hub.Clients.Clients(admins).SendAsync("Notification", searchTerm); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/CronTestModel.cs b/src/Ombi/Models/CronTestModel.cs index 9698afbff..d8193aef5 100644 --- a/src/Ombi/Models/CronTestModel.cs +++ b/src/Ombi/Models/CronTestModel.cs @@ -7,6 +7,5 @@ namespace Ombi.Models { public bool Success { get; set; } public string Message { get; set; } - public List Schedule { get; set; } = new List(); } } \ No newline at end of file diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index e75774615..2ed1edfd2 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -57,7 +57,7 @@ - + @@ -86,6 +86,7 @@ + diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 7276b9f75..bef289236 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -21,6 +21,7 @@ using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.DependencyInjection; using Ombi.Helpers; +using Ombi.Hubs; using Ombi.Mapping; using Ombi.Schedule; using Ombi.Settings.Settings.Models; @@ -109,13 +110,18 @@ namespace Ombi services.AddCors(o => o.AddPolicy("MyPolicy", builder => { - builder.AllowAnyOrigin() + builder.AllowAnyHeader() .AllowAnyMethod() - .AllowAnyHeader(); + .SetIsOriginAllowed(isOriginAllowed: _ => true) + .AllowCredentials(); })); - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + services.AddSignalR(); + services.AddSpaStaticFiles(configuration => + { + configuration.RootPath = "ClientApp/dist"; + }); // Build the intermediate service provider return services.BuildServiceProvider(); @@ -130,6 +136,8 @@ namespace Ombi ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); + app.UseQuartz().GetAwaiter().GetResult(); + var ctx = serviceProvider.GetService(); loggerFactory.AddSerilog(); @@ -188,8 +196,8 @@ namespace Ombi GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute {Attempts = 3}); // Setup the scheduler - var jobSetup = app.ApplicationServices.GetService(); - jobSetup.Setup(); + //var jobSetup = app.ApplicationServices.GetService(); + //jobSetup.Setup(); ctx.Seed(); var settingsctx = serviceProvider.GetService(); var externalctx = serviceProvider.GetService(); @@ -216,6 +224,8 @@ namespace Ombi c.SwaggerEndpoint("/swagger/v2/swagger.json", "API V2"); }); + app.UseSignalR(routes => { routes.MapHub("/hubs/notification"); }); + app.UseMvcWithDefaultRoute(); app.UseSpa(spa => diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index 716f8c0eb..c127239ff 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; @@ -139,7 +141,25 @@ namespace Ombi { x.Audience = "Ombi"; x.TokenValidationParameters = tokenValidationParameters; + x.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + var accessToken = context.Request.Query["access_token"]; + + // If the request is for our hub... + var path = context.HttpContext.Request.Path; + if (!string.IsNullOrEmpty(accessToken) && + (path.StartsWithSegments("/hubs"))) + { + // Read the token out of the query string + context.Token = accessToken; + } + return Task.CompletedTask; + } + }; }); + } } } \ No newline at end of file diff --git a/src/Ombi/appsettings.json b/src/Ombi/appsettings.json index 8a9e38008..7d352f0dc 100644 --- a/src/Ombi/appsettings.json +++ b/src/Ombi/appsettings.json @@ -2,7 +2,7 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Information", + "Default": "Debug", "System": "Information", "Microsoft": "None", "Hangfire": "None" diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index bfdf6d5bf..e764dde36 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -78,8 +78,9 @@ "ViewOnPlex": "View On Plex", "ViewOnEmby": "View On Emby", "RequestAdded": "Request for {{title}} has been added successfully", - "Similar": "Similar", - "Refine": "Refine", + "Similar":"Similar", + "Refine":"Refine", + "SearchBarPlaceholder":"Type Here to Search", "Movies": { "PopularMovies": "Popular Movies", "UpcomingMovies": "Upcoming Movies",