From 24d5cb83a4d0c1e8124967e81c2d3cf3bd7b468d Mon Sep 17 00:00:00 2001 From: Felix Glang Date: Sun, 14 Apr 2024 16:43:09 +0200 Subject: [PATCH] Add Proxy Support, Add IOptions Pattern, Add Extensions Method Currently Changes Http / Https proxy support has been added , To disguise the Ip address or if a proxy service is required IOptions pattern has been implemented. Better options handling Extensions methods have been implemented to make Program.cs smaller Added a global logger for static and extension methods appsettings.json now contains "default" data for the applications and proxy settings. The Docker variables are also specified above it. This also fixes the bug that you have to set all variables, although you only want to use Sonarr, for example --- .../ArrOptions/ArrApplicationBaseOptions.cs | 23 +++++ .../ArrOptions/LidarrInstanceOptions.cs | 9 ++ .../ArrOptions/ReadarrInstanceOptions.cs | 9 ++ .../ArrOptions/SonarrInstanceOptions.cs | 9 ++ UmlautAdaptarr/Options/GlobalOptions.cs | 18 ++++ UmlautAdaptarr/Options/Proxy.cs | 27 ++++++ UmlautAdaptarr/Options/ProxyOptions.cs | 32 +++++++ UmlautAdaptarr/Program.cs | 15 ++- UmlautAdaptarr/Providers/LidarrClient.cs | 11 ++- UmlautAdaptarr/Providers/ReadarrClient.cs | 11 ++- UmlautAdaptarr/Providers/SonarrClient.cs | 16 ++-- .../Services/ArrSyncBackgroundService.cs | 9 +- UmlautAdaptarr/Services/ProxyService.cs | 9 +- .../Services/SearchItemLookupService.cs | 14 +-- UmlautAdaptarr/Services/TitleApiService.cs | 14 +-- UmlautAdaptarr/UmlautAdaptarr.csproj | 2 + .../Utilities/GlobalStaticLogger.cs | 19 ++++ UmlautAdaptarr/Utilities/ProxyExtension.cs | 53 ++++++++++ .../Utilities/ServicesExtensions.cs | 96 +++++++++++++++++++ UmlautAdaptarr/appsettings.json | 43 +++++++++ 20 files changed, 396 insertions(+), 43 deletions(-) create mode 100644 UmlautAdaptarr/Options/ArrOptions/ArrApplicationBaseOptions.cs create mode 100644 UmlautAdaptarr/Options/ArrOptions/LidarrInstanceOptions.cs create mode 100644 UmlautAdaptarr/Options/ArrOptions/ReadarrInstanceOptions.cs create mode 100644 UmlautAdaptarr/Options/ArrOptions/SonarrInstanceOptions.cs create mode 100644 UmlautAdaptarr/Options/GlobalOptions.cs create mode 100644 UmlautAdaptarr/Options/Proxy.cs create mode 100644 UmlautAdaptarr/Options/ProxyOptions.cs create mode 100644 UmlautAdaptarr/Utilities/GlobalStaticLogger.cs create mode 100644 UmlautAdaptarr/Utilities/ProxyExtension.cs create mode 100644 UmlautAdaptarr/Utilities/ServicesExtensions.cs diff --git a/UmlautAdaptarr/Options/ArrOptions/ArrApplicationBaseOptions.cs b/UmlautAdaptarr/Options/ArrOptions/ArrApplicationBaseOptions.cs new file mode 100644 index 0000000..dc7df56 --- /dev/null +++ b/UmlautAdaptarr/Options/ArrOptions/ArrApplicationBaseOptions.cs @@ -0,0 +1,23 @@ +namespace UmlautAdaptarr.Options.ArrOptions +{ + /// + /// Base Options for ARR applications + /// + public class ArrApplicationBaseOptions + { + /// + /// Indicates whether the Arr application is enabled. + /// + public bool Enabled { get; set; } + + /// + /// The host of the ARR application. + /// + public string Host { get; set; } + + /// + /// The API key of the ARR application. + /// + public string ApiKey { get; set; } + } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Options/ArrOptions/LidarrInstanceOptions.cs b/UmlautAdaptarr/Options/ArrOptions/LidarrInstanceOptions.cs new file mode 100644 index 0000000..d5c85e2 --- /dev/null +++ b/UmlautAdaptarr/Options/ArrOptions/LidarrInstanceOptions.cs @@ -0,0 +1,9 @@ +namespace UmlautAdaptarr.Options.ArrOptions +{ + /// + /// Lidarr Options + /// + public class LidarrInstanceOptions : ArrApplicationBaseOptions + { + } +} diff --git a/UmlautAdaptarr/Options/ArrOptions/ReadarrInstanceOptions.cs b/UmlautAdaptarr/Options/ArrOptions/ReadarrInstanceOptions.cs new file mode 100644 index 0000000..530e428 --- /dev/null +++ b/UmlautAdaptarr/Options/ArrOptions/ReadarrInstanceOptions.cs @@ -0,0 +1,9 @@ +namespace UmlautAdaptarr.Options.ArrOptions +{ + /// + /// Readarr Options + /// + public class ReadarrInstanceOptions : ArrApplicationBaseOptions + { + } +} diff --git a/UmlautAdaptarr/Options/ArrOptions/SonarrInstanceOptions.cs b/UmlautAdaptarr/Options/ArrOptions/SonarrInstanceOptions.cs new file mode 100644 index 0000000..cacc6c3 --- /dev/null +++ b/UmlautAdaptarr/Options/ArrOptions/SonarrInstanceOptions.cs @@ -0,0 +1,9 @@ +namespace UmlautAdaptarr.Options.ArrOptions +{ + /// + /// Sonarr Options + /// + public class SonarrInstanceOptions : ArrApplicationBaseOptions + { + } +} diff --git a/UmlautAdaptarr/Options/GlobalOptions.cs b/UmlautAdaptarr/Options/GlobalOptions.cs new file mode 100644 index 0000000..51452a5 --- /dev/null +++ b/UmlautAdaptarr/Options/GlobalOptions.cs @@ -0,0 +1,18 @@ +namespace UmlautAdaptarr.Options +{ + /// + /// Global options for the UmlautAdaptarr application. + /// + public class GlobalOptions + { + /// + /// The host of the UmlautAdaptarr API. + /// + public string UmlautAdaptarrApiHost { get; set; } + + /// + /// The User-Agent string used in HTTP requests. + /// + public string UserAgent { get; set; } + } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Options/Proxy.cs b/UmlautAdaptarr/Options/Proxy.cs new file mode 100644 index 0000000..63aaec3 --- /dev/null +++ b/UmlautAdaptarr/Options/Proxy.cs @@ -0,0 +1,27 @@ +namespace UmlautAdaptarr.Options; + +/// +/// Represents options for proxy configuration. +/// +public class Proxy +{ + /// + /// Gets or sets a value indicating whether to use a proxy. + /// + public bool Enabled { get; set; } + + /// + /// Gets or sets the address of the proxy. + /// + public string? Address { get; set; } + + /// + /// Gets or sets the username for proxy authentication. + /// + public string? Username { get; set; } + + /// + /// Gets or sets the password for proxy authentication. + /// + public string? Password { get; set; } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Options/ProxyOptions.cs b/UmlautAdaptarr/Options/ProxyOptions.cs new file mode 100644 index 0000000..1bea2c6 --- /dev/null +++ b/UmlautAdaptarr/Options/ProxyOptions.cs @@ -0,0 +1,32 @@ +namespace UmlautAdaptarr.Options; + +/// +/// Represents options for proxy configuration. +/// +public class ProxyOptions +{ + /// + /// Gets or sets a value indicating whether to use a proxy. + /// + public bool Enabled { get; set; } + + /// + /// Gets or sets the address of the proxy. + /// + public string? Address { get; set; } + + /// + /// Gets or sets the username for proxy authentication. + /// + public string? Username { get; set; } + + /// + /// Gets or sets the password for proxy authentication. + /// + public string? Password { get; set; } + + /// + /// Bypass Local Ip Addresses , Proxy will ignore local Ip Addresses + /// + public bool BypassOnLocal { get; set; } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index 5a0818e..4345904 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -1,8 +1,10 @@ using Microsoft.Extensions.Configuration; using System.Net; +using UmlautAdaptarr.Options; using UmlautAdaptarr.Providers; using UmlautAdaptarr.Routing; using UmlautAdaptarr.Services; +using UmlautAdaptarr.Utilities; internal class Program { @@ -24,6 +26,8 @@ private static void Main(string[] args) AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli }; + var proxyOptions = configuration.GetSection("Proxy").Get(); + handler.ConfigureProxy(proxyOptions); return handler; }); @@ -46,17 +50,18 @@ private static void Main(string[] args) builder.Services.AddControllers(); builder.Services.AddHostedService(); - builder.Services.AddSingleton(); + builder.AddTitleLookupService(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.AddSonarrSupport(); + builder.AddLidarrSupport(); + builder.AddReadarrSupport(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.AddProxyService(); var app = builder.Build(); + GlobalStaticLogger.Initialize(app.Services.GetService()!); app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/UmlautAdaptarr/Providers/LidarrClient.cs b/UmlautAdaptarr/Providers/LidarrClient.cs index de953f7..51411be 100644 --- a/UmlautAdaptarr/Providers/LidarrClient.cs +++ b/UmlautAdaptarr/Providers/LidarrClient.cs @@ -1,7 +1,9 @@ using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UmlautAdaptarr.Models; +using UmlautAdaptarr.Options.ArrOptions; using UmlautAdaptarr.Services; using UmlautAdaptarr.Utilities; @@ -12,10 +14,9 @@ public class LidarrClient( IConfiguration configuration, CacheService cacheService, IMemoryCache cache, - ILogger logger) : ArrClientBase() + ILogger logger, IOptions options) : ArrClientBase() { - private readonly string _lidarrHost = configuration.GetValue("LIDARR_HOST") ?? throw new ArgumentException("LIDARR_HOST environment variable must be set"); - private readonly string _lidarrApiKey = configuration.GetValue("LIDARR_API_KEY") ?? throw new ArgumentException("LIDARR_API_KEY environment variable must be set"); + public LidarrInstanceOptions LidarrOptions { get; } = options.Value; private readonly string _mediaType = "audio"; public override async Task> FetchAllItemsAsync() @@ -25,7 +26,7 @@ public override async Task> FetchAllItemsAsync() try { - var lidarrArtistsUrl = $"{_lidarrHost}/api/v1/artist?apikey={_lidarrApiKey}"; + var lidarrArtistsUrl = $"{LidarrOptions.Host}/api/v1/artist?apikey={LidarrOptions.ApiKey}"; logger.LogInformation($"Fetching all artists from Lidarr: {UrlUtilities.RedactApiKey(lidarrArtistsUrl)}"); var artistsApiResponse = await httpClient.GetStringAsync(lidarrArtistsUrl); var artists = JsonConvert.DeserializeObject>(artistsApiResponse); @@ -40,7 +41,7 @@ public override async Task> FetchAllItemsAsync() { var artistId = (int)artist.id; - var lidarrAlbumUrl = $"{_lidarrHost}/api/v1/album?artistId={artistId}&apikey={_lidarrApiKey}"; + var lidarrAlbumUrl = $"{LidarrOptions.Host}/api/v1/album?artistId={artistId}&apikey={LidarrOptions.ApiKey}"; // TODO add caching here // Disable cache for now as it can result in problems when adding new albums that aren't displayed on the artists page initially diff --git a/UmlautAdaptarr/Providers/ReadarrClient.cs b/UmlautAdaptarr/Providers/ReadarrClient.cs index df60c88..608ac2c 100644 --- a/UmlautAdaptarr/Providers/ReadarrClient.cs +++ b/UmlautAdaptarr/Providers/ReadarrClient.cs @@ -1,7 +1,9 @@ using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UmlautAdaptarr.Models; +using UmlautAdaptarr.Options.ArrOptions; using UmlautAdaptarr.Services; using UmlautAdaptarr.Utilities; @@ -12,10 +14,11 @@ public class ReadarrClient( IConfiguration configuration, CacheService cacheService, IMemoryCache cache, + IOptions options, ILogger logger) : ArrClientBase() { - private readonly string _readarrHost = configuration.GetValue("READARR_HOST") ?? throw new ArgumentException("READARR_HOST environment variable must be set"); - private readonly string _readarrApiKey = configuration.GetValue("READARR_API_KEY") ?? throw new ArgumentException("READARR_API_KEY environment variable must be set"); + + public ReadarrInstanceOptions ReadarrOptions { get; } = options.Value; private readonly string _mediaType = "book"; public override async Task> FetchAllItemsAsync() @@ -25,7 +28,7 @@ public override async Task> FetchAllItemsAsync() try { - var readarrAuthorUrl = $"{_readarrHost}/api/v1/author?apikey={_readarrApiKey}"; + var readarrAuthorUrl = $"{ReadarrOptions.Host}/api/v1/author?apikey={ReadarrOptions.ApiKey}"; logger.LogInformation($"Fetching all authors from Readarr: {UrlUtilities.RedactApiKey(readarrAuthorUrl)}"); var authorApiResponse = await httpClient.GetStringAsync(readarrAuthorUrl); var authors = JsonConvert.DeserializeObject>(authorApiResponse); @@ -40,7 +43,7 @@ public override async Task> FetchAllItemsAsync() { var authorId = (int)author.id; - var readarrBookUrl = $"{_readarrHost}/api/v1/book?authorId={authorId}&apikey={_readarrApiKey}"; + var readarrBookUrl = $"{ReadarrOptions.Host}/api/v1/book?authorId={authorId}&apikey={ReadarrOptions.ApiKey}"; // TODO add caching here logger.LogInformation($"Fetching all books from authorId {authorId} from Readarr: {UrlUtilities.RedactApiKey(readarrBookUrl)}"); diff --git a/UmlautAdaptarr/Providers/SonarrClient.cs b/UmlautAdaptarr/Providers/SonarrClient.cs index 11cc622..82c218a 100644 --- a/UmlautAdaptarr/Providers/SonarrClient.cs +++ b/UmlautAdaptarr/Providers/SonarrClient.cs @@ -1,5 +1,7 @@ -using Newtonsoft.Json; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; using UmlautAdaptarr.Models; +using UmlautAdaptarr.Options.ArrOptions; using UmlautAdaptarr.Services; using UmlautAdaptarr.Utilities; @@ -9,10 +11,12 @@ public class SonarrClient( IHttpClientFactory clientFactory, IConfiguration configuration, TitleApiService titleService, + IOptions options, ILogger logger) : ArrClientBase() { - private readonly string _sonarrHost = configuration.GetValue("SONARR_HOST") ?? throw new ArgumentException("SONARR_HOST environment variable must be set"); - private readonly string _sonarrApiKey = configuration.GetValue("SONARR_API_KEY") ?? throw new ArgumentException("SONARR_API_KEY environment variable must be set"); + public SonarrInstanceOptions SonarrOptions { get; } = options.Value; + //private readonly string _sonarrHost = configuration.GetValue("SONARR_HOST") ?? throw new ArgumentException("SONARR_HOST environment variable must be set"); + //private readonly string _sonarrApiKey = configuration.GetValue("SONARR_API_KEY") ?? throw new ArgumentException("SONARR_API_KEY environment variable must be set"); private readonly string _mediaType = "tv"; public override async Task> FetchAllItemsAsync() @@ -22,7 +26,7 @@ public override async Task> FetchAllItemsAsync() try { - var sonarrUrl = $"{_sonarrHost}/api/v3/series?includeSeasonImages=false&apikey={_sonarrApiKey}"; + var sonarrUrl = $"{SonarrOptions.Host}/api/v3/series?includeSeasonImages=false&apikey={SonarrOptions.ApiKey}"; logger.LogInformation($"Fetching all items from Sonarr: {UrlUtilities.RedactApiKey(sonarrUrl)}"); var response = await httpClient.GetStringAsync(sonarrUrl); var shows = JsonConvert.DeserializeObject>(response); @@ -71,7 +75,7 @@ public override async Task> FetchAllItemsAsync() try { - var sonarrUrl = $"{_sonarrHost}/api/v3/series?tvdbId={externalId}&includeSeasonImages=false&apikey={_sonarrApiKey}"; + var sonarrUrl = $"{SonarrOptions.Host}/api/v3/series?tvdbId={externalId}&includeSeasonImages=false&apikey={SonarrOptions.ApiKey}"; logger.LogInformation($"Fetching item by external ID from Sonarr: {UrlUtilities.RedactApiKey(sonarrUrl)}"); var response = await httpClient.GetStringAsync(sonarrUrl); var shows = JsonConvert.DeserializeObject(response); @@ -123,7 +127,7 @@ public override async Task> FetchAllItemsAsync() return null; } - var sonarrUrl = $"{_sonarrHost}/api/v3/series?tvdbId={tvdbId}&includeSeasonImages=false&apikey={_sonarrApiKey}"; + var sonarrUrl = $"{SonarrOptions.Host}/api/v3/series?tvdbId={tvdbId}&includeSeasonImages=false&apikey={SonarrOptions.ApiKey}"; var sonarrApiResponse = await httpClient.GetStringAsync(sonarrUrl); var shows = JsonConvert.DeserializeObject(sonarrApiResponse); diff --git a/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs b/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs index bb689cf..f34714a 100644 --- a/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs +++ b/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs @@ -19,9 +19,6 @@ public class ArrSyncBackgroundService( IConfiguration configuration, ILogger logger) : BackgroundService { - private readonly bool _sonarrEnabled = configuration.GetValue("SONARR_ENABLED"); - private readonly bool _lidarrEnabled = configuration.GetValue("LIDARR_ENABLED"); - private readonly bool _readarrEnabled = configuration.GetValue("READARR_ENABLED"); protected override async Task ExecuteAsync(CancellationToken stoppingToken) { logger.LogInformation("ArrSyncBackgroundService is starting."); @@ -62,17 +59,17 @@ private async Task FetchAndUpdateDataAsync() try { var success = true; - if (_readarrEnabled) + if (readarrClient.ReadarrOptions.Enabled) { var syncSuccess = await FetchItemsFromReadarrAsync(); success = success && syncSuccess; } - if (_sonarrEnabled) + if (sonarrClient.SonarrOptions.Enabled) { var syncSuccess = await FetchItemsFromSonarrAsync(); success = success && syncSuccess; } - if (_lidarrEnabled) + if (lidarrClient.LidarrOptions.Enabled) { var syncSuccess = await FetchItemsFromLidarrAsync(); success = success && syncSuccess; diff --git a/UmlautAdaptarr/Services/ProxyService.cs b/UmlautAdaptarr/Services/ProxyService.cs index 6099ed4..95e9df8 100644 --- a/UmlautAdaptarr/Services/ProxyService.cs +++ b/UmlautAdaptarr/Services/ProxyService.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Caching.Memory; using System.Collections.Concurrent; +using Microsoft.Extensions.Options; +using UmlautAdaptarr.Options; using UmlautAdaptarr.Utilities; namespace UmlautAdaptarr.Services @@ -10,15 +12,18 @@ public class ProxyService private readonly string _userAgent; private readonly ILogger _logger; private readonly IMemoryCache _cache; + private readonly GlobalOptions _options; private static readonly ConcurrentDictionary _lastRequestTimes = new(); private static readonly TimeSpan MINIMUM_DELAY_FOR_SAME_HOST = new(0, 0, 0, 1); - public ProxyService(IHttpClientFactory clientFactory, IConfiguration configuration, ILogger logger, IMemoryCache cache) + public ProxyService(IHttpClientFactory clientFactory, ILogger logger, IMemoryCache cache, IOptions options) { + _options = options.Value; _httpClient = clientFactory.CreateClient("HttpClient") ?? throw new ArgumentNullException(nameof(clientFactory)); - _userAgent = configuration["Settings:UserAgent"] ?? throw new ArgumentException("UserAgent must be set in appsettings.json"); + _userAgent = _options.UserAgent ?? throw new ArgumentException("UserAgent must be set in appsettings.json"); _logger = logger; _cache = cache; + } private static async Task EnsureMinimumDelayAsync(string targetUri) diff --git a/UmlautAdaptarr/Services/SearchItemLookupService.cs b/UmlautAdaptarr/Services/SearchItemLookupService.cs index 0cf0ef3..c70292c 100644 --- a/UmlautAdaptarr/Services/SearchItemLookupService.cs +++ b/UmlautAdaptarr/Services/SearchItemLookupService.cs @@ -6,12 +6,8 @@ namespace UmlautAdaptarr.Services public class SearchItemLookupService(CacheService cacheService, SonarrClient sonarrClient, ReadarrClient readarrClient, - LidarrClient lidarrClient, - IConfiguration configuration) + LidarrClient lidarrClient) { - private readonly bool _sonarrEnabled = configuration.GetValue("SONARR_ENABLED"); - private readonly bool _lidarrEnabled = configuration.GetValue("LIDARR_ENABLED"); - private readonly bool _readarrEnabled = configuration.GetValue("READARR_ENABLED"); public async Task GetOrFetchSearchItemByExternalId(string mediaType, string externalId) { // Attempt to get the item from the cache first @@ -26,20 +22,20 @@ public class SearchItemLookupService(CacheService cacheService, switch (mediaType) { case "tv": - if (_sonarrEnabled) + if (sonarrClient.SonarrOptions.Enabled) { fetchedItem = await sonarrClient.FetchItemByExternalIdAsync(externalId); } break; case "audio": - if (_lidarrEnabled) + if (lidarrClient.LidarrOptions.Enabled) { fetchedItem = await lidarrClient.FetchItemByExternalIdAsync(externalId); fetchedItem = cacheService.GetSearchItemByExternalId(mediaType, externalId); } break; case "book": - if (_readarrEnabled) + if (readarrClient.ReadarrOptions.Enabled) { await readarrClient.FetchItemByExternalIdAsync(externalId); fetchedItem = cacheService.GetSearchItemByExternalId(mediaType, externalId); @@ -70,7 +66,7 @@ public class SearchItemLookupService(CacheService cacheService, switch (mediaType) { case "tv": - if (_sonarrEnabled) + if (sonarrClient.SonarrOptions.Enabled) { fetchedItem = await sonarrClient.FetchItemByTitleAsync(title); } diff --git a/UmlautAdaptarr/Services/TitleApiService.cs b/UmlautAdaptarr/Services/TitleApiService.cs index c48988e..26733c2 100644 --- a/UmlautAdaptarr/Services/TitleApiService.cs +++ b/UmlautAdaptarr/Services/TitleApiService.cs @@ -1,13 +1,15 @@ -using Newtonsoft.Json; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using UmlautAdaptarr.Options; using UmlautAdaptarr.Utilities; namespace UmlautAdaptarr.Services { - public class TitleApiService(IHttpClientFactory clientFactory, IConfiguration configuration, ILogger logger) + public class TitleApiService(IHttpClientFactory clientFactory, ILogger logger, IOptions options) { - private readonly string _umlautAdaptarrApiHost = configuration["Settings:UmlautAdaptarrApiHost"] - ?? throw new ArgumentException("UmlautAdaptarrApiHost must be set in appsettings.json"); + public GlobalOptions Options { get; } = options.Value; + private DateTime lastRequestTime = DateTime.MinValue; private async Task EnsureMinimumDelayAsync() @@ -28,7 +30,7 @@ public async Task<(string? germanTitle, string[]? aliases)> FetchGermanTitleAndA await EnsureMinimumDelayAsync(); var httpClient = clientFactory.CreateClient(); - var titleApiUrl = $"{_umlautAdaptarrApiHost}/tvshow_german.php?tvdbid={externalId}"; + var titleApiUrl = $"{Options.UmlautAdaptarrApiHost}/tvshow_german.php?tvdbid={externalId}"; logger.LogInformation($"TitleApiService GET {UrlUtilities.RedactApiKey(titleApiUrl)}"); var response = await httpClient.GetStringAsync(titleApiUrl); var titleApiResponseData = JsonConvert.DeserializeObject(response); @@ -74,7 +76,7 @@ public async Task<(string? germanTitle, string? externalId, string[]? aliases)> var httpClient = clientFactory.CreateClient(); var tvdbCleanTitle = title.Replace("ß", "ss"); - var titleApiUrl = $"{_umlautAdaptarrApiHost}/tvshow_german.php?title={tvdbCleanTitle}"; + var titleApiUrl = $"{Options.UmlautAdaptarrApiHost}/tvshow_german.php?title={tvdbCleanTitle}"; logger.LogInformation($"TitleApiService GET {UrlUtilities.RedactApiKey(titleApiUrl)}"); var titleApiResponse = await httpClient.GetStringAsync(titleApiUrl); var titleApiResponseData = JsonConvert.DeserializeObject(titleApiResponse); diff --git a/UmlautAdaptarr/UmlautAdaptarr.csproj b/UmlautAdaptarr/UmlautAdaptarr.csproj index 5b1fa31..23722e0 100644 --- a/UmlautAdaptarr/UmlautAdaptarr.csproj +++ b/UmlautAdaptarr/UmlautAdaptarr.csproj @@ -9,6 +9,8 @@ + + diff --git a/UmlautAdaptarr/Utilities/GlobalStaticLogger.cs b/UmlautAdaptarr/Utilities/GlobalStaticLogger.cs new file mode 100644 index 0000000..e7fdbbe --- /dev/null +++ b/UmlautAdaptarr/Utilities/GlobalStaticLogger.cs @@ -0,0 +1,19 @@ +namespace UmlautAdaptarr.Utilities +{ + /// + /// Service for providing a static logger to log errors and information. + /// The GlobalStaticLogger is designed to provide a static logger that can be used to log errors and information. + /// It facilitates logging for both static classes and extension methods. + /// + public static class GlobalStaticLogger + { + + public static ILogger Logger; + + /// + /// Initializes the GlobalStaticLogger with the provided logger factory. + /// + /// The ILoggerFactory instance used to create loggers. + public static void Initialize(ILoggerFactory loggerFactory) => Logger = loggerFactory.CreateLogger("GlobalStaticLogger"); + } +} diff --git a/UmlautAdaptarr/Utilities/ProxyExtension.cs b/UmlautAdaptarr/Utilities/ProxyExtension.cs new file mode 100644 index 0000000..6a448ba --- /dev/null +++ b/UmlautAdaptarr/Utilities/ProxyExtension.cs @@ -0,0 +1,53 @@ +using System; +using System.Net; +using UmlautAdaptarr.Options; + +namespace UmlautAdaptarr.Utilities +{ + /// + /// Extension methods for configuring proxies. + /// + public static class ProxyExtension + { + /// + /// Logger instance for logging proxy configurations. + /// + private static ILogger Logger = GlobalStaticLogger.Logger; + + /// + /// Configures the proxy settings for the provided HttpClientHandler instance. + /// + /// The HttpClientHandler instance to configure. + /// ProxyOptions options to be used for configuration. + /// The configured HttpClientHandler instance. + public static HttpClientHandler ConfigureProxy(this HttpClientHandler handler, ProxyOptions? proxyOptions) + { + try + { + if (proxyOptions != null && proxyOptions.Enabled) + { + Logger.LogInformation("Use Proxy {0}", proxyOptions.Address); + handler.UseProxy = true; + handler.Proxy = new WebProxy(proxyOptions.Address, proxyOptions.BypassOnLocal); + + if (!string.IsNullOrEmpty(proxyOptions.Username) && !string.IsNullOrEmpty(proxyOptions.Password)) + { + Logger.LogInformation("Use Proxy Credentials from User {0}", proxyOptions.Username); + handler.DefaultProxyCredentials = + new NetworkCredential(proxyOptions.Username, proxyOptions.Password); + } + } + else + { + Logger.LogDebug("No proxy was set"); + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Error occurred while configuring proxy, no Proxy will be used!"); + } + + return handler; + } + } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Utilities/ServicesExtensions.cs b/UmlautAdaptarr/Utilities/ServicesExtensions.cs new file mode 100644 index 0000000..1ac3957 --- /dev/null +++ b/UmlautAdaptarr/Utilities/ServicesExtensions.cs @@ -0,0 +1,96 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using UmlautAdaptarr.Options; +using UmlautAdaptarr.Options.ArrOptions; +using UmlautAdaptarr.Providers; +using UmlautAdaptarr.Services; + +namespace UmlautAdaptarr.Utilities +{ + /// + /// Extension methods for configuring services related to ARR Applications + /// + public static class ServicesExtensions + { + + /// + /// Adds a service with specified options and service to the service collection. + /// + /// The options type for the service. + /// The service type for the service. + /// The to configure the service collection. + /// The name of the configuration section containing service options. + /// The configured . + private static WebApplicationBuilder AddServiceWithOptions(this WebApplicationBuilder builder, string sectionName) + where TOptions : class + where TService : class + { + if (builder.Services == null) + { + throw new ArgumentNullException(nameof(builder), "Service collection is null."); + } + + var options = builder.Configuration.GetSection(sectionName).Get(); + if (options == null) + { + throw new InvalidOperationException($"{typeof(TService).Name} options could not be loaded from Configuration or ENV Variable."); + } + + builder.Services.Configure(builder.Configuration.GetSection(sectionName)); + builder.Services.AddSingleton(); + return builder; + } + + /// + /// Adds support for Sonarr with default options and client. + /// + /// The to configure the service collection. + /// The configured . + public static WebApplicationBuilder AddSonarrSupport(this WebApplicationBuilder builder) + { + return builder.AddServiceWithOptions("Sonarr"); + } + + /// + /// Adds support for Lidarr with default options and client. + /// + /// The to configure the service collection. + /// The configured . + public static WebApplicationBuilder AddLidarrSupport(this WebApplicationBuilder builder) + { + return builder.AddServiceWithOptions("Lidarr"); + } + + /// + /// Adds support for Readarr with default options and client. + /// + /// The to configure the service collection. + /// The configured . + public static WebApplicationBuilder AddReadarrSupport(this WebApplicationBuilder builder) + { + return builder.AddServiceWithOptions("Readarr"); + } + + /// + /// Adds a title lookup service to the service collection. + /// + /// The to configure the service collection. + /// The configured . + public static WebApplicationBuilder AddTitleLookupService(this WebApplicationBuilder builder) + { + return builder.AddServiceWithOptions("Settings"); + } + + /// + /// Adds a proxy service to the service collection. + /// + /// The to configure the service collection. + /// The configured . + public static WebApplicationBuilder AddProxyService(this WebApplicationBuilder builder) + { + return builder.AddServiceWithOptions("Settings"); + } + } + + +} diff --git a/UmlautAdaptarr/appsettings.json b/UmlautAdaptarr/appsettings.json index 7255729..32ae2bb 100644 --- a/UmlautAdaptarr/appsettings.json +++ b/UmlautAdaptarr/appsettings.json @@ -13,8 +13,51 @@ } } }, + // Settings__UserAgent=UmlautAdaptarr/1.0 + // Settings__UmlautAdaptarrApiHost=https://umlautadaptarr.pcjones.de/api/v1 "Settings": { "UserAgent": "UmlautAdaptarr/1.0", "UmlautAdaptarrApiHost": "https://umlautadaptarr.pcjones.de/api/v1" + }, + "Sonarr": { + // Docker Environment Variables: + // - Sonarr__Enabled: true (set to false to disable) + // - Sonarr__Host: your_sonarr_host_url + // - Sonarr__ApiKey: your_sonarr_api_key + "Enabled": false, + "Host": "your_sonarr_host_url", + "ApiKey": "your_sonarr_api_key" + }, + "Lidarr": { + // Docker Environment Variables: + // - Lidarr__Enabled: true (set to false to disable) + // - Lidarr__Host: your_lidarr_host_url + // - Lidarr__ApiKey: your_lidarr_api_key + "Enabled": false, + "Host": "your_lidarr_host_url", + "ApiKey": "your_lidarr_api_key" + }, + "Readarr": { + // Docker Environment Variables: + // - Readarr__Enabled: true (set to false to disable) + // - Readarr__Host: your_readarr_host_url + // - Readarr__ApiKey: your_readarr_api_key + "Enabled": false, + "Host": "your_readarr_host_url", + "ApiKey": "your_readarr_api_key" + }, + + // Docker Environment Variables: + // - Proxy__Enabled: true (set to false to disable) + // - Proxy__Address: http://yourproxyaddress:port + // - Proxy__Username: your_proxy_username + // - Proxy__Password: your_proxy_password + // - Proxy__BypassOnLocal: true (set to false to not bypass local IP addresses) + "Proxy": { + "Enabled": false, + "Address": "http://yourproxyaddress:port", + "Username": "your_proxy_username", + "Password": "your_proxy_password", + "BypassOnLocal": true } }