diff --git a/music-placeholder.psd b/music-placeholder.psd new file mode 100644 index 000000000..5715f850a Binary files /dev/null and b/music-placeholder.psd differ diff --git a/src/Ombi.Api.Lidarr/ILidarrApi.cs b/src/Ombi.Api.Lidarr/ILidarrApi.cs index 5f677412f..9b2460db9 100644 --- a/src/Ombi.Api.Lidarr/ILidarrApi.cs +++ b/src/Ombi.Api.Lidarr/ILidarrApi.cs @@ -11,7 +11,8 @@ namespace Ombi.Api.Lidarr Task> GetProfiles(string apiKey, string baseUrl); Task> GetRootFolders(string apiKey, string baseUrl); Task GetArtist(int artistId, string apiKey, string baseUrl); - Task GetArtistByForignId(string foreignArtistId, string apiKey, string baseUrl); + Task GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl); Task GetAlbumsByArtist(int artistId, string apiKey, string baseUrl); + Task GetAlbumByForeignId(string foreignArtistId, 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 b0bbaa5de..8512b6f5b 100644 --- a/src/Ombi.Api.Lidarr/LidarrApi.cs +++ b/src/Ombi.Api.Lidarr/LidarrApi.cs @@ -62,7 +62,7 @@ namespace Ombi.Api.Lidarr return Api.Request(request); } - public Task GetArtistByForignId(string foreignArtistId, string apiKey, string baseUrl) + public Task GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl) { var request = new Request($"{ApiVersion}/artist/lookup", baseUrl, HttpMethod.Get); @@ -71,6 +71,16 @@ namespace Ombi.Api.Lidarr return Api.Request(request); } + + public Task GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl) + { + var request = new Request($"{ApiVersion}/album/lookup", baseUrl, HttpMethod.Get); + + request.AddQueryString("term", $"lidarr:{foreignArtistId}"); + AddHeaders(request, apiKey); + return Api.Request(request); + } + public Task GetAlbumsByArtist(int artistId, string apiKey, string baseUrl) { var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get); diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index 2eab74b75..cb0047b96 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -36,6 +36,7 @@ namespace Ombi.Core.Engine protected IRequestServiceMain RequestService { get; } protected IMovieRequestRepository MovieRepository => RequestService.MovieRequestService; protected ITvRequestRepository TvRepository => RequestService.TvRequestService; + protected IMusicRequestRepository MusicRepository => RequestService.MusicRequestRepository; protected readonly ICacheService Cache; protected readonly ISettingsService OmbiSettings; protected readonly IRepository _subscriptionRepository; diff --git a/src/Ombi.Core/Engine/IMusicRequestEngine.cs b/src/Ombi.Core/Engine/IMusicRequestEngine.cs new file mode 100644 index 000000000..d81ccb429 --- /dev/null +++ b/src/Ombi.Core/Engine/IMusicRequestEngine.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.UI; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Core.Engine +{ + public interface IMusicRequestEngine + { + TaskApproveAlbum(AlbumRequest request); + Task ApproveAlbumById(int requestId); + Task DenyAlbumById(int modelId); + Task> GetRequests(); + Task> GetRequests(int count, int position, OrderFilterModel orderFilter); + Task GetTotal(); + Task MarkAvailable(int modelId); + Task MarkUnavailable(int modelId); + Task RemoveAlbumRequest(int requestId); + Task RequestAlbum(MusicAlbumRequestViewModel model); + Task> SearchAlbumRequest(string search); + Task UserHasRequest(string userId); + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs index e30cd2f88..95ec41b51 100644 --- a/src/Ombi.Core/Engine/MusicRequestEngine.cs +++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs @@ -1,492 +1,456 @@ -//using Ombi.Api.TheMovieDb; -//using Ombi.Core.Models.Requests; -//using Ombi.Helpers; -//using Ombi.Store.Entities; -//using System; -//using System.Collections.Generic; -//using System.Globalization; -//using System.Linq; -//using System.Security.Principal; -//using System.Threading.Tasks; -//using Microsoft.EntityFrameworkCore; -//using Microsoft.Extensions.Logging; -//using Ombi.Api.Lidarr; -//using Ombi.Api.TheMovieDb.Models; -//using Ombi.Core.Authentication; -//using Ombi.Core.Engine.Interfaces; -//using Ombi.Core.Models.UI; -//using Ombi.Core.Rule.Interfaces; -//using Ombi.Core.Settings; -//using Ombi.Settings.Settings.Models; -//using Ombi.Settings.Settings.Models.External; -//using Ombi.Store.Entities.Requests; -//using Ombi.Store.Repository; - -//namespace Ombi.Core.Engine -//{ -// public class MusicRequestEngine : BaseMediaEngine -// { -// public MusicRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, -// INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, -// OmbiUserManager manager, IRepository rl, ICacheService cache, -// ISettingsService ombiSettings, IRepository sub, ILidarrApi lidarr, -// ISettingsService lidarrSettings) -// : base(user, requestService, r, manager, cache, ombiSettings, sub) -// { -// MovieApi = movieApi; -// NotificationHelper = helper; -// Sender = sender; -// Logger = log; -// _requestLog = rl; -// _lidarrApi = lidarr; -// _lidarrSettings = lidarrSettings; -// } - -// private IMovieDbApi MovieApi { get; } -// private INotificationHelper NotificationHelper { get; } -// private IMovieSender Sender { get; } -// private ILogger Logger { get; } -// private readonly IRepository _requestLog; -// private readonly ISettingsService _lidarrSettings; -// private readonly ILidarrApi _lidarrApi; - -// /// -// /// Requests the movie. -// /// -// /// The model. -// /// -// public async Task RequestArtist(MusicArtistRequestViewModel model) -// { -// var s = await _lidarrSettings.GetSettingsAsync(); -// var artist = await _lidarrApi.GetArtistByForignId(model.ForeignArtistId, s.ApiKey, s.FullUri); -// if (artist == null) -// { -// return new RequestEngineResult -// { -// Result = false, -// Message = "There was an issue adding this Artist!", -// ErrorMessage = "Please try again later" -// }; -// } - -// var userDetails = await GetUser(); - -// var requestModel = new MovieRequests -// { -// TheMovieDbId = movieInfo.Id, -// RequestType = RequestType.Movie, -// Overview = movieInfo.Overview, -// ImdbId = movieInfo.ImdbId, -// PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath), -// Title = movieInfo.Title, -// ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate) -// ? DateTime.Parse(movieInfo.ReleaseDate) -// : DateTime.MinValue, -// Status = movieInfo.Status, -// RequestedDate = DateTime.UtcNow, -// Approved = false, -// RequestedUserId = userDetails.Id, -// Background = movieInfo.BackdropPath -// }; - -// var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); -// requestModel.DigitalReleaseDate = usDates?.ReleaseDate -// ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; - -// var ruleResults = (await RunRequestRules(requestModel)).ToList(); -// if (ruleResults.Any(x => !x.Success)) -// { -// return new RequestEngineResult -// { -// ErrorMessage = ruleResults.FirstOrDefault(x => x.Message.HasValue()).Message -// }; -// } - -// if (requestModel.Approved) // The rules have auto approved this -// { -// var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName); -// if (requestEngineResult.Result) -// { -// var result = await ApproveMovie(requestModel); -// if (result.IsError) -// { -// Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); -// return new RequestEngineResult -// { -// Message = result.Message, -// ErrorMessage = result.Message, -// Result = false -// }; -// } - -// return requestEngineResult; -// } - -// // If there are no providers then it's successful but movie has not been sent -// } - -// return await AddMovieRequest(requestModel, fullMovieName); -// } - - -// /// -// /// Gets the requests. -// /// -// /// The count. -// /// The position. -// /// The order/filter type. -// /// -// public async Task> GetRequests(int count, int position, -// OrderFilterModel orderFilter) -// { -// var shouldHide = await HideFromOtherUsers(); -// IQueryable allRequests; -// if (shouldHide.Hide) -// { -// allRequests = -// MovieRepository.GetWithUser(shouldHide -// .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); -// } -// else -// { -// allRequests = -// MovieRepository -// .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); -// } - -// switch (orderFilter.AvailabilityFilter) -// { -// case FilterType.None: -// break; -// case FilterType.Available: -// allRequests = allRequests.Where(x => x.Available); -// break; -// case FilterType.NotAvailable: -// allRequests = allRequests.Where(x => !x.Available); -// break; -// default: -// throw new ArgumentOutOfRangeException(); -// } - -// switch (orderFilter.StatusFilter) -// { -// case FilterType.None: -// break; -// case FilterType.Approved: -// allRequests = allRequests.Where(x => x.Approved); -// break; -// case FilterType.Processing: -// allRequests = allRequests.Where(x => x.Approved && !x.Available); -// break; -// case FilterType.PendingApproval: -// allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); -// break; -// default: -// throw new ArgumentOutOfRangeException(); -// } - -// var total = allRequests.Count(); - -// var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count) -// .ToListAsync(); - -// requests.ForEach(async x => -// { -// x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); -// await CheckForSubscription(shouldHide, x); -// }); -// return new RequestsViewModel -// { -// Collection = requests, -// Total = total -// }; -// } - -// private IQueryable OrderMovies(IQueryable allRequests, OrderType type) -// { -// switch (type) -// { -// case OrderType.RequestedDateAsc: -// return allRequests.OrderBy(x => x.RequestedDate); -// case OrderType.RequestedDateDesc: -// return allRequests.OrderByDescending(x => x.RequestedDate); -// case OrderType.TitleAsc: -// return allRequests.OrderBy(x => x.Title); -// case OrderType.TitleDesc: -// return allRequests.OrderByDescending(x => x.Title); -// case OrderType.StatusAsc: -// return allRequests.OrderBy(x => x.Status); -// case OrderType.StatusDesc: -// return allRequests.OrderByDescending(x => x.Status); -// default: -// throw new ArgumentOutOfRangeException(nameof(type), type, null); -// } -// } - -// public async Task GetTotal() -// { -// var shouldHide = await HideFromOtherUsers(); -// if (shouldHide.Hide) -// { -// return await MovieRepository.GetWithUser(shouldHide.UserId).CountAsync(); -// } -// else -// { -// return await MovieRepository.GetWithUser().CountAsync(); -// } -// } - -// /// -// /// Gets the requests. -// /// -// /// -// public async Task> GetRequests() -// { -// var shouldHide = await HideFromOtherUsers(); -// List allRequests; -// if (shouldHide.Hide) -// { -// allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).ToListAsync(); -// } -// else -// { -// allRequests = await MovieRepository.GetWithUser().ToListAsync(); -// } - -// allRequests.ForEach(async x => -// { -// x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); -// await CheckForSubscription(shouldHide, x); -// }); -// return allRequests; -// } - -// private async Task CheckForSubscription(HideResult shouldHide, MovieRequests x) -// { -// if (shouldHide.UserId == x.RequestedUserId) -// { -// x.ShowSubscribe = false; -// } -// else -// { -// x.ShowSubscribe = true; -// var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => -// s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Movie); -// x.Subscribed = sub != null; -// } -// } - -// /// -// /// Searches the movie request. -// /// -// /// The search. -// /// -// public async Task> SearchMovieRequest(string search) -// { -// var shouldHide = await HideFromOtherUsers(); -// List allRequests; -// if (shouldHide.Hide) -// { -// allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).ToListAsync(); -// } -// else -// { -// allRequests = await MovieRepository.GetWithUser().ToListAsync(); -// } - -// var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); -// results.ForEach(async x => -// { -// x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); -// await CheckForSubscription(shouldHide, x); -// }); -// return results; -// } - -// public async Task ApproveMovieById(int requestId) -// { -// var request = await MovieRepository.Find(requestId); -// return await ApproveMovie(request); -// } - -// public async Task DenyMovieById(int modelId) -// { -// var request = await MovieRepository.Find(modelId); -// if (request == null) -// { -// return new RequestEngineResult -// { -// ErrorMessage = "Request does not exist" -// }; -// } - -// request.Denied = true; -// // We are denying a request -// NotificationHelper.Notify(request, NotificationType.RequestDeclined); -// await MovieRepository.Update(request); - -// return new RequestEngineResult -// { -// Message = "Request successfully deleted", -// }; -// } - -// public async Task ApproveMovie(MovieRequests request) -// { -// if (request == null) -// { -// return new RequestEngineResult -// { -// ErrorMessage = "Request does not exist" -// }; -// } - -// request.Approved = true; -// request.Denied = false; -// await MovieRepository.Update(request); - -// var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification); -// if (canNotify.Success) -// { -// NotificationHelper.Notify(request, NotificationType.RequestApproved); -// } - -// if (request.Approved) -// { -// var result = await Sender.Send(request); -// if (result.Success && result.Sent) -// { -// return new RequestEngineResult -// { -// Result = true -// }; -// } - -// if (!result.Success) -// { -// Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); -// return new RequestEngineResult -// { -// Message = result.Message, -// ErrorMessage = result.Message, -// Result = false -// }; -// } - -// // If there are no providers then it's successful but movie has not been sent -// } - -// return new RequestEngineResult -// { -// Result = true -// }; -// } - -// /// -// /// Updates the movie request. -// /// -// /// The request. -// /// -// public async Task UpdateMovieRequest(MovieRequests request) -// { -// var allRequests = await MovieRepository.GetWithUser().ToListAsync(); -// var results = allRequests.FirstOrDefault(x => x.Id == request.Id); - -// results.Approved = request.Approved; -// results.Available = request.Available; -// results.Denied = request.Denied; -// results.DeniedReason = request.DeniedReason; -// results.ImdbId = request.ImdbId; -// results.IssueId = request.IssueId; -// results.Issues = request.Issues; -// results.Overview = request.Overview; -// results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath); -// results.QualityOverride = request.QualityOverride; -// results.RootPathOverride = request.RootPathOverride; - -// await MovieRepository.Update(results); -// return results; -// } - -// /// -// /// Removes the movie request. -// /// -// /// The request identifier. -// /// -// public async Task RemoveMovieRequest(int requestId) -// { -// var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); -// await MovieRepository.Delete(request); -// } - -// public async Task UserHasRequest(string userId) -// { -// return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); -// } - -// public async Task MarkUnavailable(int modelId) -// { -// var request = await MovieRepository.Find(modelId); -// if (request == null) -// { -// return new RequestEngineResult -// { -// ErrorMessage = "Request does not exist" -// }; -// } - -// request.Available = false; -// await MovieRepository.Update(request); - -// return new RequestEngineResult -// { -// Message = "Request is now unavailable", -// Result = true -// }; -// } - -// public async Task MarkAvailable(int modelId) -// { -// var request = await MovieRepository.Find(modelId); -// if (request == null) -// { -// return new RequestEngineResult -// { -// ErrorMessage = "Request does not exist" -// }; -// } - -// request.Available = true; -// request.MarkedAsAvailable = DateTime.Now; -// NotificationHelper.Notify(request, NotificationType.RequestAvailable); -// await MovieRepository.Update(request); - -// return new RequestEngineResult -// { -// Message = "Request is now available", -// Result = true -// }; -// } - -// private async Task AddMovieRequest(MovieRequests model, string movieName) -// { -// await MovieRepository.Add(model); - -// var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); -// if (result.Success) -// { -// NotificationHelper.NewRequest(model); -// } - -// await _requestLog.Add(new RequestLog -// { -// UserId = (await GetUser()).Id, -// RequestDate = DateTime.UtcNow, -// RequestId = model.Id, -// RequestType = RequestType.Movie, -// }); - -// return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"}; -// } -// } -//} \ No newline at end of file +using Ombi.Api.TheMovieDb; +using Ombi.Core.Models.Requests; +using Ombi.Helpers; +using Ombi.Store.Entities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Api.Lidarr; +using Ombi.Core.Authentication; +using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.UI; +using Ombi.Core.Rule.Interfaces; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Core.Engine +{ + public class MusicRequestEngine : BaseMediaEngine, IMusicRequestEngine + { + public MusicRequestEngine(IRequestServiceMain requestService, IPrincipal user, + INotificationHelper helper, IRuleEvaluator r, ILogger log, + OmbiUserManager manager, IRepository rl, ICacheService cache, + ISettingsService ombiSettings, IRepository sub, ILidarrApi lidarr, + ISettingsService lidarrSettings) + : base(user, requestService, r, manager, cache, ombiSettings, sub) + { + NotificationHelper = helper; + //Sender = sender; + Logger = log; + _requestLog = rl; + _lidarrApi = lidarr; + _lidarrSettings = lidarrSettings; + } + + private INotificationHelper NotificationHelper { get; } + //private IMovieSender Sender { get; } + private ILogger Logger { get; } + private readonly IRepository _requestLog; + private readonly ISettingsService _lidarrSettings; + private readonly ILidarrApi _lidarrApi; + + /// + /// Requests the Album. + /// + /// The model. + /// + public async Task RequestAlbum(MusicAlbumRequestViewModel model) + { + var s = await _lidarrSettings.GetSettingsAsync(); + var album = await _lidarrApi.GetAlbumByForeignId(model.ForeignAlbumId, s.ApiKey, s.FullUri); + if (album == null) + { + return new RequestEngineResult + { + Result = false, + Message = "There was an issue adding this album!", + ErrorMessage = "Please try again later" + }; + } + + var userDetails = await GetUser(); + + var requestModel = new AlbumRequest + { + ForeignAlbumId = model.ForeignAlbumId, + ArtistName = album.artist?.artistName, + ReleaseDate = album.releaseDate, + RequestedDate = DateTime.Now, + RequestType = RequestType.Album, + Rating = album.ratings?.value ?? 0m, + RequestedUserId = userDetails.Id, + Title = album.title, + Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url, + Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url + }; + if (requestModel.Cover.IsNullOrEmpty()) + { + requestModel.Cover = album.remoteCover; + } + + var ruleResults = (await RunRequestRules(requestModel)).ToList(); + if (ruleResults.Any(x => !x.Success)) + { + return new RequestEngineResult + { + ErrorMessage = ruleResults.FirstOrDefault(x => x.Message.HasValue()).Message + }; + } + + if (requestModel.Approved) // The rules have auto approved this + { + var requestEngineResult = await AddAlbumRequest(requestModel); + if (requestEngineResult.Result) + { + var result = await ApproveAlbum(requestModel); + if (result.IsError) + { + Logger.LogWarning("Tried auto sending Album but failed. Message: {0}", result.Message); + return new RequestEngineResult + { + Message = result.Message, + ErrorMessage = result.Message, + Result = false + }; + } + + return requestEngineResult; + } + + // If there are no providers then it's successful but album has not been sent + } + + return await AddAlbumRequest(requestModel); + } + + + /// + /// Gets the requests. + /// + /// The count. + /// The position. + /// The order/filter type. + /// + public async Task> GetRequests(int count, int position, + OrderFilterModel orderFilter) + { + var shouldHide = await HideFromOtherUsers(); + IQueryable allRequests; + if (shouldHide.Hide) + { + allRequests = + MusicRepository.GetWithUser(shouldHide + .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + } + else + { + allRequests = + MusicRepository + .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + } + + switch (orderFilter.AvailabilityFilter) + { + case FilterType.None: + break; + case FilterType.Available: + allRequests = allRequests.Where(x => x.Available); + break; + case FilterType.NotAvailable: + allRequests = allRequests.Where(x => !x.Available); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (orderFilter.StatusFilter) + { + case FilterType.None: + break; + case FilterType.Approved: + allRequests = allRequests.Where(x => x.Approved); + break; + case FilterType.Processing: + allRequests = allRequests.Where(x => x.Approved && !x.Available); + break; + case FilterType.PendingApproval: + allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var total = allRequests.Count(); + + var requests = await (OrderAlbums(allRequests, orderFilter.OrderType)).Skip(position).Take(count) + .ToListAsync(); + + requests.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return new RequestsViewModel + { + Collection = requests, + Total = total + }; + } + + private IQueryable OrderAlbums(IQueryable allRequests, OrderType type) + { + switch (type) + { + case OrderType.RequestedDateAsc: + return allRequests.OrderBy(x => x.RequestedDate); + case OrderType.RequestedDateDesc: + return allRequests.OrderByDescending(x => x.RequestedDate); + case OrderType.TitleAsc: + return allRequests.OrderBy(x => x.Title); + case OrderType.TitleDesc: + return allRequests.OrderByDescending(x => x.Title); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await MusicRepository.GetWithUser(shouldHide.UserId).CountAsync(); + } + else + { + return await MusicRepository.GetWithUser().CountAsync(); + } + } + + /// + /// Gets the requests. + /// + /// + public async Task> GetRequests() + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync(); + } + else + { + allRequests = await MusicRepository.GetWithUser().ToListAsync(); + } + + allRequests.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return allRequests; + } + + private async Task CheckForSubscription(HideResult shouldHide, AlbumRequest x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Album); + x.Subscribed = sub != null; + } + } + + /// + /// Searches the album request. + /// + /// The search. + /// + public async Task> SearchAlbumRequest(string search) + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await MusicRepository.GetWithUser(shouldHide.UserId).ToListAsync(); + } + else + { + allRequests = await MusicRepository.GetWithUser().ToListAsync(); + } + + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); + results.ForEach(async x => + { + await CheckForSubscription(shouldHide, x); + }); + return results; + } + + public async Task ApproveAlbumById(int requestId) + { + var request = await MusicRepository.Find(requestId); + return await ApproveAlbum(request); + } + + public async Task DenyAlbumById(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Denied = true; + // We are denying a request + NotificationHelper.Notify(request, NotificationType.RequestDeclined); + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request successfully deleted", + }; + } + + public async Task ApproveAlbum(AlbumRequest request) + { + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + if (!request.Approved) + { + request.Approved = true; + request.Denied = false; + await MusicRepository.Update(request); + } + + var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification); + if (canNotify.Success) + { + NotificationHelper.Notify(request, NotificationType.RequestApproved); + } + + if (request.Approved) + { + //TODO + //var result = await Sender.Send(request); + //if (result.Success && result.Sent) + //{ + // return new RequestEngineResult + // { + // Result = true + // }; + //} + + //if (!result.Success) + //{ + // Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); + // return new RequestEngineResult + // { + // Message = result.Message, + // ErrorMessage = result.Message, + // Result = false + // }; + //} + + // If there are no providers then it's successful but movie has not been sent + } + + return new RequestEngineResult + { + Result = true + }; + } + + /// + /// Removes the Album request. + /// + /// The request identifier. + /// + public async Task RemoveAlbumRequest(int requestId) + { + var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); + await MusicRepository.Delete(request); + } + + public async Task UserHasRequest(string userId) + { + return await MusicRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); + } + + public async Task MarkUnavailable(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Available = false; + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request is now unavailable", + Result = true + }; + } + + public async Task MarkAvailable(int modelId) + { + var request = await MusicRepository.Find(modelId); + if (request == null) + { + return new RequestEngineResult + { + ErrorMessage = "Request does not exist" + }; + } + + request.Available = true; + request.MarkedAsAvailable = DateTime.Now; + NotificationHelper.Notify(request, NotificationType.RequestAvailable); + await MusicRepository.Update(request); + + return new RequestEngineResult + { + Message = "Request is now available", + Result = true + }; + } + + private async Task AddAlbumRequest(AlbumRequest model) + { + await MusicRepository.Add(model); + + var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); + if (result.Success) + { + NotificationHelper.NewRequest(model); + } + + await _requestLog.Add(new RequestLog + { + UserId = (await GetUser()).Id, + RequestDate = DateTime.UtcNow, + RequestId = model.Id, + RequestType = RequestType.Album, + }); + + return new RequestEngineResult { Result = true, Message = $"{model.Title} has been successfully added!" }; + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MusicSearchEngine.cs b/src/Ombi.Core/Engine/MusicSearchEngine.cs index abfbd2721..b90fca88c 100644 --- a/src/Ombi.Core/Engine/MusicSearchEngine.cs +++ b/src/Ombi.Core/Engine/MusicSearchEngine.cs @@ -87,7 +87,7 @@ namespace Ombi.Core.Engine public async Task GetArtistAlbums(string foreignArtistId) { var settings = await GetSettings(); - return await _lidarrApi.GetArtistByForignId(foreignArtistId, settings.ApiKey, settings.FullUri); + return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri); } /// @@ -98,7 +98,7 @@ namespace Ombi.Core.Engine public async Task GetAlbumArtist(string foreignArtistId) { var settings = await GetSettings(); - return await _lidarrApi.GetArtistByForignId(foreignArtistId, settings.ApiKey, settings.FullUri); + return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri); } public async Task GetArtist(int artistId) @@ -157,7 +157,7 @@ namespace Ombi.Core.Engine } vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url; - if (vm.Cover == null) + if (vm.Cover.IsNullOrEmpty()) { vm.Cover = a.remoteCover; } diff --git a/src/Ombi.Core/Helpers/NotificationHelper.cs b/src/Ombi.Core/Helpers/NotificationHelper.cs index 9bd890c21..1615b24f7 100644 --- a/src/Ombi.Core/Helpers/NotificationHelper.cs +++ b/src/Ombi.Core/Helpers/NotificationHelper.cs @@ -40,6 +40,18 @@ namespace Ombi.Core BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); } + public void NewRequest(AlbumRequest model) + { + var notificationModel = new NotificationOptions + { + RequestId = model.Id, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = model.RequestType + }; + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); + } + public void Notify(MovieRequests model, NotificationType type) { @@ -66,5 +78,19 @@ namespace Ombi.Core }; BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); } + + public void Notify(AlbumRequest model, NotificationType type) + { + var notificationModel = new NotificationOptions + { + RequestId = model.Id, + DateTime = DateTime.Now, + NotificationType = type, + RequestType = model.RequestType, + Recipient = model.RequestedUser?.Email ?? string.Empty + }; + + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel)); + } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs b/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs index 8a269054f..0e68a38e9 100644 --- a/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs +++ b/src/Ombi.Core/Models/Requests/IRequestServiceMain.cs @@ -7,5 +7,6 @@ namespace Ombi.Core.Models.Requests { IMovieRequestRepository MovieRequestService { get; } ITvRequestRepository TvRequestService { get; } + IMusicRequestRepository MusicRequestRepository { get; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs index d4b857153..4c3426c9e 100644 --- a/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/MusicArtistRequestViewModel.cs @@ -1,15 +1,7 @@ namespace Ombi.Core.Models.Requests { - public class MusicArtistRequestViewModel + public class MusicAlbumRequestViewModel { - public string ForeignArtistId { get; set; } - public ArtistRequestOption RequestOption { get; set; } - } - - public enum ArtistRequestOption - { - AllAlbums = 0, - LatestAlbum = 1, - FirstAlbum = 2 + public string ForeignAlbumId { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index f75d08e6b..c2aeb1fd0 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -14,7 +14,6 @@ - diff --git a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs index 7eecd62f2..a55868db8 100644 --- a/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/AutoApproveRule.cs @@ -29,6 +29,8 @@ namespace Ombi.Core.Rule.Rules.Request obj.Approved = true; if (obj.RequestType == RequestType.TvShow && User.IsInRole(OmbiRoles.AutoApproveTv)) obj.Approved = true; + if (obj.RequestType == RequestType.Album && User.IsInRole(OmbiRoles.AutoApproveMusic)) + obj.Approved = true; return Task.FromResult(Success()); // We don't really care, we just don't set the obj to approve } } diff --git a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs index e9729ec35..1cdf03955 100644 --- a/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/CanRequestRule.cs @@ -23,13 +23,23 @@ namespace Ombi.Core.Rule.Rules if (obj.RequestType == RequestType.Movie) { - if (User.IsInRole(OmbiRoles.RequestMovie)) + if (User.IsInRole(OmbiRoles.RequestMovie) || User.IsInRole(OmbiRoles.AutoApproveMovie)) return Task.FromResult(Success()); return Task.FromResult(Fail("You do not have permissions to Request a Movie")); } - if (User.IsInRole(OmbiRoles.RequestTv)) - return Task.FromResult(Success()); + if (obj.RequestType == RequestType.TvShow) + { + if (User.IsInRole(OmbiRoles.RequestTv) || User.IsInRole(OmbiRoles.AutoApproveTv)) + return Task.FromResult(Success()); + } + + if (obj.RequestType == RequestType.Album) + { + if (User.IsInRole(OmbiRoles.RequestMusic) || User.IsInRole(OmbiRoles.AutoApproveMusic)) + return Task.FromResult(Success()); + } + return Task.FromResult(Fail("You do not have permissions to Request a TV Show")); } } diff --git a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs index a19ac1df8..53971bb3e 100644 --- a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs @@ -54,6 +54,7 @@ namespace Ombi.Core.Rule.Rules.Request var movieLimit = user.MovieRequestLimit; var episodeLimit = user.EpisodeRequestLimit; + var musicLimit = user.MusicRequestLimit; var requestLog = _requestLog.GetAll().Where(x => x.UserId == obj.RequestedUserId); if (obj.RequestType == RequestType.Movie) @@ -71,7 +72,7 @@ namespace Ombi.Core.Rule.Rules.Request return Fail("You have exceeded your Movie request quota!"); } } - else + else if (obj.RequestType == RequestType.TvShow) { if (episodeLimit <= 0) return Success(); @@ -94,8 +95,22 @@ namespace Ombi.Core.Rule.Rules.Request { return Fail("You have exceeded your Episode request quota!"); } + } else if (obj.RequestType == RequestType.Album) + { + if (musicLimit <= 0) + return Success(); + + var albumLogs = requestLog.Where(x => x.RequestType == RequestType.Album); + + // Count how many requests in the past 7 days + var count = await albumLogs.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + count += 1; // Since we are including this request + if (count > musicLimit) + { + return Fail("You have exceeded your Album request quota!"); + } } - return Success(); + return Success(); } } } diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index 6ca0b966b..bd7218145 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -20,11 +20,11 @@ namespace Ombi.Core.Rule.Rules.Search private IMovieRequestRepository Movie { get; } private ITvRequestRepository Tv { get; } - public Task Execute(SearchViewModel obj) + public async Task Execute(SearchViewModel obj) { if (obj.Type == RequestType.Movie) { - var movieRequests = Movie.GetRequest(obj.Id); + var movieRequests = await Movie.GetRequestAsync(obj.Id); if (movieRequests != null) // Do we already have a request for this? { @@ -33,11 +33,11 @@ namespace Ombi.Core.Rule.Rules.Search obj.Approved = movieRequests.Approved; obj.Available = movieRequests.Available; - return Task.FromResult(Success()); + return Success(); } - return Task.FromResult(Success()); + return Success(); } - else + else if (obj.Type == RequestType.Album) { //var tvRequests = Tv.GetRequest(obj.Id); //if (tvRequests != null) // Do we already have a request for this? @@ -94,8 +94,9 @@ namespace Ombi.Core.Rule.Rules.Search request.PartlyAvailable = true; } - return Task.FromResult(Success()); + return Success(); } + return Success(); } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs index 50ac607cb..3f9e2f159 100644 --- a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs +++ b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs @@ -42,6 +42,13 @@ namespace Ombi.Core.Rule.Rules.Specific sendNotification = !await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.AutoApproveTv); } } + else if (req.RequestType == RequestType.Album) + { + if (settings.DoNotSendNotificationsForAutoApprove) + { + sendNotification = !await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.AutoApproveMusic); + } + } if (await UserManager.IsInRoleAsync(requestedUser, OmbiRoles.Admin)) { diff --git a/src/Ombi.Core/Senders/INotificationHelper.cs b/src/Ombi.Core/Senders/INotificationHelper.cs index efc45020c..4ba47d761 100644 --- a/src/Ombi.Core/Senders/INotificationHelper.cs +++ b/src/Ombi.Core/Senders/INotificationHelper.cs @@ -8,7 +8,9 @@ namespace Ombi.Core { void NewRequest(FullBaseRequest model); void NewRequest(ChildRequests model); + void NewRequest(AlbumRequest model); void Notify(MovieRequests model, NotificationType type); void Notify(ChildRequests model, NotificationType type); + void Notify(AlbumRequest model, NotificationType type); } } \ No newline at end of file diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 80565a0c8..92a86c5f6 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -84,6 +84,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -134,6 +135,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.Helpers/OmbiRoles.cs b/src/Ombi.Helpers/OmbiRoles.cs index 5b62a2bae..1d584d57f 100644 --- a/src/Ombi.Helpers/OmbiRoles.cs +++ b/src/Ombi.Helpers/OmbiRoles.cs @@ -7,9 +7,11 @@ public const string Admin = nameof(Admin); public const string AutoApproveMovie = nameof(AutoApproveMovie); public const string AutoApproveTv = nameof(AutoApproveTv); + public const string AutoApproveMusic = nameof(AutoApproveMusic); public const string PowerUser = nameof(PowerUser); public const string RequestTv = nameof(RequestTv); public const string RequestMovie = nameof(RequestMovie); + public const string RequestMusic = nameof(RequestMusic); public const string Disabled = nameof(Disabled); public const string ReceivesNewsletter = nameof(ReceivesNewsletter); } diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 0c716c7c4..c177784c8 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -28,6 +28,7 @@ namespace Ombi.Store.Context void Seed(); DbSet Audit { get; set; } DbSet MovieRequests { get; set; } + DbSet AlbumRequests { get; set; } DbSet TvRequests { get; set; } DbSet ChildRequests { get; set; } DbSet Issues { get; set; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 8d6036877..03192d998 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -31,6 +31,7 @@ namespace Ombi.Store.Context public DbSet EmbyEpisode { get; set; } public DbSet MovieRequests { get; set; } + public DbSet AlbumRequests { get; set; } public DbSet TvRequests { get; set; } public DbSet ChildRequests { get; set; } diff --git a/src/Ombi.Store/Entities/OmbiUser.cs b/src/Ombi.Store/Entities/OmbiUser.cs index f67183982..9513df818 100644 --- a/src/Ombi.Store/Entities/OmbiUser.cs +++ b/src/Ombi.Store/Entities/OmbiUser.cs @@ -23,6 +23,7 @@ namespace Ombi.Store.Entities public int? MovieRequestLimit { get; set; } public int? EpisodeRequestLimit { get; set; } + public int? MusicRequestLimit { get; set; } public string UserAccessToken { get; set; } @@ -59,5 +60,6 @@ namespace Ombi.Store.Entities get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; } + } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RequestType.cs b/src/Ombi.Store/Entities/RequestType.cs index 06cd6c069..151453bdd 100644 --- a/src/Ombi.Store/Entities/RequestType.cs +++ b/src/Ombi.Store/Entities/RequestType.cs @@ -7,6 +7,7 @@ namespace Ombi.Store.Entities public enum RequestType { TvShow = 0, - Movie = 1 + Movie = 1, + Album = 2, } } diff --git a/src/Ombi.Store/Entities/Requests/AlbumRequest.cs b/src/Ombi.Store/Entities/Requests/AlbumRequest.cs new file mode 100644 index 000000000..2735603c6 --- /dev/null +++ b/src/Ombi.Store/Entities/Requests/AlbumRequest.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities.Requests +{ + [Table("AlbumRequests")] + public class AlbumRequest : BaseRequest + { + public string ForeignAlbumId { get; set; } + public string ForeignArtistId { get; set; } + public string Disk { get; set; } + public string Cover { get; set; } + public decimal Rating { get; set; } + public DateTime ReleaseDate { get; set; } + public string ArtistName { get; set; } + [NotMapped] + public bool Subscribed { get; set; } + [NotMapped] + public bool ShowSubscribe { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs b/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs new file mode 100644 index 000000000..28cb0b2f9 --- /dev/null +++ b/src/Ombi.Store/Repository/Requests/IMusicRequestRepository.cs @@ -0,0 +1,17 @@ +using System.Linq; +using System.Threading.Tasks; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Store.Repository.Requests +{ + public interface IMusicRequestRepository : IRepository + { + IQueryable GetAll(string userId); + AlbumRequest GetRequest(string foreignAlbumId); + Task GetRequestAsync(string foreignAlbumId); + IQueryable GetWithUser(); + IQueryable GetWithUser(string userId); + Task Save(); + Task Update(AlbumRequest request); + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs new file mode 100644 index 000000000..59edf265a --- /dev/null +++ b/src/Ombi.Store/Repository/Requests/MusicRequestRepository.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Store.Context; +using Ombi.Store.Entities.Requests; + +namespace Ombi.Store.Repository.Requests +{ + public class MusicRequestRepository : Repository, IMusicRequestRepository + { + public MusicRequestRepository(IOmbiContext ctx) : base(ctx) + { + Db = ctx; + } + + private IOmbiContext Db { get; } + + public Task GetRequestAsync(string foreignAlbumId) + { + return Db.AlbumRequests.Where(x => x.ForeignAlbumId == foreignAlbumId) + .Include(x => x.RequestedUser) + .FirstOrDefaultAsync(); + } + + public IQueryable GetAll(string userId) + { + return GetWithUser().Where(x => x.RequestedUserId == userId); + } + + public AlbumRequest GetRequest(string foreignAlbumId) + { + return Db.AlbumRequests.Where(x => x.ForeignAlbumId == foreignAlbumId) + .Include(x => x.RequestedUser) + .FirstOrDefault(); + } + + public IQueryable GetWithUser() + { + return Db.AlbumRequests + .Include(x => x.RequestedUser) + .ThenInclude(x => x.NotificationUserIds) + .AsQueryable(); + } + + + public IQueryable GetWithUser(string userId) + { + return Db.AlbumRequests + .Where(x => x.RequestedUserId == userId) + .Include(x => x.RequestedUser) + .ThenInclude(x => x.NotificationUserIds) + .AsQueryable(); + } + + public async Task Update(AlbumRequest request) + { + if (Db.Entry(request).State == EntityState.Detached) + { + Db.AlbumRequests.Attach(request); + Db.Update(request); + } + await Db.SaveChangesAsync(); + } + + public async Task Save() + { + await Db.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/music/albumsearch.component.html b/src/Ombi/ClientApp/app/search/music/albumsearch.component.html index b8e85da3e..317860dbd 100644 --- a/src/Ombi/ClientApp/app/search/music/albumsearch.component.html +++ b/src/Ombi/ClientApp/app/search/music/albumsearch.component.html @@ -1,4 +1,4 @@ -
+
@@ -12,14 +12,16 @@
@@ -82,7 +84,8 @@ + {{ 'Common.Request' + | translate }}
diff --git a/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts index 073e64378..ad980ff60 100644 --- a/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts +++ b/src/Ombi/ClientApp/app/search/music/musicsearch.component.ts @@ -22,7 +22,7 @@ export class MusicSearchComponent implements OnInit { public albumResult: ISearchAlbumResult[]; public result: IRequestEngineResult; public searchApplied = false; - public searchAlbum: boolean = true; + public searchAlbum: boolean = false; @Input() public issueCategories: IIssueCategory[]; @Input() public issuesEnabled: boolean; @@ -72,10 +72,10 @@ export class MusicSearchComponent implements OnInit { }); } }); - this.defaultPoster = "../../../images/default_movie_poster.png"; + this.defaultPoster = "../../../images/default-music-placeholder.png"; const base = this.platformLocation.getBaseHrefFromDOM(); if (base) { - this.defaultPoster = "../../.." + base + "/images/default_movie_poster.png"; + this.defaultPoster = "../../.." + base + "/images/default-music-placeholder.png"; } } diff --git a/src/Ombi/ClientApp/app/search/search.component.html b/src/Ombi/ClientApp/app/search/search.component.html index 35f792efa..046635812 100644 --- a/src/Ombi/ClientApp/app/search/search.component.html +++ b/src/Ombi/ClientApp/app/search/search.component.html @@ -22,11 +22,11 @@
- +
- +
diff --git a/src/Ombi/ClientApp/app/search/search.component.ts b/src/Ombi/ClientApp/app/search/search.component.ts index 9347c87f1..74221e71c 100644 --- a/src/Ombi/ClientApp/app/search/search.component.ts +++ b/src/Ombi/ClientApp/app/search/search.component.ts @@ -21,9 +21,9 @@ export class SearchComponent implements OnInit { public ngOnInit() { this.settingsService.getLidarr().subscribe(x => this.musicEnabled = x.enabled); - this.showMovie = false; + this.showMovie = true; this.showTv = false; - this.showMusic = true; + this.showMusic = false; this.issuesService.getCategories().subscribe(x => this.issueCategories = x); this.settingsService.getIssueSettings().subscribe(x => this.issuesEnabled = x.enabled); } diff --git a/src/Ombi/Controllers/MusicRequestController.cs b/src/Ombi/Controllers/MusicRequestController.cs new file mode 100644 index 000000000..3758da9d8 --- /dev/null +++ b/src/Ombi/Controllers/MusicRequestController.cs @@ -0,0 +1,144 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Core.Engine; +using Ombi.Core.Models.Requests; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Store.Entities.Requests; +using Ombi.Attributes; +using Ombi.Core.Models.UI; + +namespace Ombi.Controllers +{ + [Authorize] + [Route("api/v1/request/music")] + [Produces("application/json")] + public class MusicRequestController : Controller + { + public MusicRequestController(IMusicRequestEngine engine) + { + _engine = engine; + } + + private readonly IMusicRequestEngine _engine; + + /// + /// Gets album requests. + /// + /// The count of items you want to return. + /// The position. + /// The way we want to order. + /// + /// + [HttpGet("{count:int}/{position:int}/{orderType:int}/{statusType:int}/{availabilityType:int}")] + public async Task> GetRequests(int count, int position, int orderType, int statusType, int availabilityType) + { + return await _engine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); + } + + /// + /// Gets the total amount of album requests. + /// + [HttpGet("total")] + public async Task GetTotalAlbums() + { + return await _engine.GetTotal(); + } + + /// + /// Gets all album requests. + /// + [HttpGet] + public async Task> GetRequests() + { + return await _engine.GetRequests(); + } + + /// + /// Requests a album. + /// + /// The album. + /// + [HttpPost] + public async Task Request([FromBody] MusicAlbumRequestViewModel album) + { + return await _engine.RequestAlbum(album); + } + + /// + /// Searches for a specific album request + /// + /// The search term. + /// + [HttpGet("search/{searchTerm}")] + public async Task> Search(string searchTerm) + { + return await _engine.SearchAlbumRequest(searchTerm); + } + + /// + /// Deletes the specified album request. + /// + /// The request identifier. + /// + [HttpDelete("{requestId:int}")] + [PowerUser] + public async Task DeleteRequest(int requestId) + { + await _engine.RemoveAlbumRequest(requestId); + } + + /// + /// Approves the specified album request. + /// + /// The albums's ID + /// + [HttpPost("approve")] + [PowerUser] + public async Task ApproveAlbum([FromBody] AlbumUpdateModel model) + { + return await _engine.ApproveAlbumById(model.Id); + } + + /// + /// Set's the specified album as available + /// + /// The album's ID + /// + [HttpPost("available")] + [PowerUser] + public async Task MarkAvailable([FromBody] AlbumUpdateModel model) + { + return await _engine.MarkAvailable(model.Id); + } + + /// + /// Set's the specified album as unavailable + /// + /// The album's ID + /// + [HttpPost("unavailable")] + [PowerUser] + public async Task MarkUnAvailable([FromBody] AlbumUpdateModel model) + { + return await _engine.MarkUnavailable(model.Id); + } + + /// + /// Denies the specified album request. + /// + /// The album's ID + /// + [HttpPut("deny")] + [PowerUser] + public async Task Deny([FromBody] AlbumUpdateModel model) + { + return await _engine.DenyAlbumById(model.Id); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/AlbumUpdateModel.cs b/src/Ombi/Models/AlbumUpdateModel.cs new file mode 100644 index 000000000..7d7374c43 --- /dev/null +++ b/src/Ombi/Models/AlbumUpdateModel.cs @@ -0,0 +1,33 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: MovieUpdateModel.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +namespace Ombi.Core.Models.Requests +{ + public class AlbumUpdateModel + { + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/wwwroot/images/default-music-placeholder.png b/src/Ombi/wwwroot/images/default-music-placeholder.png new file mode 100644 index 000000000..f6decc9e0 Binary files /dev/null and b/src/Ombi/wwwroot/images/default-music-placeholder.png differ