mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-15 01:32:55 -07:00
Lots of fixes. Becoming more stable now. #865
This commit is contained in:
parent
1c6ddc74cb
commit
dcf97a1008
31 changed files with 1021 additions and 381 deletions
|
@ -6,13 +6,13 @@ namespace Ombi.Api.Discord
|
||||||
{
|
{
|
||||||
public class DiscordApi : IDiscordApi
|
public class DiscordApi : IDiscordApi
|
||||||
{
|
{
|
||||||
public DiscordApi()
|
public DiscordApi(IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Endpoint => "https://discordapp.com/api/"; //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
|
private string Endpoint => "https://discordapp.com/api/"; //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
public async Task SendMessage(DiscordWebhookBody body, string webhookId, string webhookToken)
|
public async Task SendMessage(DiscordWebhookBody body, string webhookId, string webhookToken)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,12 +9,12 @@ namespace Ombi.Api.Emby
|
||||||
{
|
{
|
||||||
public class EmbyApi : IEmbyApi
|
public class EmbyApi : IEmbyApi
|
||||||
{
|
{
|
||||||
public EmbyApi()
|
public EmbyApi(IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all users from the Emby Instance
|
/// Returns all users from the Emby Instance
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System.Net.Http;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Ombi.Api.Plex.Models;
|
using Ombi.Api.Plex.Models;
|
||||||
using Ombi.Api.Plex.Models.Server;
|
using Ombi.Api.Plex.Models.Server;
|
||||||
using Ombi.Api.Plex.Models.Status;
|
using Ombi.Api.Plex.Models.Status;
|
||||||
|
@ -10,12 +8,12 @@ namespace Ombi.Api.Plex
|
||||||
{
|
{
|
||||||
public class PlexApi : IPlexApi
|
public class PlexApi : IPlexApi
|
||||||
{
|
{
|
||||||
public PlexApi()
|
public PlexApi(IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
||||||
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
||||||
|
|
|
@ -12,18 +12,18 @@ namespace Ombi.Api.Radarr
|
||||||
{
|
{
|
||||||
public class RadarrApi : IRadarrApi
|
public class RadarrApi : IRadarrApi
|
||||||
{
|
{
|
||||||
public RadarrApi(ILogger<RadarrApi> logger)
|
public RadarrApi(ILogger<RadarrApi> logger, IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
private ILogger<RadarrApi> Logger { get; }
|
private ILogger<RadarrApi> Logger { get; }
|
||||||
|
|
||||||
public async Task<List<RadarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
public async Task<List<RadarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/profile", HttpMethod.Get);
|
var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
|
||||||
|
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
return await Api.Request<List<RadarrProfile>>(request);
|
return await Api.Request<List<RadarrProfile>>(request);
|
||||||
|
@ -31,7 +31,7 @@ namespace Ombi.Api.Radarr
|
||||||
|
|
||||||
public async Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
public async Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/rootfolder", HttpMethod.Get);
|
var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
|
||||||
|
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
return await Api.Request<List<RadarrRootFolder>>(request);
|
return await Api.Request<List<RadarrRootFolder>>(request);
|
||||||
|
@ -39,7 +39,7 @@ namespace Ombi.Api.Radarr
|
||||||
|
|
||||||
public async Task<SystemStatus> SystemStatus(string apiKey, string baseUrl)
|
public async Task<SystemStatus> SystemStatus(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/system/status", HttpMethod.Get);
|
var request = new Request("/api/system/status", baseUrl, HttpMethod.Get);
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
return await Api.Request<SystemStatus>(request);
|
return await Api.Request<SystemStatus>(request);
|
||||||
|
@ -47,7 +47,7 @@ namespace Ombi.Api.Radarr
|
||||||
|
|
||||||
public async Task<List<MovieResponse>> GetMovies(string apiKey, string baseUrl)
|
public async Task<List<MovieResponse>> GetMovies(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/movie", HttpMethod.Get);
|
var request = new Request("/api/movie", baseUrl, HttpMethod.Get);
|
||||||
AddHeaders(request, apiKey);
|
AddHeaders(request, apiKey);
|
||||||
|
|
||||||
return await Api.Request<List<MovieResponse>>(request);
|
return await Api.Request<List<MovieResponse>>(request);
|
||||||
|
@ -55,7 +55,7 @@ namespace Ombi.Api.Radarr
|
||||||
|
|
||||||
public async Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow = false)
|
public async Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow = false)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/movie", HttpMethod.Post);
|
var request = new Request("/api/movie", baseUrl, HttpMethod.Post);
|
||||||
|
|
||||||
var options = new RadarrAddMovieResponse
|
var options = new RadarrAddMovieResponse
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,7 @@ namespace Ombi.Api.Radarr
|
||||||
}
|
}
|
||||||
catch (JsonSerializationException jse)
|
catch (JsonSerializationException jse)
|
||||||
{
|
{
|
||||||
Logger.LogError(LoggingEvents.RadarrApiException,jse, "Error When adding movie to Radarr");
|
Logger.LogError(LoggingEvents.RadarrApiException, jse, "Error When adding movie to Radarr");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,16 @@ namespace Ombi.Api.Sonarr
|
||||||
public class SonarrApi : ISonarrApi
|
public class SonarrApi : ISonarrApi
|
||||||
{
|
{
|
||||||
|
|
||||||
public SonarrApi()
|
public SonarrApi(IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
public async Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
public async Task<IEnumerable<SonarrProfile>> GetProfiles(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/profile", HttpMethod.Get);
|
var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
|
||||||
|
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace Ombi.Api.Sonarr
|
||||||
|
|
||||||
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
public async Task<IEnumerable<SonarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
|
||||||
{
|
{
|
||||||
var request = new Request(baseUrl, "/api/rootfolder", HttpMethod.Get);
|
var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
|
||||||
|
|
||||||
request.AddHeader("X-Api-Key", apiKey);
|
request.AddHeader("X-Api-Key", apiKey);
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,14 @@ namespace Ombi.Api.TvMaze
|
||||||
{
|
{
|
||||||
public class TvMazeApi : ITvMazeApi
|
public class TvMazeApi : ITvMazeApi
|
||||||
{
|
{
|
||||||
public TvMazeApi(ILogger<TvMazeApi> logger)
|
public TvMazeApi(ILogger<TvMazeApi> logger, IApi api)
|
||||||
{
|
{
|
||||||
Api = new Ombi.Api.Api();
|
Api = api;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
//Mapper = mapper;
|
//Mapper = mapper;
|
||||||
}
|
}
|
||||||
private string Uri = "http://api.tvmaze.com";
|
private string Uri = "http://api.tvmaze.com";
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
private ILogger<TvMazeApi> Logger { get; }
|
private ILogger<TvMazeApi> Logger { get; }
|
||||||
|
|
||||||
public async Task<List<TvMazeSearch>> Search(string searchTerm)
|
public async Task<List<TvMazeSearch>> Search(string searchTerm)
|
||||||
|
|
|
@ -4,11 +4,20 @@ using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
namespace Ombi.Api
|
namespace Ombi.Api
|
||||||
{
|
{
|
||||||
public class Api
|
public class Api : IApi
|
||||||
{
|
{
|
||||||
|
public Api(ILogger<Api> log)
|
||||||
|
{
|
||||||
|
Logger = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILogger<Api> Logger { get; }
|
||||||
|
|
||||||
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
|
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
@ -36,12 +45,10 @@ namespace Ombi.Api
|
||||||
{
|
{
|
||||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
// Logging
|
Logger.LogError(LoggingEvents.ApiException, $"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}");
|
||||||
}
|
}
|
||||||
// do something with the response
|
// do something with the response
|
||||||
var data = httpResponseMessage.Content;
|
var data = httpResponseMessage.Content;
|
||||||
|
|
||||||
|
|
||||||
var receivedString = await data.ReadAsStringAsync();
|
var receivedString = await data.ReadAsStringAsync();
|
||||||
if (request.ContentType == ContentType.Json)
|
if (request.ContentType == ContentType.Json)
|
||||||
{
|
{
|
||||||
|
@ -82,7 +89,7 @@ namespace Ombi.Api
|
||||||
{
|
{
|
||||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
// Logging
|
Logger.LogError(LoggingEvents.ApiException, $"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}");
|
||||||
}
|
}
|
||||||
// do something with the response
|
// do something with the response
|
||||||
var data = httpResponseMessage.Content;
|
var data = httpResponseMessage.Content;
|
||||||
|
@ -116,7 +123,7 @@ namespace Ombi.Api
|
||||||
{
|
{
|
||||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
// Logging
|
Logger.LogError(LoggingEvents.ApiException, $"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
src/Ombi.Api/IApi.cs
Normal file
11
src/Ombi.Api/IApi.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api
|
||||||
|
{
|
||||||
|
public interface IApi
|
||||||
|
{
|
||||||
|
Task Request(Request request);
|
||||||
|
Task<T> Request<T>(Request request);
|
||||||
|
Task<string> RequestContent(Request request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,13 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -29,6 +29,7 @@ using Ombi.Store.Repository;
|
||||||
using Ombi.Core.Rules;
|
using Ombi.Core.Rules;
|
||||||
using Ombi.Notifications.Agents;
|
using Ombi.Notifications.Agents;
|
||||||
using Ombi.Schedule.Jobs.Radarr;
|
using Ombi.Schedule.Jobs.Radarr;
|
||||||
|
using Ombi.Api;
|
||||||
|
|
||||||
namespace Ombi.DependencyInjection
|
namespace Ombi.DependencyInjection
|
||||||
{
|
{
|
||||||
|
@ -52,11 +53,12 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<ITvRequestEngine, TvRequestEngine>();
|
services.AddTransient<ITvRequestEngine, TvRequestEngine>();
|
||||||
services.AddTransient<ITvSearchEngine, TvSearchEngine>();
|
services.AddTransient<ITvSearchEngine, TvSearchEngine>();
|
||||||
services.AddSingleton<IRuleEvaluator, RuleEvaluator>();
|
services.AddSingleton<IRuleEvaluator, RuleEvaluator>();
|
||||||
services.AddSingleton<IMovieSender, MovieSender>();
|
services.AddTransient<IMovieSender, MovieSender>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterApi(this IServiceCollection services)
|
public static void RegisterApi(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.AddTransient<IApi, Api.Api>();
|
||||||
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
||||||
services.AddTransient<IPlexApi, PlexApi>();
|
services.AddTransient<IPlexApi, PlexApi>();
|
||||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||||
|
@ -78,7 +80,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
||||||
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
||||||
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
|
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>));
|
||||||
}
|
}
|
||||||
public static void RegisterServices(this IServiceCollection services)
|
public static void RegisterServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,6 @@ namespace Ombi.Settings.Settings.Models.External
|
||||||
public string ApiKey { get; set; }
|
public string ApiKey { get; set; }
|
||||||
public string DefaultQualityProfile { get; set; }
|
public string DefaultQualityProfile { get; set; }
|
||||||
public string DefaultRootPath { get; set; }
|
public string DefaultRootPath { get; set; }
|
||||||
public string FullRootPath { get; set; }
|
|
||||||
public bool AddOnly { get; set; }
|
public bool AddOnly { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,7 +13,5 @@
|
||||||
/// The root path.
|
/// The root path.
|
||||||
/// </value>
|
/// </value>
|
||||||
public string RootPath { get; set; }
|
public string RootPath { get; set; }
|
||||||
public string FullRootPath { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,21 +4,24 @@ using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
|
||||||
namespace Ombi.Settings.Settings
|
namespace Ombi.Settings.Settings
|
||||||
{
|
{
|
||||||
public class SettingsServiceV2<T> : ISettingsService<T>
|
public class SettingsService<T> : ISettingsService<T>
|
||||||
where T : Ombi.Settings.Settings.Models.Settings, new()
|
where T : Ombi.Settings.Settings.Models.Settings, new()
|
||||||
{
|
{
|
||||||
|
|
||||||
public SettingsServiceV2(ISettingsRepository repo)
|
public SettingsService(ISettingsRepository repo, IDataProtectionProvider provider)
|
||||||
{
|
{
|
||||||
Repo = repo;
|
Repo = repo;
|
||||||
EntityName = typeof(T).Name;
|
EntityName = typeof(T).Name;
|
||||||
|
_protector = provider.CreateProtector(GetType().FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISettingsRepository Repo { get; }
|
private ISettingsRepository Repo { get; }
|
||||||
private string EntityName { get; }
|
private string EntityName { get; }
|
||||||
|
private readonly IDataProtector _protector;
|
||||||
|
|
||||||
public T GetSettings()
|
public T GetSettings()
|
||||||
{
|
{
|
||||||
|
@ -119,12 +122,12 @@ namespace Ombi.Settings.Settings
|
||||||
|
|
||||||
private string EncryptSettings(GlobalSettings settings)
|
private string EncryptSettings(GlobalSettings settings)
|
||||||
{
|
{
|
||||||
return StringCipher.EncryptString(settings.Content, $"Ombiv3SettingsEncryptionPassword");
|
return _protector.Protect(settings.Content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DecryptSettings(GlobalSettings settings)
|
private string DecryptSettings(GlobalSettings settings)
|
||||||
{
|
{
|
||||||
return StringCipher.DecryptString(settings.Content, $"Ombiv3SettingsEncryptionPassword");
|
return _protector.Unprotect(settings.Content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,16 +10,16 @@ namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
public class TheMovieDbApi : IMovieDbApi
|
public class TheMovieDbApi : IMovieDbApi
|
||||||
{
|
{
|
||||||
public TheMovieDbApi(IMapper mapper)
|
public TheMovieDbApi(IMapper mapper, IApi api)
|
||||||
{
|
{
|
||||||
Api = new Api();
|
Api = api;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMapper Mapper { get; }
|
private IMapper Mapper { get; }
|
||||||
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
||||||
private static readonly string BaseUri ="http://api.themoviedb.org/3/";
|
private static readonly string BaseUri ="http://api.themoviedb.org/3/";
|
||||||
private Api Api { get; }
|
private IApi Api { get; }
|
||||||
|
|
||||||
public async Task<MovieResponseDto> GetMovieInformation(int movieId)
|
public async Task<MovieResponseDto> GetMovieInformation(int movieId)
|
||||||
{
|
{
|
||||||
|
|
20
src/Ombi.Updater/Installer.cs
Normal file
20
src/Ombi.Updater/Installer.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ombi.Updater
|
||||||
|
{
|
||||||
|
public class Installer
|
||||||
|
{
|
||||||
|
public void Start(StartupOptions options)
|
||||||
|
{
|
||||||
|
// Kill Ombi Process
|
||||||
|
var p = new ProcessProvider();
|
||||||
|
|
||||||
|
p.Kill(options.OmbiProcessId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
8
src/Ombi.Updater/Ombi.Updater.csproj
Normal file
8
src/Ombi.Updater/Ombi.Updater.csproj
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
203
src/Ombi.Updater/ProcessProvider.cs
Normal file
203
src/Ombi.Updater/ProcessProvider.cs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ombi.Updater
|
||||||
|
{
|
||||||
|
public class ProcessProvider
|
||||||
|
{
|
||||||
|
public const string OmbiProcessName = "Ombi";
|
||||||
|
|
||||||
|
public int GetCurrentProcessId()
|
||||||
|
{
|
||||||
|
return Process.GetCurrentProcess().Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessInfo GetCurrentProcess()
|
||||||
|
{
|
||||||
|
return ConvertToProcessInfo(Process.GetCurrentProcess());
|
||||||
|
}
|
||||||
|
public bool Exists(int processId)
|
||||||
|
{
|
||||||
|
return GetProcessById(processId) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(string processName)
|
||||||
|
{
|
||||||
|
return GetProcessesByName(processName).Any();
|
||||||
|
}
|
||||||
|
public ProcessInfo GetProcessById(int id)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Finding process with Id:{0}", id);
|
||||||
|
|
||||||
|
var processInfo = ConvertToProcessInfo(Process.GetProcesses().FirstOrDefault(p => p.Id == id));
|
||||||
|
|
||||||
|
if (processInfo == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Unable to find process with ID {0}", id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Found process {0}", processInfo.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return processInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProcessInfo> FindProcessByName(string name)
|
||||||
|
{
|
||||||
|
return GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void WaitForExit(Process process)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Waiting for process {0} to exit.", process.ProcessName);
|
||||||
|
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPriority(int processId, ProcessPriorityClass priority)
|
||||||
|
{
|
||||||
|
var process = Process.GetProcessById(processId);
|
||||||
|
|
||||||
|
Console.WriteLine("Updating [{0}] process priority from {1} to {2}",
|
||||||
|
process.ProcessName,
|
||||||
|
process.PriorityClass,
|
||||||
|
priority);
|
||||||
|
|
||||||
|
process.PriorityClass = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Kill(int processId)
|
||||||
|
{
|
||||||
|
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);
|
||||||
|
|
||||||
|
if (process == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Cannot find process with id: {0}", processId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.Refresh();
|
||||||
|
|
||||||
|
if (process.Id != Process.GetCurrentProcess().Id && process.HasExited)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Process has already exited");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("[{0}]: Killing process", process.Id);
|
||||||
|
process.Kill();
|
||||||
|
Console.WriteLine("[{0}]: Waiting for exit", process.Id);
|
||||||
|
process.WaitForExit();
|
||||||
|
Console.WriteLine("[{0}]: Process terminated successfully", process.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void KillAll(string processName)
|
||||||
|
{
|
||||||
|
var processes = GetProcessesByName(processName);
|
||||||
|
|
||||||
|
Console.WriteLine("Found {0} processes to kill", processes.Count);
|
||||||
|
|
||||||
|
foreach (var processInfo in processes)
|
||||||
|
{
|
||||||
|
if (processInfo.Id == Process.GetCurrentProcess().Id)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Tried killing own process, skipping: {0} [{1}]", processInfo.Id, processInfo.ProcessName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Killing process: {0} [{1}]", processInfo.Id, processInfo.ProcessName);
|
||||||
|
Kill(processInfo.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ProcessInfo ConvertToProcessInfo(Process process)
|
||||||
|
{
|
||||||
|
if (process == null) return null;
|
||||||
|
|
||||||
|
process.Refresh();
|
||||||
|
|
||||||
|
ProcessInfo processInfo = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (process.Id <= 0) return null;
|
||||||
|
|
||||||
|
processInfo = new ProcessInfo
|
||||||
|
{
|
||||||
|
Id = process.Id,
|
||||||
|
Name = process.ProcessName,
|
||||||
|
StartPath = GetExeFileName(process)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.Id != Process.GetCurrentProcess().Id && process.HasExited)
|
||||||
|
{
|
||||||
|
processInfo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processInfo;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetExeFileName(Process process)
|
||||||
|
{
|
||||||
|
return process.MainModule.FileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<System.Diagnostics.Process> GetProcessesByName(string name)
|
||||||
|
{
|
||||||
|
//TODO: move this to an OS specific class
|
||||||
|
|
||||||
|
var monoProcesses = Process.GetProcessesByName("mono")
|
||||||
|
.Union(Process.GetProcessesByName("mono-sgen"))
|
||||||
|
.Where(process =>
|
||||||
|
process.Modules.Cast<ProcessModule>()
|
||||||
|
.Any(module =>
|
||||||
|
module.ModuleName.ToLower() == name.ToLower() + ".exe"));
|
||||||
|
|
||||||
|
var processes = Process.GetProcessesByName(name)
|
||||||
|
.Union(monoProcesses).ToList();
|
||||||
|
|
||||||
|
Console.WriteLine("Found {0} processes with the name: {1}", processes.Count, name);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" - [{0}] {1}", process.Id, process.ProcessName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Don't crash on gettings some log data.
|
||||||
|
}
|
||||||
|
|
||||||
|
return processes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ProcessInfo
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string StartPath { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("{0}:{1} [{2}]", Id, Name ?? "Unknown", StartPath ?? "Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/Ombi.Updater/Program.cs
Normal file
44
src/Ombi.Updater/Program.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ombi.Updater
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("=======================================");
|
||||||
|
Console.WriteLine(" Starting the Ombi Updater");
|
||||||
|
Console.WriteLine("=======================================");
|
||||||
|
|
||||||
|
|
||||||
|
var options = CheckArgs(args);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StartupOptions CheckArgs(string[] args)
|
||||||
|
{
|
||||||
|
if(args.Length <= 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No Args Provided... Exiting");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = new ProcessProvider();
|
||||||
|
var ombiProc = p.FindProcessByName("Ombi").FirstOrDefault().Id;
|
||||||
|
|
||||||
|
return new StartupOptions
|
||||||
|
{
|
||||||
|
ApplicationPath = args[0],
|
||||||
|
OmbiProcessId = ombiProc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StartupOptions
|
||||||
|
{
|
||||||
|
public string ApplicationPath { get; set; }
|
||||||
|
public int OmbiProcessId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Radarr", "Ombi.Api
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Updater", "Ombi.Updater\Ombi.Updater.csproj", "{6294A82D-4915-4FC3-B301-8F985716F34C}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Update", "Update", "{D11FE57E-1E57-491D-A1D4-01AEF4BE5CB6}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -147,6 +151,10 @@ Global
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6294A82D-4915-4FC3-B301-8F985716F34C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6294A82D-4915-4FC3-B301-8F985716F34C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6294A82D-4915-4FC3-B301-8F985716F34C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6294A82D-4915-4FC3-B301-8F985716F34C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -166,5 +174,6 @@ Global
|
||||||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
|
{6294A82D-4915-4FC3-B301-8F985716F34C} = {D11FE57E-1E57-491D-A1D4-01AEF4BE5CB6}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -46,11 +46,12 @@ export interface IPlexLibraries {
|
||||||
|
|
||||||
export interface ISonarrSettings extends IExternalSettings {
|
export interface ISonarrSettings extends IExternalSettings {
|
||||||
apiKey: string,
|
apiKey: string,
|
||||||
enable: boolean,
|
enabled: boolean,
|
||||||
qualityProfile: string,
|
qualityProfile: string,
|
||||||
seasonFolders: boolean,
|
seasonFolders: boolean,
|
||||||
rootPath: string,
|
rootPath: string,
|
||||||
fullRootPath: string,
|
fullRootPath: string,
|
||||||
|
addOnly:boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRadarrSettings extends IExternalSettings {
|
export interface IRadarrSettings extends IExternalSettings {
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button [disabled]="form.invalid" type="submit" (click)="test(form)" class="btn btn-primary-outline">
|
<button [disabled]="form.invalid" type="button" (click)="test(form)" class="btn btn-primary-outline">
|
||||||
Test Connectivity
|
Test
|
||||||
<div id="spinner"></div>
|
<div id="spinner"></div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,26 +1,38 @@
|
||||||
|
|
||||||
<settings-menu></settings-menu>
|
<settings-menu></settings-menu>
|
||||||
<div *ngIf="settings">
|
<div *ngIf="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Radarr Settings</legend>
|
<legend>Radarr Settings</legend>
|
||||||
<div style="float: right;">
|
<div style="float: right;">
|
||||||
<span style="vertical-align: top;">Advanced</span>
|
<span style="vertical-align: top;">Advanced</span>
|
||||||
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
|
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
|
||||||
</div>
|
</div>
|
||||||
|
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||||
|
<div *ngIf="form.invalid" class="alert alert-danger">
|
||||||
|
<div *ngIf="form.dirty">
|
||||||
|
<div *ngIf="form.get('ip').hasError('required')">The IP/Hostname is required</div>
|
||||||
|
<div *ngIf="form.get('port').hasError('required')">The Port is required</div>
|
||||||
|
<div *ngIf="form.get('apiKey').hasError('required')">The Api Key is required</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div *ngIf="form.get('defaultQualityProfile').hasError('required')">A Default Quality Profile is required</div>
|
||||||
|
<div *ngIf="form.get('defaultRootPath').hasError('required')">A Default Root Path is required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="enable" [(ngModel)]="settings.enable" ng-checked="settings.enable">
|
<input type="checkbox" id="enable" formControlName="enabled" ng-checked="form.enabled">
|
||||||
<label for="enable">Enable</label>
|
<label for="enable">Enable</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input hidden="hidden" name="FullRootPath" id="fullRootPath" value="settings.enable" />
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="Ip" class="control-label">Hostname or IP</label>
|
<label for="Ip" class="control-label">Hostname or IP</label>
|
||||||
<div class="">
|
<div class="">
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.ip" id="Ip" name="Ip" placeholder="localhost" value="{{settings.ip}}">
|
<input type="text" class="form-control form-control-custom "id="Ip" name="Ip" placeholder="localhost" formControlName="ip">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -28,7 +40,7 @@
|
||||||
<label for="portNumber" class="control-label">Port</label>
|
<label for="portNumber" class="control-label">Port</label>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.port" id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}">
|
<input type="text" class="form-control form-control-custom " formControlName="port" id="portNumber" name="Port" placeholder="Port Number" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -36,39 +48,39 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ApiKey" class="control-label">API Key</label>
|
<label for="ApiKey" class="control-label">API Key</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.apiKey" id="ApiKey" name="ApiKey" value="{{settings.apiKey}}">
|
<input type="text" class="form-control form-control-custom " id="ApiKey" name="ApiKey" formControlName="apiKey">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
|
||||||
<input type="checkbox" id="Ssl" name="Ssl" ng-checked="settings.ssl"><label for="Ssl">SSL</label>
|
<input type="checkbox" id="Ssl" name="Ssl" formControlName="ssl"><label for="Ssl">SSL</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="SubDir" class="control-label">Base Url</label>
|
<label for="SubDir" class="control-label">Base Url</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom" [(ngModel)]="settings.subDir" id="SubDir" name="SubDir" value="@Model.SubDir">
|
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" name="SubDir">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" (click)="getProfiles()" class="btn btn-primary-outline">Get Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"> </span></button>
|
<button type="submit" (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>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="select" class="control-label">Quality Profiles</label>
|
<label for="select" class="control-label">Quality Profiles</label>
|
||||||
<div id="profiles">
|
<div id="profiles">
|
||||||
<select class="form-control form-control-custom" id="select" *ngFor='let quality of qualities'>
|
<select formControlName="defaultQualityProfile" class="form-control form-control-custom" id="select">
|
||||||
<option [selected]="qualityProfile === quality.name" [ngValue]="selectedQuality" value='{{quality.id}}'>{{quality.name}}</option>
|
<option *ngFor='let quality of qualities' value='{{quality.id}}'>{{quality.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" (click)="getRootFolders()" class="btn btn-primary-outline">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin" ></span></button>
|
<button type="submit" (click)="getRootFolders(form)" class="btn btn-primary-outline">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin"></span></button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -76,38 +88,30 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||||
<div id="rootFolders">
|
<div id="rootFolders">
|
||||||
<select class="form-control form-control-custom" *ngFor='let folder of rootFolders'>
|
<select formControlName="defaultRootPath" class="form-control form-control-custom">
|
||||||
<option [selected]="rootPath === folder.name" [ngValue]="selectedRootFolder" value='{{folder.id}}'>{{folder.name}}</option>
|
<option *ngFor='let folder of rootFolders' value='{{folder.id}}'>{{folder.path}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" *ngIf="advanced">
|
<div class="form-group" *ngIf="advanced">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="addOnly" [(ngModel)]="settings.addOnly" ng-checked="settings.addOnly">
|
<input type="checkbox" id="addOnly" formControlName="addOnly">
|
||||||
<label for="addOnly">Do not search</label>
|
<label for="addOnly">Do not search</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<input type="checkbox" id="SeasonFolders" name="SeasonFolders" ng-checked="settings.seasonFolders">
|
|
||||||
<label for="SeasonFolders">Enable season folders</label>
|
|
||||||
</div>
|
|
||||||
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button (click)="test()" type="submit" class="btn btn-primary-outline">Test Connectivity <span id="spinner" ></span></button>
|
<button [disabled]="form.invalid" (click)="test()" class="btn btn-primary-outline">Test Connectivity <span id="spinner"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button (click)="save()" type="submit" class="btn btn-primary-outline ">Submit</button>
|
<button type="submit" [disabled]="form.invalid" class="btn btn-primary-outline ">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Subject } from 'rxjs/Subject';
|
import { Subject } from 'rxjs/Subject';
|
||||||
import "rxjs/add/operator/takeUntil";
|
import "rxjs/add/operator/takeUntil";
|
||||||
|
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
import { IRadarrSettings } from '../../interfaces/ISettings';
|
import { IRadarrSettings } from '../../interfaces/ISettings';
|
||||||
import { IRadarrProfile, IRadarrRootFolder } from '../../interfaces/IRadarr';
|
import { IRadarrProfile, IRadarrRootFolder } from '../../interfaces/IRadarr';
|
||||||
|
@ -14,35 +15,53 @@ import { NotificationService } from "../../services/notification.service";
|
||||||
})
|
})
|
||||||
export class RadarrComponent implements OnInit {
|
export class RadarrComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService, private radarrService: RadarrService, private notificationService: NotificationService) { }
|
constructor(private settingsService: SettingsService, private radarrService: RadarrService, private notificationService: NotificationService,
|
||||||
|
private fb: FormBuilder) { }
|
||||||
settings: IRadarrSettings;
|
|
||||||
|
|
||||||
qualities: IRadarrProfile[];
|
qualities: IRadarrProfile[];
|
||||||
rootFolders: IRadarrRootFolder[];
|
rootFolders: IRadarrRootFolder[];
|
||||||
|
|
||||||
selectedRootFolder: IRadarrRootFolder;
|
|
||||||
selectedQuality: IRadarrProfile;
|
|
||||||
|
|
||||||
profilesRunning: boolean;
|
profilesRunning: boolean;
|
||||||
rootFoldersRunning: boolean;
|
rootFoldersRunning: boolean;
|
||||||
|
|
||||||
advanced = false;
|
advanced = false;
|
||||||
private subscriptions = new Subject<void>();
|
private subscriptions = new Subject<void>();
|
||||||
|
|
||||||
|
form : FormGroup;
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
this.settingsService.getRadarr()
|
this.settingsService.getRadarr()
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.settings = x;
|
|
||||||
|
this.form = this.fb.group({
|
||||||
|
enabled: [x.enabled],
|
||||||
|
apiKey: [x.apiKey, [Validators.required]],
|
||||||
|
defaultQualityProfile: [x.defaultQualityProfile, [Validators.required]],
|
||||||
|
defaultRootPath: [x.defaultRootPath, [Validators.required]],
|
||||||
|
ssl: [x.ssl],
|
||||||
|
subDir: [x.subDir],
|
||||||
|
ip: [x.ip, [Validators.required]],
|
||||||
|
port: [x.port, [Validators.required]],
|
||||||
|
addOnly: [x.addOnly],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (x.defaultQualityProfile)
|
||||||
|
{
|
||||||
|
this.getProfiles(this.form);
|
||||||
|
}
|
||||||
|
if (x.defaultRootPath)
|
||||||
|
{
|
||||||
|
this.getRootFolders(this.form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getProfiles() {
|
getProfiles(form: FormGroup) {
|
||||||
this.profilesRunning = true;
|
this.profilesRunning = true;
|
||||||
this.radarrService.getQualityProfiles(this.settings).subscribe(x => {
|
this.radarrService.getQualityProfiles(form.value).subscribe(x => {
|
||||||
this.qualities = x;
|
this.qualities = x;
|
||||||
|
|
||||||
this.profilesRunning = false;
|
this.profilesRunning = false;
|
||||||
|
@ -50,9 +69,9 @@ export class RadarrComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootFolders() {
|
getRootFolders(form: FormGroup) {
|
||||||
this.rootFoldersRunning = true;
|
this.rootFoldersRunning = true;
|
||||||
this.radarrService.getRootFolders(this.settings).subscribe(x => {
|
this.radarrService.getRootFolders(form.value).subscribe(x => {
|
||||||
this.rootFolders = x;
|
this.rootFolders = x;
|
||||||
|
|
||||||
this.rootFoldersRunning = false;
|
this.rootFoldersRunning = false;
|
||||||
|
@ -64,14 +83,21 @@ export class RadarrComponent implements OnInit {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
onSubmit(form: FormGroup) {
|
||||||
this.settingsService.saveRadarr(this.settings).subscribe(x => {
|
if (form.invalid) {
|
||||||
|
this.notificationService.error("Validation", "Please check your entered values");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = <IRadarrSettings>form.value;
|
||||||
|
this.settingsService.saveRadarr(settings).subscribe(x => {
|
||||||
if (x) {
|
if (x) {
|
||||||
this.notificationService.success("Settings Saved", "Successfully saved Radarr settings");
|
this.notificationService.success("Settings Saved", "Successfully saved Radarr settings");
|
||||||
} else {
|
} else {
|
||||||
this.notificationService.success("Settings Saved", "There was an error when saving the Radarr settings");
|
this.notificationService.success("Settings Saved", "There was an error when saving the Radarr settings");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { RadarrComponent } from './radarr/radarr.component';
|
||||||
import { LandingPageComponent } from './landingpage/landingpage.component';
|
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||||
import { CustomizationComponent } from './customization/customization.component';
|
import { CustomizationComponent } from './customization/customization.component';
|
||||||
import { EmailNotificationComponent } from './notifications/emailnotification.component';
|
import { EmailNotificationComponent } from './notifications/emailnotification.component';
|
||||||
|
import { DiscordComponent } from './notifications/discord.component';
|
||||||
import { NotificationTemplate } from './notifications/notificationtemplate.component';
|
import { NotificationTemplate } from './notifications/notificationtemplate.component';
|
||||||
|
|
||||||
import { SettingsMenuComponent } from './settingsmenu.component';
|
import { SettingsMenuComponent } from './settingsmenu.component';
|
||||||
|
@ -36,6 +37,7 @@ const routes: Routes = [
|
||||||
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'Settings/Email', component: EmailNotificationComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/Email', component: EmailNotificationComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'Settings/Discord', component: DiscordComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -60,11 +62,12 @@ const routes: Routes = [
|
||||||
EmbyComponent,
|
EmbyComponent,
|
||||||
LandingPageComponent,
|
LandingPageComponent,
|
||||||
CustomizationComponent,
|
CustomizationComponent,
|
||||||
|
DiscordComponent,
|
||||||
SonarrComponent,
|
SonarrComponent,
|
||||||
RadarrComponent,
|
RadarrComponent,
|
||||||
EmailNotificationComponent,
|
EmailNotificationComponent,
|
||||||
HumanizePipe,
|
HumanizePipe,
|
||||||
NotificationTemplate
|
NotificationTemplate,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule
|
RouterModule
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>
|
||||||
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Discord']">Discord</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushbullet']">Pushbullet</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushbullet']">Pushbullet</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushover']">Pushover</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushover']">Pushover</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,22 +1,36 @@
|
||||||
|
|
||||||
<settings-menu></settings-menu>
|
<settings-menu></settings-menu>
|
||||||
<div *ngIf="settings">
|
<div *ngIf="form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Sonarr Settings</legend>
|
<legend>Sonarr Settings</legend>
|
||||||
|
<div style="float: right;">
|
||||||
|
<span style="vertical-align: top;">Advanced</span>
|
||||||
|
<p-inputSwitch id="customInputSwitch" [(ngModel)]="advanced"></p-inputSwitch>
|
||||||
|
</div>
|
||||||
|
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:5%;">
|
||||||
|
<div *ngIf="form.invalid" class="alert alert-danger">
|
||||||
|
<div *ngIf="form.dirty">
|
||||||
|
<div *ngIf="form.get('ip').hasError('required')">The IP/Hostname is required</div>
|
||||||
|
<div *ngIf="form.get('port').hasError('required')">The Port is required</div>
|
||||||
|
<div *ngIf="form.get('apiKey').hasError('required')">The Api Key is required</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div *ngIf="form.get('qualityProfile').hasError('required')">A Default Quality Profile is required</div>
|
||||||
|
<div *ngIf="form.get('rootPath').hasError('required')">A Default Root Path is required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" [(ngModel)]="settings.enable" ng-checked="settings.enable">
|
<input type="checkbox" id="enable" formControlName="enabled" >
|
||||||
<label for="enable">Enable</label>
|
<label for="enable">Enable</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input hidden="hidden" name="FullRootPath" id="fullRootPath" value="settings.enable" />
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="Ip" class="control-label">Sonarr Hostname or IP</label>
|
<label for="Ip" class="control-label">Sonarr Hostname or IP</label>
|
||||||
<div class="">
|
<div class="">
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.ip" id="Ip" name="Ip" placeholder="localhost" value="{{settings.ip}}">
|
<input type="text" class="form-control form-control-custom " formControlName="ip" id="Ip" name="Ip" placeholder="localhost" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -24,7 +38,7 @@
|
||||||
<label for="portNumber" class="control-label">Port</label>
|
<label for="portNumber" class="control-label">Port</label>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.port" id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}">
|
<input type="text" class="form-control form-control-custom " formControlName="port" id="portNumber" name="Port" placeholder="Port Number" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -32,39 +46,39 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ApiKey" class="control-label">Sonarr API Key</label>
|
<label for="ApiKey" class="control-label">Sonarr API Key</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom " [(ngModel)]="settings.apiKey" id="ApiKey" name="ApiKey" value="{{settings.apiKey}}">
|
<input type="text" class="form-control form-control-custom " formControlName="apiKey" id="ApiKey" name="ApiKey">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
|
||||||
<input type="checkbox" id="Ssl" name="Ssl" ng-checked="settings.ssl"><label for="Ssl">SSL</label>
|
<input type="checkbox" id="Ssl" name="Ssl" formControlName="ssl"><label for="Ssl">SSL</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="SubDir" class="control-label">Sonarr Base Url</label>
|
<label for="SubDir" class="control-label">Sonarr Base Url</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom" [(ngModel)]="settings.subDir" id="SubDir" name="SubDir" value="@Model.SubDir">
|
<input type="text" class="form-control form-control-custom" formControlName="subDir" id="SubDir" name="SubDir" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" (click)="getProfiles()" class="btn btn-primary-outline">Get Quality Profiles <span *ngIf="profilesRunning" class="fa fa-spinner fa-spin"></span></button>
|
<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>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="select" class="control-label">Quality Profiles</label>
|
<label for="select" class="control-label">Quality Profiles</label>
|
||||||
<div id="profiles">
|
<div id="profiles">
|
||||||
<select class="form-control form-control-custom" id="select" *ngFor='let quality of qualities'>
|
<select class="form-control form-control-custom" id="select" formControlName="qualityProfile">
|
||||||
<option [selected]="qualityProfile === quality.name" [ngValue]="selectedQuality" value='{{quality.id}}'>{{quality.name}}</option>
|
<option *ngFor='let quality of qualities' value='{{quality.id}}'>{{quality.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" (click)="getRootFolders()" class="btn btn-primary-outline">Get Root Folders <span *ngIf="rootFoldersRunning" class="fa fa-spinner fa-spin" ></span></button>
|
<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>
|
||||||
|
|
||||||
|
@ -72,8 +86,8 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||||
<div id="rootFolders">
|
<div id="rootFolders">
|
||||||
<select class="form-control form-control-custom" *ngFor='let folder of rootFolders'>
|
<select class="form-control form-control-custom" formControlName="rootPath">
|
||||||
<option [selected]="rootPath === folder.name" [ngValue]="selectedRootFolder" value='{{folder.id}}'>{{folder.name}}</option>
|
<option *ngFor='let folder of rootFolders' value='{{folder.id}}'>{{folder.path}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,22 +95,31 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<input type="checkbox" id="SeasonFolders" name="SeasonFolders" ng-checked="settings.seasonFolders">
|
<input type="checkbox" id="SeasonFolders" name="SeasonFolders"formControlName="seasonFolders">
|
||||||
<label for="SeasonFolders">Enable season folders</label>
|
<label for="SeasonFolders">Enable season folders</label>
|
||||||
</div>
|
</div>
|
||||||
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
|
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngIf="advanced">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="addOnly" formControlName="addOnly">
|
||||||
|
<label for="addOnly">Do not search</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button (click)="test()" type="submit" class="btn btn-primary-outline">Test Connectivity <span id="spinner"> </span></button>
|
<button type="button" (click)="test()" class="btn btn-primary-outline">Test Connectivity <span id="spinner"> </span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button (click)="save()" type="submit" class="btn btn-primary-outline ">Submit</button>
|
<button type="submit" class="btn btn-primary-outline ">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { Subject } from 'rxjs/Subject';
|
import { Subject } from 'rxjs/Subject';
|
||||||
import "rxjs/add/operator/takeUntil";
|
import "rxjs/add/operator/takeUntil";
|
||||||
|
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
import { ISonarrSettings } from '../../interfaces/ISettings'
|
|
||||||
import { ISonarrProfile, ISonarrRootFolder } from '../../interfaces/ISonarr'
|
import { ISonarrProfile, ISonarrRootFolder } from '../../interfaces/ISonarr'
|
||||||
import { SettingsService } from '../../services/settings.service';
|
import { SettingsService } from '../../services/settings.service';
|
||||||
import { SonarrService } from '../../services/applications/sonarr.service';
|
import { SonarrService } from '../../services/applications/sonarr.service';
|
||||||
|
@ -14,9 +14,8 @@ import { NotificationService } from "../../services/notification.service";
|
||||||
})
|
})
|
||||||
export class SonarrComponent implements OnInit, OnDestroy {
|
export class SonarrComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService, private sonarrService: SonarrService, private notificationService: NotificationService) { }
|
constructor(private settingsService: SettingsService, private sonarrService: SonarrService, private notificationService: NotificationService,
|
||||||
|
private fb : FormBuilder) { }
|
||||||
settings: ISonarrSettings;
|
|
||||||
|
|
||||||
qualities: ISonarrProfile[];
|
qualities: ISonarrProfile[];
|
||||||
rootFolders: ISonarrRootFolder[];
|
rootFolders: ISonarrRootFolder[];
|
||||||
|
@ -27,20 +26,43 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
||||||
profilesRunning: boolean;
|
profilesRunning: boolean;
|
||||||
rootFoldersRunning: boolean;
|
rootFoldersRunning: boolean;
|
||||||
private subscriptions = new Subject<void>();
|
private subscriptions = new Subject<void>();
|
||||||
|
form : FormGroup;
|
||||||
|
advanced = false;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
this.settingsService.getSonarr()
|
this.settingsService.getSonarr()
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.settings = x;
|
|
||||||
|
this.form = this.fb.group({
|
||||||
|
enabled: [x.enabled],
|
||||||
|
apiKey: [x.apiKey, [Validators.required]],
|
||||||
|
qualityProfile: [x.qualityProfile, [Validators.required]],
|
||||||
|
rootPath: [x.rootPath, [Validators.required]],
|
||||||
|
ssl: [x.ssl],
|
||||||
|
subDir: [x.subDir],
|
||||||
|
ip: [x.ip, [Validators.required]],
|
||||||
|
port: [x.port, [Validators.required]],
|
||||||
|
addOnly: [x.addOnly],
|
||||||
|
seasonFolders: [x.seasonFolders],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (x.qualityProfile)
|
||||||
|
{
|
||||||
|
this.getProfiles(this.form);
|
||||||
|
}
|
||||||
|
if (x.rootPath)
|
||||||
|
{
|
||||||
|
this.getRootFolders(this.form);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getProfiles() {
|
getProfiles(form:FormGroup) {
|
||||||
this.profilesRunning = true;
|
this.profilesRunning = true;
|
||||||
this.sonarrService.getQualityProfiles(this.settings)
|
this.sonarrService.getQualityProfiles(form.value)
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.qualities = x;
|
this.qualities = x;
|
||||||
|
@ -50,9 +72,9 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootFolders() {
|
getRootFolders(form:FormGroup) {
|
||||||
this.rootFoldersRunning = true;
|
this.rootFoldersRunning = true;
|
||||||
this.sonarrService.getRootFolders(this.settings)
|
this.sonarrService.getRootFolders(form.value)
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
this.rootFolders = x;
|
this.rootFolders = x;
|
||||||
|
@ -66,17 +88,12 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
onSubmit(form:FormGroup) {
|
||||||
|
if (form.invalid) {
|
||||||
if (!this.qualities || !this.rootFolders) {
|
this.notificationService.error("Validation", "Please check your entered values");
|
||||||
|
return
|
||||||
this.notificationService.error("Settings Saved", "Please make sure we have selected a quality profile");
|
|
||||||
}
|
}
|
||||||
if (!this.rootFolders) {
|
this.settingsService.saveSonarr(form.value)
|
||||||
|
|
||||||
this.notificationService.error("Settings Saved", "Please make sure we have a root folder");
|
|
||||||
}
|
|
||||||
this.settingsService.saveSonarr(this.settings)
|
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.subscribe(x => {
|
||||||
if (x) {
|
if (x) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace Ombi.Controllers.External
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="service">The service.</param>
|
/// <param name="service">The service.</param>
|
||||||
/// <param name="notification">The notification.</param>
|
/// <param name="notification">The notification.</param>
|
||||||
|
/// <param name="emailN">The notification.</param>
|
||||||
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN)
|
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN)
|
||||||
{
|
{
|
||||||
Service = service;
|
Service = service;
|
||||||
|
@ -42,7 +43,7 @@ namespace Ombi.Controllers.External
|
||||||
public bool Discord([FromBody] DiscordNotificationSettings settings)
|
public bool Discord([FromBody] DiscordNotificationSettings settings)
|
||||||
{
|
{
|
||||||
settings.Enabled = true;
|
settings.Enabled = true;
|
||||||
BackgroundJob.Enqueue(() => Service.PublishTest(new NotificationOptions{NotificationType = NotificationType.Test}, settings, DiscordNotification));
|
BackgroundJob.Enqueue(() => Service.PublishTest(new NotificationOptions{NotificationType = NotificationType.Test}, settings, (DiscordNotification)DiscordNotification));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,7 @@ namespace Ombi.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model.</param>
|
/// <param name="model">The model.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("notifications/email")]
|
[HttpPost("notifications/discord")]
|
||||||
public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model)
|
public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model)
|
||||||
{
|
{
|
||||||
// Save the email settings
|
// Save the email settings
|
||||||
|
@ -247,7 +247,7 @@ namespace Ombi.Controllers
|
||||||
/// Gets the discord Notification Settings.
|
/// Gets the discord Notification Settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("notifications/email")]
|
[HttpGet("notifications/discord")]
|
||||||
public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings()
|
public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings()
|
||||||
{
|
{
|
||||||
var emailSettings = await Get<DiscordNotificationSettings>();
|
var emailSettings = await Get<DiscordNotificationSettings>();
|
||||||
|
@ -262,7 +262,7 @@ namespace Ombi.Controllers
|
||||||
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
|
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
|
||||||
{
|
{
|
||||||
var templates = await TemplateRepository.GetAllTemplates(agent);
|
var templates = await TemplateRepository.GetAllTemplates(agent);
|
||||||
return templates.ToList();
|
return templates.OrderBy(x => x.NotificationType.ToString()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
737
src/Ombi/package-lock.json
generated
737
src/Ombi/package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue