mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-13 18:16:55 -07:00
Merge branch 'develop' into feature/request-queue
This commit is contained in:
commit
72c411d5d3
33 changed files with 575 additions and 198 deletions
37
CHANGELOG.md
37
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]
|
||||
|
|
|
@ -53,8 +53,6 @@ namespace Ombi.Api.Emby
|
|||
{
|
||||
username,
|
||||
pw = password,
|
||||
password = password.GetSha1Hash().ToLower(),
|
||||
passwordMd5 = password.CalcuateMd5Hash()
|
||||
};
|
||||
|
||||
request.AddJsonBody(body);
|
||||
|
|
13
src/Ombi.Api.Sonarr/ISonarrV3Api.cs
Normal file
13
src/Ombi.Api.Sonarr/ISonarrV3Api.cs
Normal file
|
@ -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<IEnumerable<LanguageProfiles>> LanguageProfiles(string apiKey, string baseUrl);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,9 @@ namespace Ombi.Api.Sonarr.Models
|
|||
public int id { get; set; }
|
||||
public List<SonarrImage> images { get; set; }
|
||||
|
||||
// V3 Property
|
||||
public int languageProfileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is for us
|
||||
/// </summary>
|
||||
|
|
30
src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
Normal file
30
src/Ombi.Api.Sonarr/Models/V3/LanguageProfiles.cs
Normal file
|
@ -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; }
|
||||
}
|
||||
|
||||
}
|
|
@ -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<IEnumerable<SonarrProfile>> 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<List<SonarrProfile>>(request);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SonarrRootFolder>> 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<List<SonarrRootFolder>>(request);
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SonarrSeries>> 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<List<SonarrSeries>>(request);
|
||||
|
||||
|
@ -63,7 +64,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> 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<SonarrSeries>(request);
|
||||
if (result?.seasons?.Length > 0)
|
||||
|
@ -82,7 +83,7 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<SonarrSeries> 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<SonarrSeries>(request);
|
||||
|
@ -94,7 +95,7 @@ namespace Ombi.Api.Sonarr
|
|||
{
|
||||
return new NewSeries { ErrorMessages = new List<string> { 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
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<Episode>> 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<List<Episode>>(request);
|
||||
}
|
||||
|
@ -134,14 +135,14 @@ namespace Ombi.Api.Sonarr
|
|||
/// <returns></returns>
|
||||
public async Task<Episode> 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<Episode>(request);
|
||||
}
|
||||
|
||||
public async Task<EpisodeUpdateResult> 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<EpisodeUpdateResult>(request);
|
||||
|
@ -189,7 +190,7 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
private async Task<CommandResult> 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<CommandResult>(request);
|
||||
|
@ -197,7 +198,7 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
public async Task<SystemStatus> 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<SystemStatus>(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);
|
||||
|
||||
|
|
25
src/Ombi.Api.Sonarr/SonarrV3Api.cs
Normal file
25
src/Ombi.Api.Sonarr/SonarrV3Api.cs
Normal file
|
@ -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<IEnumerable<LanguageProfiles>> LanguageProfiles(string apiKey, string baseUrl)
|
||||
{
|
||||
var request = new Request($"{ApiBaseUrl}languageprofile", baseUrl, HttpMethod.Get);
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
return await Api.Request<List<LanguageProfiles>>(request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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<SeasonRequests>(),
|
||||
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;
|
||||
|
|
82
src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
Normal file
82
src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs
Normal file
|
@ -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<BaseRequest>
|
||||
{
|
||||
public ExistingPlexRequestRule(IPlexContentRepository rv)
|
||||
{
|
||||
_plexContent = rv;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plexContent;
|
||||
|
||||
/// <summary>
|
||||
/// We check if the request exists, if it does then we don't want to re-request it.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<RuleResult> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
if (profiles.RadarrRootPath > 0)
|
||||
{
|
||||
rootFolderPath = await RadarrRootPath(profiles.SonarrRootPathAnime, settings);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,11 +21,12 @@ namespace Ombi.Core.Senders
|
|||
{
|
||||
public class TvSender : ITvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
|
||||
ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> 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<TvSender> Logger { get; }
|
||||
|
@ -212,6 +214,10 @@ namespace Ombi.Core.Senders
|
|||
qualityToUse = model.ParentRequest.QualityOverride.Value;
|
||||
}
|
||||
|
||||
// Are we using v3 sonarr?
|
||||
var sonarrV3 = s.V3;
|
||||
var languageProfileId = s.LanguageProfile;
|
||||
|
||||
try
|
||||
{
|
||||
// Does the series actually exist?
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
services.AddTransient<ISonarrApi, SonarrApi>();
|
||||
services.AddTransient<ISonarrV3Api, SonarrV3Api>();
|
||||
services.AddTransient<ISlackApi, SlackApi>();
|
||||
services.AddTransient<ITvMazeApi, TvMazeApi>();
|
||||
services.AddTransient<ITraktApi, TraktApi>();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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> Issues { get; set; }
|
||||
|
|
|
@ -72,6 +72,8 @@ export interface ISonarrSettings extends IExternalSettings {
|
|||
rootPathAnime: string;
|
||||
fullRootPath: string;
|
||||
addOnly: boolean;
|
||||
v3: boolean;
|
||||
languageProfile: number;
|
||||
}
|
||||
|
||||
export interface IRadarrSettings extends IExternalSettings {
|
||||
|
|
|
@ -7,3 +7,8 @@ export interface ISonarrProfile {
|
|||
name: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ILanguageProfiles {
|
||||
name: string;
|
||||
id: number;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<div class="centered col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-push-5 col-md-2">
|
||||
<div class="col-md-push-3 col-md-6">
|
||||
<div *ngIf="customizationSettings.logo">
|
||||
<img [src]="customizationSettings.logo" style="width:100%"/>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<a id="tvTabButton" aria-controls="profile" role="tab" data-toggle="tab" (click)="selectTvTab()" href="#tvTab"><i class="fa fa-television"></i> {{ 'Requests.TvTab' | translate }}</a>
|
||||
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<li role="presentation" *ngIf="musicEnabled">
|
||||
<a id="albumTabButton" aria-controls="profile" role="tab" data-toggle="tab" (click)="selectMusicTab()" href="#albumTab"><i class="fa fa-music"></i> {{ 'Requests.MusicTab' | translate }}</a>
|
||||
|
||||
</li>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||
|
||||
<div class="input-group">
|
||||
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons" (keyup)="search($event)">
|
||||
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons"
|
||||
(keyup)="search($event)">
|
||||
<div class="input-group-addon right-radius">
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
|
@ -25,12 +26,13 @@
|
|||
<!-- Movie content -->
|
||||
<div id="movieList">
|
||||
<div *ngIf="searchApplied && movieResults?.length <= 0" class='no-search-results'>
|
||||
<i class='fa fa-film no-search-results-icon'></i><div class='no-search-results-text' [translate]="'Search.NoResults'"></div>
|
||||
<i class='fa fa-film no-search-results-icon'></i>
|
||||
<div class='no-search-results-text' [translate]="'Search.NoResults'"></div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let result of movieResults">
|
||||
|
||||
<div class="row" >
|
||||
<div class="row">
|
||||
|
||||
<div class="myBg backdrop" [style.background-image]="result.background"></div>
|
||||
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
|
||||
|
@ -44,60 +46,77 @@
|
|||
<h4>{{result.title}} ({{result.releaseDate | amLocal | amDateFormat: 'YYYY'}})</h4>
|
||||
</a>
|
||||
<span class="tags">
|
||||
<span *ngIf="result.releaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{ 'Search.TheatricalRelease' | translate: {date: result.releaseDate | amLocal | amDateFormat: 'LL'} }}</span>
|
||||
<span *ngIf="result.digitalReleaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}</span>
|
||||
<span *ngIf="result.releaseDate" class="label label-info" id="releaseDateLabel" target="_blank">{{
|
||||
'Search.TheatricalRelease' | translate: {date: result.releaseDate | amLocal |
|
||||
amDateFormat: 'LL'} }}</span>
|
||||
<span *ngIf="result.digitalReleaseDate" class="label label-info" id="releaseDateLabel"
|
||||
target="_blank">{{ 'Search.DigitalDate' | translate: {date: result.digitalReleaseDate |
|
||||
amLocal | amDateFormat: 'LL'} }}</span>
|
||||
|
||||
<a *ngIf="result.homepage" href="{{result.homepage}}" id="homePageLabel" target="_blank"><span class="label label-info" [translate]="'Search.Movies.HomePage'"></span></a>
|
||||
<a *ngIf="result.homepage" href="{{result.homepage}}" id="homePageLabel" target="_blank"><span
|
||||
class="label label-info" [translate]="'Search.Movies.HomePage'"></span></a>
|
||||
|
||||
<a *ngIf="result.trailer" href="{{result.trailer}}" id="trailerLabel" target="_blank"><span class="label label-info" [translate]="'Search.Movies.Trailer'"></span></a>
|
||||
<a *ngIf="result.trailer" href="{{result.trailer}}" id="trailerLabel" target="_blank"><span
|
||||
class="label label-info" [translate]="'Search.Movies.Trailer'"></span></a>
|
||||
<span *ngIf="result.quality" id="qualityLabel" class="label label-success">{{result.quality}}p</span>
|
||||
|
||||
<ng-template [ngIf]="result.available"><span class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span></ng-template>
|
||||
<ng-template [ngIf]="result.approved && !result.available"><span class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span></ng-template>
|
||||
<ng-template [ngIf]="result.requested && !result.approved && !result.available"><span class="label label-warning" id="pendingApprovalLabel" [translate]="'Common.PendingApproval'"></span></ng-template>
|
||||
<ng-template [ngIf]="!result.requested && !result.available && !result.approved"><span class="label label-danger" id="notRequestedLabel" [translate]="'Common.NotRequested'"></span></ng-template>
|
||||
<ng-template [ngIf]="result.available"><span class="label label-success" id="availableLabel"
|
||||
[translate]="'Common.Available'"></span></ng-template>
|
||||
<ng-template [ngIf]="result.approved && !result.available"><span class="label label-info"
|
||||
id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span></ng-template>
|
||||
<ng-template [ngIf]="result.requested && !result.approved && !result.available"><span class="label label-warning"
|
||||
id="pendingApprovalLabel" [translate]="'Common.PendingApproval'"></span></ng-template>
|
||||
<ng-template [ngIf]="!result.requested && !result.available && !result.approved"><span
|
||||
class="label label-danger" id="notRequestedLabel" [translate]="'Common.NotRequested'"></span></ng-template>
|
||||
|
||||
|
||||
</span>
|
||||
|
||||
<br/>
|
||||
<br />
|
||||
</div>
|
||||
<p style="font-size: 0.9rem !important">{{result.overview}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-2 small-padding">
|
||||
<div class="row" *ngIf="result.requested">
|
||||
<div class="col-md-2 col-md-push-10">
|
||||
|
||||
<a *ngIf="result.showSubscribe && !result.subscribed" style="color:white" (click)="subscribe(result)" pTooltip="Subscribe for notifications"> <i class="fa fa-rss"></i></a>
|
||||
<a *ngIf="result.showSubscribe && result.subscribed" style="color:red" (click)="unSubscribe(result)" pTooltip="Unsubscribe notification"> <i class="fa fa-rss"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="result.available">
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> {{ 'Common.Available' | translate }}</button>
|
||||
<button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i>
|
||||
{{ 'Common.Available' | translate }}</button>
|
||||
</div>
|
||||
<div *ngIf="!result.available">
|
||||
<div *ngIf="result.requested || result.approved; then requestedBtn else notRequestedBtn"></div>
|
||||
<ng-template #requestedBtn>
|
||||
<button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]><i class="fa fa-check"></i> {{ 'Common.Requested' | translate }}</button>
|
||||
<button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]><i
|
||||
class="fa fa-check"></i> {{ 'Common.Requested' | translate }}</button>
|
||||
</ng-template>
|
||||
<ng-template #notRequestedBtn>
|
||||
<button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline" (click)="request(result)">
|
||||
<i *ngIf="result.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i *ngIf="!result.requestProcessing && !result.processed" class="fa fa-plus"></i>
|
||||
<i *ngIf="result.processed && !result.requestProcessing" class="fa fa-check"></i> {{ 'Common.Request' | translate }}</button>
|
||||
<button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline"
|
||||
(click)="request(result)">
|
||||
<i *ngIf="result.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i
|
||||
*ngIf="!result.requestProcessing && !result.processed" class="fa fa-plus"></i>
|
||||
<i *ngIf="result.processed && !result.requestProcessing" class="fa fa-check"></i> {{
|
||||
'Common.Request' | translate }}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
<button style="text-align: right" class="btn btn-sm btn-info-outline" (click)="similarMovies(result.id)"> <i class="fa fa-eye"></i> {{ 'Search.Similar' | translate }}</button>
|
||||
<div *ngIf="result.requested">
|
||||
<a *ngIf="result.showSubscribe && !result.subscribed" style="text-align: right" class="btn btn btn-success-outline"
|
||||
(click)="subscribe(result)" pTooltip="Subscribe for notifications when this movie becomes available">
|
||||
<i class="fa fa-rss"></i> Subscribe</a>
|
||||
<a *ngIf="result.showSubscribe && result.subscribed" style="text-align: right;" class="btn btn btn-warning-outline"
|
||||
(click)="unSubscribe(result)" pTooltip="Unsubscribe notifications when this movie becomes available">
|
||||
<i class="fa fa-rss"></i> Unsubscribe</a>
|
||||
</div>
|
||||
<button style="text-align: right" class="btn btn-sm btn-info-outline" (click)="similarMovies(result.id)">
|
||||
<i class="fa fa-eye"></i> {{ 'Search.Similar' | translate }}</button>
|
||||
|
||||
<br/>
|
||||
<br />
|
||||
<div *ngIf="result.available">
|
||||
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> View On Plex</a>
|
||||
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> View On Emby</a>
|
||||
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
|
||||
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' | translate}}</a>
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> Report Issue
|
||||
<i class="fa fa-plus"></i> {{'Request.ReportIssue' | translate}}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
|
@ -108,8 +127,8 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -117,4 +136,4 @@
|
|||
|
||||
|
||||
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequestTitle"
|
||||
[issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"></issue-report>
|
||||
[issueCategory]="issueCategorySelected" [id]="issueRequestId" [providerId]="issueProviderId"></issue-report>
|
|
@ -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}!`);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ISonarrProfile[]> {
|
||||
return this.http.get<ISonarrProfile[]>(`${this.url}/Profiles/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getV3LanguageProfiles(settings: ISonarrSettings): Observable<ILanguageProfiles[]> {
|
||||
return this.http.post<ILanguageProfiles[]>(`${this.url}/v3/languageprofiles/`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,25 +19,28 @@
|
|||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Hostname or IP</label>
|
||||
<label for="Ip" class="control-label">Hostname or IP
|
||||
|
||||
<i *ngIf="form.get('ip').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="IP/Hostname is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" formControlName="ip" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
|
||||
<small *ngIf="form.get('ip').hasError('required')" class="error-text">The IP/Hostname is required</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port</label>
|
||||
<label for="portNumber" class="control-label">Port
|
||||
<i *ngIf="form.get('port').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Port is required"></i></label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " formControlName="port" id="portNumber" name="Port" placeholder="Port Number" [ngClass]="{'form-error': form.get('port').hasError('required')}">
|
||||
<small *ngIf="form.get('port').hasError('required')" class="error-text">The Port is required</small>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">API Key</label>
|
||||
<label for="ApiKey" class="control-label">API Key <i *ngIf="form.get('apiKey').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="API Key is required"></i></label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}" id="ApiKey" name="ApiKey" formControlName="apiKey">
|
||||
<small *ngIf="form.get('apiKey').hasError('required')" class="error-text">The API Key is required</small>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
@ -56,19 +59,22 @@
|
|||
<div class="col-md-6">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
<label for="select" class="control-label">Quality Profiles
|
||||
<i *ngIf="form.get('defaultQualityProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Quality Profile is required"></i>
|
||||
</label>
|
||||
<div id="profiles">
|
||||
<select formControlName="defaultQualityProfile" class="form-control form-control-custom col-md-5 form-half" id="select" [ngClass]="{'form-error': form.get('defaultQualityProfile').hasError('required')}">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option>
|
||||
</select>
|
||||
<button (click)="getProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"> </span></button>
|
||||
|
||||
<small *ngIf="form.get('defaultQualityProfile').hasError('required')" class="error-text">A Default Quality Profile is required</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||
<label for="rootFolders" class="control-label">Default Root Folders
|
||||
|
||||
<i *ngIf="form.get('defaultRootPath').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Root Path is required"></i>
|
||||
</label>
|
||||
<div id="rootFolders">
|
||||
<select formControlName="defaultRootPath" class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('defaultRootPath').hasError('required')}">
|
||||
<option *ngFor="let folder of rootFolders" value="{{folder.path}}" >{{folder.path}}</option>
|
||||
|
@ -76,12 +82,14 @@
|
|||
<button (click)="getRootFolders(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
|
||||
</div>
|
||||
<small *ngIf="form.get('defaultRootPath').hasError('required')" class="error-text">A Default Root Path is required</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="languageProfileId" class="control-label">Language Profile</label>
|
||||
<label for="languageProfileId" class="control-label">Language Profile
|
||||
|
||||
<i *ngIf="form.get('languageProfileId').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Language Profile is required"></i>
|
||||
</label>
|
||||
<div id="languageProfileId">
|
||||
<select formControlName="languageProfileId" class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('languageProfileId').hasError('required')}">
|
||||
<option *ngFor="let folder of languageProfiles" value="{{folder.id}}" >{{folder.name}}</option>
|
||||
|
@ -89,12 +97,14 @@
|
|||
<button (click)="getLanguageProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get Languages <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
|
||||
</div>
|
||||
<small *ngIf="form.get('languageProfileId').hasError('required')" class="error-text">A Language profile is required</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="metadataProfileId" class="control-label">Metadata Profile</label>
|
||||
<label for="metadataProfileId" class="control-label">Metadata Profile
|
||||
|
||||
<i *ngIf="form.get('metadataProfileId').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="Metadata Profile is required"></i>
|
||||
</label>
|
||||
<div id="metadataProfileId">
|
||||
|
||||
<select formControlName="metadataProfileId" class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('metadataProfileId').hasError('required')}">
|
||||
|
@ -102,7 +112,6 @@
|
|||
</select>
|
||||
<button (click)="getMetadataProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get Metadata <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
|
||||
<small *ngIf="form.get('metadataProfileId').hasError('required')" class="error-text">A Metadata profile is required</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="form">
|
||||
<fieldset>
|
||||
<legend>Radarr Settings</legend>
|
||||
|
@ -19,25 +18,34 @@
|
|||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Hostname or IP</label>
|
||||
<label for="Ip" class="control-label">Hostname or IP
|
||||
<i *ngIf="form.get('ip').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="IP/Hostname is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" formControlName="ip" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
|
||||
<small *ngIf="form.get('ip').hasError('required')" class="error-text">The IP/Hostname is required</small>
|
||||
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost"
|
||||
formControlName="ip" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port</label>
|
||||
<label for="portNumber" class="control-label">Port
|
||||
<i *ngIf="form.get('port').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="Port is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " formControlName="port" id="portNumber" name="Port" placeholder="Port Number" [ngClass]="{'form-error': form.get('port').hasError('required')}">
|
||||
<small *ngIf="form.get('port').hasError('required')" class="error-text">The Port is required</small>
|
||||
<input type="text" class="form-control form-control-custom " formControlName="port" id="portNumber"
|
||||
name="Port" placeholder="Port Number" [ngClass]="{'form-error': form.get('port').hasError('required')}">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">API Key</label>
|
||||
<label for="ApiKey" class="control-label">API Key
|
||||
<i *ngIf="form.get('apiKey').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="Api Key is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}" id="ApiKey" name="ApiKey" formControlName="apiKey">
|
||||
<small *ngIf="form.get('apiKey').hasError('required')" class="error-text">The API Key is required</small>
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}"
|
||||
id="ApiKey" name="ApiKey" formControlName="apiKey">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
@ -49,63 +57,73 @@
|
|||
<div class="form-group">
|
||||
<label for="SubDir" class="control-label">Base Url</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" name="SubDir">
|
||||
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir"
|
||||
name="SubDir">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button (click)="getProfiles(form)" type="button" class="btn btn-primary-outline">Get Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"> </span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
<label for="select" class="control-label">Quality Profiles
|
||||
<i *ngIf="form.get('defaultQualityProfile').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="Quality Profile is required"></i>
|
||||
</label>
|
||||
<div id="profiles">
|
||||
<select formControlName="defaultQualityProfile" class="form-control form-control-custom" id="select" [ngClass]="{'form-error': form.get('defaultQualityProfile').hasError('required')}">
|
||||
<select formControlName="defaultQualityProfile" class="form-control form-control-custom col-md-5 form-half"
|
||||
id="select" [ngClass]="{'form-error': form.get('defaultQualityProfile').hasError('required')}">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option>
|
||||
</select>
|
||||
<button (click)="getProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get
|
||||
Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"> </span></button>
|
||||
</div>
|
||||
<small *ngIf="form.get('defaultQualityProfile').hasError('required')" class="error-text">A Default Quality Profile is required</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button (click)="getRootFolders(form)" type="button" class="btn btn-primary-outline">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
<label for="rootFolders" class="control-label">Default Root Folders
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||
<i *ngIf="form.get('defaultRootPath').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="Root Path is required"></i>
|
||||
</label>
|
||||
<div id="rootFolders">
|
||||
<select formControlName="defaultRootPath" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('defaultRootPath').hasError('required')}">
|
||||
<option *ngFor="let folder of rootFolders" value="{{folder.path}}" >{{folder.path}}</option>
|
||||
<select formControlName="defaultRootPath" class="form-control form-control-custom col-md-5 form-half"
|
||||
[ngClass]="{'form-error': form.get('defaultRootPath').hasError('required')}">
|
||||
<option *ngFor="let folder of rootFolders" value="{{folder.path}}">{{folder.path}}</option>
|
||||
</select>
|
||||
<button (click)="getRootFolders(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Get
|
||||
Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
|
||||
</div>
|
||||
<small *ngIf="form.get('defaultRootPath').hasError('required')" class="error-text">A Default Root Path is required</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Minimum Availability</label>
|
||||
<label for="rootFolders" class="control-label">Default Minimum Availability
|
||||
<i *ngIf="form.get('minimumAvailability').hasError('required')" class="fa fa-exclamation-circle error-text"
|
||||
pTooltip="Minimum Availability is required"></i>
|
||||
</label>
|
||||
<div id="rootFolders">
|
||||
<select formControlName="minimumAvailability" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('minimumAvailability').hasError('required')}">
|
||||
<select formControlName="minimumAvailability" class="form-control form-control-custom col-md-5 form-half"
|
||||
[ngClass]="{'form-error': form.get('minimumAvailability').hasError('required')}">
|
||||
<option *ngFor="let min of minimumAvailabilityOptions" value="{{min.value}}">{{min.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<small *ngIf="form.get('minimumAvailability').hasError('required')" type="button" class="error-text">A Default Minimum Availability is required</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group" *ngIf="advanced" style="color:#ff761b">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="addOnly" formControlName="addOnly">
|
||||
<label for="addOnly">Do not search</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button type="button" [disabled]="form.invalid" (click)="test(form)" class="btn btn-primary-outline">Test Connectivity <span id="spinner"></span></button>
|
||||
<button type="button" [disabled]="form.invalid" (click)="test(form)" class="btn btn-primary-outline">Test
|
||||
Connectivity <span id="spinner"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<i class="fa fa-music" aria-hidden="true"></i> Music <span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Lidarr']">Lidarr (beta)</a></li>
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Lidarr']">Lidarr</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="form">
|
||||
<fieldset>
|
||||
<legend>Sonarr Settings</legend>
|
||||
|
@ -9,6 +8,7 @@
|
|||
</div>
|
||||
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:5%;">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
@ -16,27 +16,46 @@
|
|||
<label for="enable">Enable</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Sonarr Hostname or IP</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " formControlName="ip" id="Ip" name="Ip" placeholder="localhost" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
|
||||
<small *ngIf="form.get('ip').hasError('required')" class="error-text">The IP/Hostname is required</small>
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="v3" formControlName="v3">
|
||||
<label for="v3">V3</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port</label>
|
||||
<label for="Ip" class="control-label">Sonarr Hostname or IP
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('port').hasError('required')}" formControlName="port" id="portNumber" name="Port" placeholder="Port Number">
|
||||
<small *ngIf="form.get('port').hasError('required')" class="error-text">The Port is required</small>
|
||||
<i *ngIf="form.get('ip').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The IP/Hostname is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " formControlName="ip" id="Ip" name="Ip"
|
||||
placeholder="localhost" [ngClass]="{'form-error': form.get('ip').hasError('required')}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port
|
||||
|
||||
<i *ngIf="form.get('port').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The Port is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('port').hasError('required')}"
|
||||
formControlName="port" id="portNumber" name="Port" placeholder="Port Number">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">Sonarr API Key</label>
|
||||
<label for="ApiKey" class="control-label">Sonarr API Key
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}" formControlName="apiKey" id="ApiKey" name="ApiKey">
|
||||
<small *ngIf="form.get('apiKey').hasError('required')" class="error-text">The API Key is required</small>
|
||||
<i *ngIf="form.get('apiKey').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="The API Key is required"></i>
|
||||
</label>
|
||||
|
||||
<input type="text" class="form-control form-control-custom " [ngClass]="{'form-error': form.get('apiKey').hasError('required')}"
|
||||
formControlName="apiKey" id="ApiKey" name="ApiKey">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
|
@ -48,63 +67,82 @@
|
|||
<div class="form-group">
|
||||
<label for="SubDir" class="control-label">Sonarr Base Url</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" name="SubDir">
|
||||
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir"
|
||||
name="SubDir">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button type="button" (click)="getProfiles(form)" class="btn btn-primary-outline">Get Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="profiles" class="control-label">Quality Profiles
|
||||
<i *ngIf="form.get('qualityProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Quality Profile is required"></i>
|
||||
</label>
|
||||
<div id="profiles">
|
||||
<select class="form-control form-control-custom" [ngClass]="{'form-error': form.get('qualityProfile').hasError('required')}" id="select" formControlName="qualityProfile">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}" >{{quality.name}}</option>
|
||||
<select class="form-control form-control-custom col-md-5 form-half" [ngClass]="{'form-error': form.get('qualityProfile').hasError('required')}"
|
||||
id="select" formControlName="qualityProfile">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<small *ngIf="form.get('qualityProfile').hasError('required')" class="error-text">A Default Quality Profile is required</small>
|
||||
<button type="button" (click)="getProfiles(form)" class="btn btn-primary-outline col-md-4 col-md-push-1">
|
||||
Load Qualities <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<label for="select" class="control-label">Quality Profiles (Anime)</label>
|
||||
<div id="qualityProfileAnime">
|
||||
<select class="form-control form-control-custom" id="qualityProfileAnime" formControlName="qualityProfileAnime">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}" >{{quality.name}}</option>
|
||||
<select class="form-control form-control-custom col-md-5 form-half" id="qualityProfileAnime" formControlName="qualityProfileAnime">
|
||||
<option *ngFor="let quality of qualities" value="{{quality.id}}">{{quality.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button type="button" (click)="getRootFolders(form)" class="btn btn-primary-outline">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||
<div class="form-group col-md-12">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders
|
||||
|
||||
<i *ngIf="form.get('rootPath').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Default Root Path is required"></i>
|
||||
</label>
|
||||
<div id="rootFolders">
|
||||
<select class="form-control form-control-custom" formControlName="rootPath" [ngClass]="{'form-error': form.get('rootPath').hasError('required')}">
|
||||
<select class="form-control form-control-custom col-md-5 form-half" formControlName="rootPath"
|
||||
[ngClass]="{'form-error': form.get('rootPath').hasError('required')}">
|
||||
<option *ngFor="let folder of rootFolders" value="{{folder.id}}">{{folder.path}}</option>
|
||||
</select>
|
||||
<button type="button" (click)="getRootFolders(form)" class="btn btn-primary-outline col-md-4 col-md-push-1">
|
||||
Load Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<small *ngIf="form.get('rootPath').hasError('required')" class="error-text">A Default Root Path is required</small>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="rootFoldersAnime" class="control-label">Default Root Folders (Anime)</label>
|
||||
<div id="rootFoldersAnime">
|
||||
<select class="form-control form-control-custom" formControlName="rootPathAnime">
|
||||
<select class="form-control form-control-custom col-md-5 form-half" formControlName="rootPathAnime">
|
||||
<option *ngFor="let folder of rootFoldersAnime" value="{{folder.id}}">{{folder.path}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-12" *ngIf="form.controls.v3.value">
|
||||
<label for="select" class="control-label">Language Profiles
|
||||
<i *ngIf="form.get('languageProfile').hasError('required')" class="fa fa-exclamation-circle error-text" pTooltip="A Language Profile is required"></i>
|
||||
</label>
|
||||
<div id="langaugeProfile">
|
||||
<select formControlName="languageProfile" class="form-control form-control-custom col-md-5 form-half"
|
||||
id="select" [ngClass]="{'form-error': form.get('languageProfile').hasError('required')}">
|
||||
<option *ngFor="let lang of languageProfiles" [ngValue]="lang.id">{{lang.name}}</option>
|
||||
</select>
|
||||
<button (click)="getLanguageProfiles(form)" type="button" class="btn btn-primary-outline col-md-4 col-md-push-1">Load
|
||||
Languages <span *ngIf="langRunning" class="fa fa-spinner fa-spin"> </span></button>
|
||||
|
||||
<div class="form-group">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group col-md-12">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="SeasonFolders" name="SeasonFolders" formControlName="seasonFolders">
|
||||
<label for="SeasonFolders">Enable season folders</label>
|
||||
|
@ -112,25 +150,26 @@
|
|||
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="advanced" style="color:#ff761b">
|
||||
<div class="form-group col-md-12" *ngIf="advanced" style="color:#ff761b">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="addOnly" formControlName="addOnly">
|
||||
<label for="addOnly">Do not search</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button type="button" (click)="test(form)" class="btn btn-primary-outline">Test Connectivity <span id="spinner"> </span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group col-md-6">
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary-outline ">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<div>
|
||||
<button type="button" (click)="test(form)" class="btn btn-primary-outline">Test Connectivity
|
||||
<span id="spinner"> </span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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<SonarrSettings> settings)
|
||||
public SonarrController(ISonarrApi sonarr, ISonarrV3Api sonarrv3, ISettingsService<SonarrSettings> settings)
|
||||
{
|
||||
SonarrApi = sonarr;
|
||||
SonarrV3Api = sonarrv3;
|
||||
SonarrSettings = settings;
|
||||
SonarrSettings.ClearCache();
|
||||
}
|
||||
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISonarrV3Api SonarrV3Api { get; }
|
||||
private ISettingsService<SonarrSettings> SonarrSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -82,5 +85,36 @@ namespace Ombi.Controllers.External
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sonarr V3 language profiles
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("v3/LanguageProfiles")]
|
||||
[PowerUser]
|
||||
public async Task<IEnumerable<LanguageProfiles>> GetLanguageProfiles()
|
||||
{
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
return await SonarrV3Api.LanguageProfiles(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sonarr V3 language profiles
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("v3/LanguageProfiles")]
|
||||
[PowerUser]
|
||||
public async Task<IEnumerable<LanguageProfiles>> GetLanguageProfiles([FromBody] SonarrSettings settings)
|
||||
{
|
||||
return await SonarrV3Api.LanguageProfiles(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -863,6 +863,7 @@ namespace Ombi.Controllers
|
|||
{
|
||||
var ombiUser = new OmbiUser
|
||||
{
|
||||
Alias = user.Alias,
|
||||
Email = user.EmailAddress,
|
||||
UserName = user.UserName
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue