mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-14 17:22:54 -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 DiscordApi()
|
||||
public DiscordApi(IApi api)
|
||||
{
|
||||
Api = new Api();
|
||||
Api = api;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -9,12 +9,12 @@ namespace Ombi.Api.Emby
|
|||
{
|
||||
public class EmbyApi : IEmbyApi
|
||||
{
|
||||
public EmbyApi()
|
||||
public EmbyApi(IApi api)
|
||||
{
|
||||
Api = new Api();
|
||||
Api = api;
|
||||
}
|
||||
|
||||
private Api Api { get; }
|
||||
private IApi Api { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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 Newtonsoft.Json;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.Server;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
|
@ -10,12 +8,12 @@ namespace Ombi.Api.Plex
|
|||
{
|
||||
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 FriendsUri = "https://plex.tv/pms/friends/all";
|
||||
|
|
|
@ -12,18 +12,18 @@ namespace Ombi.Api.Radarr
|
|||
{
|
||||
public class RadarrApi : IRadarrApi
|
||||
{
|
||||
public RadarrApi(ILogger<RadarrApi> logger)
|
||||
public RadarrApi(ILogger<RadarrApi> logger, IApi api)
|
||||
{
|
||||
Api = new Api();
|
||||
Api = api;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private Api Api { get; }
|
||||
private IApi Api { get; }
|
||||
private ILogger<RadarrApi> Logger { get; }
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
var request = new Request(baseUrl, "/api/rootfolder", HttpMethod.Get);
|
||||
var request = new Request("/api/rootfolder", baseUrl, HttpMethod.Get);
|
||||
|
||||
AddHeaders(request, apiKey);
|
||||
return await Api.Request<List<RadarrRootFolder>>(request);
|
||||
|
@ -39,7 +39,7 @@ namespace Ombi.Api.Radarr
|
|||
|
||||
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);
|
||||
|
||||
return await Api.Request<SystemStatus>(request);
|
||||
|
@ -47,7 +47,7 @@ namespace Ombi.Api.Radarr
|
|||
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
var request = new Request(baseUrl, "/api/movie", HttpMethod.Post);
|
||||
var request = new Request("/api/movie", baseUrl, HttpMethod.Post);
|
||||
|
||||
var options = new RadarrAddMovieResponse
|
||||
{
|
||||
|
|
|
@ -9,16 +9,16 @@ namespace Ombi.Api.Sonarr
|
|||
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)
|
||||
{
|
||||
var request = new Request(baseUrl, "/api/profile", HttpMethod.Get);
|
||||
var request = new Request("/api/profile", baseUrl, HttpMethod.Get);
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace Ombi.Api.Sonarr
|
|||
|
||||
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);
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ namespace Ombi.Api.TvMaze
|
|||
{
|
||||
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;
|
||||
//Mapper = mapper;
|
||||
}
|
||||
private string Uri = "http://api.tvmaze.com";
|
||||
private Api Api { get; }
|
||||
private IApi Api { get; }
|
||||
private ILogger<TvMazeApi> Logger { get; }
|
||||
|
||||
public async Task<List<TvMazeSearch>> Search(string searchTerm)
|
||||
|
|
|
@ -4,11 +4,20 @@ using System.Net.Http;
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Helpers;
|
||||
|
||||
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
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
|
@ -36,12 +45,10 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
// Logging
|
||||
Logger.LogError(LoggingEvents.ApiException, $"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}");
|
||||
}
|
||||
// do something with the response
|
||||
var data = httpResponseMessage.Content;
|
||||
|
||||
|
||||
var receivedString = await data.ReadAsStringAsync();
|
||||
if (request.ContentType == ContentType.Json)
|
||||
{
|
||||
|
@ -82,7 +89,7 @@ namespace Ombi.Api
|
|||
{
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
// Logging
|
||||
Logger.LogError(LoggingEvents.ApiException, $"StatusCode: {httpResponseMessage.StatusCode}, Reason: {httpResponseMessage.ReasonPhrase}");
|
||||
}
|
||||
// do something with the response
|
||||
var data = httpResponseMessage.Content;
|
||||
|
@ -116,7 +123,7 @@ namespace Ombi.Api
|
|||
{
|
||||
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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -29,6 +29,7 @@ using Ombi.Store.Repository;
|
|||
using Ombi.Core.Rules;
|
||||
using Ombi.Notifications.Agents;
|
||||
using Ombi.Schedule.Jobs.Radarr;
|
||||
using Ombi.Api;
|
||||
|
||||
namespace Ombi.DependencyInjection
|
||||
{
|
||||
|
@ -52,11 +53,12 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ITvRequestEngine, TvRequestEngine>();
|
||||
services.AddTransient<ITvSearchEngine, TvSearchEngine>();
|
||||
services.AddSingleton<IRuleEvaluator, RuleEvaluator>();
|
||||
services.AddSingleton<IMovieSender, MovieSender>();
|
||||
services.AddTransient<IMovieSender, MovieSender>();
|
||||
}
|
||||
|
||||
public static void RegisterApi(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IApi, Api.Api>();
|
||||
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
|
||||
services.AddTransient<IPlexApi, PlexApi>();
|
||||
services.AddTransient<IEmbyApi, EmbyApi>();
|
||||
|
@ -78,7 +80,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
||||
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
||||
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
|
||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>));
|
||||
}
|
||||
public static void RegisterServices(this IServiceCollection services)
|
||||
{
|
||||
|
|
|
@ -8,7 +8,6 @@ namespace Ombi.Settings.Settings.Models.External
|
|||
public string ApiKey { get; set; }
|
||||
public string DefaultQualityProfile { get; set; }
|
||||
public string DefaultRootPath { get; set; }
|
||||
public string FullRootPath { get; set; }
|
||||
public bool AddOnly { get; set; }
|
||||
}
|
||||
}
|
|
@ -13,7 +13,5 @@
|
|||
/// The root path.
|
||||
/// </value>
|
||||
public string RootPath { get; set; }
|
||||
public string FullRootPath { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -4,21 +4,24 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
||||
namespace Ombi.Settings.Settings
|
||||
{
|
||||
public class SettingsServiceV2<T> : ISettingsService<T>
|
||||
public class SettingsService<T> : ISettingsService<T>
|
||||
where T : Ombi.Settings.Settings.Models.Settings, new()
|
||||
{
|
||||
|
||||
public SettingsServiceV2(ISettingsRepository repo)
|
||||
public SettingsService(ISettingsRepository repo, IDataProtectionProvider provider)
|
||||
{
|
||||
Repo = repo;
|
||||
EntityName = typeof(T).Name;
|
||||
_protector = provider.CreateProtector(GetType().FullName);
|
||||
}
|
||||
|
||||
private ISettingsRepository Repo { get; }
|
||||
private string EntityName { get; }
|
||||
private readonly IDataProtector _protector;
|
||||
|
||||
public T GetSettings()
|
||||
{
|
||||
|
@ -119,12 +122,12 @@ namespace Ombi.Settings.Settings
|
|||
|
||||
private string EncryptSettings(GlobalSettings settings)
|
||||
{
|
||||
return StringCipher.EncryptString(settings.Content, $"Ombiv3SettingsEncryptionPassword");
|
||||
return _protector.Protect(settings.Content);
|
||||
}
|
||||
|
||||
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 TheMovieDbApi(IMapper mapper)
|
||||
public TheMovieDbApi(IMapper mapper, IApi api)
|
||||
{
|
||||
Api = new Api();
|
||||
Api = api;
|
||||
Mapper = mapper;
|
||||
}
|
||||
|
||||
private IMapper Mapper { get; }
|
||||
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
||||
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)
|
||||
{
|
||||
|
|
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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -166,5 +174,6 @@ Global
|
|||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {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
|
||||
EndGlobal
|
||||
|
|
|
@ -46,11 +46,12 @@ export interface IPlexLibraries {
|
|||
|
||||
export interface ISonarrSettings extends IExternalSettings {
|
||||
apiKey: string,
|
||||
enable: boolean,
|
||||
enabled: boolean,
|
||||
qualityProfile: string,
|
||||
seasonFolders: boolean,
|
||||
rootPath: string,
|
||||
fullRootPath: string,
|
||||
addOnly:boolean,
|
||||
}
|
||||
|
||||
export interface IRadarrSettings extends IExternalSettings {
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button [disabled]="form.invalid" type="submit" (click)="test(form)" class="btn btn-primary-outline">
|
||||
Test Connectivity
|
||||
<button [disabled]="form.invalid" type="button" (click)="test(form)" class="btn btn-primary-outline">
|
||||
Test
|
||||
<div id="spinner"></div>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,26 +1,38 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="settings">
|
||||
<div *ngIf="form">
|
||||
<fieldset>
|
||||
<legend>Radarr 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)">
|
||||
<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="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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input hidden="hidden" name="FullRootPath" id="fullRootPath" value="settings.enable" />
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Hostname or IP</label>
|
||||
<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>
|
||||
|
||||
|
@ -28,7 +40,7 @@
|
|||
<label for="portNumber" class="control-label">Port</label>
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -36,39 +48,39 @@
|
|||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">API Key</label>
|
||||
<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 class="form-group">
|
||||
<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 class="form-group">
|
||||
<label for="SubDir" class="control-label">Base Url</label>
|
||||
<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 class="form-group">
|
||||
<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 class="form-group">
|
||||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
<div id="profiles">
|
||||
<select class="form-control form-control-custom" id="select" *ngFor='let quality of qualities'>
|
||||
<option [selected]="qualityProfile === quality.name" [ngValue]="selectedQuality" value='{{quality.id}}'>{{quality.name}}</option>
|
||||
<select formControlName="defaultQualityProfile" class="form-control form-control-custom" id="select">
|
||||
<option *ngFor='let quality of qualities' value='{{quality.id}}'>{{quality.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<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>
|
||||
|
||||
|
@ -76,38 +88,30 @@
|
|||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||
<div id="rootFolders">
|
||||
<select class="form-control form-control-custom" *ngFor='let folder of rootFolders'>
|
||||
<option [selected]="rootPath === folder.name" [ngValue]="selectedRootFolder" value='{{folder.id}}'>{{folder.name}}</option>
|
||||
<select formControlName="defaultRootPath" class="form-control form-control-custom">
|
||||
<option *ngFor='let folder of rootFolders' value='{{folder.id}}'>{{folder.path}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="advanced">
|
||||
<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>
|
||||
</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>
|
||||
<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 class="form-group">
|
||||
<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>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||
|
||||
import { IRadarrSettings } from '../../interfaces/ISettings';
|
||||
import { IRadarrProfile, IRadarrRootFolder } from '../../interfaces/IRadarr';
|
||||
|
@ -14,35 +15,53 @@ import { NotificationService } from "../../services/notification.service";
|
|||
})
|
||||
export class RadarrComponent implements OnInit {
|
||||
|
||||
constructor(private settingsService: SettingsService, private radarrService: RadarrService, private notificationService: NotificationService) { }
|
||||
|
||||
settings: IRadarrSettings;
|
||||
|
||||
constructor(private settingsService: SettingsService, private radarrService: RadarrService, private notificationService: NotificationService,
|
||||
private fb: FormBuilder) { }
|
||||
qualities: IRadarrProfile[];
|
||||
rootFolders: IRadarrRootFolder[];
|
||||
|
||||
selectedRootFolder: IRadarrRootFolder;
|
||||
selectedQuality: IRadarrProfile;
|
||||
|
||||
profilesRunning: boolean;
|
||||
rootFoldersRunning: boolean;
|
||||
|
||||
advanced = false;
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
form : FormGroup;
|
||||
ngOnInit(): void {
|
||||
|
||||
this.settingsService.getRadarr()
|
||||
.takeUntil(this.subscriptions)
|
||||
.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.radarrService.getQualityProfiles(this.settings).subscribe(x => {
|
||||
this.radarrService.getQualityProfiles(form.value).subscribe(x => {
|
||||
this.qualities = x;
|
||||
|
||||
this.profilesRunning = false;
|
||||
|
@ -50,9 +69,9 @@ export class RadarrComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
getRootFolders() {
|
||||
getRootFolders(form: FormGroup) {
|
||||
this.rootFoldersRunning = true;
|
||||
this.radarrService.getRootFolders(this.settings).subscribe(x => {
|
||||
this.radarrService.getRootFolders(form.value).subscribe(x => {
|
||||
this.rootFolders = x;
|
||||
|
||||
this.rootFoldersRunning = false;
|
||||
|
@ -64,14 +83,21 @@ export class RadarrComponent implements OnInit {
|
|||
// TODO
|
||||
}
|
||||
|
||||
save() {
|
||||
this.settingsService.saveRadarr(this.settings).subscribe(x => {
|
||||
onSubmit(form: FormGroup) {
|
||||
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) {
|
||||
this.notificationService.success("Settings Saved", "Successfully saved Radarr settings");
|
||||
} else {
|
||||
this.notificationService.success("Settings Saved", "There was an error when saving the Radarr settings");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
|
|
@ -20,6 +20,7 @@ import { RadarrComponent } from './radarr/radarr.component';
|
|||
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||
import { CustomizationComponent } from './customization/customization.component';
|
||||
import { EmailNotificationComponent } from './notifications/emailnotification.component';
|
||||
import { DiscordComponent } from './notifications/discord.component';
|
||||
import { NotificationTemplate } from './notifications/notificationtemplate.component';
|
||||
|
||||
import { SettingsMenuComponent } from './settingsmenu.component';
|
||||
|
@ -36,6 +37,7 @@ const routes: Routes = [
|
|||
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Email', component: EmailNotificationComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Discord', component: DiscordComponent, canActivate: [AuthGuard] },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -60,11 +62,12 @@ const routes: Routes = [
|
|||
EmbyComponent,
|
||||
LandingPageComponent,
|
||||
CustomizationComponent,
|
||||
DiscordComponent,
|
||||
SonarrComponent,
|
||||
RadarrComponent,
|
||||
EmailNotificationComponent,
|
||||
HumanizePipe,
|
||||
NotificationTemplate
|
||||
NotificationTemplate,
|
||||
],
|
||||
exports: [
|
||||
RouterModule
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<ul class="dropdown-menu">
|
||||
<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/Discord']">Discord</a></li>
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushbullet']">Pushbullet</a></li>
|
||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Pushover']">Pushover</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="settings">
|
||||
<div *ngIf="form">
|
||||
<fieldset>
|
||||
<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="checkbox">
|
||||
<input type="checkbox" [(ngModel)]="settings.enable" ng-checked="settings.enable">
|
||||
<input type="checkbox" id="enable" formControlName="enabled" >
|
||||
<label for="enable">Enable</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input hidden="hidden" name="FullRootPath" id="fullRootPath" value="settings.enable" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Ip" class="control-label">Sonarr Hostname or IP</label>
|
||||
<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>
|
||||
|
||||
|
@ -24,7 +38,7 @@
|
|||
<label for="portNumber" class="control-label">Port</label>
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -32,39 +46,39 @@
|
|||
<div class="form-group">
|
||||
<label for="ApiKey" class="control-label">Sonarr API Key</label>
|
||||
<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 class="form-group">
|
||||
<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 class="form-group">
|
||||
<label for="SubDir" class="control-label">Sonarr Base Url</label>
|
||||
<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 class="form-group">
|
||||
<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 class="form-group">
|
||||
<label for="select" class="control-label">Quality Profiles</label>
|
||||
<div id="profiles">
|
||||
<select class="form-control form-control-custom" id="select" *ngFor='let quality of qualities'>
|
||||
<option [selected]="qualityProfile === quality.name" [ngValue]="selectedQuality" value='{{quality.id}}'>{{quality.name}}</option>
|
||||
<select class="form-control form-control-custom" id="select" formControlName="qualityProfile">
|
||||
<option *ngFor='let quality of qualities' value='{{quality.id}}'>{{quality.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<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>
|
||||
|
||||
|
@ -72,8 +86,8 @@
|
|||
<div class="form-group">
|
||||
<label for="rootFolders" class="control-label">Default Root Folders</label>
|
||||
<div id="rootFolders">
|
||||
<select class="form-control form-control-custom" *ngFor='let folder of rootFolders'>
|
||||
<option [selected]="rootPath === folder.name" [ngValue]="selectedRootFolder" value='{{folder.id}}'>{{folder.name}}</option>
|
||||
<select class="form-control form-control-custom" formControlName="rootPath">
|
||||
<option *ngFor='let folder of rootFolders' value='{{folder.id}}'>{{folder.path}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,22 +95,31 @@
|
|||
|
||||
<div class="form-group">
|
||||
<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>
|
||||
</div>
|
||||
<label>Enabled Season Folders to organize seasons into individual folders within a show.</label>
|
||||
</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>
|
||||
<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 class="form-group">
|
||||
<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>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||
|
||||
import { ISonarrSettings } from '../../interfaces/ISettings'
|
||||
import { ISonarrProfile, ISonarrRootFolder } from '../../interfaces/ISonarr'
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { SonarrService } from '../../services/applications/sonarr.service';
|
||||
|
@ -14,9 +14,8 @@ import { NotificationService } from "../../services/notification.service";
|
|||
})
|
||||
export class SonarrComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(private settingsService: SettingsService, private sonarrService: SonarrService, private notificationService: NotificationService) { }
|
||||
|
||||
settings: ISonarrSettings;
|
||||
constructor(private settingsService: SettingsService, private sonarrService: SonarrService, private notificationService: NotificationService,
|
||||
private fb : FormBuilder) { }
|
||||
|
||||
qualities: ISonarrProfile[];
|
||||
rootFolders: ISonarrRootFolder[];
|
||||
|
@ -27,20 +26,43 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
|||
profilesRunning: boolean;
|
||||
rootFoldersRunning: boolean;
|
||||
private subscriptions = new Subject<void>();
|
||||
form : FormGroup;
|
||||
advanced = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.settingsService.getSonarr()
|
||||
.takeUntil(this.subscriptions)
|
||||
.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.sonarrService.getQualityProfiles(this.settings)
|
||||
this.sonarrService.getQualityProfiles(form.value)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.qualities = x;
|
||||
|
@ -50,9 +72,9 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
getRootFolders() {
|
||||
getRootFolders(form:FormGroup) {
|
||||
this.rootFoldersRunning = true;
|
||||
this.sonarrService.getRootFolders(this.settings)
|
||||
this.sonarrService.getRootFolders(form.value)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.rootFolders = x;
|
||||
|
@ -66,17 +88,12 @@ export class SonarrComponent implements OnInit, OnDestroy {
|
|||
// TODO
|
||||
}
|
||||
|
||||
save() {
|
||||
|
||||
if (!this.qualities || !this.rootFolders) {
|
||||
|
||||
this.notificationService.error("Settings Saved", "Please make sure we have selected a quality profile");
|
||||
onSubmit(form:FormGroup) {
|
||||
if (form.invalid) {
|
||||
this.notificationService.error("Validation", "Please check your entered values");
|
||||
return
|
||||
}
|
||||
if (!this.rootFolders) {
|
||||
|
||||
this.notificationService.error("Settings Saved", "Please make sure we have a root folder");
|
||||
}
|
||||
this.settingsService.saveSonarr(this.settings)
|
||||
this.settingsService.saveSonarr(form.value)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
if (x) {
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Ombi.Controllers.External
|
|||
/// </summary>
|
||||
/// <param name="service">The service.</param>
|
||||
/// <param name="notification">The notification.</param>
|
||||
/// <param name="emailN">The notification.</param>
|
||||
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN)
|
||||
{
|
||||
Service = service;
|
||||
|
@ -42,7 +43,7 @@ namespace Ombi.Controllers.External
|
|||
public bool Discord([FromBody] DiscordNotificationSettings settings)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ namespace Ombi.Controllers
|
|||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("notifications/email")]
|
||||
[HttpPost("notifications/discord")]
|
||||
public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model)
|
||||
{
|
||||
// Save the email settings
|
||||
|
@ -247,7 +247,7 @@ namespace Ombi.Controllers
|
|||
/// Gets the discord Notification Settings.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("notifications/email")]
|
||||
[HttpGet("notifications/discord")]
|
||||
public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings()
|
||||
{
|
||||
var emailSettings = await Get<DiscordNotificationSettings>();
|
||||
|
@ -262,7 +262,7 @@ namespace Ombi.Controllers
|
|||
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent 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