diff --git a/CHANGELOG.md b/CHANGELOG.md index a9fdea5a0..5e60b7be3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,43 @@ ## (unreleased) +### **New Features** + +- Updated the emby api since we no longer need the extra parameters to send to emby to log in a local user #2546. [Jamie] + +- Added the ability to get the ombi user via a Plex Token #2591. [Jamie] + +### **Fixes** + +- Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie] + +- Fixed #2603. [Jamie] + +- Fixed the issue with the user overrides #2646. [Jamie] + +- Fixed the issue where we could sometimes allow the request of a whole series when the user shouldn't be able to. [Jamie] + +- Fixed the issue where we were marking episodes as available with the Emby connection when they have not yet aired #2417 #2623. [TidusJar] + +- Fixed the issue where we were marking the whole season as wanted in Sonarr rather than the individual episode #2629. [TidusJar] + +- Fixed #2623. [Jamie] + +- Fixed #2633. [TidusJar] + +- Fixed #2639. [Jamie] + +- Show the TV show as available when we have all the episodes but future episodes have not aired. #2585. [Jamie] + + +## v3.0.3945 (2018-10-25) + +### **New Features** + +- Update Readme for Lidarr. [Qstick] + +- Update CHANGELOG.md. [Jamie] + ### **Fixes** - New translations en.json (French) [Jamie] diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index fa8414ac9..43a2badb6 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -53,8 +53,6 @@ namespace Ombi.Api.Emby { username, pw = password, - password = password.GetSha1Hash().ToLower(), - passwordMd5 = password.CalcuateMd5Hash() }; request.AddJsonBody(body); diff --git a/src/Ombi.Api.Sonarr/ISonarrV3Api.cs b/src/Ombi.Api.Sonarr/ISonarrV3Api.cs new file mode 100644 index 000000000..1d3ea3468 --- /dev/null +++ b/src/Ombi.Api.Sonarr/ISonarrV3Api.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Api.Sonarr.Models; +using System.Net.Http; +using Ombi.Api.Sonarr.Models.V3; + +namespace Ombi.Api.Sonarr +{ + public interface ISonarrV3Api : ISonarrApi + { + Task> LanguageProfiles(string apiKey, string baseUrl); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Sonarr/Models/NewSeries.cs b/src/Ombi.Api.Sonarr/Models/NewSeries.cs index ef18baddb..d6f721b0b 100644 --- a/src/Ombi.Api.Sonarr/Models/NewSeries.cs +++ b/src/Ombi.Api.Sonarr/Models/NewSeries.cs @@ -27,6 +27,9 @@ namespace Ombi.Api.Sonarr.Models public int id { get; set; } public List images { get; set; } + // V3 Property + public int languageProfileId { get; set; } + /// /// This is for us /// diff --git a/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs b/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs new file mode 100644 index 000000000..aa3b199bd --- /dev/null +++ b/src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs @@ -0,0 +1,30 @@ +namespace Ombi.Api.Sonarr.Models.V3 +{ + public class LanguageProfiles + { + public string name { get; set; } + public bool upgradeAllowed { get; set; } + public Cutoff cutoff { get; set; } + public Languages[] languages { get; set; } + public int id { get; set; } + } + + public class Cutoff + { + public int id { get; set; } + public string name { get; set; } + } + + public class Languages + { + public Language languages { get; set; } + public bool allowed { get; set; } + } + + public class Language + { + public int id { get; set; } + public string name { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.Api.Sonarr/SonarrApi.cs b/src/Ombi.Api.Sonarr/SonarrApi.cs index 7fd74d2a3..0b0df4c15 100644 --- a/src/Ombi.Api.Sonarr/SonarrApi.cs +++ b/src/Ombi.Api.Sonarr/SonarrApi.cs @@ -16,18 +16,19 @@ namespace Ombi.Api.Sonarr Api = api; } - private IApi Api { get; } + protected IApi Api { get; } + protected virtual string ApiBaseUrl => "/api/"; public async Task> GetProfiles(string apiKey, string baseUrl) { - var request = new Request("/api/profile", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}profile", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); return await Api.Request>(request); } public async Task> GetRootFolders(string apiKey, string baseUrl) { - var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}rootfolder", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); return await Api.Request>(request); } @@ -40,7 +41,7 @@ namespace Ombi.Api.Sonarr /// public async Task> GetSeries(string apiKey, string baseUrl) { - var request = new Request("/api/series", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}series", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); var results = await Api.Request>(request); @@ -63,7 +64,7 @@ namespace Ombi.Api.Sonarr /// public async Task GetSeriesById(int id, string apiKey, string baseUrl) { - var request = new Request($"/api/series/{id}", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}series/{id}", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); var result = await Api.Request(request); if (result?.seasons?.Length > 0) @@ -82,7 +83,7 @@ namespace Ombi.Api.Sonarr /// public async Task UpdateSeries(SonarrSeries updated, string apiKey, string baseUrl) { - var request = new Request("/api/series/", baseUrl, HttpMethod.Put); + var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Put); request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(updated); return await Api.Request(request); @@ -94,7 +95,7 @@ namespace Ombi.Api.Sonarr { return new NewSeries { ErrorMessages = new List { seriesToAdd.Validate() } }; } - var request = new Request("/api/series/", baseUrl, HttpMethod.Post); + var request = new Request($"{ApiBaseUrl}series/", baseUrl, HttpMethod.Post); request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(seriesToAdd); @@ -120,7 +121,7 @@ namespace Ombi.Api.Sonarr /// public async Task> GetEpisodes(int seriesId, string apiKey, string baseUrl) { - var request = new Request($"/api/Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}Episode?seriesId={seriesId}", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); return await Api.Request>(request); } @@ -134,14 +135,14 @@ namespace Ombi.Api.Sonarr /// public async Task GetEpisodeById(int episodeId, string apiKey, string baseUrl) { - var request = new Request($"/api/Episode/{episodeId}", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}Episode/{episodeId}", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); return await Api.Request(request); } public async Task UpdateEpisode(Episode episodeToUpdate, string apiKey, string baseUrl) { - var request = new Request($"/api/Episode/", baseUrl, HttpMethod.Put); + var request = new Request($"{ApiBaseUrl}Episode/", baseUrl, HttpMethod.Put); request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(episodeToUpdate); return await Api.Request(request); @@ -189,7 +190,7 @@ namespace Ombi.Api.Sonarr private async Task Command(string apiKey, string baseUrl, object body) { - var request = new Request($"/api/Command/", baseUrl, HttpMethod.Post); + var request = new Request($"{ApiBaseUrl}Command/", baseUrl, HttpMethod.Post); request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(body); return await Api.Request(request); @@ -197,7 +198,7 @@ namespace Ombi.Api.Sonarr public async Task SystemStatus(string apiKey, string baseUrl) { - var request = new Request("/api/system/status", baseUrl, HttpMethod.Get); + var request = new Request($"{ApiBaseUrl}system/status", baseUrl, HttpMethod.Get); request.AddHeader("X-Api-Key", apiKey); return await Api.Request(request); @@ -217,7 +218,7 @@ namespace Ombi.Api.Sonarr ignoreEpisodesWithoutFiles = false, } }; - var request = new Request("/api/seasonpass", baseUrl, HttpMethod.Post); + var request = new Request($"{ApiBaseUrl}seasonpass", baseUrl, HttpMethod.Post); request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(seasonPass); diff --git a/src/Ombi.Api.Sonarr/SonarrV3Api.cs b/src/Ombi.Api.Sonarr/SonarrV3Api.cs new file mode 100644 index 000000000..64377ee4a --- /dev/null +++ b/src/Ombi.Api.Sonarr/SonarrV3Api.cs @@ -0,0 +1,25 @@ +using System.Net.Http; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Api.Sonarr.Models.V3; + +namespace Ombi.Api.Sonarr +{ + public class SonarrV3Api : SonarrApi, ISonarrV3Api + { + public SonarrV3Api(IApi api) : base(api) + { + + } + + protected override string ApiBaseUrl => "/api/v3/"; + + public async Task> LanguageProfiles(string apiKey, string baseUrl) + { + var request = new Request($"{ApiBaseUrl}languageprofile", baseUrl, HttpMethod.Get); + request.AddHeader("X-Api-Key", apiKey); + + return await Api.Request>(request); + } + } +} diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index f1a5b9880..290bedd6b 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -116,6 +116,7 @@ namespace Ombi.Core.Engine } // Remove the ID since this is a new child + // This was a TVDBID for the request rules to run tvBuilder.ChildRequest.Id = 0; if (!tvBuilder.ChildRequest.SeasonRequests.Any()) { diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index e5277e236..334284484 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -41,7 +41,7 @@ namespace Ombi.Core.Helpers ShowInfo = await TvApi.ShowLookupByTheTvDbId(id); Results = await MovieDbApi.SearchTv(ShowInfo.name); foreach (TvSearchResult result in Results) { - if (result.Name == ShowInfo.name) + if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase)) { var showIds = await MovieDbApi.GetTvExternals(result.Id); ShowInfo.externals.imdb = showIds.imdb_id; @@ -64,14 +64,15 @@ namespace Ombi.Core.Helpers { ChildRequest = new ChildRequests { - Id = model.TvDbId, + Id = model.TvDbId, // This is set to 0 after the request rules have run, the request rules needs it to identify the request RequestType = RequestType.TvShow, RequestedDate = DateTime.UtcNow, Approved = false, RequestedUserId = userId, SeasonRequests = new List(), Title = ShowInfo.name, - SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.OrdinalIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard + ReleaseYear = FirstAir, + SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard }; return this; diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs new file mode 100644 index 000000000..5d7658c83 --- /dev/null +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Core.Rule.Rules.Request +{ + public class ExistingPlexRequestRule : BaseRequestRule, IRules + { + public ExistingPlexRequestRule(IPlexContentRepository rv) + { + _plexContent = rv; + } + + private readonly IPlexContentRepository _plexContent; + + /// + /// We check if the request exists, if it does then we don't want to re-request it. + /// + /// The object. + /// + public async Task Execute(BaseRequest obj) + { + if (obj.RequestType == RequestType.TvShow) + { + var tvRequest = (ChildRequests) obj; + + var tvContent = _plexContent.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Show); + // We need to do a check on the TVDBId + var anyTvDbMatches = await tvContent.Include(x => x.Episodes).FirstOrDefaultAsync(x => x.HasTvDb && x.TvDbId.Equals(tvRequest.Id.ToString())); // the Id on the child is the tvdbid at this point + if (anyTvDbMatches == null) + { + // So we do not have a TVDB Id, that really sucks. + // Let's try and match on the title and year of the show + var titleAndYearMatch = await tvContent.Include(x=> x.Episodes).FirstOrDefaultAsync(x => + x.Title.Equals(tvRequest.Title, StringComparison.InvariantCultureIgnoreCase) + && x.ReleaseYear == tvRequest.ReleaseYear.Year.ToString()); + if (titleAndYearMatch != null) + { + // We have a match! Suprise Motherfucker + return CheckExistingContent(tvRequest, titleAndYearMatch); + } + + // We do not have this + return Success(); + } + // looks like we have a match on the TVDbID + return CheckExistingContent(tvRequest, anyTvDbMatches); + } + return Success(); + } + + + private RuleResult CheckExistingContent(ChildRequests child, PlexServerContent content) + { + foreach (var season in child.SeasonRequests) + { + var currentSeasonRequest = + content.Episodes.Where(x => x.SeasonNumber == season.SeasonNumber).ToList(); + if (!currentSeasonRequest.Any()) + { + continue; + } + foreach (var e in season.Episodes) + { + var hasEpisode = currentSeasonRequest.Any(x => x.EpisodeNumber == e.EpisodeNumber); + if (hasEpisode) + { + return Fail($"We already have episodes requested from series {child.Title}"); + } + } + } + + return Success(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 49a6c9efa..9124eb9c9 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -129,13 +129,17 @@ namespace Ombi.Core.Senders 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.RadarrRootPath > 0) + { + var tempPath = await RadarrRootPath(profiles.RadarrRootPath, settings); + if (tempPath.HasValue()) + { + rootFolderPath = tempPath; + } } - if (profiles.SonarrQualityProfileAnime > 0) + if (profiles.RadarrQualityProfile > 0) { - qualityToUse = profiles.SonarrQualityProfileAnime; + qualityToUse = profiles.RadarrQualityProfile; } } @@ -191,7 +195,7 @@ namespace Ombi.Core.Senders { var paths = await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); var selectedPath = paths.FirstOrDefault(x => x.id == overrideId); - return selectedPath.path; + return selectedPath?.path ?? String.Empty; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index cc686aa38..d0946b6f4 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -21,11 +21,12 @@ namespace Ombi.Core.Senders { public class TvSender : ITvSender { - public TvSender(ISonarrApi sonarrApi, ILogger log, ISettingsService sonarrSettings, + public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger log, ISettingsService sonarrSettings, ISettingsService dog, IDogNzbApi dogApi, ISettingsService srSettings, ISickRageApi srApi, IRepository userProfiles, IRepository requestQueue, INotificationHelper notify) { SonarrApi = sonarrApi; + SonarrV3Api = sonarrV3Api; Logger = log; SonarrSettings = sonarrSettings; DogNzbSettings = dog; @@ -38,6 +39,7 @@ namespace Ombi.Core.Senders } private ISonarrApi SonarrApi { get; } + private ISonarrV3Api SonarrV3Api { get; } private IDogNzbApi DogNzbApi { get; } private ISickRageApi SickRageApi { get; } private ILogger Logger { get; } @@ -211,6 +213,10 @@ namespace Ombi.Core.Senders { qualityToUse = model.ParentRequest.QualityOverride.Value; } + + // Are we using v3 sonarr? + var sonarrV3 = s.V3; + var languageProfileId = s.LanguageProfile; try { @@ -241,6 +247,11 @@ namespace Ombi.Core.Senders } }; + if (sonarrV3) + { + newSeries.languageProfileId = languageProfileId; + } + // Montitor the correct seasons, // If we have that season in the model then it's monitored! var seasonsToAdd = GetSeasonsToCreate(model); @@ -392,7 +403,7 @@ namespace Ombi.Core.Senders var sea = new Season { seasonNumber = i, - monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0) + monitored = false }; seasonsToUpdate.Add(sea); } diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 836bc4f5b..190e79203 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -107,6 +107,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index f66ff89ab..23ad24268 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -82,6 +82,13 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var ep in allEpisodes.Items) { processed++; + + if (ep.LocationType.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase)) + { + // For some reason Emby is not respecting the `IsVirtualItem` field. + continue; + } + // Let's make sure we have the parent request, stop those pesky forign key errors, // Damn me having data integrity var parent = await _repo.GetByEmbyId(ep.SeriesId); diff --git a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs index e645097ef..a34bd89e1 100644 --- a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs +++ b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs @@ -174,7 +174,7 @@ namespace Ombi.Schedule.Processor var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV3")); var releases = await client.Repository.Release.GetAll("tidusjar", "ombi"); - var latest = releases.FirstOrDefault(x => x.TagName == releaseTag); + var latest = releases.FirstOrDefault(x => x.TagName.Equals(releaseTag, StringComparison.InvariantCultureIgnoreCase)); if (latest.Name.Contains("V2", CompareOptions.IgnoreCase)) { latest = null; diff --git a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs index 0c7e17900..bbbe58fd3 100644 --- a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs @@ -18,5 +18,7 @@ public string QualityProfileAnime { get; set; } public string RootPathAnime { get; set; } public bool AddOnly { get; set; } + public bool V3 { get; set; } + public int LanguageProfile { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/ChildRequests.cs b/src/Ombi.Store/Entities/Requests/ChildRequests.cs index 3b5156ce5..f212aa3e7 100644 --- a/src/Ombi.Store/Entities/Requests/ChildRequests.cs +++ b/src/Ombi.Store/Entities/Requests/ChildRequests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Ombi.Store.Repository.Requests; @@ -22,6 +23,8 @@ namespace Ombi.Store.Entities.Requests [NotMapped] public bool ShowSubscribe { get; set; } + [NotMapped] + public DateTime ReleaseYear { get; set; } // Used in the ExistingPlexRequestRule.cs [ForeignKey(nameof(IssueId))] public List Issues { get; set; } diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index 7e57c0e64..979ef2310 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -72,6 +72,8 @@ export interface ISonarrSettings extends IExternalSettings { rootPathAnime: string; fullRootPath: string; addOnly: boolean; + v3: boolean; + languageProfile: number; } export interface IRadarrSettings extends IExternalSettings { diff --git a/src/Ombi/ClientApp/app/interfaces/ISonarr.ts b/src/Ombi/ClientApp/app/interfaces/ISonarr.ts index 3c71b835c..ffe9e9b6d 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISonarr.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISonarr.ts @@ -7,3 +7,8 @@ export interface ISonarrProfile { name: string; id: number; } + +export interface ILanguageProfiles { + name: string; + id: number; +} diff --git a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html index 27545b64b..03878033f 100644 --- a/src/Ombi/ClientApp/app/landingpage/landingpage.component.html +++ b/src/Ombi/ClientApp/app/landingpage/landingpage.component.html @@ -3,7 +3,7 @@
-
+
diff --git a/src/Ombi/ClientApp/app/requests/request.component.html b/src/Ombi/ClientApp/app/requests/request.component.html index d2df3c97a..db72b9d17 100644 --- a/src/Ombi/ClientApp/app/requests/request.component.html +++ b/src/Ombi/ClientApp/app/requests/request.component.html @@ -9,7 +9,7 @@ {{ 'Requests.TvTab' | translate }} -
  • +
  • {{ 'Requests.MusicTab' | translate }}
  • diff --git a/src/Ombi/ClientApp/app/requests/request.component.ts b/src/Ombi/ClientApp/app/requests/request.component.ts index d0fa9d0b1..b318d619b 100644 --- a/src/Ombi/ClientApp/app/requests/request.component.ts +++ b/src/Ombi/ClientApp/app/requests/request.component.ts @@ -15,6 +15,7 @@ export class RequestComponent implements OnInit { public issueCategories: IIssueCategory[]; public issuesEnabled = false; + public musicEnabled: boolean; constructor(private issuesService: IssuesService, private settingsService: SettingsService) { @@ -23,6 +24,7 @@ export class RequestComponent implements OnInit { public ngOnInit(): void { this.issuesService.getCategories().subscribe(x => this.issueCategories = x); + this.settingsService.lidarrEnabled().subscribe(x => this.musicEnabled = x); this.settingsService.getIssueSettings().subscribe(x => this.issuesEnabled = x.enabled); } diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index 05dbf15ad..a31bed3b5 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -2,7 +2,8 @@
    - +

    {{result.title}} ({{result.releaseDate | amLocal | amDateFormat: 'YYYY'}})

    - - {{ 'Search.TheatricalRelease' | translate: {date: result.releaseDate | amLocal | amDateFormat: 'LL'} }} - {{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }} - - - - - {{result.quality}}p - - - - - - - - - -
    + + {{ + 'Search.TheatricalRelease' | translate: {date: result.releaseDate | amLocal | + amDateFormat: 'LL'} }} + {{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | + amLocal | amDateFormat: 'LL'} }} + + + + + {{result.quality}}p + + + + + + + + + +

    {{result.overview}}

    -
    -
    - - - -
    -
    - +
    -
    - - - - - - -
    - - -
    +
    + + + + + + +
    + + + +
    -
    -
    +
    +
    @@ -117,4 +136,4 @@ + [issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"> \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.ts b/src/Ombi/ClientApp/app/search/moviesearch.component.ts index f4d19565a..600fc19eb 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.ts +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.ts @@ -172,7 +172,7 @@ export class MovieSearchComponent implements OnInit { r.subscribed = true; this.requestService.subscribeToMovie(r.requestId) .subscribe(x => { - this.notificationService.success("Subscribed To Movie!"); + this.notificationService.success(`Subscribed To Movie ${r.title}!`); }); } diff --git a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts index 756c6ea6c..bce47acf9 100644 --- a/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/sonarr.service.ts @@ -5,7 +5,7 @@ import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { ISonarrSettings } from "../../interfaces"; -import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; +import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; import { ServiceHelpers } from "../service.helpers"; @Injectable() @@ -27,4 +27,8 @@ export class SonarrService extends ServiceHelpers { public getQualityProfilesWithoutSettings(): Observable { return this.http.get(`${this.url}/Profiles/`, {headers: this.headers}); } + + public getV3LanguageProfiles(settings: ISonarrSettings): Observable { + return this.http.post(`${this.url}/v3/languageprofiles/`, JSON.stringify(settings), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html index 50938412f..5e898e719 100644 --- a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html +++ b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html @@ -19,25 +19,28 @@
    - + - The IP/Hostname is required
    - + - The Port is required +
    - + - The API Key is required +
    @@ -56,19 +59,22 @@
    - +
    - - A Default Quality Profile is required
    - +
    @@ -89,12 +97,14 @@
    - A Language profile is required
    - +
    - A Metadata profile 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 909a64226..08a8035c2 100644 --- a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html +++ b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html @@ -1,5 +1,4 @@ - - +
    Radarr Settings @@ -19,25 +18,34 @@
    - + - - The IP/Hostname is required +
    - + - - The Port is required +
    - - - - The API Key is required + + +
    @@ -49,63 +57,73 @@
    - +
    +
    -
    - -
    -
    -
    - +
    - +
    - A Default Quality Profile is required
    -
    -
    - - -
    -
    - +
    - + -
    - A Default Root Path is required + + +
    + +
    - +
    -
    - - A Default Minimum Availability is required
    - +
    +
    +
    - +
    @@ -118,4 +136,4 @@
    -
    +
    \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index eae56bce2..91405d1c5 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -54,7 +54,7 @@ Music diff --git a/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.html b/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.html index 6a419feab..f016fd56e 100644 --- a/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.html +++ b/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.html @@ -1,5 +1,4 @@ - - +
    Sonarr Settings @@ -10,33 +9,53 @@
    -
    -
    - - +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    + + +
    - - - - The IP/Hostname is required + + +
    - + - - The Port is required +
    - - - - The API Key is required + + +
    @@ -48,63 +67,82 @@
    - +
    -
    -
    - + +
    + +
    + + + +
    +
    -
    -
    - -
    - -
    - A Default Quality Profile is required - -
    -
    + + +
    - +
    -
    -
    - -
    -
    -
    - +
    +
    - +
    - A Default Root Path is required -
    -
    + +
    -
    - +
    +
    + +
    + + -
    +
    +
    + + +
    @@ -112,26 +150,27 @@
    -
    +
    -
    -
    - +
    +
    + +
    -
    - -
    +
    - +
    +
    -
    +
    \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.ts b/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.ts index a1c03be58..11845a06e 100644 --- a/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.ts +++ b/src/Ombi/ClientApp/app/settings/sonarr/sonarr.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; -import { ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; +import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; import { ISonarrSettings } from "../../interfaces"; import { SonarrService } from "../../services"; @@ -18,10 +18,13 @@ export class SonarrComponent implements OnInit { public qualitiesAnime: ISonarrProfile[]; public rootFolders: ISonarrRootFolder[]; public rootFoldersAnime: ISonarrRootFolder[]; + public languageProfiles: ILanguageProfiles[]; public selectedRootFolder: ISonarrRootFolder; public selectedQuality: ISonarrProfile; + public selectedLanguageProfiles: ILanguageProfiles; public profilesRunning: boolean; public rootFoldersRunning: boolean; + public langRunning: boolean; public form: FormGroup; public advanced = false; @@ -47,6 +50,8 @@ export class SonarrComponent implements OnInit { port: [x.port, [Validators.required]], addOnly: [x.addOnly], seasonFolders: [x.seasonFolders], + v3: [x.v3], + languageProfile: [x.languageProfile], }); if (x.qualityProfile) { @@ -55,11 +60,19 @@ export class SonarrComponent implements OnInit { if (x.rootPath) { this.getRootFolders(this.form); } + if(x.languageProfile) { + this.getLanguageProfiles(this.form); + } + if(x.v3) { + this.form.controls.languageProfile.setValidators([Validators.required]); + } }); this.rootFolders = []; this.qualities = []; + this.languageProfiles = []; this.rootFolders.push({ path: "Please Select", id: -1 }); this.qualities.push({ name: "Please Select", id: -1 }); + this.languageProfiles.push({ name: "Please Select", id: -1 }); } public getProfiles(form: FormGroup) { @@ -88,6 +101,18 @@ export class SonarrComponent implements OnInit { }); } + public getLanguageProfiles(form: FormGroup) { + this.langRunning = true; + this.sonarrService.getV3LanguageProfiles(form.value) + .subscribe(x => { + this.languageProfiles = x; + this.languageProfiles.unshift({ name: "Please Select", id: -1 }); + + this.langRunning = false; + this.notificationService.success("Successfully retrieved the Languge Profiles"); + }); + } + public test(form: FormGroup) { if (form.invalid) { this.notificationService.error("Please check your entered values"); diff --git a/src/Ombi/ClientApp/styles/Styles.scss b/src/Ombi/ClientApp/styles/Styles.scss index b41e1c2a7..d98e4cf83 100644 --- a/src/Ombi/ClientApp/styles/Styles.scss +++ b/src/Ombi/ClientApp/styles/Styles.scss @@ -27,4 +27,4 @@ $bg-colour-disabled: #252424; .label { margin: 3px; -} \ No newline at end of file +} diff --git a/src/Ombi/Controllers/External/SonarrController.cs b/src/Ombi/Controllers/External/SonarrController.cs index c3401736d..bc2534ab8 100644 --- a/src/Ombi/Controllers/External/SonarrController.cs +++ b/src/Ombi/Controllers/External/SonarrController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ombi.Api.Sonarr; using Ombi.Api.Sonarr.Models; +using Ombi.Api.Sonarr.Models.V3; using Ombi.Attributes; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; @@ -16,14 +17,16 @@ namespace Ombi.Controllers.External [Produces("application/json")] public class SonarrController : Controller { - public SonarrController(ISonarrApi sonarr, ISettingsService settings) + public SonarrController(ISonarrApi sonarr, ISonarrV3Api sonarrv3, ISettingsService settings) { SonarrApi = sonarr; + SonarrV3Api = sonarrv3; SonarrSettings = settings; SonarrSettings.ClearCache(); } private ISonarrApi SonarrApi { get; } + private ISonarrV3Api SonarrV3Api { get; } private ISettingsService SonarrSettings { get; } /// @@ -82,5 +85,36 @@ namespace Ombi.Controllers.External return null; } + + /// + /// Gets the Sonarr V3 language profiles + /// + /// + [HttpGet("v3/LanguageProfiles")] + [PowerUser] + public async Task> GetLanguageProfiles() + { + var settings = await SonarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await SonarrV3Api.LanguageProfiles(settings.ApiKey, settings.FullUri); + } + + return null; + } + + + /// + /// Gets the Sonarr V3 language profiles + /// + /// The settings. + /// + [HttpPost("v3/LanguageProfiles")] + [PowerUser] + public async Task> GetLanguageProfiles([FromBody] SonarrSettings settings) + { + return await SonarrV3Api.LanguageProfiles(settings.ApiKey, settings.FullUri); + } + } } \ No newline at end of file diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index 61b3f06d8..f963db0b8 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -863,6 +863,7 @@ namespace Ombi.Controllers { var ombiUser = new OmbiUser { + Alias = user.Alias, Email = user.EmailAddress, UserName = user.UserName };