diff --git a/README.md b/README.md index 37354708a..adaa11c42 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ ____ [![Patreon](https://www.ombi.io/img/patreondonate.svg)](https://patreon.com/tidusjar/Ombi) [![Paypal](https://www.ombi.io/img/paypaldonate.svg)](https://paypal.me/PlexRequestsNet) -[![Gratipay User](https://img.shields.io/gratipay/user/tidusjar.svg)](https://gratipay.com/Ombi/) [![Report a bug](http://i.imgur.com/xSpw482.png)](https://github.com/tidusjar/Ombi/issues/new) [![Feature request](http://i.imgur.com/mFO0OuX.png)](http://feathub.com/tidusjar/Ombi) @@ -100,10 +99,6 @@ You may need to install libwind8. # FAQ Do you have an issue or a question? if so check out our [FAQ](https://github.com/tidusjar/Ombi/wiki/FAQ)! -# Docker - -Looking for a Docker Image? Well [rogueosb](https://github.com/rogueosb/) has created a docker image for us, You can find it [here](https://github.com/rogueosb/docker-plexrequestsnet) :smile: - # Contributors We are looking for any contributions to the project! Just pick up a task, if you have any questions ask and i'll get straight on it! diff --git a/src/Ombi.Api.CouchPotato/CouchPotatoApi.cs b/src/Ombi.Api.CouchPotato/CouchPotatoApi.cs new file mode 100644 index 000000000..8980c4b11 --- /dev/null +++ b/src/Ombi.Api.CouchPotato/CouchPotatoApi.cs @@ -0,0 +1,84 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Ombi.Api.CouchPotato.Models; +using Ombi.Helpers; + +namespace Ombi.Api.CouchPotato +{ + public class CouchPotatoApi : ICouchPotatoApi + { + public CouchPotatoApi(IApi api, ILogger log) + { + _api = api; + _log = log; + } + + private readonly IApi _api; + private readonly ILogger _log; + + public async Task AddMovie(string imdbid, string apiKey, string title, string baseUrl, string profileId = default(string)) + { + var request = new Request($"/api/{apiKey}/movie.add", baseUrl, HttpMethod.Get); + + request.AddQueryString("title", title); + request.AddQueryString("identifier", imdbid); + if (!string.IsNullOrEmpty(profileId)) + { + request.AddQueryString("profile_id", profileId); + } + + var obj = await _api.Request(request); + + if (obj.Count > 0) + { + try + { + var result = (bool)obj["success"]; + return result; + } + catch (Exception e) + { + _log.LogError(LoggingEvents.CouchPotatoApi, e, "Error calling AddMovie"); + return false; + } + } + return false; + } + + public async Task Status(string url, string apiKey) + { + var request = new Request($"api/{apiKey}/app.available/", url, HttpMethod.Get); + return await _api.Request(request); + } + + public async Task GetProfiles(string url, string apiKey) + { + var request = new Request($"api/{apiKey}/profile.list/", url, HttpMethod.Get); + return await _api.Request(request); + } + + public async Task GetMovies(string baseUrl, string apiKey, string[] status) + { + var request = new Request($"/api/{apiKey}/movie.list", baseUrl, HttpMethod.Get); + + request.AddQueryString("status",string.Join(",", status)); + request.OnBeforeDeserialization = json => + { + json.Replace("[]", "{}"); + }; + return await _api.Request(request); + } + + public async Task GetApiKey(string baseUrl, string username, string password) + { + var request = new Request("getkey/",baseUrl, HttpMethod.Get); + request.AddQueryString("u",username.CalcuateMd5Hash()); + request.AddQueryString("p",password.CalcuateMd5Hash()); + + return await _api.Request(request); + } + } +} diff --git a/src/Ombi.Api.CouchPotato/ICouchPotatoApi.cs b/src/Ombi.Api.CouchPotato/ICouchPotatoApi.cs new file mode 100644 index 000000000..fdf567497 --- /dev/null +++ b/src/Ombi.Api.CouchPotato/ICouchPotatoApi.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Ombi.Api.CouchPotato.Models; + +namespace Ombi.Api.CouchPotato +{ + public interface ICouchPotatoApi + { + Task AddMovie(string imdbid, string apiKey, string title, string baseUrl, string profileId = null); + Task GetApiKey(string baseUrl, string username, string password); + Task GetMovies(string baseUrl, string apiKey, string[] status); + Task GetProfiles(string url, string apiKey); + Task Status(string url, string apiKey); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.CouchPotato/Models/CouchPotatoApiKey.cs b/src/Ombi.Api.CouchPotato/Models/CouchPotatoApiKey.cs new file mode 100644 index 000000000..9fdfe6473 --- /dev/null +++ b/src/Ombi.Api.CouchPotato/Models/CouchPotatoApiKey.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Ombi.Api.CouchPotato.Models +{ + public class CouchPotatoApiKey + { + [JsonProperty("success")] + public bool success { get; set; } + [JsonProperty("api_key")] + public string ApiKey { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.CouchPotato/Models/CouchPotatoMovies.cs b/src/Ombi.Api.CouchPotato/Models/CouchPotatoMovies.cs new file mode 100644 index 000000000..a1b2619cf --- /dev/null +++ b/src/Ombi.Api.CouchPotato/Models/CouchPotatoMovies.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; + +namespace Ombi.Api.CouchPotato.Models +{ + public class CouchPotatoMovies + { + public List movies { get; set; } + public int total { get; set; } + public bool success { get; set; } + public bool empty { get; set; } + } + + public class Movie + { + public string _id { get; set; } + public string _rev { get; set; } + public string _t { get; set; } + public object category_id { get; set; } + public Files files { get; set; } + public Identifiers identifiers { get; set; } + public Info info { get; set; } + public int last_edit { get; set; } + public string profile_id { get; set; } + public List releases { get; set; } + public string status { get; set; } + public List tags { get; set; } + public string title { get; set; } + public string type { get; set; } + } + public class CouchPotatoAdd + { + public Movie movie { get; set; } + public bool success { get; set; } + } + + public class Rating + { + public List imdb { get; set; } + } + + public class Images + { + public List actors { get; set; } + public List backdrop { get; set; } + public List backdrop_original { get; set; } + public List banner { get; set; } + public List clear_art { get; set; } + public List disc_art { get; set; } + public List extra_fanart { get; set; } + public List extra_thumbs { get; set; } + public List landscape { get; set; } + public List logo { get; set; } + public List poster { get; set; } + public List poster_original { get; set; } + } + + public class Info + { + public List actor_roles { get; set; } + public List actors { get; set; } + public List directors { get; set; } + public List genres { get; set; } + public Images images { get; set; } + public string imdb { get; set; } + public string mpaa { get; set; } + public string original_title { get; set; } + public string plot { get; set; } + public Rating rating { get; set; } + public Release_Date release_date { get; set; } + public string released { get; set; } + public int runtime { get; set; } + public string tagline { get; set; } + public List titles { get; set; } + public int tmdb_id { get; set; } + public string type { get; set; } + public bool via_imdb { get; set; } + public bool via_tmdb { get; set; } + public List writers { get; set; } + public int year { get; set; } + } + + public class Release_Date + { + public bool bluray { get; set; } + public int dvd { get; set; } + public int expires { get; set; } + public int theater { get; set; } + } + + public class Files + { + public List image_poster { get; set; } + } + + public class Identifiers + { + public string imdb { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.CouchPotato/Models/CouchPotatoProfiles.cs b/src/Ombi.Api.CouchPotato/Models/CouchPotatoProfiles.cs new file mode 100644 index 000000000..44b1b817d --- /dev/null +++ b/src/Ombi.Api.CouchPotato/Models/CouchPotatoProfiles.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Ombi.Api.CouchPotato.Models +{ + public class ProfileList + { + public bool core { get; set; } + public bool hide { get; set; } + public string _rev { get; set; } + public List finish { get; set; } + public List qualities { get; set; } + public string _id { get; set; } + public string _t { get; set; } + public string label { get; set; } + public int minimum_score { get; set; } + public List stop_after { get; set; } + public List wait_for { get; set; } + public int order { get; set; } + [JsonProperty(PropertyName = "3d")] + public List threeD { get; set; } + } + + public class CouchPotatoProfiles + { + public List list { get; set; } + public bool success { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.CouchPotato/Models/CouchPotatoStatus.cs b/src/Ombi.Api.CouchPotato/Models/CouchPotatoStatus.cs new file mode 100644 index 000000000..4721f221f --- /dev/null +++ b/src/Ombi.Api.CouchPotato/Models/CouchPotatoStatus.cs @@ -0,0 +1,7 @@ +namespace Ombi.Api.CouchPotato.Models +{ + public class CouchPotatoStatus + { + public bool success { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj new file mode 100644 index 000000000..a3651df3c --- /dev/null +++ b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Ombi.Api/Api.cs b/src/Ombi.Api/Api.cs index de1ec8caf..354f90d91 100644 --- a/src/Ombi.Api/Api.cs +++ b/src/Ombi.Api/Api.cs @@ -52,6 +52,7 @@ namespace Ombi.Api var receivedString = await data.ReadAsStringAsync(); if (request.ContentType == ContentType.Json) { + request.OnBeforeDeserialization?.Invoke(receivedString); return JsonConvert.DeserializeObject(receivedString, Settings); } else diff --git a/src/Ombi.Api/Request.cs b/src/Ombi.Api/Request.cs index 67453faf3..16dd38055 100644 --- a/src/Ombi.Api/Request.cs +++ b/src/Ombi.Api/Request.cs @@ -25,6 +25,8 @@ namespace Ombi.Api public string BaseUrl { get; } public HttpMethod HttpMethod { get; } + public Action OnBeforeDeserialization { get; set; } + private string FullUrl { get diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index e2652edeb..ab5684306 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -64,7 +64,7 @@ namespace Ombi.Core.Engine RequestType = RequestType.Movie, Overview = movieInfo.Overview, ImdbId = movieInfo.ImdbId, - PosterPath = movieInfo.PosterPath, + PosterPath = movieInfo.PosterPath.TrimStart('/'), Title = movieInfo.Title, ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate) ? DateTime.Parse(movieInfo.ReleaseDate) diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index ef3638741..da64e99b6 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -10,9 +10,7 @@ using System.Linq; using System.Security.Principal; using System.Threading.Tasks; using Ombi.Core.Rule.Interfaces; -using StackExchange.Profiling; using Microsoft.Extensions.Caching.Memory; -using Ombi.Api.Trakt; using Ombi.Core.Authentication; using Ombi.Helpers; @@ -55,22 +53,14 @@ namespace Ombi.Core.Engine /// public async Task> Search(string search) { - using (MiniProfiler.Current.Step("Starting Movie Search Engine")) - using (MiniProfiler.Current.Step("Searching Movie")) + var result = await MovieApi.SearchMovie(search); + + if (result != null) { - var result = await MovieApi.SearchMovie(search); - - using (MiniProfiler.Current.Step("Fin API, Transforming")) - { - if (result != null) - { - Logger.LogDebug("Search Result: {result}", result); - return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API - } - } - - return null; + Logger.LogDebug("Search Result: {result}", result); + return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } + return null; } /// @@ -174,7 +164,7 @@ namespace Ombi.Core.Engine // So set the ImdbId to viewMovie.Id and then set it back afterwards var oldId = viewMovie.Id; viewMovie.CustomId = viewMovie.ImdbId ?? string.Empty; - + await RunSearchRules(viewMovie); viewMovie.Id = oldId; diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index cf5d1ef3c..8cfae9506 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -15,11 +15,7 @@ using System.Linq; using System.Security.Principal; using System.Threading.Tasks; using Ombi.Core.Rule.Interfaces; -using Ombi.Store.Entities.Requests; using Ombi.Store.Repository.Requests; -using Ombi.Store.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Ombi.Core.Authentication; using Ombi.Helpers; @@ -117,11 +113,7 @@ namespace Ombi.Core.Engine }); } } - - var existingRequests = await GetTvRequests(); - var plexSettings = await PlexSettings.GetSettingsAsync(); - var embySettings = await EmbySettings.GetSettingsAsync(); - return await ProcessResult(mapped, existingRequests, plexSettings, embySettings); + return await ProcessResult(mapped); } public async Task> GetShowInformationTreeNode(int tvdbid) @@ -189,127 +181,21 @@ namespace Ombi.Core.Engine }; } - private async Task> ProcessResults(IEnumerable items) { - var existingRequests = await GetTvRequests(); - - var plexSettings = await PlexSettings.GetSettingsAsync(); - var embySettings = await EmbySettings.GetSettingsAsync(); - var retVal = new List(); foreach (var tvMazeSearch in items) { var viewT = Mapper.Map(tvMazeSearch); - retVal.Add(await ProcessResult(viewT, existingRequests, plexSettings, embySettings)); + retVal.Add(await ProcessResult(viewT)); } return retVal; } - private async Task ProcessResult(SearchTvShowViewModel item, Dictionary existingRequests, PlexSettings plexSettings, EmbySettings embySettings) + private async Task ProcessResult(SearchTvShowViewModel item) { - if (embySettings.Enable) - { - var content = await EmbyContentRepo.Get(item.Id.ToString()); - - if (content != null) - { - item.Available = true; - } - - // Let's go through the episodes now - if (item.SeasonRequests.Any()) - { - var allEpisodes = EmbyContentRepo.GetAllEpisodes().Include(x => x.Series); - foreach (var season in item.SeasonRequests) - { - foreach (var episode in season.Episodes) - { - var epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && item.Id.ToString() == x.Series.ProviderId); - if (epExists != null) - { - episode.Available = true; - } - } - } - } - } - if (plexSettings.Enable) - { - var content = await PlexContentRepo.Get(item.Id.ToString()); - - if (content != null) - { - item.Available = true; - item.PlexUrl = content.Url; - } - // Let's go through the episodes now - if (item.SeasonRequests.Any()) - { - var allEpisodes = PlexContentRepo.GetAllEpisodes(); - foreach (var season in item.SeasonRequests) - { - foreach (var episode in season.Episodes) - { - var epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.Series.ProviderId == item.Id.ToString()); - if (epExists != null) - { - episode.Available = true; - } - } - } - } - } - - if (item.SeasonRequests.Any() && item.SeasonRequests.All(x => x.Episodes.All(e => e.Approved))) - { - item.FullyAvailable = true; - } - - if (item.Id > 0) - { - var tvdbid = item.Id; - if (existingRequests.ContainsKey(tvdbid)) - { - var existingRequest = existingRequests[tvdbid]; - - item.Requested = true; - item.Approved = existingRequest.ChildRequests.Any(x => x.Approved); - - // Let's modify the seasonsrequested to reflect what we have requested... - foreach (var season in item.SeasonRequests) - { - foreach (var existingRequestChildRequest in existingRequest.ChildRequests) - { - // Find the existing request season - var existingSeason = - existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber); - if (existingSeason == null) continue; - - foreach (var ep in existingSeason.Episodes) - { - // Find the episode from what we are searching - var episodeSearching = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == ep.EpisodeNumber); - if (episodeSearching == null) - { - continue; - } - episodeSearching.Requested = true; - episodeSearching.Available = ep.Available; - episodeSearching.Approved = ep.Season.ChildRequest.Approved; - } - } - } - } - // TODO CHECK SONARR/RADARR - //if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid)) - // // compare to the sonarr/sickrage db - //{ - // item.Requested = true; - //} - } + item.CustomId = item.Id.ToString(); + await RunSearchRules(item); return item; } diff --git a/src/Ombi.Core/Models/Search/SearchMovieViewModel.cs b/src/Ombi.Core/Models/Search/SearchMovieViewModel.cs index 73732b4b5..42952cd73 100644 --- a/src/Ombi.Core/Models/Search/SearchMovieViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchMovieViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Ombi.Store.Entities; namespace Ombi.Core.Models.Search { @@ -22,5 +23,8 @@ namespace Ombi.Core.Models.Search public string Trailer { get; set; } public string Homepage { get; set; } public string ImdbId { get; set; } + public int RootPathOverride { get; set; } + public int QualityOverride { get; set; } + public override RequestType Type => RequestType.Movie; } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs index c7bbac246..90f32eda6 100644 --- a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs @@ -1,6 +1,7 @@ using Ombi.Core.Models.Requests; using Ombi.Store.Repository.Requests; using System.Collections.Generic; +using Ombi.Store.Entities; namespace Ombi.Core.Models.Search { @@ -54,5 +55,7 @@ namespace Ombi.Core.Models.Search /// This is where we have EVERY Episode for that series /// public bool FullyAvailable { get; set; } + + public override RequestType Type => RequestType.TvShow; } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/SearchViewModel.cs b/src/Ombi.Core/Models/Search/SearchViewModel.cs index fec2af075..2771d1be8 100644 --- a/src/Ombi.Core/Models/Search/SearchViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchViewModel.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations.Schema; +using Ombi.Store.Entities; namespace Ombi.Core.Models.Search { - public class SearchViewModel + public abstract class SearchViewModel { public int Id { get; set; } public bool Approved { get; set; } @@ -10,6 +11,7 @@ namespace Ombi.Core.Models.Search public bool Available { get; set; } public string PlexUrl { get; set; } public string Quality { get; set; } + public abstract RequestType Type { get; } /// diff --git a/src/Ombi.Core/Rule/Interfaces/IRules.cs b/src/Ombi.Core/Rule/Interfaces/IRules.cs index a46749cd1..020991502 100644 --- a/src/Ombi.Core/Rule/Interfaces/IRules.cs +++ b/src/Ombi.Core/Rule/Interfaces/IRules.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -using Ombi.Core.Models.Requests; namespace Ombi.Core.Rule.Interfaces { - public interface IRules where T : new() + public interface IRules { Task Execute(T obj); } diff --git a/src/Ombi.Core/Rule/RuleEvaluator.cs b/src/Ombi.Core/Rule/RuleEvaluator.cs index 2f3b96b2a..a0c272c00 100644 --- a/src/Ombi.Core/Rule/RuleEvaluator.cs +++ b/src/Ombi.Core/Rule/RuleEvaluator.cs @@ -73,7 +73,7 @@ namespace Ombi.Core.Rule } - private void GetTypes(IServiceProvider provider, Assembly ass, string baseSearchType, List> ruleList) where T : new() + private void GetTypes(IServiceProvider provider, Assembly ass, string baseSearchType, List> ruleList) { foreach (var ti in ass.DefinedTypes) { diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 44670e7f8..20ad75b19 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -1,6 +1,9 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; using Ombi.Store.Repository; namespace Ombi.Core.Rule.Rules.Search @@ -20,6 +23,28 @@ namespace Ombi.Core.Rule.Rules.Search if (item != null) { obj.Available = true; + + if (obj.Type == RequestType.TvShow) + { + var searchResult = (SearchTvShowViewModel)obj; + // Let's go through the episodes now + if (searchResult.SeasonRequests.Any()) + { + var allEpisodes = EmbyContentRepository.GetAllEpisodes().Include(x => x.Series); + foreach (var season in searchResult.SeasonRequests) + { + foreach (var episode in season.Episodes) + { + var epExists = await allEpisodes.FirstOrDefaultAsync(x => + x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && item.ProviderId.ToString() == x.Series.ProviderId); + if (epExists != null) + { + episode.Available = true; + } + } + } + } + } } return Success(); } diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index b84f9c652..4a5faad6b 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -21,28 +22,75 @@ namespace Ombi.Core.Rule.Rules.Search public Task Execute(SearchViewModel obj) { - var movieRequests = Movie.GetRequest(obj.Id); - if (movieRequests != null) // Do we already have a request for this? + if (obj.Type == RequestType.Movie) { + var movieRequests = Movie.GetRequest(obj.Id); + if (movieRequests != null) // Do we already have a request for this? + { - obj.Requested = true; - obj.Approved = movieRequests.Approved; - obj.Available = movieRequests.Available; + obj.Requested = true; + obj.Approved = movieRequests.Approved; + obj.Available = movieRequests.Available; + return Task.FromResult(Success()); + } return Task.FromResult(Success()); } - - var tvRequests = Tv.GetRequest(obj.Id); - if (tvRequests != null) // Do we already have a request for this? + else { + //var tvRequests = Tv.GetRequest(obj.Id); + //if (tvRequests != null) // Do we already have a request for this? + //{ + + // obj.Requested = true; + // obj.Approved = tvRequests.ChildRequests.Any(x => x.Approved); + // obj.Available = tvRequests.ChildRequests.Any(x => x.Available); + + // return Task.FromResult(Success()); + //} + + var request = (SearchTvShowViewModel) obj; + var tvRequests = Tv.GetRequest(obj.Id); + if (tvRequests != null) // Do we already have a request for this? + { + + request.Requested = true; + request.Approved = tvRequests.ChildRequests.Any(x => x.Approved); + + // Let's modify the seasonsrequested to reflect what we have requested... + foreach (var season in request.SeasonRequests) + { + foreach (var existingRequestChildRequest in tvRequests.ChildRequests) + { + // Find the existing request season + var existingSeason = + existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber); + if (existingSeason == null) continue; + + foreach (var ep in existingSeason.Episodes) + { + // Find the episode from what we are searching + var episodeSearching = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == ep.EpisodeNumber); + if (episodeSearching == null) + { + continue; + } + episodeSearching.Requested = true; + episodeSearching.Available = ep.Available; + episodeSearching.Approved = ep.Season.ChildRequest.Approved; + } + } + } + } + + if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Approved))) + { + request.FullyAvailable = true; + } - obj.Requested = true; - obj.Approved = tvRequests.ChildRequests.Any(x => x.Approved); - obj.Available = tvRequests.ChildRequests.Any(x => x.Available); return Task.FromResult(Success()); } - return Task.FromResult(Success()); } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs index c293e00be..1b406cc93 100644 --- a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs @@ -1,6 +1,9 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; using Ombi.Store.Repository; namespace Ombi.Core.Rule.Rules.Search @@ -22,6 +25,29 @@ namespace Ombi.Core.Rule.Rules.Search obj.Available = true; obj.PlexUrl = item.Url; obj.Quality = item.Quality; + + if (obj.Type == RequestType.TvShow) + { + var search = (SearchTvShowViewModel)obj; + // Let's go through the episodes now + if (search.SeasonRequests.Any()) + { + var allEpisodes = PlexContentRepository.GetAllEpisodes(); + foreach (var season in search.SeasonRequests) + { + foreach (var episode in season.Episodes) + { + var epExists = await allEpisodes.FirstOrDefaultAsync(x => + x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && + x.Series.ProviderId == item.ProviderId.ToString()); + if (epExists != null) + { + episode.Available = true; + } + } + } + } + } } return Success(); } diff --git a/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs index b2187e206..1c936012a 100644 --- a/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/RadarrCacheRule.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Context; +using Ombi.Store.Entities; namespace Ombi.Core.Rule.Rules.Search { @@ -18,13 +19,16 @@ namespace Ombi.Core.Rule.Rules.Search public async Task Execute(SearchViewModel obj) { - // Check if it's in Radarr - var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id); - if (result != null) + if (obj.Type == RequestType.Movie) { - obj.Approved = true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something? + // Check if it's in Radarr + var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id); + if (result != null) + { + obj.Approved = + true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something? + } } - return Success(); } } diff --git a/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs new file mode 100644 index 000000000..d444222cf --- /dev/null +++ b/src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Models.Search; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Core.Rule.Rules.Search +{ + public class SonarrCacheRule : BaseSearchRule, IRules + { + public SonarrCacheRule(IOmbiContext ctx) + { + _ctx = ctx; + } + + private readonly IOmbiContext _ctx; + + public async Task Execute(SearchViewModel obj) + { + if (obj.Type == RequestType.TvShow) + { + // Check if it's in Radarr + var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == obj.Id); + if (result != null) + { + obj.Approved = + true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something? + } + } + return Success(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Senders/IMovieSender.cs b/src/Ombi.Core/Senders/IMovieSender.cs index 4ed06b429..28e29cab0 100644 --- a/src/Ombi.Core/Senders/IMovieSender.cs +++ b/src/Ombi.Core/Senders/IMovieSender.cs @@ -5,6 +5,6 @@ namespace Ombi.Core { public interface IMovieSender { - Task Send(MovieRequests model, string qualityId = ""); + Task Send(MovieRequests model); } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 051081df2..bb5738978 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -1,4 +1,5 @@ -using Ombi.Core.Settings; +using System.Linq; +using Ombi.Core.Settings; using Ombi.Settings.Settings.Models.External; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -21,7 +22,7 @@ namespace Ombi.Core private IRadarrApi RadarrApi { get; } private ILogger Log { get; } - public async Task Send(MovieRequests model, string qualityId = "") + public async Task Send(MovieRequests model) { //var cpSettings = await CouchPotatoSettings.GetSettingsAsync(); //var watcherSettings = await WatcherSettings.GetSettingsAsync(); @@ -39,7 +40,7 @@ namespace Ombi.Core if (radarrSettings.Enabled) { - return await SendToRadarr(model, radarrSettings, qualityId); + return await SendToRadarr(model, radarrSettings); } return new MovieSenderResult @@ -49,22 +50,16 @@ namespace Ombi.Core }; } - private async Task SendToRadarr(MovieRequests model, RadarrSettings settings, string qualityId) + private async Task SendToRadarr(MovieRequests model, RadarrSettings settings) { - var qualityProfile = 0; - if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality + var qualityToUse = int.Parse(settings.DefaultQualityProfile); + if (model.QualityOverride > 0) { - int.TryParse(qualityId, out qualityProfile); + qualityToUse = model.QualityOverride; } - if (qualityProfile <= 0) - { - int.TryParse(settings.DefaultQualityProfile, out qualityProfile); - } - - //var rootFolderPath = model.RootFolderSelected <= 0 ? settings.FullRootPath : GetRootPath(model.RootFolderSelected, settings); - var rootFolderPath = settings.DefaultRootPath; // TODO Allow changing in the UI - var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityProfile, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, settings.MinimumAvailability); + var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings); + var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, settings.MinimumAvailability); if (!string.IsNullOrEmpty(result.Error?.message)) { @@ -77,5 +72,12 @@ namespace Ombi.Core } return new MovieSenderResult { Success = true, MovieSent = false }; } + + private async Task RadarrRootPath(int overrideId, RadarrSettings settings) + { + var paths = await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + var selectedPath = paths.FirstOrDefault(x => x.id == overrideId); + return selectedPath.path; + } } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 46fa14470..574cf6978 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -8,6 +8,7 @@ using Ombi.Api.Sonarr.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; using Ombi.Store.Entities.Requests; namespace Ombi.Core.Senders diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 1be7d1dc5..02eae541b 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -1,9 +1,11 @@ using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Security.Principal; using Hangfire; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -31,6 +33,7 @@ using Ombi.Store.Repository; using Ombi.Notifications.Agents; using Ombi.Schedule.Jobs.Radarr; using Ombi.Api; +using Ombi.Api.CouchPotato; using Ombi.Api.FanartTv; using Ombi.Api.Mattermost; using Ombi.Api.Pushbullet; @@ -42,6 +45,7 @@ using Ombi.Core.Senders; using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex; +using Ombi.Schedule.Jobs.Sonarr; using Ombi.Schedule.Ombi; using Ombi.Store.Repository.Requests; using PlexContentCacher = Ombi.Schedule.Jobs.Plex.PlexContentCacher; @@ -94,12 +98,12 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } - public static void RegisterStore(this IServiceCollection services) - { + public static void RegisterStore(this IServiceCollection services) { services.AddEntityFrameworkSqlite().AddDbContext(); - + services.AddScoped(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6 services.AddTransient(); services.AddTransient(); @@ -142,6 +146,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 124d81439..8dc69d646 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Ombi.Helpers/CacheKeys.cs b/src/Ombi.Helpers/CacheKeys.cs index f1c64fa38..db66d4e13 100644 --- a/src/Ombi.Helpers/CacheKeys.cs +++ b/src/Ombi.Helpers/CacheKeys.cs @@ -16,5 +16,7 @@ namespace Ombi.Helpers public const string TopRatedMovies = nameof(TopRatedMovies); public const string UpcomingMovies = nameof(UpcomingMovies); public const string NowPlayingMovies = nameof(NowPlayingMovies); + public const string RadarrRootProfiles = nameof(RadarrRootProfiles); + public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles); } } diff --git a/src/Ombi.Helpers/LoggingEvents.cs b/src/Ombi.Helpers/LoggingEvents.cs index e4a349049..d74ca2556 100644 --- a/src/Ombi.Helpers/LoggingEvents.cs +++ b/src/Ombi.Helpers/LoggingEvents.cs @@ -8,13 +8,15 @@ namespace Ombi.Helpers public static EventId Api => new EventId(1000); public static EventId RadarrApi => new EventId(1001); + public static EventId CouchPotatoApi => new EventId(1002); public static EventId Cacher => new EventId(2000); public static EventId RadarrCacher => new EventId(2001); - public static EventId PlexEpisodeCacher => new EventId(2001); - public static EventId EmbyContentCacher => new EventId(2002); - public static EventId PlexUserImporter => new EventId(2003); - public static EventId EmbyUserImporter => new EventId(2004); + public static EventId PlexEpisodeCacher => new EventId(2002); + public static EventId EmbyContentCacher => new EventId(2003); + public static EventId PlexUserImporter => new EventId(2004); + public static EventId EmbyUserImporter => new EventId(2005); + public static EventId SonarrCacher => new EventId(2006); public static EventId MovieSender => new EventId(3000); diff --git a/src/Ombi.Helpers/StoragePathSingleton.cs b/src/Ombi.Helpers/StoragePathSingleton.cs new file mode 100644 index 000000000..c502c4a54 --- /dev/null +++ b/src/Ombi.Helpers/StoragePathSingleton.cs @@ -0,0 +1,13 @@ +namespace Ombi.Helpers +{ + public class StoragePathSingleton + { + private static StoragePathSingleton instance; + + private StoragePathSingleton() { } + + public static StoragePathSingleton Instance => instance ?? (instance = new StoragePathSingleton()); + + public string StoragePath { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 9e48e771e..92cd8bdf2 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -42,7 +42,7 @@ namespace Ombi.Notifications.Agents try { var a = settings.Token; - var b = settings.WebookId; + var b = settings.WebHookId; } catch (IndexOutOfRangeException) { @@ -164,7 +164,7 @@ namespace Ombi.Notifications.Agents }; } - await Api.SendMessage(discordBody, settings.WebookId, settings.Token); + await Api.SendMessage(discordBody, settings.WebHookId, settings.Token); } catch (Exception e) { diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 6b13a10bb..c0fb6d5f8 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -3,6 +3,7 @@ using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Plex; using Ombi.Schedule.Jobs.Radarr; +using Ombi.Schedule.Jobs.Sonarr; using Ombi.Schedule.Ombi; namespace Ombi.Schedule @@ -11,7 +12,7 @@ namespace Ombi.Schedule { public JobSetup(IPlexContentCacher plexContentCacher, IRadarrCacher radarrCacher, IOmbiAutomaticUpdater updater, IEmbyContentCacher embyCacher, IPlexUserImporter userImporter, - IEmbyUserImporter embyUserImporter) + IEmbyUserImporter embyUserImporter, ISonarrCacher cache) { PlexContentCacher = plexContentCacher; RadarrCacher = radarrCacher; @@ -19,6 +20,7 @@ namespace Ombi.Schedule EmbyContentCacher = embyCacher; PlexUserImporter = userImporter; EmbyUserImporter = embyUserImporter; + SonarrCacher = cache; } private IPlexContentCacher PlexContentCacher { get; } @@ -27,17 +29,17 @@ namespace Ombi.Schedule private IPlexUserImporter PlexUserImporter { get; } private IEmbyContentCacher EmbyContentCacher { get; } private IEmbyUserImporter EmbyUserImporter { get; } + private ISonarrCacher SonarrCacher { get; } public void Setup() { RecurringJob.AddOrUpdate(() => PlexContentCacher.CacheContent(), Cron.Hourly(20)); RecurringJob.AddOrUpdate(() => EmbyContentCacher.Start(), Cron.Hourly(5)); RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(10)); - RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily(1)); + RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(15)); + RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily(5)); RecurringJob.AddOrUpdate(() => EmbyUserImporter.Start(), Cron.Daily); - RecurringJob.AddOrUpdate(() => Updater.Update(null), Cron.Daily(3)); - - //BackgroundJob.Enqueue(() => PlexUserImporter.Start()); + RecurringJob.AddOrUpdate(() => Updater.Update(null), Cron.HourInterval(6)); } } } diff --git a/src/Ombi.Schedule/Jobs/Radarr/RadarrCacher.cs b/src/Ombi.Schedule/Jobs/Radarr/RadarrCacher.cs index fb42be325..9667af648 100644 --- a/src/Ombi.Schedule/Jobs/Radarr/RadarrCacher.cs +++ b/src/Ombi.Schedule/Jobs/Radarr/RadarrCacher.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -28,8 +29,11 @@ namespace Ombi.Schedule.Jobs.Radarr private ILogger Logger { get; } private readonly IOmbiContext _ctx; + private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); + public async Task CacheContent() { + await SemaphoreSlim.WaitAsync(); try { var settings = RadarrSettings.GetSettings(); @@ -48,7 +52,7 @@ namespace Ombi.Schedule.Jobs.Radarr { if (m.tmdbId > 0) { - movieIds.Add(new RadarrCache { TheMovieDbId = m.tmdbId }); + movieIds.Add(new RadarrCache {TheMovieDbId = m.tmdbId}); } else { @@ -70,6 +74,10 @@ namespace Ombi.Schedule.Jobs.Radarr { Logger.LogInformation(LoggingEvents.RadarrCacher, "Radarr is not setup, cannot cache episodes"); } + finally + { + SemaphoreSlim.Release(); + } } public async Task> GetCachedContent() diff --git a/src/Ombi.Schedule/Jobs/Sonarr/ISonarrCacher.cs b/src/Ombi.Schedule/Jobs/Sonarr/ISonarrCacher.cs new file mode 100644 index 000000000..e3b968784 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Sonarr/ISonarrCacher.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Sonarr +{ + public interface ISonarrCacher + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs b/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs new file mode 100644 index 000000000..d2687f432 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs @@ -0,0 +1,117 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Ombi.Api.Sonarr; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Context; +using Ombi.Store.Entities; + +namespace Ombi.Schedule.Jobs.Sonarr +{ + public class SonarrCacher : ISonarrCacher + { + public SonarrCacher(ISettingsService s, ISonarrApi api, ILogger l, IOmbiContext ctx) + { + _settings = s; + _api = api; + _log = l; + } + + private readonly ISettingsService _settings; + private readonly ISonarrApi _api; + private readonly ILogger _log; + private readonly IOmbiContext _ctx; + + private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); + public async Task Start() + { + await SemaphoreSlim.WaitAsync(); + try + { + var settings = await _settings.GetSettingsAsync(); + if (!settings.Enabled) + { + return; + } + var series = await _api.GetSeries(settings.ApiKey, settings.FullUri); + if (series != null) + { + var ids = series.Select(x => x.tvdbId); + + await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrCache"); + var entites = ids.Select(id => new SonarrCache {TvDbId = id}).ToList(); + + await _ctx.SonarrCache.AddRangeAsync(entites); + await _ctx.SaveChangesAsync(); + } + } + catch (Exception e) + { + _log.LogError(LoggingEvents.SonarrCacher, e, "Exception when trying to cache Sonarr"); + } + finally + { + SemaphoreSlim.Release(); + } + } + + + //public void Queued() + //{ + // var settings = SonarrSettings.GetSettings(); + // if (settings.Enabled) + // { + // Job.SetRunning(true, JobNames.SonarrCacher); + // try + // { + // var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri); + // if (series != null) + // { + // Cache.Set(CacheKeys.SonarrQueued, series, CacheKeys.TimeFrameMinutes.SchedulerCaching); + // } + // } + // catch (System.Exception ex) + // { + // Log.Error(ex, "Failed caching queued items from Sonarr"); + // } + // finally + // { + // Job.Record(JobNames.SonarrCacher); + // Job.SetRunning(false, JobNames.SonarrCacher); + // } + // } + //} + + //// we do not want to set here... + //public IEnumerable QueuedIds() + //{ + // var result = new List(); + + // var series = Cache.Get>(CacheKeys.SonarrQueued); + // if (series != null) + // { + // foreach (var s in series) + // { + // var cached = new SonarrCachedResult { TvdbId = s.tvdbId }; + // foreach (var season in s.seasons) + // { + // cached.Seasons.Add(new SonarrSeasons + // { + // SeasonNumber = season.seasonNumber, + // Monitored = season.monitored + // }); + // } + + // result.Add(cached); + // } + // } + // return result; + //} + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 328194252..04106e767 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -23,6 +23,7 @@ + diff --git a/src/Ombi.Settings/Settings/Models/External/CouchPotatoSettings.cs b/src/Ombi.Settings/Settings/Models/External/CouchPotatoSettings.cs new file mode 100644 index 000000000..19a79e1d7 --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/External/CouchPotatoSettings.cs @@ -0,0 +1,13 @@ +using Ombi.Core.Settings.Models.External; + +namespace Ombi.Settings.Settings.Models.External +{ + public class CouchPotatoSettings : ExternalSettings + { + public bool Enabled { get; set; } + public string ApiKey { get; set; } + public string DefaultProfileId { get; set; } + public string Username { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs index 5601ca20e..96df77281 100644 --- a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Ombi.Settings.Settings.Models.External; namespace Ombi.Core.Settings.Models.External { diff --git a/src/Ombi.Settings/Settings/Models/External/ExternalSettings.cs b/src/Ombi.Settings/Settings/Models/External/ExternalSettings.cs index 6504a32ba..e93f36483 100644 --- a/src/Ombi.Settings/Settings/Models/External/ExternalSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/ExternalSettings.cs @@ -1,10 +1,9 @@ -using System; -using Newtonsoft.Json; +using Newtonsoft.Json; using Ombi.Helpers; -namespace Ombi.Core.Settings.Models.External +namespace Ombi.Settings.Settings.Models.External { - public abstract class ExternalSettings : Ombi.Settings.Settings.Models.Settings + public abstract class ExternalSettings : Models.Settings { public bool Ssl { get; set; } public string SubDir { get; set; } diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index 94963a03d..79a70c2f6 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Ombi.Settings.Settings.Models.External; namespace Ombi.Core.Settings.Models.External { diff --git a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs index 6b2b0d3b0..789b8f384 100644 --- a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs @@ -1,4 +1,4 @@ -namespace Ombi.Core.Settings.Models.External +namespace Ombi.Settings.Settings.Models.External { public class SonarrSettings : ExternalSettings { diff --git a/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs index 3352d7153..2616fa4c3 100644 --- a/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs @@ -10,7 +10,7 @@ namespace Ombi.Settings.Settings.Models.Notifications public string Username { get; set; } [JsonIgnore] - public string WebookId => SplitWebUrl(4); + public string WebHookId => SplitWebUrl(4); [JsonIgnore] public string Token => SplitWebUrl(5); diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index f4a7910e1..63612ea8c 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -33,6 +33,7 @@ namespace Ombi.Store.Context DbSet MovieIssues { get; set; } DbSet TvIssues { get; set; } DbSet Tokens { get; set; } + DbSet SonarrCache { get; set; } EntityEntry Update(object entity); EntityEntry Update(TEntity entity) where TEntity : class; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 257e6a3c8..138d6806c 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; @@ -14,12 +15,10 @@ namespace Ombi.Store.Context public OmbiContext() { if (_created) return; - + _created = true; Database.Migrate(); - // Add the notifcation templates - } public DbSet NotificationTemplates { get; set; } @@ -38,12 +37,14 @@ namespace Ombi.Store.Context public DbSet Audit { get; set; } public DbSet Tokens { get; set; } + public DbSet SonarrCache { get; set; } public DbSet ApplicationConfigurations { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseSqlite("Data Source=Ombi.db"); + var i = StoragePathSingleton.Instance; + optionsBuilder.UseSqlite($"Data Source={Path.Combine(i.StoragePath,"Ombi.db")}"); } protected override void OnModelCreating(ModelBuilder builder) diff --git a/src/Ombi.Store/Entities/Requests/MovieRequests.cs b/src/Ombi.Store/Entities/Requests/MovieRequests.cs index 11b12c7f6..680a1c8bb 100644 --- a/src/Ombi.Store/Entities/Requests/MovieRequests.cs +++ b/src/Ombi.Store/Entities/Requests/MovieRequests.cs @@ -11,5 +11,8 @@ namespace Ombi.Store.Entities.Requests public int? IssueId { get; set; } [ForeignKey(nameof(IssueId))] public List Issues { get; set; } + + public int RootPathOverride { get; set; } + public int QualityOverride { get; set; } } } diff --git a/src/Ombi.Store/Entities/SonarrCache.cs b/src/Ombi.Store/Entities/SonarrCache.cs new file mode 100644 index 000000000..33e06f7d9 --- /dev/null +++ b/src/Ombi.Store/Entities/SonarrCache.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("SonarrCache")] + public class SonarrCache : Entity + { + public int TvDbId { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.Designer.cs b/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.Designer.cs new file mode 100644 index 000000000..9ea6c0745 --- /dev/null +++ b/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.Designer.cs @@ -0,0 +1,747 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20171002113357_SonarrCacher")] + partial class SonarrCacher + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ProviderId"); + + b.Property("Title"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("Key"); + + b.Property("ProviderId"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("Title"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("MovieId"); + + b.Property("Subect"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("Subect"); + + b.Property("TvId"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("TvId"); + + b.ToTable("TvIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexContent") + .WithMany("Seasons") + .HasForeignKey("PlexContentId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "Child") + .WithMany() + .HasForeignKey("TvId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.cs b/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.cs new file mode 100644 index 000000000..36bdbca20 --- /dev/null +++ b/src/Ombi.Store/Migrations/20171002113357_SonarrCacher.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class SonarrCacher : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "QualityOverride", + table: "MovieRequests", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "RootPathOverride", + table: "MovieRequests", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "SonarrCache", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + TvDbId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SonarrCache", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SonarrCache"); + + migrationBuilder.DropColumn( + name: "QualityOverride", + table: "MovieRequests"); + + migrationBuilder.DropColumn( + name: "RootPathOverride", + table: "MovieRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 1a46eb954..e744a7fd6 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -460,6 +460,8 @@ namespace Ombi.Store.Migrations b.Property("PosterPath"); + b.Property("QualityOverride"); + b.Property("ReleaseDate"); b.Property("RequestType"); @@ -468,6 +470,8 @@ namespace Ombi.Store.Migrations b.Property("RequestedUserId"); + b.Property("RootPathOverride"); + b.Property("Status"); b.Property("TheMovieDbId"); @@ -529,6 +533,18 @@ namespace Ombi.Store.Migrations b.ToTable("TvRequests"); }); + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => { b.Property("Id") diff --git a/src/Ombi.TheMovieDbApi/Models/MovieResponse.cs b/src/Ombi.TheMovieDbApi/Models/MovieResponse.cs index f619f647a..17aceca5d 100644 --- a/src/Ombi.TheMovieDbApi/Models/MovieResponse.cs +++ b/src/Ombi.TheMovieDbApi/Models/MovieResponse.cs @@ -45,8 +45,8 @@ namespace Ombi.TheMovieDbApi.Models public ProductionCompanies[] production_companies { get; set; } public ProductionCountries[] production_countries { get; set; } public string release_date { get; set; } - public int revenue { get; set; } - public int runtime { get; set; } + public float revenue { get; set; } + public float runtime { get; set; } public SpokenLanguages[] spoken_languages { get; set; } public string status { get; set; } public string tagline { get; set; } diff --git a/src/Ombi.TheMovieDbApi/Models/MovieResponseDTO.cs b/src/Ombi.TheMovieDbApi/Models/MovieResponseDTO.cs index b679ba5cf..0fd27a23f 100644 --- a/src/Ombi.TheMovieDbApi/Models/MovieResponseDTO.cs +++ b/src/Ombi.TheMovieDbApi/Models/MovieResponseDTO.cs @@ -15,8 +15,8 @@ public float Popularity { get; set; } public string PosterPath { get; set; } public string ReleaseDate { get; set; } - public int Revenue { get; set; } - public int Runtime { get; set; } + public float Revenue { get; set; } + public float Runtime { get; set; } public string Status { get; set; } public string Tagline { get; set; } public string Title { get; set; } diff --git a/src/Ombi.sln b/src/Ombi.sln index 111bfb95b..da9d4aac9 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -80,6 +80,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Pushover", "Ombi.A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Schedule.Tests", "Ombi.Schedule.Tests\Ombi.Schedule.Tests.csproj", "{BDD8B924-016E-4CDA-9FFA-50B0A34BCD3C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.CouchPotato", "Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj", "{87D7897D-7C73-4856-A0AA-FF5948F4EA86}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -202,6 +204,10 @@ Global {BDD8B924-016E-4CDA-9FFA-50B0A34BCD3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {BDD8B924-016E-4CDA-9FFA-50B0A34BCD3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {BDD8B924-016E-4CDA-9FFA-50B0A34BCD3C}.Release|Any CPU.Build.0 = Release|Any CPU + {87D7897D-7C73-4856-A0AA-FF5948F4EA86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87D7897D-7C73-4856-A0AA-FF5948F4EA86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87D7897D-7C73-4856-A0AA-FF5948F4EA86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87D7897D-7C73-4856-A0AA-FF5948F4EA86}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -230,6 +236,7 @@ Global {737B2620-FE5A-4135-A017-79C269A7D36C} = {9293CA11-360A-4C20-A674-B9E794431BF5} {CA55DD4F-4EFF-4906-A848-35FCC7BD5654} = {9293CA11-360A-4C20-A674-B9E794431BF5} {BDD8B924-016E-4CDA-9FFA-50B0A34BCD3C} = {6F42AB98-9196-44C4-B888-D5E409F415A1} + {87D7897D-7C73-4856-A0AA-FF5948F4EA86} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} diff --git a/src/Ombi/ClientApp/app/interfaces/ICouchPotato.ts b/src/Ombi/ClientApp/app/interfaces/ICouchPotato.ts new file mode 100644 index 000000000..fb22cd0c0 --- /dev/null +++ b/src/Ombi/ClientApp/app/interfaces/ICouchPotato.ts @@ -0,0 +1,25 @@ +export interface ICouchPotatoProfiles { + success: boolean; + list: IProfileList[]; +} + +export interface IProfileList { + core: boolean; + hide: boolean; + _rev: string; + finish: boolean[]; + qualities: string[]; + _id: string; + _t: string; + label: string; + minimum_score: number; + stop_after: number[]; + wait_for: object[]; + order: number; + threeD: object[]; +} + +export interface ICouchPotatoApiKey { + success: boolean; + apiKey: string; +} diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index 70aeed4b9..e67c63ed0 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -79,6 +79,8 @@ export interface IRequestGrid { export interface IMovieRequests extends IFullBaseRequest { theMovieDbId: number; + rootPathOverride: number; + qualityOverride: number; } export interface IFullBaseRequest extends IBaseRequest { diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index ce9dc5b37..678eb57df 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -119,3 +119,11 @@ export interface IAbout { processArchitecture: string; applicationBasePath: string; } + +export interface ICouchPotatoSettings extends IExternalSettings { + enabled: boolean; + apiKey: string; + defaultProfileId: string; + username: string; + password: string; +} diff --git a/src/Ombi/ClientApp/app/interfaces/index.ts b/src/Ombi/ClientApp/app/interfaces/index.ts index 791a20b19..21427ec8c 100644 --- a/src/Ombi/ClientApp/app/interfaces/index.ts +++ b/src/Ombi/ClientApp/app/interfaces/index.ts @@ -1,4 +1,5 @@ export * from "./ICommon"; +export * from "./ICouchPotato"; export * from "./IImages"; export * from "./IMediaServerStatus"; export * from "./INotificationSettings"; diff --git a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html index 2678f4bb9..4372394c3 100644 --- a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html +++ b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html @@ -41,7 +41,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/login/resetpassword.component.html b/src/Ombi/ClientApp/app/login/resetpassword.component.html index cd109ddff..c990da5dc 100644 --- a/src/Ombi/ClientApp/app/login/resetpassword.component.html +++ b/src/Ombi/ClientApp/app/login/resetpassword.component.html @@ -7,7 +7,7 @@ include the remember me checkbox
-
+

-
-
The Access Token is required
-
You can find this here: https://www.pushbullet.com/#settings/account
-
- -
+ + + The Access Token is required
diff --git a/src/Ombi/ClientApp/app/settings/notifications/pushover.component.html b/src/Ombi/ClientApp/app/settings/notifications/pushover.component.html index e07316583..0a03604e5 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/pushover.component.html +++ b/src/Ombi/ClientApp/app/settings/notifications/pushover.component.html @@ -13,14 +13,12 @@
-
-
The Access Token is required
-
+
-
- -
+ + + The Access Token is required
diff --git a/src/Ombi/ClientApp/app/settings/notifications/slack.component.html b/src/Ombi/ClientApp/app/settings/notifications/slack.component.html index be4504722..450838d2b 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/slack.component.html +++ b/src/Ombi/ClientApp/app/settings/notifications/slack.component.html @@ -14,17 +14,15 @@
-
-
The Webhook Url is required
-
- +
Click Here and follow the guide. You will then have a Webhook Url - + + The Webhook Url is required
diff --git a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html index 4ae9eade1..714355447 100644 --- a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html +++ b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html @@ -8,19 +8,6 @@
-
-
-
The IP/Hostname is required
-
The Port is required
-
The Api Key is required
-
-
-
A Default Quality Profile is required
-
A Default Root Path is required
-
A Default Minimum Availability is required
-
-
-
@@ -33,25 +20,24 @@
-
- -
+ + + The IP/Hostname is required
-
- -
+ + The Port is required
-
- -
+ + + The API Key is required
@@ -76,10 +62,11 @@
-
+ A Default Quality Profile is required
@@ -92,19 +79,22 @@
- -
+
+ A Default Root Path is required
-
+ + A Default Minimum Availability is required
diff --git a/src/Ombi/ClientApp/app/settings/settings.module.ts b/src/Ombi/ClientApp/app/settings/settings.module.ts index b3ef0a31a..938a2a7e0 100644 --- a/src/Ombi/ClientApp/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/app/settings/settings.module.ts @@ -8,10 +8,11 @@ import { ClipboardModule } from "ngx-clipboard/dist"; import { AuthGuard } from "../auth/auth.guard"; import { AuthModule } from "../auth/auth.module"; import { AuthService } from "../auth/auth.service"; -import { JobService, RadarrService, SonarrService, TesterService, ValidationService } from "../services"; +import { CouchPotatoService, JobService, RadarrService, SonarrService, TesterService, ValidationService } from "../services"; import { PipeModule } from "../pipes/pipe.module"; import { AboutComponent } from "./about/about.component"; +import { CouchPotatoComponent } from "./couchpotato/couchpotato.component"; import { CustomizationComponent } from "./customization/customization.component"; import { EmbyComponent } from "./emby/emby.component"; import { LandingPageComponent } from "./landingpage/landingpage.component"; @@ -51,6 +52,7 @@ const routes: Routes = [ { path: "Settings/Mattermost", component: MattermostComponent, canActivate: [AuthGuard] }, { path: "Settings/UserManagement", component: UserManagementComponent, canActivate: [AuthGuard] }, { path: "Settings/Update", component: UpdateComponent, canActivate: [AuthGuard] }, + { path: "Settings/CouchPotato", component: CouchPotatoComponent, canActivate: [AuthGuard] }, ]; @NgModule({ @@ -91,6 +93,7 @@ const routes: Routes = [ UpdateComponent, AboutComponent, WikiComponent, + CouchPotatoComponent, ], exports: [ RouterModule, @@ -103,6 +106,7 @@ const routes: Routes = [ ValidationService, TesterService, JobService, + CouchPotatoService, ], }) diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index 813b8c49d..2237891ba 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -39,7 +39,7 @@ Movies
-
-
-
The IP/Hostname is required
-
The Port is required
-
The Api Key is required
-
-
-
A Default Quality Profile is required
-
A Default Root Path is required
-
-
+
@@ -29,25 +19,24 @@
-
- -
+ + + The IP/Hostname is required
-
- -
+ + The Port is required
-
- -
+ + + The API Key is required
@@ -72,10 +61,12 @@
-
+ A Default Quality Profile is required +
@@ -88,10 +79,12 @@
-
+ A Default Root Path is required +
diff --git a/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.ts index 8fc1d24ca..42b06a1f2 100644 --- a/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.ts @@ -102,7 +102,7 @@ export class UserManagementComponent implements OnInit { public runImporter(): void { this.jobService.runPlexImporter().subscribe(); - this.jobService.runPlexImporter().subscribe(); + this.jobService.runEmbyImporter().subscribe(); } private filter(query: string, users: IUsersModel[]): IUsersModel[] { diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index e1491e0b0..3342a75e7 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -816,3 +816,11 @@ a > h4 { a > h4:hover { text-decoration: underline; } + +.form-error { + border: 1px solid #d9534f; +} + +.error-text { + color: #d9534f; +} diff --git a/src/Ombi/Controllers/External/CouchPotatoController.cs b/src/Ombi/Controllers/External/CouchPotatoController.cs new file mode 100644 index 000000000..dd7a07a9d --- /dev/null +++ b/src/Ombi/Controllers/External/CouchPotatoController.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.CouchPotato; +using Ombi.Api.CouchPotato.Models; +using Ombi.Attributes; +using Ombi.Settings.Settings.Models.External; + +namespace Ombi.Controllers.External +{ + [Admin] + [ApiV1] + [Produces("application/json")] + public class CouchPotatoController + { + public CouchPotatoController(ICouchPotatoApi api) + { + _api = api; + } + + private readonly ICouchPotatoApi _api; + + [HttpPost("profile")] + public async Task GetQualityProfiles([FromBody] CouchPotatoSettings settings) + { + var profiles = await _api.GetProfiles(settings.FullUri, settings.ApiKey); + + return profiles; + } + + [HttpPost("apikey")] + public async Task GetApiKey([FromBody] CouchPotatoSettings settings) + { + var apiKey = await _api.GetApiKey(settings.FullUri, settings.Username, settings.Password); + return apiKey; + } + } +} \ No newline at end of file diff --git a/src/Ombi/Controllers/External/EmbyController.cs b/src/Ombi/Controllers/External/EmbyController.cs index 9dfd6e5b4..df865be61 100644 --- a/src/Ombi/Controllers/External/EmbyController.cs +++ b/src/Ombi/Controllers/External/EmbyController.cs @@ -13,9 +13,6 @@ using Ombi.Models.External; namespace Ombi.Controllers.External { - /// - /// - /// [Admin] [ApiV1] [Produces("application/json")] diff --git a/src/Ombi/Controllers/External/RadarrController.cs b/src/Ombi/Controllers/External/RadarrController.cs index 6e4884112..8240b637e 100644 --- a/src/Ombi/Controllers/External/RadarrController.cs +++ b/src/Ombi/Controllers/External/RadarrController.cs @@ -1,28 +1,33 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; using Ombi.Api.Radarr; using Ombi.Api.Radarr.Models; using Ombi.Attributes; using Ombi.Core.Settings; +using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; namespace Ombi.Controllers.External { - [Admin] + [PowerUser] [ApiV1] [Produces("application/json")] public class RadarrController : Controller { - public RadarrController(IRadarrApi radarr, ISettingsService settings) + public RadarrController(IRadarrApi radarr, ISettingsService settings, + IMemoryCache mem) { RadarrApi = radarr; RadarrSettings = settings; + Cache = mem; } private IRadarrApi RadarrApi { get; } private ISettingsService RadarrSettings { get; } - + private IMemoryCache Cache { get; } /// /// Gets the Radarr profiles. /// @@ -44,5 +49,37 @@ namespace Ombi.Controllers.External { return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); } + + /// + /// Gets the Radarr profiles using the saved settings + /// The data is cached for an hour + /// + /// + [HttpGet("Profiles")] + public async Task> GetProfiles() + { + return await Cache.GetOrCreate(CacheKeys.RadarrQualityProfiles, async entry => + { + entry.AbsoluteExpiration = DateTimeOffset.Now.AddHours(1); + var settings = await RadarrSettings.GetSettingsAsync(); + return await RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + }); + } + + /// + /// Gets the Radar root folders using the saved settings. + /// The data is cached for an hour + /// + /// + [HttpGet("RootFolders")] + public async Task> GetRootFolders() + { + return await Cache.GetOrCreate(CacheKeys.RadarrRootProfiles, async entry => + { + entry.AbsoluteExpiration = DateTimeOffset.Now.AddHours(1); + var settings = await RadarrSettings.GetSettingsAsync(); + return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + }); + } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/External/SonarrController.cs b/src/Ombi/Controllers/External/SonarrController.cs index 765649e68..119010f81 100644 --- a/src/Ombi/Controllers/External/SonarrController.cs +++ b/src/Ombi/Controllers/External/SonarrController.cs @@ -7,6 +7,7 @@ using Ombi.Api.Sonarr.Models; using Ombi.Attributes; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; +using Ombi.Settings.Settings.Models.External; namespace Ombi.Controllers.External { diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index c45ba689d..ca3809866 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -7,6 +7,7 @@ using AutoMapper; using Hangfire; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.PlatformAbstractions; using Ombi.Api.Emby; using Ombi.Attributes; @@ -52,7 +53,8 @@ namespace Ombi.Controllers IEmbyApi embyApi, IPlexContentCacher cacher, IEmbyContentCacher embyCacher, - IRadarrCacher radarrCacher) + IRadarrCacher radarrCacher, + IMemoryCache memCache) { SettingsResolver = resolver; Mapper = mapper; @@ -61,6 +63,7 @@ namespace Ombi.Controllers _plexContentCacher = cacher; _embyContentCacher = embyCacher; _radarrCacher = radarrCacher; + _cache = memCache; } private ISettingsResolver SettingsResolver { get; } @@ -70,6 +73,7 @@ namespace Ombi.Controllers private readonly IPlexContentCacher _plexContentCacher; private readonly IEmbyContentCacher _embyContentCacher; private readonly IRadarrCacher _radarrCacher; + private readonly IMemoryCache _cache; /// /// Gets the Ombi settings. @@ -290,6 +294,8 @@ namespace Ombi.Controllers var result = await Save(settings); if (result) { + _cache.Remove(CacheKeys.RadarrRootProfiles); + _cache.Remove(CacheKeys.RadarrQualityProfiles); BackgroundJob.Enqueue(() => _radarrCacher.CacheContent()); } return result; @@ -337,6 +343,26 @@ namespace Ombi.Controllers return await Get(); } + /// + /// Gets the CouchPotatoSettings Settings. + /// + /// + [HttpGet("CouchPotato")] + public async Task CouchPotatoSettings() + { + return await Get(); + } + + /// + /// Save the CouchPotatoSettings settings. + /// + /// The settings. + /// + [HttpPost("CouchPotato")] + public async Task CouchPotatoSettings([FromBody]CouchPotatoSettings settings) + { + return await Save(settings); + } /// /// Saves the email notification settings. diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 2567d4c78..f5e84e554 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -9,6 +9,8 @@ using Ombi.Store.Entities; using CommandLine; using CommandLine.Text; using Microsoft.AspNetCore; +using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; namespace Ombi { @@ -33,6 +35,8 @@ namespace Ombi UrlArgs = host; var urlValue = string.Empty; + var instance = StoragePathSingleton.Instance; + instance.StoragePath = storagePath ?? string.Empty; using (var ctx = new OmbiContext()) { var config = ctx.ApplicationConfigurations.ToList(); diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 6bded6b44..72134c286 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SpaServices.Webpack; using Microsoft.AspNetCore.StaticFiles; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -36,6 +37,7 @@ namespace Ombi { public class Startup { + public static StoragePathSingleton StoragePath => StoragePathSingleton.Instance; public Startup(IHostingEnvironment env) { Console.WriteLine(env.ContentRootPath); @@ -48,11 +50,24 @@ namespace Ombi //if (env.IsDevelopment()) //{ - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .WriteTo.RollingFile(Path.Combine(env.ContentRootPath, "Logs", "log-{Date}.txt")) - .WriteTo.SQLite("Ombi.db", "Logs", LogEventLevel.Debug) - .CreateLogger(); + Serilog.ILogger config; + if (string.IsNullOrEmpty(StoragePath.StoragePath)) + { + config = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.SQLite("Ombi.db", "Logs", LogEventLevel.Debug) + .CreateLogger(); + } + else + { + config = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.SQLite(Path.Combine(StoragePath.StoragePath, "Ombi.db"), "Logs", LogEventLevel.Debug) + .CreateLogger(); + } + Log.Logger = config; + + //} //if (env.IsProduction()) //{ @@ -69,9 +84,10 @@ namespace Ombi // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { + // Add framework services. services.AddDbContext(); - + services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders() @@ -113,6 +129,7 @@ namespace Ombi x.UseConsole(); }); + // Build the intermediate service provider return services.BuildServiceProvider(); } @@ -138,11 +155,20 @@ namespace Ombi }); } - app.UseHangfireServer(); - app.UseHangfireDashboard("/hangfire", new DashboardOptions + var ombiService = + app.ApplicationServices.GetService>(); + var settings = ombiService.GetSettings(); + if (settings.BaseUrl.HasValue()) { - Authorization = new[] { new HangfireAuthorizationFilter() } - }); + app.UsePathBase(settings.BaseUrl); + } + + app.UseHangfireServer(); + app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire", + new DashboardOptions + { + Authorization = new[] {new HangfireAuthorizationFilter()} + }); // Setup the scheduler var jobSetup = app.ApplicationServices.GetService(); @@ -151,14 +177,6 @@ namespace Ombi var provider = new FileExtensionContentTypeProvider { Mappings = { [".map"] = "application/octet-stream" } }; - var ombiService = - app.ApplicationServices.GetService>(); - var settings = ombiService.GetSettings(); - if (settings.BaseUrl.HasValue()) - { - app.UsePathBase(settings.BaseUrl); - } - app.UseStaticFiles(new StaticFileOptions() { ContentTypeProvider = provider,