mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
commit
c0e9227e38
226 changed files with 12167 additions and 510 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -243,3 +243,6 @@ _Pvt_Extensions
|
|||
# CAKE - C# Make
|
||||
/Tools/*
|
||||
*.db-journal
|
||||
|
||||
# Ignore local vscode config
|
||||
*.vscode
|
||||
|
|
153
CHANGELOG.md
153
CHANGELOG.md
|
@ -1,5 +1,152 @@
|
|||
# Changelog
|
||||
|
||||
## (unreleased)
|
||||
|
||||
### **New Features**
|
||||
|
||||
- Added the request limits in the ui for music. [Jamie]
|
||||
|
||||
- Added the root folders and qualities per user! [Jamie]
|
||||
|
||||
- Updated all the MS packages. [TidusJar]
|
||||
|
||||
- Update the .net core packages to fix "CVE-2018-8409: ASP.NET Core Denial Of Service Vulnerability" [TidusJar]
|
||||
|
||||
- Change way remainingrequests component is notified. [Kenton Royal]
|
||||
|
||||
- Added the music request limits. [TidusJar]
|
||||
|
||||
- Added the Notification Preferences to the user. [TidusJar]
|
||||
|
||||
- Added the API to add user notification preferences. [TidusJar]
|
||||
|
||||
- Added more logging into the updater. [Jamie]
|
||||
|
||||
- Update CHANGELOG.md. [Jamie]
|
||||
|
||||
### **Fixes**
|
||||
|
||||
- Fixed #2518. [TidusJar]
|
||||
|
||||
- Fixed #2522. [TidusJar]
|
||||
|
||||
- Fixed #2485. [TidusJar]
|
||||
|
||||
- Fixed #2516. [TidusJar]
|
||||
|
||||
- Fix bug in which requested TV wasn't logging for some users. [Kenton Royal]
|
||||
|
||||
- Add to translations. [Kenton Royal]
|
||||
|
||||
- Add html for displaying remaining requests on users page. [Kenton Royal]
|
||||
|
||||
- Add quota fields to user view model. [Kenton Royal]
|
||||
|
||||
- Users can now see the music search tab #2493. [TidusJar]
|
||||
|
||||
- Add href to a tags so that a pointer cursor shows on requests page. [Stephen Panzer]
|
||||
|
||||
- Allow Lidarr to specify if we should search for the album. [TidusJar]
|
||||
|
||||
- Fixed the issue if in Radarr we only want to add and monitor, if the movie already exists we search for it. [TidusJar]
|
||||
|
||||
- Fix bug causing wrong time to be displayed for next request. [Kenton Royal]
|
||||
|
||||
- Bodge fix test to prevent compile error. [Kenton Royal]
|
||||
|
||||
- Fix displaying year in issue dialog. [Stephen Panzer]
|
||||
|
||||
- Add clearfix class. Closes #2486. [Stephen Panzer]
|
||||
|
||||
- Correct path of lidarr component import for unix systems. [Kenton Royal]
|
||||
|
||||
- Refactor code. [Kenton Royal]
|
||||
|
||||
- Fix formatting error. [Kenton Royal]
|
||||
|
||||
- Revert "Revert request.service.ts to version on upstream/develop" [Kenton Royal]
|
||||
|
||||
- Revert request.service.ts to version on upstream/develop. [Kenton Royal]
|
||||
|
||||
- Fix lint errors. [Kenton Royal]
|
||||
|
||||
- Move logic for notifying when reuqest is complete. [Kenton Royal]
|
||||
|
||||
- Remove import. [Kenton Royal]
|
||||
|
||||
- Remove unused module. [Kenton Royal]
|
||||
|
||||
- Refactor code. [Kenton Royal]
|
||||
|
||||
- Add text to translation file. [Kenton Royal]
|
||||
|
||||
- Fix query for fetching requested tv shows. [Kenton Royal]
|
||||
|
||||
- Add vscode to gitignore. [Kenton Royal]
|
||||
|
||||
- Fix lint errors. [Kenton Royal]
|
||||
|
||||
- Remove unused methods from SearchController. [Kenton Royal]
|
||||
|
||||
- Remove local vscode files. [Kenton Royal]
|
||||
|
||||
- Fix bug when submitting requests for multiple episodes accross multiple seasons. [Kenton Royal]
|
||||
|
||||
- Fix bug with TV requests in which requesting a seasion would treat request as single episode. [Kenton Royal]
|
||||
|
||||
- Fix issues with remaining count updating. [Kenton Royal]
|
||||
|
||||
- Trigger update of request limit on new request. [Kenton Royal]
|
||||
|
||||
- Add logic for movie request count. [Kenton Royal]
|
||||
|
||||
- Add logic for retriving request information. [Kenton Royal]
|
||||
|
||||
- Move to seperate component and display for both TV and movies. [Kenton Royal]
|
||||
|
||||
- Add dummy for request counter. [Kenton Royal]
|
||||
|
||||
- Fix scss import for unix systems. [Kenton Royal]
|
||||
|
||||
- Add methods to interface and add model class. [Kenton Royal]
|
||||
|
||||
- !fixed lint. [TidusJar]
|
||||
|
||||
- Fixed #2481. [TidusJar]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
||||
- New translations en.json (Spanish) [Jamie]
|
||||
|
||||
- New translations en.json (Portuguese, Brazilian) [Jamie]
|
||||
|
||||
- New translations en.json (Polish) [Jamie]
|
||||
|
||||
- New translations en.json (Norwegian) [Jamie]
|
||||
|
||||
- New translations en.json (Italian) [Jamie]
|
||||
|
||||
- New translations en.json (German) [Jamie]
|
||||
|
||||
- New translations en.json (French) [Jamie]
|
||||
|
||||
- New translations en.json (Dutch) [Jamie]
|
||||
|
||||
- New translations en.json (Danish) [Jamie]
|
||||
|
||||
- Fixed #2475. [Jamie]
|
||||
|
||||
- Stript out certain characters when sending a pushover message #2385. [TidusJar]
|
||||
|
||||
- Add default values for Priority and Sound. [David Pooley]
|
||||
|
||||
- Allow for the ability to set Pushover notification sound and priority from within Ombi. [David Pooley]
|
||||
|
||||
- It works now when we request an album when we do not have the artist in Lidarr. Waiting on https://github.com/lidarr/Lidarr/issues/459 to do when we have the artist. [Jamie]
|
||||
|
||||
- Fix non-Windows builds. Fixes #2453. [Joe Groocock]
|
||||
|
||||
|
||||
## v3.0.3587 (2018-08-19)
|
||||
|
||||
### **New Features**
|
||||
|
@ -30,6 +177,12 @@
|
|||
|
||||
### **Fixes**
|
||||
|
||||
- Now include the release year in the issue title #2381. [TidusJar]
|
||||
|
||||
- Made the OAuth a Popout to work with Org. [Jamie]
|
||||
|
||||
- Fixed #2418. [TidusJar]
|
||||
|
||||
- #2408 Added the feature to delete comments on issues. [Jamie]
|
||||
|
||||
- New translations en.json (Swedish) [Jamie]
|
||||
|
|
BIN
music-placeholder.psd
Normal file
BIN
music-placeholder.psd
Normal file
Binary file not shown.
27
src/Ombi.Api.Lidarr/ILidarrApi.cs
Normal file
27
src/Ombi.Api.Lidarr/ILidarrApi.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Api.Lidarr
|
||||
{
|
||||
public interface ILidarrApi
|
||||
{
|
||||
Task<List<AlbumLookup>> AlbumLookup(string searchTerm, string apiKey, string baseUrl);
|
||||
Task<List<ArtistLookup>> ArtistLookup(string searchTerm, string apiKey, string baseUrl);
|
||||
Task<List<LidarrProfile>> GetProfiles(string apiKey, string baseUrl);
|
||||
Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
|
||||
Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl);
|
||||
Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl);
|
||||
Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId);
|
||||
Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl);
|
||||
Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl);
|
||||
Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl);
|
||||
Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl);
|
||||
Task<AlbumResponse> MontiorAlbum(int albumId, string apiKey, string baseUrl);
|
||||
Task<List<AlbumResponse>> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl);
|
||||
Task<List<MetadataProfile>> GetMetadataProfile(string apiKey, string baseUrl);
|
||||
Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl);
|
||||
Task<LidarrStatus> Status(string apiKey, string baseUrl);
|
||||
Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
170
src/Ombi.Api.Lidarr/LidarrApi.cs
Normal file
170
src/Ombi.Api.Lidarr/LidarrApi.cs
Normal file
|
@ -0,0 +1,170 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Api.Lidarr
|
||||
{
|
||||
public class LidarrApi : ILidarrApi
|
||||
{
|
||||
public LidarrApi(ILogger<LidarrApi> logger, IApi api)
|
||||
{
|
||||
Api = api;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private IApi Api { get; }
|
||||
private ILogger Logger { get; }
|
||||
|
||||
private const string ApiVersion = "/api/v1";
|
||||
|
||||
public Task<List<LidarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/qualityprofile", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LidarrProfile>>(request);
|
||||
}
|
||||
|
||||
public Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/rootfolder", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LidarrRootFolder>>(request);
|
||||
}
|
||||
|
||||
public async Task<List<ArtistLookup>> ArtistLookup(string searchTerm, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/Artist/lookup", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("term", searchTerm);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return await Api.Request<List<ArtistLookup>>(request);
|
||||
}
|
||||
|
||||
public Task<List<AlbumLookup>> AlbumLookup(string searchTerm, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/Album/lookup", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("term", searchTerm);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumLookup>>(request);
|
||||
}
|
||||
|
||||
public Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist/{artistId}", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<ArtistResult>(request);
|
||||
}
|
||||
|
||||
public async Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist/lookup", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("term", $"lidarr:{foreignArtistId}");
|
||||
AddHeaders(request, apiKey);
|
||||
return (await Api.Request<List<ArtistResult>>(request)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<AlbumLookup> 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);
|
||||
var albums = await Api.Request<List<AlbumLookup>>(request);
|
||||
return albums.FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId)
|
||||
{
|
||||
var request = new Request(string.Empty, $"https://api.lidarr.audio/api/v0.3/artist/{foreignArtistId}",
|
||||
HttpMethod.Get) {IgnoreBaseUrlAppend = true};
|
||||
return Api.Request<AlbumByArtistResponse>(request);
|
||||
}
|
||||
|
||||
public Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<ArtistResult>>(request);
|
||||
}
|
||||
|
||||
public Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumResponse>>(request);
|
||||
}
|
||||
|
||||
public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);
|
||||
request.AddJsonBody(artist);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<ArtistResult>(request);
|
||||
}
|
||||
|
||||
public async Task<AlbumResponse> MontiorAlbum(int albumId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album/monitor", baseUrl, HttpMethod.Put);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
albumIds = new[] { albumId },
|
||||
monitored = true
|
||||
});
|
||||
AddHeaders(request, apiKey);
|
||||
return (await Api.Request<List<AlbumResponse>>(request)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<List<AlbumResponse>> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
|
||||
request.AddQueryString("artistId", artistId.ToString());
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<AlbumResponse>>(request);
|
||||
}
|
||||
|
||||
public Task<List<LanguageProfiles>> GetLanguageProfile(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/languageprofile", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<LanguageProfiles>>(request);
|
||||
}
|
||||
|
||||
public Task<List<MetadataProfile>> GetMetadataProfile(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/metadataprofile", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<List<MetadataProfile>>(request);
|
||||
}
|
||||
|
||||
public Task<LidarrStatus> Status(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/system/status", baseUrl, HttpMethod.Get);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<LidarrStatus>(request);
|
||||
}
|
||||
|
||||
public Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiVersion}/command/AlbumSearch", baseUrl, HttpMethod.Post);
|
||||
request.AddJsonBody(albumIds);
|
||||
AddHeaders(request, apiKey);
|
||||
return Api.Request<CommandResult>(request);
|
||||
}
|
||||
|
||||
private void AddHeaders(Request request, string key)
|
||||
{
|
||||
request.AddHeader("X-Api-Key", key);
|
||||
}
|
||||
}
|
||||
}
|
34
src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs
Normal file
34
src/Ombi.Api.Lidarr/Models/AlbumByArtistResponse.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumByArtistResponse
|
||||
{
|
||||
public Album[] Albums { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string Disambiguation { get; set; }
|
||||
public string Id { get; set; }
|
||||
public Image[] Images { get; set; }
|
||||
public Link[] Links { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public Rating Rating { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
public class Rating
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
}
|
||||
|
||||
public class Album
|
||||
{
|
||||
public string Disambiguation { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public string[] ReleaseStatuses { get; set; }
|
||||
public string[] SecondaryTypes { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
25
src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
Normal file
25
src/Ombi.Api.Lidarr/Models/AlbumLookup.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumLookup
|
||||
{
|
||||
public string title { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string foreignAlbumId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public string[] secondaryTypes { get; set; }
|
||||
public int mediumCount { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
//public object[] releases { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
//public object[] media { get; set; }
|
||||
public Artist artist { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remoteCover { get; set; }
|
||||
}
|
||||
}
|
27
src/Ombi.Api.Lidarr/Models/AlbumResponse.cs
Normal file
27
src/Ombi.Api.Lidarr/Models/AlbumResponse.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class AlbumResponse
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string foreignAlbumId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public object[] secondaryTypes { get; set; }
|
||||
public int mediumCount { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public Currentrelease currentRelease { get; set; }
|
||||
public Release[] releases { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public Medium[] media { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
25
src/Ombi.Api.Lidarr/Models/Artist.cs
Normal file
25
src/Ombi.Api.Lidarr/Models/Artist.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Artist
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public object[] links { get; set; }
|
||||
public object[] images { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
}
|
||||
}
|
49
src/Ombi.Api.Lidarr/Models/ArtistAdd.cs
Normal file
49
src/Ombi.Api.Lidarr/Models/ArtistAdd.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Net.Mime;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class ArtistAdd
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remotePoster { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public Addoptions addOptions { get; set; }
|
||||
public string rootFolderPath { get; set; }
|
||||
}
|
||||
|
||||
public class Addoptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Future = 1
|
||||
/// Missing = 2
|
||||
/// Existing = 3
|
||||
/// First = 5
|
||||
/// Latest = 4
|
||||
/// None = 6
|
||||
/// </summary>
|
||||
public int selectedOption { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public bool searchForMissingAlbums { get; set; }
|
||||
public string[] AlbumsToMonitor { get; set; } // Uses the MusicBrainzAlbumId!
|
||||
}
|
||||
}
|
32
src/Ombi.Api.Lidarr/Models/ArtistLookup.cs
Normal file
32
src/Ombi.Api.Lidarr/Models/ArtistLookup.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Net.Mime;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class ArtistLookup
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string artistType { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string remotePoster { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
}
|
||||
}
|
93
src/Ombi.Api.Lidarr/Models/ArtistResult.cs
Normal file
93
src/Ombi.Api.Lidarr/Models/ArtistResult.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
|
||||
public class ArtistResult
|
||||
{
|
||||
public string status { get; set; }
|
||||
public bool ended { get; set; }
|
||||
public DateTime lastInfoSync { get; set; }
|
||||
public string artistName { get; set; }
|
||||
public string foreignArtistId { get; set; }
|
||||
public int tadbId { get; set; }
|
||||
public int discogsId { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string artistType { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public Link[] links { get; set; }
|
||||
public Nextalbum nextAlbum { get; set; }
|
||||
public Image[] images { get; set; }
|
||||
public string path { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int languageProfileId { get; set; }
|
||||
public int metadataProfileId { get; set; }
|
||||
public bool albumFolder { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public string cleanName { get; set; }
|
||||
public string sortName { get; set; }
|
||||
public object[] tags { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Statistics statistics { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Nextalbum
|
||||
{
|
||||
public string foreignAlbumId { get; set; }
|
||||
public int artistId { get; set; }
|
||||
public string title { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public int duration { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public object[] images { get; set; }
|
||||
public object[] genres { get; set; }
|
||||
public Medium[] media { get; set; }
|
||||
public DateTime lastInfoSync { get; set; }
|
||||
public DateTime added { get; set; }
|
||||
public string albumType { get; set; }
|
||||
public object[] secondaryTypes { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public Release[] releases { get; set; }
|
||||
public Currentrelease currentRelease { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class Currentrelease
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string title { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int mediaCount { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string[] country { get; set; }
|
||||
public string format { get; set; }
|
||||
public string[] label { get; set; }
|
||||
}
|
||||
|
||||
public class Medium
|
||||
{
|
||||
public int number { get; set; }
|
||||
public string name { get; set; }
|
||||
public string format { get; set; }
|
||||
}
|
||||
|
||||
public class Release
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string title { get; set; }
|
||||
public DateTime releaseDate { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int mediaCount { get; set; }
|
||||
public string disambiguation { get; set; }
|
||||
public string[] country { get; set; }
|
||||
public string format { get; set; }
|
||||
public string[] label { get; set; }
|
||||
}
|
||||
}
|
15
src/Ombi.Api.Lidarr/Models/CommandResult.cs
Normal file
15
src/Ombi.Api.Lidarr/Models/CommandResult.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
|
||||
public class CommandResult
|
||||
{
|
||||
public string name { get; set; }
|
||||
public DateTime startedOn { get; set; }
|
||||
public DateTime stateChangeTime { get; set; }
|
||||
public bool sendUpdatesToClient { get; set; }
|
||||
public string state { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Image.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Image.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Image
|
||||
{
|
||||
public string coverType { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/LanguageProfiles.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LanguageProfiles
|
||||
{
|
||||
public string name { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
23
src/Ombi.Api.Lidarr/Models/LidarrProfile.cs
Normal file
23
src/Ombi.Api.Lidarr/Models/LidarrProfile.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Quality
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class Item
|
||||
{
|
||||
public Quality quality { get; set; }
|
||||
public bool allowed { get; set; }
|
||||
}
|
||||
|
||||
public class LidarrProfile
|
||||
{
|
||||
public string name { get; set; }
|
||||
public List<Item> items { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs
Normal file
11
src/Ombi.Api.Lidarr/Models/LidarrRootFolder.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrRootFolder
|
||||
{
|
||||
public string path { get; set; }
|
||||
public long freeSpace { get; set; }
|
||||
public object[] unmappedFolders { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
}
|
31
src/Ombi.Api.Lidarr/Models/LidarrStatus.cs
Normal file
31
src/Ombi.Api.Lidarr/Models/LidarrStatus.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class LidarrStatus
|
||||
{
|
||||
public string version { get; set; }
|
||||
public DateTime buildTime { get; set; }
|
||||
public bool isDebug { get; set; }
|
||||
public bool isProduction { get; set; }
|
||||
public bool isAdmin { get; set; }
|
||||
public bool isUserInteractive { get; set; }
|
||||
public string startupPath { get; set; }
|
||||
public string appData { get; set; }
|
||||
public string osName { get; set; }
|
||||
public string osVersion { get; set; }
|
||||
public bool isMonoRuntime { get; set; }
|
||||
public bool isMono { get; set; }
|
||||
public bool isLinux { get; set; }
|
||||
public bool isOsx { get; set; }
|
||||
public bool isWindows { get; set; }
|
||||
public string mode { get; set; }
|
||||
public string branch { get; set; }
|
||||
public string authentication { get; set; }
|
||||
public string sqliteVersion { get; set; }
|
||||
public int migrationVersion { get; set; }
|
||||
public string urlBase { get; set; }
|
||||
public string runtimeVersion { get; set; }
|
||||
public string runtimeName { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Link.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Link.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Link
|
||||
{
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/MetadataProfile.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/MetadataProfile.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class MetadataProfile
|
||||
{
|
||||
public string name { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Lidarr/Models/Ratings.cs
Normal file
8
src/Ombi.Api.Lidarr/Models/Ratings.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Ratings
|
||||
{
|
||||
public int votes { get; set; }
|
||||
public decimal value { get; set; }
|
||||
}
|
||||
}
|
12
src/Ombi.Api.Lidarr/Models/Statistics.cs
Normal file
12
src/Ombi.Api.Lidarr/Models/Statistics.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Api.Lidarr.Models
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public int albumCount { get; set; }
|
||||
public int trackFileCount { get; set; }
|
||||
public int trackCount { get; set; }
|
||||
public int totalTrackCount { get; set; }
|
||||
public int sizeOnDisk { get; set; }
|
||||
public decimal percentOfEpisodes { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj
Normal file
11
src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -5,6 +5,6 @@ namespace Ombi.Api.Pushover
|
|||
{
|
||||
public interface IPushoverApi
|
||||
{
|
||||
Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken);
|
||||
Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound);
|
||||
}
|
||||
}
|
|
@ -16,13 +16,13 @@ namespace Ombi.Api.Pushover
|
|||
private readonly IApi _api;
|
||||
private const string PushoverEndpoint = "https://api.pushover.net/1";
|
||||
|
||||
public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken)
|
||||
public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken, sbyte priority, string sound)
|
||||
{
|
||||
if (message.Contains("'"))
|
||||
{
|
||||
message = message.Replace("'", "'");
|
||||
}
|
||||
var request = new Request($"messages.json?token={accessToken}&user={userToken}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post);
|
||||
var request = new Request($"messages.json?token={accessToken}&user={userToken}&priority={priority}&sound={sound}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post);
|
||||
|
||||
var result = await _api.Request<PushoverResponse>(request);
|
||||
return result;
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Ombi.Api
|
|||
public bool IgnoreErrors { get; set; }
|
||||
public bool Retry { get; set; }
|
||||
public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>();
|
||||
public bool IgnoreBaseUrlAppend { get; set; }
|
||||
|
||||
public Action<string> OnBeforeDeserialization { get; set; }
|
||||
|
||||
|
@ -38,7 +39,7 @@ namespace Ombi.Api
|
|||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(BaseUrl))
|
||||
{
|
||||
sb.Append(!BaseUrl.EndsWith("/") ? string.Format("{0}/", BaseUrl) : BaseUrl);
|
||||
sb.Append(!BaseUrl.EndsWith("/") && !IgnoreBaseUrlAppend ? string.Format("{0}/", BaseUrl) : BaseUrl);
|
||||
}
|
||||
sb.Append(Endpoint.StartsWith("/") ? Endpoint.Remove(0, 1) : Endpoint);
|
||||
return sb.ToString();
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<PackageReference Include="Moq" Version="4.9.0" />
|
||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.8.0"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -19,12 +19,14 @@ namespace Ombi.Core.Tests.Rule.Search
|
|||
|
||||
MovieMock = new Mock<IMovieRequestRepository>();
|
||||
TvMock = new Mock<ITvRequestRepository>();
|
||||
Rule = new ExistingRule(MovieMock.Object, TvMock.Object);
|
||||
MusicMock = new Mock<IMusicRequestRepository>();
|
||||
Rule = new ExistingRule(MovieMock.Object, TvMock.Object, MusicMock.Object);
|
||||
}
|
||||
|
||||
private ExistingRule Rule { get; set; }
|
||||
private Mock<IMovieRequestRepository> MovieMock { get; set; }
|
||||
private Mock<ITvRequestRepository> TvMock { get; set; }
|
||||
private Mock<IMusicRequestRepository> MusicMock { get; set; }
|
||||
|
||||
|
||||
[Test]
|
||||
|
|
26
src/Ombi.Core.Tests/StringHelperTests.cs
Normal file
26
src/Ombi.Core.Tests/StringHelperTests.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Core.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StringHelperTests
|
||||
{
|
||||
[TestCaseSource(nameof(StripCharsData))]
|
||||
public string StripCharacters(string str, char[] chars)
|
||||
{
|
||||
return str.StripCharacters(chars);
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> StripCharsData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new TestCaseData("this!is^a*string",new []{'!','^','*'}).Returns("thisisastring").SetName("Basic Strip Multipe Chars");
|
||||
yield return new TestCaseData("What is this madness'",new []{'\'','^','*'}).Returns("What is this madness").SetName("Basic Strip Multipe Chars");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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> OmbiSettings;
|
||||
protected readonly IRepository<RequestSubscription> _subscriptionRepository;
|
||||
|
|
27
src/Ombi.Core/Engine/IMusicRequestEngine.cs
Normal file
27
src/Ombi.Core/Engine/IMusicRequestEngine.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IMusicRequestEngine
|
||||
{
|
||||
Task<RequestEngineResult>ApproveAlbum(AlbumRequest request);
|
||||
Task<RequestEngineResult> ApproveAlbumById(int requestId);
|
||||
Task<RequestEngineResult> DenyAlbumById(int modelId);
|
||||
Task<IEnumerable<AlbumRequest>> GetRequests();
|
||||
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, OrderFilterModel orderFilter);
|
||||
Task<int> GetTotal();
|
||||
Task<RequestEngineResult> MarkAvailable(int modelId);
|
||||
Task<RequestEngineResult> MarkUnavailable(int modelId);
|
||||
Task RemoveAlbumRequest(int requestId);
|
||||
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
|
||||
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
|
||||
Task<bool> UserHasRequest(string userId);
|
||||
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,5 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<RequestEngineResult> ApproveMovie(MovieRequests request);
|
||||
Task<RequestEngineResult> ApproveMovieById(int requestId);
|
||||
Task<RequestEngineResult> DenyMovieById(int modelId);
|
||||
|
||||
|
||||
}
|
||||
}
|
16
src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
Normal file
16
src/Ombi.Core/Engine/Interfaces/IMusicSearchEngine.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Core.Models.Search;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public interface IMusicSearchEngine
|
||||
{
|
||||
Task<ArtistResult> GetAlbumArtist(string foreignArtistId);
|
||||
Task<ArtistResult> GetArtist(int artistId);
|
||||
Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId);
|
||||
Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search);
|
||||
Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
|
@ -22,5 +23,6 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<int> GetTotal();
|
||||
Task UnSubscribeRequest(int requestId, RequestType type);
|
||||
Task SubscribeToRequest(int requestId, RequestType type);
|
||||
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Core.Models;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -336,6 +337,7 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
request.MarkedAsApproved = DateTime.Now;
|
||||
request.Approved = true;
|
||||
request.Denied = false;
|
||||
await MovieRepository.Update(request);
|
||||
|
@ -483,5 +485,49 @@ namespace Ombi.Core.Engine
|
|||
|
||||
return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"};
|
||||
}
|
||||
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int limit = user.MovieRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Movie);
|
||||
|
||||
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
|
||||
.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
503
src/Ombi.Core/Engine/MusicRequestEngine.cs
Normal file
503
src/Ombi.Core/Engine/MusicRequestEngine.cs
Normal file
|
@ -0,0 +1,503 @@
|
|||
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;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Senders;
|
||||
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<MusicRequestEngine> log,
|
||||
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
|
||||
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, ILidarrApi lidarr,
|
||||
ISettingsService<LidarrSettings> lidarrSettings, IMusicSender sender)
|
||||
: base(user, requestService, r, manager, cache, ombiSettings, sub)
|
||||
{
|
||||
NotificationHelper = helper;
|
||||
_musicSender = 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> _requestLog;
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly IMusicSender _musicSender;
|
||||
|
||||
/// <summary>
|
||||
/// Requests the Album.
|
||||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestEngineResult> 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,
|
||||
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requests.
|
||||
/// </summary>
|
||||
/// <param name="count">The count.</param>
|
||||
/// <param name="position">The position.</param>
|
||||
/// <param name="orderFilter">The order/filter type.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position,
|
||||
OrderFilterModel orderFilter)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
IQueryable<AlbumRequest> 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<AlbumRequest>
|
||||
{
|
||||
Collection = requests,
|
||||
Total = total
|
||||
};
|
||||
}
|
||||
|
||||
private IQueryable<AlbumRequest> OrderAlbums(IQueryable<AlbumRequest> 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<int> GetTotal()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
if (shouldHide.Hide)
|
||||
{
|
||||
return await MusicRepository.GetWithUser(shouldHide.UserId).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await MusicRepository.GetWithUser().CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requests.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<AlbumRequest>> GetRequests()
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<AlbumRequest> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the album request.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search)
|
||||
{
|
||||
var shouldHide = await HideFromOtherUsers();
|
||||
List<AlbumRequest> 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<RequestEngineResult> ApproveAlbumById(int requestId)
|
||||
{
|
||||
var request = await MusicRepository.Find(requestId);
|
||||
return await ApproveAlbum(request);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> 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<RequestEngineResult> ApproveAlbum(AlbumRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = "Request does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
request.MarkedAsApproved = DateTime.Now;
|
||||
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)
|
||||
{
|
||||
var result = await _musicSender.Send(request);
|
||||
if (result.Success && result.Sent)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = true
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
Logger.LogWarning("Tried auto sending album 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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Album request.
|
||||
/// </summary>
|
||||
/// <param name="requestId">The request identifier.</param>
|
||||
/// <returns></returns>
|
||||
public async Task RemoveAlbumRequest(int requestId)
|
||||
{
|
||||
var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
await MusicRepository.Delete(request);
|
||||
}
|
||||
|
||||
public async Task<bool> UserHasRequest(string userId)
|
||||
{
|
||||
return await MusicRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> 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<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int limit = user.MusicRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Album);
|
||||
|
||||
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
|
||||
.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<RequestEngineResult> 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<RequestEngineResult> 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!" };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
219
src/Ombi.Core/Engine/MusicSearchEngine.cs
Normal file
219
src/Ombi.Core/Engine/MusicSearchEngine.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Search;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MusicSearchEngine : BaseMediaEngine, IMusicSearchEngine
|
||||
{
|
||||
public MusicSearchEngine(IPrincipal identity, IRequestServiceMain service, ILidarrApi lidarrApi, IMapper mapper,
|
||||
ILogger<MusicSearchEngine> logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub,
|
||||
ISettingsService<LidarrSettings> lidarrSettings)
|
||||
: base(identity, service, r, um, mem, s, sub)
|
||||
{
|
||||
_lidarrApi = lidarrApi;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
Mapper = mapper;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private IMapper Mapper { get; }
|
||||
private ILogger Logger { get; }
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified album.
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchAlbumViewModel>> SearchAlbum(string search)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.AlbumLookup(search, settings.ApiKey, settings.FullUri);
|
||||
var vm = new List<SearchAlbumViewModel>();
|
||||
foreach (var r in result)
|
||||
{
|
||||
vm.Add(await MapIntoAlbumVm(r, settings));
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the specified artist
|
||||
/// </summary>
|
||||
/// <param name="search">The search.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchArtistViewModel>> SearchArtist(string search)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.ArtistLookup(search, settings.ApiKey, settings.FullUri);
|
||||
|
||||
var vm = new List<SearchArtistViewModel>();
|
||||
foreach (var r in result)
|
||||
{
|
||||
vm.Add(await MapIntoArtistVm(r));
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all albums by the specified artist
|
||||
/// </summary>
|
||||
/// <param name="foreignArtistId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchAlbumViewModel>> GetArtistAlbums(string foreignArtistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
var result = await _lidarrApi.GetAlbumsByArtist(foreignArtistId);
|
||||
// We do not want any Singles (This will include EP's)
|
||||
var albumsOnly =
|
||||
result.Albums.Where(x => !x.Type.Equals("Single", StringComparison.InvariantCultureIgnoreCase));
|
||||
var vm = new List<SearchAlbumViewModel>();
|
||||
foreach (var album in albumsOnly)
|
||||
{
|
||||
vm.Add(await MapIntoAlbumVm(album, result.Id, result.ArtistName, settings));
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the artist that produced the album
|
||||
/// </summary>
|
||||
/// <param name="foreignArtistId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ArtistResult> GetAlbumArtist(string foreignArtistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
return await _lidarrApi.GetArtistByForeignId(foreignArtistId, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
public async Task<ArtistResult> GetArtist(int artistId)
|
||||
{
|
||||
var settings = await GetSettings();
|
||||
return await _lidarrApi.GetArtist(artistId, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
private async Task<SearchArtistViewModel> MapIntoArtistVm(ArtistLookup a)
|
||||
{
|
||||
var vm = new SearchArtistViewModel
|
||||
{
|
||||
ArtistName = a.artistName,
|
||||
ArtistType = a.artistType,
|
||||
Banner = a.images?.FirstOrDefault(x => x.coverType.Equals("banner"))?.url,
|
||||
Logo = a.images?.FirstOrDefault(x => x.coverType.Equals("logo"))?.url,
|
||||
CleanName = a.cleanName,
|
||||
Disambiguation = a.disambiguation,
|
||||
ForignArtistId = a.foreignArtistId,
|
||||
Links = a.links,
|
||||
Overview = a.overview,
|
||||
};
|
||||
|
||||
var poster = a.images?.FirstOrDefault(x => x.coverType.Equals("poaster"));
|
||||
if (poster == null)
|
||||
{
|
||||
vm.Poster = a.remotePoster;
|
||||
}
|
||||
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrArtist);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(AlbumLookup 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
|
||||
};
|
||||
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.artist?.foreignArtistId;
|
||||
vm.ArtistName = a.artist?.artistName;
|
||||
}
|
||||
|
||||
vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url;
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
vm.Cover = a.remoteCover;
|
||||
}
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
|
||||
|
||||
await RunSearchRules(vm);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<SearchAlbumViewModel> MapIntoAlbumVm(Album a, string artistId, string artistName, LidarrSettings settings)
|
||||
{
|
||||
var fullAlbum = await _lidarrApi.GetAlbumByForeignId(a.Id, settings.ApiKey, settings.FullUri);
|
||||
var vm = new SearchAlbumViewModel
|
||||
{
|
||||
ForeignAlbumId = a.Id,
|
||||
Monitored = fullAlbum.monitored,
|
||||
Rating = fullAlbum.ratings?.value ?? 0m,
|
||||
ReleaseDate = fullAlbum.releaseDate,
|
||||
Title = a.Title,
|
||||
Disk = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
|
||||
ForeignArtistId = artistId,
|
||||
ArtistName = artistName,
|
||||
Cover = fullAlbum.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url
|
||||
};
|
||||
|
||||
if (vm.Cover.IsNullOrEmpty())
|
||||
{
|
||||
vm.Cover = fullAlbum.remoteCover;
|
||||
}
|
||||
|
||||
await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum);
|
||||
|
||||
await RunSearchRules(vm);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private LidarrSettings _settings;
|
||||
private async Task<LidarrSettings> GetSettings()
|
||||
{
|
||||
return _settings ?? (_settings = await _lidarrSettings.GetSettingsAsync());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Core.Models;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -587,6 +588,15 @@ namespace Ombi.Core.Engine
|
|||
NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(),
|
||||
});
|
||||
|
||||
if (model.Approved)
|
||||
{
|
||||
// Autosend
|
||||
|
@ -602,15 +612,58 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
});
|
||||
|
||||
return new RequestEngineResult { Result = true };
|
||||
}
|
||||
|
||||
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = await GetUser();
|
||||
|
||||
// If user is still null after attempting to get the logged in user, return null.
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int limit = user.EpisodeRequestLimit ?? 0;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = false,
|
||||
Limit = 0,
|
||||
Remaining = 0,
|
||||
NextRequest = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
IQueryable<RequestLog> log = _requestLog.GetAll()
|
||||
.Where(x => x.UserId == user.Id
|
||||
&& x.RequestType == RequestType.TvShow
|
||||
&& x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
|
||||
// Needed, due to a bug which would cause all episode counts to be 0
|
||||
int zeroEpisodeCount = await log.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
|
||||
|
||||
int episodeCount = await log.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
|
||||
|
||||
int count = limit - (zeroEpisodeCount + episodeCount);
|
||||
|
||||
DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate)
|
||||
.Select(x => x.RequestDate)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return new RequestQuotaCountModel()
|
||||
{
|
||||
HasLimit = true,
|
||||
Limit = limit,
|
||||
Remaining = count,
|
||||
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,16 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (searchResult != null)
|
||||
{
|
||||
return await ProcessResults(searchResult);
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in searchResult)
|
||||
{
|
||||
if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -145,12 +154,16 @@ namespace Ombi.Core.Engine
|
|||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in items)
|
||||
{
|
||||
var viewT = Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
retVal.Add(await ProcessResult(viewT));
|
||||
retVal.Add(await ProcessResult(tvMazeSearch));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult<T>(T tvMazeSearch)
|
||||
{
|
||||
return Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult(SearchTvShowViewModel item)
|
||||
{
|
||||
item.TheTvDbId = item.Id.ToString();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
15
src/Ombi.Core/Models/RequestQuotaCountModel.cs
Normal file
15
src/Ombi.Core/Models/RequestQuotaCountModel.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Core.Models
|
||||
{
|
||||
public class RequestQuotaCountModel
|
||||
{
|
||||
public bool HasLimit { get; set; }
|
||||
|
||||
public int Limit { get; set; }
|
||||
|
||||
public int Remaining { get; set; }
|
||||
|
||||
public DateTime NextRequest { get; set; }
|
||||
}
|
||||
}
|
|
@ -7,5 +7,6 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
IMovieRequestRepository MovieRequestService { get; }
|
||||
ITvRequestRepository TvRequestService { get; }
|
||||
IMusicRequestRepository MusicRequestRepository { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class MusicAlbumRequestViewModel
|
||||
{
|
||||
public string ForeignAlbumId { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,13 +5,15 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
public class RequestService : IRequestServiceMain
|
||||
{
|
||||
public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie)
|
||||
public RequestService(ITvRequestRepository tv, IMovieRequestRepository movie, IMusicRequestRepository music)
|
||||
{
|
||||
TvRequestService = tv;
|
||||
MovieRequestService = movie;
|
||||
MusicRequestRepository = music;
|
||||
}
|
||||
|
||||
public ITvRequestRepository TvRequestService { get; }
|
||||
public IMusicRequestRepository MusicRequestRepository { get; }
|
||||
public IMovieRequestRepository MovieRequestService { get; }
|
||||
}
|
||||
}
|
23
src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
Normal file
23
src/Ombi.Core/Models/Search/SearchAlbumViewModel.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
public class SearchAlbumViewModel : SearchViewModel
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string AlbumType { get; set; }
|
||||
public decimal Rating { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string ForeignArtistId { get; set; }
|
||||
public string Cover { get; set; }
|
||||
public string Disk { get; set; }
|
||||
public decimal PercentOfTracks { get; set; }
|
||||
public override RequestType Type => RequestType.Album;
|
||||
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
|
||||
public bool FullyAvailable => PercentOfTracks == 100;
|
||||
}
|
||||
}
|
19
src/Ombi.Core/Models/Search/SearchArtistViewModel.cs
Normal file
19
src/Ombi.Core/Models/Search/SearchArtistViewModel.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using Ombi.Api.Lidarr.Models;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
public class SearchArtistViewModel
|
||||
{
|
||||
public string ArtistName { get; set; }
|
||||
public string ForignArtistId { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public string Disambiguation { get; set; }
|
||||
public string Banner { get; set; }
|
||||
public string Poster { get; set; }
|
||||
public string Logo { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string ArtistType { get; set; }
|
||||
public string CleanName { get; set; }
|
||||
public Link[] Links { get; set; } // Couldn't be bothered to map it
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.UI
|
||||
{
|
||||
|
@ -16,6 +17,11 @@ namespace Ombi.Core.Models.UI
|
|||
public UserType UserType { get; set; }
|
||||
public int MovieRequestLimit { get; set; }
|
||||
public int EpisodeRequestLimit { get; set; }
|
||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
||||
public int MusicRequestLimit { get; set; }
|
||||
public UserQualityProfiles UserQualityProfiles { get; set; }
|
||||
}
|
||||
|
||||
public class ClaimCheckboxes
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.2.0" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="2.0.0-preview1-final" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||
|
@ -22,6 +21,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
public enum SpecificRules
|
||||
{
|
||||
CanSendNotification,
|
||||
LidarrArtist,
|
||||
LidarrAlbum,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
@ -81,21 +82,40 @@ namespace Ombi.Core.Rule.Rules.Request
|
|||
// Get the count of requests to be made
|
||||
foreach (var s in child.SeasonRequests)
|
||||
{
|
||||
requestCount = s.Episodes.Count;
|
||||
requestCount += s.Episodes.Count;
|
||||
}
|
||||
|
||||
var tvLogs = requestLog.Where(x => x.RequestType == RequestType.TvShow);
|
||||
|
||||
// Count how many requests in the past 7 days
|
||||
var tv = tvLogs.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
|
||||
var count = await tv.Select(x => x.EpisodeCount).CountAsync();
|
||||
count += requestCount; // Add the amount of requests in
|
||||
|
||||
// Needed, due to a bug which would cause all episode counts to be 0
|
||||
var zeroEpisodeCount = await tv.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
|
||||
|
||||
var episodeCount = await tv.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
|
||||
|
||||
var count = requestCount + episodeCount + zeroEpisodeCount; // Add the amount of requests in
|
||||
if (count > episodeLimit)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,20 +11,22 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
{
|
||||
public class ExistingRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public ExistingRule(IMovieRequestRepository movie, ITvRequestRepository tv)
|
||||
public ExistingRule(IMovieRequestRepository movie, ITvRequestRepository tv, IMusicRequestRepository music)
|
||||
{
|
||||
Movie = movie;
|
||||
Tv = tv;
|
||||
Music = music;
|
||||
}
|
||||
|
||||
private IMovieRequestRepository Movie { get; }
|
||||
private IMusicRequestRepository Music { get; }
|
||||
private ITvRequestRepository Tv { get; }
|
||||
|
||||
public Task<RuleResult> Execute(SearchViewModel obj)
|
||||
public async Task<RuleResult> 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 +35,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
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
//var tvRequests = Tv.GetRequest(obj.Id);
|
||||
//if (tvRequests != null) // Do we already have a request for this?
|
||||
|
@ -50,7 +52,7 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
// return Task.FromResult(Success());
|
||||
//}
|
||||
|
||||
var request = (SearchTvShowViewModel) obj;
|
||||
var request = (SearchTvShowViewModel)obj;
|
||||
var tvRequests = Tv.GetRequest(obj.Id);
|
||||
if (tvRequests != null) // Do we already have a request for this?
|
||||
{
|
||||
|
@ -94,8 +96,24 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
request.PartlyAvailable = true;
|
||||
}
|
||||
|
||||
return Task.FromResult(Success());
|
||||
return Success();
|
||||
}
|
||||
if (obj.Type == RequestType.Album)
|
||||
{
|
||||
var album = (SearchAlbumViewModel) obj;
|
||||
var albumRequest = await Music.GetRequestAsync(album.ForeignAlbumId);
|
||||
if (albumRequest != null) // Do we already have a request for this?
|
||||
{
|
||||
obj.Requested = true;
|
||||
obj.RequestId = albumRequest.Id;
|
||||
obj.Approved = albumRequest.Approved;
|
||||
obj.Available = albumRequest.Available;
|
||||
|
||||
return Success();
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
36
src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs
Normal file
36
src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public class LidarrAlbumCacheRule : SpecificRule, ISpecificRule<object>
|
||||
{
|
||||
public LidarrAlbumCacheRule(IRepository<LidarrAlbumCache> db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private readonly IRepository<LidarrAlbumCache> _db;
|
||||
|
||||
public Task<RuleResult> Execute(object objec)
|
||||
{
|
||||
var obj = (SearchAlbumViewModel) objec;
|
||||
// Check if it's in Lidarr
|
||||
var result = _db.GetAll().FirstOrDefault(x => x.ForeignAlbumId.Equals(obj.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (result != null)
|
||||
{
|
||||
obj.PercentOfTracks = result.PercentOfTracks;
|
||||
obj.Monitored = true; // It's in Lidarr so it's monitored
|
||||
}
|
||||
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
|
||||
public override SpecificRules Rule => SpecificRules.LidarrAlbum;
|
||||
}
|
||||
}
|
35
src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs
Normal file
35
src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public class LidarrArtistCacheRule : SpecificRule, ISpecificRule<object>
|
||||
{
|
||||
public LidarrArtistCacheRule(IRepository<LidarrArtistCache> db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private readonly IRepository<LidarrArtistCache> _db;
|
||||
|
||||
public Task<RuleResult> Execute(object objec)
|
||||
{
|
||||
var obj = (SearchArtistViewModel) objec;
|
||||
// Check if it's in Lidarr
|
||||
var result = _db.GetAll().FirstOrDefault(x => x.ForeignArtistId.Equals(obj.ForignArtistId, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (result != null)
|
||||
{
|
||||
obj.Monitored = true; // It's in Lidarr so it's monitored
|
||||
}
|
||||
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
|
||||
public override SpecificRules Rule => SpecificRules.LidarrArtist;
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
{
|
||||
|
|
10
src/Ombi.Core/Senders/IMusicSender.cs
Normal file
10
src/Ombi.Core/Senders/IMusicSender.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public interface IMusicSender
|
||||
{
|
||||
Task<SenderResult> Send(AlbumRequest model);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.CouchPotato;
|
||||
using Ombi.Api.DogNzb.Models;
|
||||
|
@ -9,6 +10,8 @@ using Ombi.Helpers;
|
|||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
|
@ -16,7 +19,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
public MovieSender(ISettingsService<RadarrSettings> radarrSettings, IRadarrApi api, ILogger<MovieSender> log,
|
||||
ISettingsService<DogNzbSettings> dogSettings, IDogNzbApi dogApi, ISettingsService<CouchPotatoSettings> cpSettings,
|
||||
ICouchPotatoApi cpApi)
|
||||
ICouchPotatoApi cpApi, IRepository<UserQualityProfiles> userProfiles)
|
||||
{
|
||||
RadarrSettings = radarrSettings;
|
||||
RadarrApi = api;
|
||||
|
@ -25,6 +28,7 @@ namespace Ombi.Core.Senders
|
|||
DogNzbApi = dogApi;
|
||||
CouchPotatoSettings = cpSettings;
|
||||
CouchPotatoApi = cpApi;
|
||||
_userProfiles = userProfiles;
|
||||
}
|
||||
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
|
@ -34,6 +38,7 @@ namespace Ombi.Core.Senders
|
|||
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CouchPotatoSettings { get; }
|
||||
private ICouchPotatoApi CouchPotatoApi { get; }
|
||||
private readonly IRepository<UserQualityProfiles> _userProfiles;
|
||||
|
||||
public async Task<SenderResult> Send(MovieRequests model)
|
||||
{
|
||||
|
@ -88,13 +93,33 @@ namespace Ombi.Core.Senders
|
|||
|
||||
private async Task<SenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings)
|
||||
{
|
||||
|
||||
var qualityToUse = int.Parse(settings.DefaultQualityProfile);
|
||||
|
||||
var rootFolderPath = settings.DefaultRootPath;
|
||||
|
||||
var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
|
||||
if (profiles != null)
|
||||
{
|
||||
if (profiles.SonarrRootPathAnime > 0)
|
||||
{
|
||||
rootFolderPath = await RadarrRootPath(profiles.SonarrRootPathAnime, settings);
|
||||
}
|
||||
if (profiles.SonarrQualityProfileAnime > 0)
|
||||
{
|
||||
qualityToUse = profiles.SonarrQualityProfileAnime;
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides on the request take priority
|
||||
if (model.QualityOverride > 0)
|
||||
{
|
||||
qualityToUse = model.QualityOverride;
|
||||
}
|
||||
|
||||
var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings);
|
||||
if (model.RootPathOverride > 0)
|
||||
{
|
||||
rootFolderPath = await RadarrRootPath(model.RootPathOverride, settings);
|
||||
}
|
||||
|
||||
// Check if the movie already exists? Since it could be unmonitored
|
||||
var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
|
||||
|
@ -123,7 +148,10 @@ namespace Ombi.Core.Senders
|
|||
existingMovie.monitored = true;
|
||||
await RadarrApi.UpdateMovie(existingMovie, settings.ApiKey, settings.FullUri);
|
||||
// Search for it
|
||||
await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri);
|
||||
if (!settings.AddOnly)
|
||||
{
|
||||
await RadarrApi.MovieSearch(new[] {existingMovie.id}, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
return new SenderResult { Success = true, Sent = true };
|
||||
}
|
||||
|
|
130
src/Ombi.Core/Senders/MusicSender.cs
Normal file
130
src/Ombi.Core/Senders/MusicSender.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Serilog;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
public class MusicSender : IMusicSender
|
||||
{
|
||||
public MusicSender(ISettingsService<LidarrSettings> lidarr, ILidarrApi lidarrApi)
|
||||
{
|
||||
_lidarrSettings = lidarr;
|
||||
_lidarrApi = lidarrApi;
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
|
||||
public async Task<SenderResult> Send(AlbumRequest model)
|
||||
{
|
||||
var settings = await _lidarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SendToLidarr(model, settings);
|
||||
}
|
||||
|
||||
return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" };
|
||||
}
|
||||
|
||||
private async Task<SenderResult> SendToLidarr(AlbumRequest model, LidarrSettings settings)
|
||||
{
|
||||
var qualityToUse = int.Parse(settings.DefaultQualityProfile);
|
||||
//if (model.QualityOverride > 0)
|
||||
//{
|
||||
// qualityToUse = model.QualityOverride;
|
||||
//}
|
||||
|
||||
var rootFolderPath = /*model.RootPathOverride <= 0 ?*/ settings.DefaultRootPath /*: await RadarrRootPath(model.RootPathOverride, settings)*/;
|
||||
|
||||
// Need to get the artist
|
||||
var artist = await _lidarrApi.GetArtistByForeignId(model.ForeignArtistId, settings.ApiKey, settings.FullUri);
|
||||
|
||||
if (artist == null || artist.id <= 0)
|
||||
{
|
||||
// Create artist
|
||||
var newArtist = new ArtistAdd
|
||||
{
|
||||
foreignArtistId = model.ForeignArtistId,
|
||||
addOptions = new Addoptions
|
||||
{
|
||||
monitored = true,
|
||||
searchForMissingAlbums = false,
|
||||
selectedOption = 6, // None
|
||||
AlbumsToMonitor = new[] {model.ForeignAlbumId}
|
||||
},
|
||||
added = DateTime.Now,
|
||||
monitored = true,
|
||||
albumFolder = settings.AlbumFolder,
|
||||
artistName = model.ArtistName,
|
||||
cleanName = model.ArtistName.ToLowerInvariant().RemoveSpaces(),
|
||||
images = new Image[] { },
|
||||
languageProfileId = settings.LanguageProfileId,
|
||||
links = new Link[] {},
|
||||
metadataProfileId = settings.MetadataProfileId,
|
||||
qualityProfileId = qualityToUse,
|
||||
rootFolderPath = rootFolderPath,
|
||||
};
|
||||
|
||||
var result = await _lidarrApi.AddArtist(newArtist, settings.ApiKey, settings.FullUri);
|
||||
if (result != null && result.id > 0)
|
||||
{
|
||||
// Setup the albums
|
||||
return new SenderResult { Message = "Album has been requested!", Sent = true, Success = true };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await SetupAlbum(model, artist, settings);
|
||||
}
|
||||
|
||||
return new SenderResult { Success = false, Sent = false, Message = "Album is already monitored" };
|
||||
}
|
||||
|
||||
private async Task<SenderResult> SetupAlbum(AlbumRequest model, ArtistResult artist, LidarrSettings settings)
|
||||
{
|
||||
// Get the album id
|
||||
var albums = await _lidarrApi.GetAllAlbumsByArtistId(artist.id, settings.ApiKey, settings.FullUri);
|
||||
var album = albums.FirstOrDefault(x =>
|
||||
x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase));
|
||||
var maxRetryCount = 10; // 5 seconds
|
||||
var currentRetry = 0;
|
||||
while (!albums.Any() || album == null)
|
||||
{
|
||||
if (currentRetry >= maxRetryCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
currentRetry++;
|
||||
await Task.Delay(500);
|
||||
albums = await _lidarrApi.GetAllAlbumsByArtistId(artist.id, settings.ApiKey, settings.FullUri);
|
||||
album = albums.FirstOrDefault(x =>
|
||||
x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
// Get the album we want.
|
||||
|
||||
if (album == null)
|
||||
{
|
||||
return new SenderResult { Message = "Could not find album in Lidarr", Sent = false, Success = false };
|
||||
}
|
||||
|
||||
var result = await _lidarrApi.MontiorAlbum(album.id, settings.ApiKey, settings.FullUri);
|
||||
if (!settings.AddOnly)
|
||||
{
|
||||
await _lidarrApi.AlbumSearch(new[] {result.id}, settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
if (result.monitored)
|
||||
{
|
||||
return new SenderResult {Message = "Album has been requested!", Sent = true, Success = true};
|
||||
}
|
||||
return new SenderResult { Message = "Could not set album to monitored", Sent = false, Success = false };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Api.DogNzb.Models;
|
||||
|
@ -12,7 +13,9 @@ using Ombi.Api.Sonarr.Models;
|
|||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Senders
|
||||
{
|
||||
|
@ -20,7 +23,7 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISickRageApi srApi)
|
||||
ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
Logger = log;
|
||||
|
@ -29,6 +32,7 @@ namespace Ombi.Core.Senders
|
|||
DogNzbApi = dogApi;
|
||||
SickRageSettings = srSettings;
|
||||
SickRageApi = srApi;
|
||||
UserQualityProfiles = userProfiles;
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
|
@ -38,6 +42,7 @@ namespace Ombi.Core.Senders
|
|||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
|
||||
private ISettingsService<SickRageSettings> SickRageSettings { get; }
|
||||
private IRepository<UserQualityProfiles> UserQualityProfiles { get; }
|
||||
|
||||
public async Task<SenderResult> Send(ChildRequests model)
|
||||
{
|
||||
|
@ -122,13 +127,25 @@ namespace Ombi.Core.Senders
|
|||
string rootFolderPath;
|
||||
string seriesType;
|
||||
|
||||
var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
|
||||
|
||||
if (model.SeriesType == SeriesType.Anime)
|
||||
{
|
||||
// Get the root path from the rootfolder selected.
|
||||
// For some reason, if we haven't got one use the first root folder in Sonarr
|
||||
// TODO make this overrideable via the UI
|
||||
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPathAnime), s);
|
||||
int.TryParse(s.QualityProfileAnime, out qualityToUse);
|
||||
if (profiles != null)
|
||||
{
|
||||
if (profiles.SonarrRootPathAnime > 0)
|
||||
{
|
||||
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s);
|
||||
}
|
||||
if (profiles.SonarrQualityProfileAnime > 0)
|
||||
{
|
||||
qualityToUse = profiles.SonarrQualityProfileAnime;
|
||||
}
|
||||
}
|
||||
seriesType = "anime";
|
||||
|
||||
}
|
||||
|
@ -137,11 +154,22 @@ namespace Ombi.Core.Senders
|
|||
int.TryParse(s.QualityProfile, out qualityToUse);
|
||||
// Get the root path from the rootfolder selected.
|
||||
// For some reason, if we haven't got one use the first root folder in Sonarr
|
||||
// TODO make this overrideable via the UI
|
||||
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder ?? int.Parse(s.RootPath), s);
|
||||
if (profiles != null)
|
||||
{
|
||||
if (profiles.SonarrRootPath > 0)
|
||||
{
|
||||
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s);
|
||||
}
|
||||
if (profiles.SonarrQualityProfile > 0)
|
||||
{
|
||||
qualityToUse = profiles.SonarrQualityProfile;
|
||||
}
|
||||
}
|
||||
seriesType = "standard";
|
||||
}
|
||||
|
||||
// Overrides on the request take priority
|
||||
if (model.ParentRequest.QualityOverride.HasValue)
|
||||
{
|
||||
qualityToUse = model.ParentRequest.QualityOverride.Value;
|
||||
|
|
|
@ -32,6 +32,7 @@ using Ombi.Api.CouchPotato;
|
|||
using Ombi.Api.DogNzb;
|
||||
using Ombi.Api.FanartTv;
|
||||
using Ombi.Api.Github;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Mattermost;
|
||||
using Ombi.Api.Notifications;
|
||||
using Ombi.Api.Pushbullet;
|
||||
|
@ -53,9 +54,11 @@ using PlexContentCacher = Ombi.Schedule.Jobs.Plex;
|
|||
using Ombi.Api.Telegram;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Processor;
|
||||
using Ombi.Schedule.Jobs.Lidarr;
|
||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
using Ombi.Schedule.Jobs.SickRage;
|
||||
using Ombi.Schedule.Processor;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.DependencyInjection
|
||||
{
|
||||
|
@ -82,7 +85,10 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IUserStatsEngine, UserStatsEngine>();
|
||||
services.AddTransient<IMovieSender, MovieSender>();
|
||||
services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>();
|
||||
services.AddTransient<IMusicSearchEngine, MusicSearchEngine>();
|
||||
services.AddTransient<IMusicRequestEngine, MusicRequestEngine>();
|
||||
services.AddTransient<ITvSender, TvSender>();
|
||||
services.AddTransient<IMusicSender, MusicSender>();
|
||||
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
||||
}
|
||||
|
@ -117,6 +123,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ISickRageApi, SickRageApi>();
|
||||
services.AddTransient<IAppVeyorApi, AppVeyorApi>();
|
||||
services.AddTransient<IOneSignalApi, OneSignalApi>();
|
||||
services.AddTransient<ILidarrApi, LidarrApi>();
|
||||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services) {
|
||||
|
@ -131,6 +138,7 @@ namespace Ombi.DependencyInjection
|
|||
|
||||
services.AddTransient<ITvRequestRepository, TvRequestRepository>();
|
||||
services.AddTransient<IMovieRequestRepository, MovieRequestRepository>();
|
||||
services.AddTransient<IMusicRequestRepository, MusicRequestRepository>();
|
||||
services.AddTransient<IAuditRepository, AuditRepository>();
|
||||
services.AddTransient<IApplicationConfigRepository, ApplicationConfigRepository>();
|
||||
services.AddTransient<ITokenRepository, TokenRepository>();
|
||||
|
@ -180,6 +188,9 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IRefreshMetadata, RefreshMetadata>();
|
||||
services.AddTransient<INewsletterJob, NewsletterJob>();
|
||||
services.AddTransient<IPlexRecentlyAddedSync, PlexRecentlyAddedSync>();
|
||||
services.AddTransient<ILidarrAlbumSync, LidarrAlbumSync>();
|
||||
services.AddTransient<ILidarrArtistSync, LidarrArtistSync>();
|
||||
services.AddTransient<ILidarrAvailabilityChecker, LidarrAvailabilityChecker>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Github\Ombi.Api.Github.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Mattermost\Ombi.Api.Mattermost.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Notifications\Ombi.Api.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Ombi.Helpers
|
|||
public const string NowPlayingMovies = nameof(NowPlayingMovies);
|
||||
public const string RadarrRootProfiles = nameof(RadarrRootProfiles);
|
||||
public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles);
|
||||
public const string LidarrRootFolders = nameof(LidarrRootFolders);
|
||||
public const string LidarrQualityProfiles = nameof(LidarrQualityProfiles);
|
||||
public const string FanartTv = nameof(FanartTv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Ombi.Helpers
|
|||
public static EventId CouchPotatoCacher => new EventId(2007);
|
||||
public static EventId PlexContentCacher => new EventId(2008);
|
||||
public static EventId SickRageCacher => new EventId(2009);
|
||||
public static EventId LidarrArtistCache => new EventId(2010);
|
||||
|
||||
public static EventId MovieSender => new EventId(3000);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -75,5 +75,14 @@ namespace Ombi.Helpers
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static string RemoveSpaces(this string str)
|
||||
{
|
||||
return str.Replace(" ", "");
|
||||
}
|
||||
public static string StripCharacters(this string str, params char[] chars)
|
||||
{
|
||||
return string.Concat(str.Where(c => !chars.Contains(c)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,8 +20,9 @@ namespace Ombi.Notifications.Agents
|
|||
{
|
||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn,
|
||||
ILogger<DiscordNotification> log, INotificationTemplatesRepository r,
|
||||
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub)
|
||||
: base(sn, r, m, t,s,log, sub)
|
||||
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref)
|
||||
: base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
@ -130,12 +131,18 @@ namespace Ombi.Notifications.Agents
|
|||
title = MovieRequest.Title;
|
||||
image = MovieRequest.PosterPath;
|
||||
}
|
||||
else
|
||||
else if (model.RequestType == RequestType.TvShow)
|
||||
{
|
||||
user = TvRequest.RequestedUser.UserAlias;
|
||||
title = TvRequest.ParentRequest.Title;
|
||||
image = TvRequest.ParentRequest.PosterPath;
|
||||
}
|
||||
else if (model.RequestType == RequestType.Album)
|
||||
{
|
||||
user = AlbumRequest.RequestedUser.UserAlias;
|
||||
title = AlbumRequest.Title;
|
||||
image = AlbumRequest.Cover;
|
||||
}
|
||||
var message = $"Hello! The user '{user}' has requested {title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
|
|
|
@ -22,7 +22,8 @@ namespace Ombi.Notifications.Agents
|
|||
public class EmailNotification : BaseNotification<EmailNotificationSettings>, IEmailNotification
|
||||
{
|
||||
public EmailNotification(ISettingsService<EmailNotificationSettings> settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService<CustomizationSettings> c,
|
||||
ILogger<EmailNotification> log, UserManager<OmbiUser> um, IRepository<RequestSubscription> sub) : base(settings, r, m, t, c, log, sub)
|
||||
ILogger<EmailNotification> log, UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(settings, r, m, t, c, log, sub, music, userPref)
|
||||
{
|
||||
EmailProvider = prov;
|
||||
Logger = log;
|
||||
|
|
|
@ -21,7 +21,8 @@ namespace Ombi.Notifications.Agents
|
|||
public class MattermostNotification : BaseNotification<MattermostNotificationSettings>, IMattermostNotification
|
||||
{
|
||||
public MattermostNotification(IMattermostApi api, ISettingsService<MattermostNotificationSettings> sn, ILogger<MattermostNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub) : base(sn, r, m, t,s,log, sub)
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
|
|
@ -22,7 +22,8 @@ namespace Ombi.Notifications.Agents
|
|||
{
|
||||
public MobileNotification(IOneSignalApi api, ISettingsService<MobileNotificationSettings> sn, ILogger<MobileNotification> log, INotificationTemplatesRepository r,
|
||||
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<NotificationUserId> notification,
|
||||
UserManager<OmbiUser> um, IRepository<RequestSubscription> sub) : base(sn, r, m, t, s,log, sub)
|
||||
UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
_api = api;
|
||||
_logger = log;
|
||||
|
|
|
@ -17,7 +17,8 @@ namespace Ombi.Notifications.Agents
|
|||
public class PushbulletNotification : BaseNotification<PushbulletSettings>, IPushbulletNotification
|
||||
{
|
||||
public PushbulletNotification(IPushbulletApi api, ISettingsService<PushbulletSettings> sn, ILogger<PushbulletNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub) : base(sn, r, m, t,s,log, sub)
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
|
|
@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents
|
|||
public class PushoverNotification : BaseNotification<PushoverSettings>, IPushoverNotification
|
||||
{
|
||||
public PushoverNotification(IPushoverApi api, ISettingsService<PushoverSettings> sn, ILogger<PushoverNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub) : base(sn, r, m, t, s, log, sub)
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
@ -177,7 +178,8 @@ namespace Ombi.Notifications.Agents
|
|||
{
|
||||
try
|
||||
{
|
||||
await Api.PushAsync(settings.AccessToken, model.Message, settings.UserToken);
|
||||
//&+' < >
|
||||
await Api.PushAsync(settings.AccessToken, model.Message.StripCharacters('&','+','<','>'), settings.UserToken, settings.Priority, settings.Sound);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents
|
|||
public class SlackNotification : BaseNotification<SlackNotificationSettings>, ISlackNotification
|
||||
{
|
||||
public SlackNotification(ISlackApi api, ISettingsService<SlackNotificationSettings> sn, ILogger<SlackNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub) : base(sn, r, m, t, s, log, sub)
|
||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace Ombi.Notifications.Agents
|
|||
public TelegramNotification(ITelegramApi api, ISettingsService<TelegramSettings> sn, ILogger<TelegramNotification> log,
|
||||
INotificationTemplatesRepository r, IMovieRequestRepository m,
|
||||
ITvRequestRepository t, ISettingsService<CustomizationSettings> s
|
||||
, IRepository<RequestSubscription> sub) : base(sn, r, m, t,s,log, sub)
|
||||
, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t,s,log, sub, music, userPref)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace Ombi.Notifications.Interfaces
|
|||
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
||||
{
|
||||
protected BaseNotification(ISettingsService<T> settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv,
|
||||
ISettingsService<CustomizationSettings> customization, ILogger<BaseNotification<T>> log, IRepository<RequestSubscription> sub)
|
||||
ISettingsService<CustomizationSettings> customization, ILogger<BaseNotification<T>> log, IRepository<RequestSubscription> sub, IMusicRequestRepository album,
|
||||
IRepository<UserNotificationPreferences> notificationUserPreferences)
|
||||
{
|
||||
Settings = settings;
|
||||
TemplateRepository = templateRepo;
|
||||
|
@ -30,19 +31,24 @@ namespace Ombi.Notifications.Interfaces
|
|||
CustomizationSettings.ClearCache();
|
||||
RequestSubscription = sub;
|
||||
_log = log;
|
||||
AlbumRepository = album;
|
||||
UserNotificationPreferences = notificationUserPreferences;
|
||||
}
|
||||
|
||||
protected ISettingsService<T> Settings { get; }
|
||||
protected INotificationTemplatesRepository TemplateRepository { get; }
|
||||
protected IMovieRequestRepository MovieRepository { get; }
|
||||
protected ITvRequestRepository TvRepository { get; }
|
||||
protected IMusicRequestRepository AlbumRepository { get; }
|
||||
protected CustomizationSettings Customization { get; set; }
|
||||
protected IRepository<RequestSubscription> RequestSubscription { get; set; }
|
||||
protected IRepository<UserNotificationPreferences> UserNotificationPreferences { get; set; }
|
||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private readonly ILogger<BaseNotification<T>> _log;
|
||||
|
||||
|
||||
protected ChildRequests TvRequest { get; set; }
|
||||
protected AlbumRequest AlbumRequest { get; set; }
|
||||
protected MovieRequests MovieRequest { get; set; }
|
||||
protected IQueryable<OmbiUser> SubsribedUsers { get; private set; }
|
||||
|
||||
|
@ -130,10 +136,14 @@ namespace Ombi.Notifications.Interfaces
|
|||
{
|
||||
MovieRequest = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
}
|
||||
else
|
||||
else if (type == RequestType.TvShow)
|
||||
{
|
||||
TvRequest = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
}
|
||||
else if (type == RequestType.Album)
|
||||
{
|
||||
AlbumRequest = await AlbumRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<T> GetConfiguration()
|
||||
|
@ -160,7 +170,7 @@ namespace Ombi.Notifications.Interfaces
|
|||
{
|
||||
return new NotificationMessageContent { Disabled = true };
|
||||
}
|
||||
var parsed = Parse(model, template);
|
||||
var parsed = Parse(model, template, agent);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
@ -171,20 +181,32 @@ namespace Ombi.Notifications.Interfaces
|
|||
return subs.Select(x => x.User);
|
||||
}
|
||||
|
||||
private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template)
|
||||
protected UserNotificationPreferences GetUserPreference(string userId, NotificationAgent agent)
|
||||
{
|
||||
return UserNotificationPreferences.GetAll()
|
||||
.FirstOrDefault(x => x.Enabled && x.Agent == agent && x.UserId == userId);
|
||||
}
|
||||
|
||||
private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent)
|
||||
{
|
||||
var resolver = new NotificationMessageResolver();
|
||||
var curlys = new NotificationMessageCurlys();
|
||||
var preference = GetUserPreference(model.UserId, agent);
|
||||
if (model.RequestType == RequestType.Movie)
|
||||
{
|
||||
_log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization);
|
||||
|
||||
curlys.Setup(model, MovieRequest, Customization);
|
||||
curlys.Setup(model, MovieRequest, Customization, preference);
|
||||
}
|
||||
else
|
||||
else if (model.RequestType == RequestType.TvShow)
|
||||
{
|
||||
_log.LogDebug("Notification options: {@model}, Req: {@TvRequest}, Settings: {@Customization}", model, TvRequest, Customization);
|
||||
curlys.Setup(model, TvRequest, Customization);
|
||||
curlys.Setup(model, TvRequest, Customization, preference);
|
||||
}
|
||||
else if (model.RequestType == RequestType.Album)
|
||||
{
|
||||
_log.LogDebug("Notification options: {@model}, Req: {@AlbumRequest}, Settings: {@Customization}", model, AlbumRequest, Customization);
|
||||
curlys.Setup(model, AlbumRequest, Customization, preference);
|
||||
}
|
||||
var parsed = resolver.ParseMessage(template, curlys);
|
||||
|
||||
|
|
|
@ -14,9 +14,10 @@ namespace Ombi.Notifications
|
|||
{
|
||||
public class NotificationMessageCurlys
|
||||
{
|
||||
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s)
|
||||
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||
{
|
||||
LoadIssues(opts);
|
||||
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
|
@ -47,14 +48,49 @@ namespace Ombi.Notifications
|
|||
|
||||
if (req?.RequestType == RequestType.Movie)
|
||||
{
|
||||
PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase)
|
||||
PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase)
|
||||
? "https://image.tmdb.org/t/p/w300{0}" : "https://image.tmdb.org/t/p/w300/{0}", req?.PosterPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PosterImage = req?.PosterPath;
|
||||
}
|
||||
|
||||
|
||||
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
||||
}
|
||||
|
||||
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||
{
|
||||
LoadIssues(opts);
|
||||
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
opts.Substitutes.TryGetValue("Title", out title);
|
||||
}
|
||||
else
|
||||
{
|
||||
title = req?.Title;
|
||||
}
|
||||
ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty;
|
||||
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName;
|
||||
RequestedUser = req?.RequestedUser?.UserName;
|
||||
if (UserName.IsNullOrEmpty())
|
||||
{
|
||||
// Can be set if it's an issue
|
||||
UserName = req?.RequestedUser?.UserName;
|
||||
}
|
||||
|
||||
Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName;
|
||||
Title = title;
|
||||
RequestedDate = req?.RequestedDate.ToString("D");
|
||||
if (Type.IsNullOrEmpty())
|
||||
{
|
||||
Type = req?.RequestType.Humanize();
|
||||
}
|
||||
Year = req?.ReleaseDate.Year.ToString();
|
||||
PosterImage = (req?.Cover.HasValue() ?? false) ? req.Cover : req?.Disk ?? string.Empty;
|
||||
|
||||
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
||||
}
|
||||
|
||||
|
@ -67,9 +103,10 @@ namespace Ombi.Notifications
|
|||
Alias = username.Alias.HasValue() ? username.Alias : username.UserName;
|
||||
}
|
||||
|
||||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s)
|
||||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||
{
|
||||
LoadIssues(opts);
|
||||
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
|
@ -187,6 +224,7 @@ namespace Ombi.Notifications
|
|||
public string IssueStatus { get; set; }
|
||||
public string IssueSubject { get; set; }
|
||||
public string NewIssueComment { get; set; }
|
||||
public string UserPreference { get; set; }
|
||||
|
||||
// System Defined
|
||||
private string LongDate => DateTime.Now.ToString("D");
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Ombi.Schedule.Tests
|
|||
var emailSettings = new Mock<ISettingsService<EmailNotificationSettings>>();
|
||||
var customziation = new Mock<ISettingsService<CustomizationSettings>>();
|
||||
var newsletterSettings = new Mock<ISettingsService<NewsletterSettings>>();
|
||||
var newsletter = new NewsletterJob(null, null, null, null, null, null, customziation.Object, emailSettings.Object, null, null, newsletterSettings.Object, null);
|
||||
var newsletter = new NewsletterJob(null, null, null, null, null, null, customziation.Object, emailSettings.Object, null, null, newsletterSettings.Object, null, null, null, null);
|
||||
|
||||
var ep = new List<int>();
|
||||
foreach (var i in episodes)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.3" />
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
<PackageReference Include="Nunit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.8.0" />
|
||||
|
|
|
@ -4,6 +4,7 @@ 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.Radarr;
|
||||
|
@ -19,7 +20,7 @@ namespace Ombi.Schedule
|
|||
IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter,
|
||||
IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache,
|
||||
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync, IRefreshMetadata refresh,
|
||||
INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex)
|
||||
INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist)
|
||||
{
|
||||
_plexContentSync = plexContentSync;
|
||||
_radarrSync = radarrSync;
|
||||
|
@ -34,6 +35,7 @@ namespace Ombi.Schedule
|
|||
_refreshMetadata = refresh;
|
||||
_newsletter = newsletter;
|
||||
_plexRecentlyAddedSync = recentlyAddedPlex;
|
||||
_lidarrArtistSync = artist;
|
||||
}
|
||||
|
||||
private readonly IPlexContentSync _plexContentSync;
|
||||
|
@ -49,6 +51,7 @@ namespace Ombi.Schedule
|
|||
private readonly ISettingsService<JobSettings> _jobSettings;
|
||||
private readonly IRefreshMetadata _refreshMetadata;
|
||||
private readonly INewsletterJob _newsletter;
|
||||
private readonly ILidarrArtistSync _lidarrArtistSync;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
|
@ -62,6 +65,7 @@ namespace Ombi.Schedule
|
|||
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(() => _updater.Update(null), JobSettingsHelper.Updater(s));
|
||||
|
||||
|
|
13
src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs
Normal file
13
src/Ombi.Schedule/Jobs/Lidarr/ILidarrAlbumSync.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public interface ILidarrAlbumSync
|
||||
{
|
||||
Task CacheContent();
|
||||
void Dispose();
|
||||
Task<IEnumerable<LidarrAlbumCache>> GetCachedContent();
|
||||
}
|
||||
}
|
13
src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs
Normal file
13
src/Ombi.Schedule/Jobs/Lidarr/ILidarrArtistSync.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public interface ILidarrArtistSync
|
||||
{
|
||||
Task CacheContent();
|
||||
void Dispose();
|
||||
Task<IEnumerable<LidarrArtistCache>> GetCachedContent();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public interface ILidarrAvailabilityChecker
|
||||
{
|
||||
Task Start();
|
||||
}
|
||||
}
|
119
src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs
Normal file
119
src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public class LidarrAlbumSync : ILidarrAlbumSync
|
||||
{
|
||||
public LidarrAlbumSync(ISettingsService<LidarrSettings> lidarr, ILidarrApi lidarrApi, ILogger<LidarrAlbumSync> log, IOmbiContext ctx,
|
||||
IBackgroundJobClient job, ILidarrAvailabilityChecker availability)
|
||||
{
|
||||
_lidarrSettings = lidarr;
|
||||
_lidarrApi = lidarrApi;
|
||||
_logger = log;
|
||||
_ctx = ctx;
|
||||
_job = job;
|
||||
_availability = availability;
|
||||
_lidarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IOmbiContext _ctx;
|
||||
private readonly IBackgroundJobClient _job;
|
||||
private readonly ILidarrAvailabilityChecker _availability;
|
||||
|
||||
public async Task CacheContent()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await _lidarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var albums = await _lidarrApi.GetAllAlbums(settings.ApiKey, settings.FullUri);
|
||||
if (albums != null && albums.Any())
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrAlbumCache");
|
||||
|
||||
var albumCache = new List<LidarrAlbumCache>();
|
||||
foreach (var a in albums)
|
||||
{
|
||||
if (a.id > 0)
|
||||
{
|
||||
albumCache.Add(new LidarrAlbumCache
|
||||
{
|
||||
ArtistId = a.artistId,
|
||||
ForeignAlbumId = a.foreignAlbumId,
|
||||
ReleaseDate = a.releaseDate,
|
||||
TrackCount = a.currentRelease.trackCount,
|
||||
Monitored = a.monitored,
|
||||
Title = a.title,
|
||||
PercentOfTracks = a.statistics?.percentOfEpisodes ?? 0m,
|
||||
AddedAt = DateTime.Now,
|
||||
});
|
||||
}
|
||||
}
|
||||
await _ctx.LidarrAlbumCache.AddRangeAsync(albumCache);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
_logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr Album");
|
||||
}
|
||||
|
||||
_job.Enqueue(() => _availability.Start());
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.LogInformation(LoggingEvents.LidarrArtistCache, "Lidarr is not setup, cannot cache Album");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LidarrAlbumCache>> GetCachedContent()
|
||||
{
|
||||
return await _ctx.LidarrAlbumCache.ToListAsync();
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_ctx?.Dispose();
|
||||
_lidarrSettings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
115
src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs
Normal file
115
src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Serilog;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public class LidarrArtistSync : ILidarrArtistSync
|
||||
{
|
||||
public LidarrArtistSync(ISettingsService<LidarrSettings> lidarr, ILidarrApi lidarrApi, ILogger<LidarrArtistSync> log, IOmbiContext ctx,
|
||||
IBackgroundJobClient background, ILidarrAlbumSync album)
|
||||
{
|
||||
_lidarrSettings = lidarr;
|
||||
_lidarrApi = lidarrApi;
|
||||
_logger = log;
|
||||
_ctx = ctx;
|
||||
_job = background;
|
||||
_albumSync = album;
|
||||
_lidarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IOmbiContext _ctx;
|
||||
private readonly IBackgroundJobClient _job;
|
||||
private readonly ILidarrAlbumSync _albumSync;
|
||||
|
||||
public async Task CacheContent()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await _lidarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var artists = await _lidarrApi.GetArtists(settings.ApiKey, settings.FullUri);
|
||||
if (artists != null && artists.Any())
|
||||
{
|
||||
// Let's remove the old cached data
|
||||
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrArtistCache");
|
||||
|
||||
var artistCache = new List<LidarrArtistCache>();
|
||||
foreach (var a in artists)
|
||||
{
|
||||
if (a.id > 0)
|
||||
{
|
||||
artistCache.Add(new LidarrArtistCache
|
||||
{
|
||||
ArtistId = a.id,
|
||||
ArtistName = a.artistName,
|
||||
ForeignArtistId = a.foreignArtistId,
|
||||
Monitored = a.monitored
|
||||
});
|
||||
}
|
||||
}
|
||||
await _ctx.LidarrArtistCache.AddRangeAsync(artistCache);
|
||||
|
||||
await _ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(LoggingEvents.Cacher, ex, "Failed caching queued items from Lidarr");
|
||||
}
|
||||
|
||||
_job.Enqueue(() => _albumSync.CacheContent());
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.LogInformation(LoggingEvents.LidarrArtistCache, "Lidarr is not setup, cannot cache Artist");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LidarrArtistCache>> GetCachedContent()
|
||||
{
|
||||
return await _ctx.LidarrArtistCache.ToListAsync();
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_ctx?.Dispose();
|
||||
_lidarrSettings?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
73
src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs
Normal file
73
src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Lidarr
|
||||
{
|
||||
public class LidarrAvailabilityChecker : ILidarrAvailabilityChecker
|
||||
{
|
||||
public LidarrAvailabilityChecker(IMusicRequestRepository requests, IRepository<LidarrAlbumCache> albums, ILogger<LidarrAvailabilityChecker> log,
|
||||
IBackgroundJobClient job, INotificationService notification)
|
||||
{
|
||||
_cachedAlbums = albums;
|
||||
_requestRepository = requests;
|
||||
_logger = log;
|
||||
_job = job;
|
||||
_notificationService = notification;
|
||||
}
|
||||
|
||||
private readonly IMusicRequestRepository _requestRepository;
|
||||
private readonly IRepository<LidarrAlbumCache> _cachedAlbums;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IBackgroundJobClient _job;
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
var allAlbumRequests = _requestRepository.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available);
|
||||
var albumsToUpdate = new List<AlbumRequest>();
|
||||
foreach (var request in allAlbumRequests)
|
||||
{
|
||||
// Check if we have it cached
|
||||
var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId));
|
||||
if (cachedAlbum != null)
|
||||
{
|
||||
if (cachedAlbum.Monitored && cachedAlbum.FullyAvailable)
|
||||
{
|
||||
request.Available = true;
|
||||
request.MarkedAsAvailable = DateTime.Now;
|
||||
albumsToUpdate.Add(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var albumRequest in albumsToUpdate)
|
||||
{
|
||||
await _requestRepository.Update(albumRequest);
|
||||
var recipient = albumRequest.RequestedUser.Email.HasValue() ? albumRequest.RequestedUser.Email : string.Empty;
|
||||
|
||||
_logger.LogDebug("AlbumId: {0}, RequestUser: {1}", albumRequest.Id, recipient);
|
||||
|
||||
_job.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.RequestAvailable,
|
||||
RequestId = albumRequest.Id,
|
||||
RequestType = RequestType.Album,
|
||||
Recipient = recipient,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Ombi
|
||||
{
|
||||
|
@ -22,13 +23,20 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
|
||||
protected virtual void AddMediaServerUrl(StringBuilder sb, string mediaurl, string url)
|
||||
{
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
|
||||
sb.AppendFormat("<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">", url);
|
||||
sb.Append("</a>");
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
if (url.HasValue())
|
||||
{
|
||||
sb.Append("<tr>");
|
||||
sb.Append(
|
||||
"<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", mediaurl);
|
||||
sb.AppendFormat(
|
||||
"<img class=\"poster-overlay\" src=\"{0}\" width=\"150\" height=\"225\" style=\"border: none;-ms-interpolation-mode: bicubic; max-width: 100%;display: block; visibility: hidden; \">",
|
||||
url);
|
||||
sb.Append("</a>");
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
}
|
||||
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
}
|
||||
|
@ -44,9 +52,9 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
{
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td class=\"title\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 0.9rem; vertical-align: top; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; line-height: 1.2rem; padding: 5px; \">");
|
||||
sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
|
||||
if(url.HasValue()) sb.AppendFormat("<a href=\"{0}\" target=\"_blank\">", url);
|
||||
sb.AppendFormat("<h1 style=\"white-space: normal; line-height: 1;\" >{0}</h1>", title);
|
||||
sb.Append("</a>");
|
||||
if (url.HasValue()) sb.Append("</a>");
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ using MailKit;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Api.TvMaze;
|
||||
|
@ -18,6 +20,7 @@ using Ombi.Notifications;
|
|||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
@ -29,7 +32,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> addedLog,
|
||||
IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService<CustomizationSettings> custom,
|
||||
ISettingsService<EmailNotificationSettings> emailSettings, INotificationTemplatesRepository templateRepo,
|
||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log)
|
||||
UserManager<OmbiUser> um, ISettingsService<NewsletterSettings> newsletter, ILogger<NewsletterJob> log,
|
||||
ILidarrApi lidarrApi, IRepository<LidarrAlbumCache> albumCache, ISettingsService<LidarrSettings> lidarrSettings)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
|
@ -46,6 +50,10 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
_customizationSettings.ClearCache();
|
||||
_newsletterSettings.ClearCache();
|
||||
_log = log;
|
||||
_lidarrApi = lidarrApi;
|
||||
_lidarrAlbumRepository = albumCache;
|
||||
_lidarrSettings = lidarrSettings;
|
||||
_lidarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
|
@ -60,6 +68,9 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
private readonly ISettingsService<NewsletterSettings> _newsletterSettings;
|
||||
private readonly UserManager<OmbiUser> _userManager;
|
||||
private readonly ILogger _log;
|
||||
private readonly ILidarrApi _lidarrApi;
|
||||
private readonly IRepository<LidarrAlbumCache> _lidarrAlbumRepository;
|
||||
private readonly ISettingsService<LidarrSettings> _lidarrSettings;
|
||||
|
||||
public async Task Start(NewsletterSettings settings, bool test)
|
||||
{
|
||||
|
@ -87,21 +98,26 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
// Get the Content
|
||||
var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||
var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking();
|
||||
var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking();
|
||||
|
||||
var addedLog = _recentlyAddedLog.GetAll();
|
||||
var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
|
||||
var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId);
|
||||
var addedAlbumLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Lidarr && x.ContentType == ContentType.Album).Select(x => x.AlbumId);
|
||||
|
||||
var addedPlexEpisodesLogIds =
|
||||
addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode);
|
||||
var addedEmbyEpisodesLogIds =
|
||||
addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode);
|
||||
|
||||
|
||||
// Filter out the ones that we haven't sent yet
|
||||
var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && x.HasTheMovieDb && !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId)));
|
||||
var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && x.HasTheMovieDb && !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId)));
|
||||
var lidarrContentAlbumsToSend = lidarrContent.Where(x => !addedAlbumLogIds.Contains(x.ForeignAlbumId)).ToHashSet();
|
||||
_log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count());
|
||||
_log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count());
|
||||
_log.LogInformation("Albums to send: {0}", lidarrContentAlbumsToSend.Count());
|
||||
|
||||
var plexEpisodesToSend =
|
||||
FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).Where(x => x.Series.HasTvDb).AsNoTracking(), addedPlexEpisodesLogIds);
|
||||
|
@ -117,11 +133,12 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie ).OrderByDescending(x => x.AddedAt).Take(10);
|
||||
var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet();
|
||||
var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
body = await BuildHtml(plexm, embym, plext, embyt, settings);
|
||||
var lidarr = lidarrContent.OrderByDescending(x => x.AddedAt).Take(10).ToHashSet();
|
||||
body = await BuildHtml(plexm, embym, plext, embyt, lidarr, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings);
|
||||
body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, lidarrContentAlbumsToSend, settings);
|
||||
if (body.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
|
@ -298,7 +315,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
return resolver.ParseMessage(template, curlys);
|
||||
}
|
||||
|
||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend, HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, NewsletterSettings settings)
|
||||
private async Task<string> BuildHtml(IQueryable<PlexServerContent> plexContentToSend, IQueryable<EmbyContent> embyContentToSend,
|
||||
HashSet<PlexEpisode> plexEpisodes, HashSet<EmbyEpisode> embyEp, HashSet<LidarrAlbumCache> albums, NewsletterSettings settings)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
|
@ -340,6 +358,24 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
sb.Append("</table>");
|
||||
}
|
||||
|
||||
|
||||
if (albums.Any() && !settings.DisableMusic)
|
||||
{
|
||||
sb.Append("<h1 style=\"text-align: center; max-width: 1042px;\">New Albums</h1><br /><br />");
|
||||
sb.Append(
|
||||
"<table class=\"movies-table\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif; font-size: 14px; vertical-align: top; \">");
|
||||
sb.Append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; \">");
|
||||
sb.Append("<tr>");
|
||||
await ProcessAlbums(albums, sb);
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
sb.Append("</table>");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
@ -382,6 +418,40 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
}
|
||||
private async Task ProcessAlbums(HashSet<LidarrAlbumCache> albumsToSend, StringBuilder sb)
|
||||
{
|
||||
var settings = await _lidarrSettings.GetSettingsAsync();
|
||||
int count = 0;
|
||||
var ordered = albumsToSend.OrderByDescending(x => x.AddedAt);
|
||||
foreach (var content in ordered)
|
||||
{
|
||||
var info = await _lidarrApi.GetAlbumByForeignId(content.ForeignAlbumId, settings.ApiKey, settings.FullUri);
|
||||
if (info == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
CreateAlbumHtmlContent(sb, info);
|
||||
count += 1;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(e, "Error when Processing Lidarr Album {0}", info.title);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndLoopHtml(sb);
|
||||
}
|
||||
|
||||
if (count == 2)
|
||||
{
|
||||
count = 0;
|
||||
sb.Append("</tr>");
|
||||
sb.Append("<tr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessEmbyMovies(IQueryable<EmbyContent> embyContent, StringBuilder sb)
|
||||
{
|
||||
|
@ -467,6 +537,41 @@ namespace Ombi.Schedule.Jobs.Ombi
|
|||
}
|
||||
}
|
||||
|
||||
private void CreateAlbumHtmlContent(StringBuilder sb, AlbumLookup info)
|
||||
{
|
||||
var cover = info.images
|
||||
.FirstOrDefault(x => x.coverType.Equals("cover", StringComparison.InvariantCultureIgnoreCase))?.url;
|
||||
if (cover.IsNullOrEmpty())
|
||||
{
|
||||
cover = info.remoteCover;
|
||||
}
|
||||
AddBackgroundInsideTable(sb, cover);
|
||||
var disk = info.images
|
||||
.FirstOrDefault(x => x.coverType.Equals("disc", StringComparison.InvariantCultureIgnoreCase))?.url;
|
||||
if (disk.IsNullOrEmpty())
|
||||
{
|
||||
disk = info.remoteCover;
|
||||
}
|
||||
AddPosterInsideTable(sb, disk);
|
||||
|
||||
AddMediaServerUrl(sb, string.Empty, string.Empty);
|
||||
AddInfoTable(sb);
|
||||
|
||||
var releaseDate = $"({info.releaseDate.Year})";
|
||||
|
||||
AddTitle(sb, string.Empty, $"{info.title} {releaseDate}");
|
||||
|
||||
var summary = info.artist?.artistName ?? string.Empty;
|
||||
if (summary.Length > 280)
|
||||
{
|
||||
summary = summary.Remove(280);
|
||||
summary = summary + "...</p>";
|
||||
}
|
||||
AddParagraph(sb, summary);
|
||||
|
||||
AddGenres(sb, $"Type: {info.albumType}");
|
||||
}
|
||||
|
||||
private async Task ProcessPlexTv(HashSet<PlexEpisode> plexContent, StringBuilder sb)
|
||||
{
|
||||
var series = new List<PlexServerContent>();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
||||
|
|
16
src/Ombi.Settings/Settings/Models/External/LidarrSettings.cs
vendored
Normal file
16
src/Ombi.Settings/Settings/Models/External/LidarrSettings.cs
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Ombi.Core.Settings.Models.External;
|
||||
|
||||
namespace Ombi.Settings.Settings.Models.External
|
||||
{
|
||||
public class LidarrSettings : ExternalSettings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public string DefaultQualityProfile { get; set; }
|
||||
public string DefaultRootPath { get; set; }
|
||||
public bool AlbumFolder { get; set; }
|
||||
public int LanguageProfileId { get; set; }
|
||||
public int MetadataProfileId { get; set; }
|
||||
public bool AddOnly { get; set; }
|
||||
}
|
||||
}
|
|
@ -13,5 +13,6 @@
|
|||
public string SickRageSync { get; set; }
|
||||
public string RefreshMetadata { get; set; }
|
||||
public string Newsletter { get; set; }
|
||||
public string LidarrArtistSync { get; set; }
|
||||
}
|
||||
}
|
|
@ -52,6 +52,10 @@ namespace Ombi.Settings.Settings.Models
|
|||
{
|
||||
return Get(s.RefreshMetadata, Cron.DayInterval(3));
|
||||
}
|
||||
public static string LidarrArtistSync(JobSettings s)
|
||||
{
|
||||
return Get(s.LidarrArtistSync, Cron.Hourly(40));
|
||||
}
|
||||
|
||||
private static string Get(string settings, string defaultCron)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Ombi.Settings.Settings.Models.Notifications
|
|||
{
|
||||
public bool DisableTv { get; set; }
|
||||
public bool DisableMovies { get; set; }
|
||||
public bool DisableMusic { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public List<string> ExternalEmails { get; set; } = new List<string>();
|
||||
}
|
||||
|
|
|
@ -8,5 +8,7 @@ namespace Ombi.Settings.Settings.Models.Notifications
|
|||
public bool Enabled { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
public string UserToken { get; set; }
|
||||
public sbyte Priority { get; set; } = 0;
|
||||
public string Sound { get; set; } = "pushover";
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ namespace Ombi.Store.Context
|
|||
void Seed();
|
||||
DbSet<Audit> Audit { get; set; }
|
||||
DbSet<MovieRequests> MovieRequests { get; set; }
|
||||
DbSet<AlbumRequest> AlbumRequests { get; set; }
|
||||
DbSet<TvRequests> TvRequests { get; set; }
|
||||
DbSet<ChildRequests> ChildRequests { get; set; }
|
||||
DbSet<Issues> Issues { get; set; }
|
||||
|
@ -39,6 +40,8 @@ namespace Ombi.Store.Context
|
|||
EntityEntry<TEntity> Update<TEntity>(TEntity entity) where TEntity : class;
|
||||
DbSet<CouchPotatoCache> CouchPotatoCache { get; set; }
|
||||
DbSet<SickRageCache> SickRageCache { get; set; }
|
||||
DbSet<LidarrArtistCache> LidarrArtistCache { get; set; }
|
||||
DbSet<LidarrAlbumCache> LidarrAlbumCache { get; set; }
|
||||
DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||
DbSet<RequestLog> RequestLogs { get; set; }
|
||||
DbSet<RecentlyAddedLog> RecentlyAddedLogs { get; set; }
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace Ombi.Store.Context
|
|||
public DbSet<EmbyEpisode> EmbyEpisode { get; set; }
|
||||
|
||||
public DbSet<MovieRequests> MovieRequests { get; set; }
|
||||
public DbSet<AlbumRequest> AlbumRequests { get; set; }
|
||||
public DbSet<TvRequests> TvRequests { get; set; }
|
||||
public DbSet<ChildRequests> ChildRequests { get; set; }
|
||||
|
||||
|
@ -44,11 +45,14 @@ namespace Ombi.Store.Context
|
|||
public DbSet<Audit> Audit { get; set; }
|
||||
public DbSet<Tokens> Tokens { get; set; }
|
||||
public DbSet<SonarrCache> SonarrCache { get; set; }
|
||||
public DbSet<LidarrArtistCache> LidarrArtistCache { get; set; }
|
||||
public DbSet<LidarrAlbumCache> LidarrAlbumCache { get; set; }
|
||||
public DbSet<SonarrEpisodeCache> SonarrEpisodeCache { get; set; }
|
||||
public DbSet<SickRageCache> SickRageCache { get; set; }
|
||||
public DbSet<SickRageEpisodeCache> SickRageEpisodeCache { get; set; }
|
||||
public DbSet<RequestSubscription> RequestSubscription { get; set; }
|
||||
|
||||
public DbSet<UserNotificationPreferences> UserNotificationPreferences { get; set; }
|
||||
public DbSet<UserQualityProfiles> UserQualityProfileses { get; set; }
|
||||
public DbSet<ApplicationConfiguration> ApplicationConfigurations { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
|
@ -117,8 +121,8 @@ namespace Ombi.Store.Context
|
|||
Database.ExecuteSqlCommand("VACUUM;");
|
||||
|
||||
// Make sure we have the roles
|
||||
var roles = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter);
|
||||
if (!roles.Any())
|
||||
var newsletterRole = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter);
|
||||
if (!newsletterRole.Any())
|
||||
{
|
||||
Roles.Add(new IdentityRole(OmbiRoles.ReceivesNewsletter)
|
||||
{
|
||||
|
@ -126,6 +130,19 @@ namespace Ombi.Store.Context
|
|||
});
|
||||
SaveChanges();
|
||||
}
|
||||
var requestMusicRole = Roles.Where(x => x.Name == OmbiRoles.RequestMusic);
|
||||
if (!requestMusicRole.Any())
|
||||
{
|
||||
Roles.Add(new IdentityRole(OmbiRoles.RequestMusic)
|
||||
{
|
||||
NormalizedName = OmbiRoles.RequestMusic.ToUpper()
|
||||
});
|
||||
Roles.Add(new IdentityRole(OmbiRoles.AutoApproveMusic)
|
||||
{
|
||||
NormalizedName = OmbiRoles.AutoApproveMusic.ToUpper()
|
||||
});
|
||||
SaveChanges();
|
||||
}
|
||||
|
||||
// Make sure we have the API User
|
||||
var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase));
|
||||
|
|
23
src/Ombi.Store/Entities/LidarrAlbumCache.cs
Normal file
23
src/Ombi.Store/Entities/LidarrAlbumCache.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("LidarrAlbumCache")]
|
||||
public class LidarrAlbumCache : Entity
|
||||
{
|
||||
public int ArtistId { get; set; }
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public int TrackCount { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string Title { get; set; }
|
||||
public decimal PercentOfTracks { get; set; }
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public bool PartiallyAvailable => PercentOfTracks != 100 && PercentOfTracks > 0;
|
||||
[NotMapped]
|
||||
public bool FullyAvailable => PercentOfTracks == 100;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue