From 24d5cb83a4d0c1e8124967e81c2d3cf3bd7b468d Mon Sep 17 00:00:00 2001 From: Felix Glang Date: Sun, 14 Apr 2024 16:43:09 +0200 Subject: [PATCH 01/24] 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 @@ internal class Program AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli }; + var proxyOptions = configuration.GetSection("Proxy").Get(); + handler.ConfigureProxy(proxyOptions); return handler; }); @@ -46,17 +50,18 @@ internal class Program 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers { 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers { 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Providers 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 @@ namespace UmlautAdaptarr.Services 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 } } From 5cd90b7b206a3a5d5dc58bd3d732e7c26897b8b9 Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 22:28:38 +0200 Subject: [PATCH 02/24] Remove secrets example --- UmlautAdaptarr/secrets_example.json | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 UmlautAdaptarr/secrets_example.json diff --git a/UmlautAdaptarr/secrets_example.json b/UmlautAdaptarr/secrets_example.json deleted file mode 100644 index 8f58df8..0000000 --- a/UmlautAdaptarr/secrets_example.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "SONARR_ENABLED": false, - "SONARR_HOST": "http://localhost:8989", - "SONARR_API_KEY": "", - "LIDARR_ENABLED": false, - "LIDARR_HOST": "http://localhost:8686", - "LIDARR_API_KEY": "", - "READARR_ENABLED": false, - "READARR_HOST": "http://localhost:8787", - "READARR_API_KEY": "" -} \ No newline at end of file From 12d9217964d25fcd6e4bfe0b5fb3dcdb850208f9 Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 22:29:10 +0200 Subject: [PATCH 03/24] Add todo --- UmlautAdaptarr/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index 4345904..55f3117 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -33,6 +33,7 @@ internal class Program builder.Services.AddMemoryCache(options => { + // TODO cache size limit? option? //options.SizeLimit = 20000; }); From 389d685e95c6d2c39d23dabe170928e51261dadb Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 22:44:07 +0200 Subject: [PATCH 04/24] Code cleanup --- UmlautAdaptarr/Program.cs | 2 - UmlautAdaptarr/Providers/ArrClientFactory.cs | 15 ++ UmlautAdaptarr/Providers/LidarrClient.cs | 3 +- UmlautAdaptarr/Providers/ReadarrClient.cs | 1 - UmlautAdaptarr/Providers/SonarrClient.cs | 3 - .../Services/ArrSyncBackgroundService.cs | 1 - UmlautAdaptarr/Services/ProxyService.cs | 2 - .../Services/SearchItemLookupService.cs | 2 +- .../Services/TitleMatchingService.cs | 4 +- .../Services/TitleQueryServiceLegacy.cs | 162 ------------------ .../Utilities/ServicesExtensions.cs | 6 +- 11 files changed, 20 insertions(+), 181 deletions(-) create mode 100644 UmlautAdaptarr/Providers/ArrClientFactory.cs delete mode 100644 UmlautAdaptarr/Services/TitleQueryServiceLegacy.cs diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index 55f3117..622a10e 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -1,7 +1,5 @@ -using Microsoft.Extensions.Configuration; using System.Net; using UmlautAdaptarr.Options; -using UmlautAdaptarr.Providers; using UmlautAdaptarr.Routing; using UmlautAdaptarr.Services; using UmlautAdaptarr.Utilities; diff --git a/UmlautAdaptarr/Providers/ArrClientFactory.cs b/UmlautAdaptarr/Providers/ArrClientFactory.cs new file mode 100644 index 0000000..55a8fec --- /dev/null +++ b/UmlautAdaptarr/Providers/ArrClientFactory.cs @@ -0,0 +1,15 @@ +namespace UmlautAdaptarr.Providers +{ + public static class ArrClientFactory + { + public static IEnumerable CreateClients( + Func constructor, IConfiguration configuration, string configKey) where TClient : ArrClientBase + { + var hosts = configuration.GetValue(configKey)?.Split(',') ?? throw new ArgumentException($"{configKey} environment variable must be set if the app is enabled"); + foreach (var host in hosts) + { + yield return constructor(host.Trim()); + } + } + } +} \ No newline at end of file diff --git a/UmlautAdaptarr/Providers/LidarrClient.cs b/UmlautAdaptarr/Providers/LidarrClient.cs index 51411be..cde2677 100644 --- a/UmlautAdaptarr/Providers/LidarrClient.cs +++ b/UmlautAdaptarr/Providers/LidarrClient.cs @@ -11,7 +11,6 @@ namespace UmlautAdaptarr.Providers { public class LidarrClient( IHttpClientFactory clientFactory, - IConfiguration configuration, CacheService cacheService, IMemoryCache cache, ILogger logger, IOptions options) : ArrClientBase() @@ -136,7 +135,7 @@ namespace UmlautAdaptarr.Providers { try { - // this should never be called at the moment + // this should never be called at the moment throw new NotImplementedException(); } catch (Exception ex) diff --git a/UmlautAdaptarr/Providers/ReadarrClient.cs b/UmlautAdaptarr/Providers/ReadarrClient.cs index 608ac2c..ce4a772 100644 --- a/UmlautAdaptarr/Providers/ReadarrClient.cs +++ b/UmlautAdaptarr/Providers/ReadarrClient.cs @@ -11,7 +11,6 @@ namespace UmlautAdaptarr.Providers { public class ReadarrClient( IHttpClientFactory clientFactory, - IConfiguration configuration, CacheService cacheService, IMemoryCache cache, IOptions options, diff --git a/UmlautAdaptarr/Providers/SonarrClient.cs b/UmlautAdaptarr/Providers/SonarrClient.cs index 82c218a..b9d9ef6 100644 --- a/UmlautAdaptarr/Providers/SonarrClient.cs +++ b/UmlautAdaptarr/Providers/SonarrClient.cs @@ -9,14 +9,11 @@ namespace UmlautAdaptarr.Providers { public class SonarrClient( IHttpClientFactory clientFactory, - IConfiguration configuration, TitleApiService titleService, IOptions options, ILogger logger) : ArrClientBase() { 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() diff --git a/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs b/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs index f34714a..00fedfb 100644 --- a/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs +++ b/UmlautAdaptarr/Services/ArrSyncBackgroundService.cs @@ -16,7 +16,6 @@ namespace UmlautAdaptarr.Services LidarrClient lidarrClient, ReadarrClient readarrClient, CacheService cacheService, - IConfiguration configuration, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) diff --git a/UmlautAdaptarr/Services/ProxyService.cs b/UmlautAdaptarr/Services/ProxyService.cs index 95e9df8..e398a91 100644 --- a/UmlautAdaptarr/Services/ProxyService.cs +++ b/UmlautAdaptarr/Services/ProxyService.cs @@ -23,7 +23,6 @@ namespace UmlautAdaptarr.Services _userAgent = _options.UserAgent ?? throw new ArgumentException("UserAgent must be set in appsettings.json"); _logger = logger; _cache = cache; - } private static async Task EnsureMinimumDelayAsync(string targetUri) @@ -91,7 +90,6 @@ namespace UmlautAdaptarr.Services { _logger.LogError(ex, $"Error proxying request: {UrlUtilities.RedactApiKey(targetUri)}. Error: {ex.Message}"); - // Create a response message indicating an internal server error var errorResponse = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError) { Content = new StringContent($"An error occurred while processing your request: {ex.Message}") diff --git a/UmlautAdaptarr/Services/SearchItemLookupService.cs b/UmlautAdaptarr/Services/SearchItemLookupService.cs index c70292c..eb68495 100644 --- a/UmlautAdaptarr/Services/SearchItemLookupService.cs +++ b/UmlautAdaptarr/Services/SearchItemLookupService.cs @@ -30,7 +30,7 @@ namespace UmlautAdaptarr.Services case "audio": if (lidarrClient.LidarrOptions.Enabled) { - fetchedItem = await lidarrClient.FetchItemByExternalIdAsync(externalId); + await lidarrClient.FetchItemByExternalIdAsync(externalId); fetchedItem = cacheService.GetSearchItemByExternalId(mediaType, externalId); } break; diff --git a/UmlautAdaptarr/Services/TitleMatchingService.cs b/UmlautAdaptarr/Services/TitleMatchingService.cs index 1c6c847..d229474 100644 --- a/UmlautAdaptarr/Services/TitleMatchingService.cs +++ b/UmlautAdaptarr/Services/TitleMatchingService.cs @@ -228,7 +228,7 @@ namespace UmlautAdaptarr.Services titleElement.Value = newTitle; logger.LogInformation($"TitleMatchingService - Title changed: '{originalTitle}' to '{newTitle}'"); - break; // Break after the first successful match and modification + break; } } } @@ -242,7 +242,7 @@ namespace UmlautAdaptarr.Services private static char FindFirstSeparator(string title) { var match = WordSeperationCharRegex().Match(title); - return match.Success ? match.Value.First() : ' '; // Default to space if no separator found + return match.Success ? match.Value.First() : ' '; } private static string ReconstructTitleWithSeparator(string title, char separator) diff --git a/UmlautAdaptarr/Services/TitleQueryServiceLegacy.cs b/UmlautAdaptarr/Services/TitleQueryServiceLegacy.cs deleted file mode 100644 index eba17d4..0000000 --- a/UmlautAdaptarr/Services/TitleQueryServiceLegacy.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; -using Newtonsoft.Json; -using UmlautAdaptarr.Models; -using UmlautAdaptarr.Providers; -using UmlautAdaptarr.Utilities; - -namespace UmlautAdaptarr.Services -{ - public class TitleQueryServiceLegacy( - IMemoryCache memoryCache, - ILogger logger, - IConfiguration configuration, - IHttpClientFactory clientFactory, - SonarrClient sonarrClient) - { - private readonly HttpClient _httpClient = clientFactory.CreateClient("HttpClient") ?? throw new ArgumentNullException(); - 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 _umlautAdaptarrApiHost = configuration["Settings:UmlautAdaptarrApiHost"] ?? throw new ArgumentException("UmlautAdaptarrApiHost must be set in appsettings.json"); - - /*public async Task<(bool hasGermanUmlaut, string? GermanTitle, string ExpectedTitle)> QueryGermanShowTitleByTVDBId(string tvdbId) - { - var sonarrCacheKey = $"SearchItem_Sonarr_{tvdbId}"; - - if (memoryCache.TryGetValue(sonarrCacheKey, out SearchItem? cachedItem)) - { - return (cachedItem?.HasGermanUmlaut ?? false, cachedItem?.GermanTitle, cachedItem?.ExpectedTitle ?? string.Empty); - } - else - { - var sonarrUrl = $"{_sonarrHost}/api/v3/series?tvdbId={tvdbId}&includeSeasonImages=false&apikey={_sonarrApiKey}"; - var sonarrApiResponse = await _httpClient.GetStringAsync(sonarrUrl); - var shows = JsonConvert.DeserializeObject(sonarrApiResponse); - - if (shows == null) - { - logger.LogError($"Parsing Sonarr API response for TVDB ID {tvdbId} resulted in null"); - return (false, null, string.Empty); - } - else if (shows.Count == 0) - { - logger.LogWarning($"No results found for TVDB ID {tvdbId}"); - return (false, null, string.Empty); - } - - var expectedTitle = (string)shows[0].title; - if (expectedTitle == null) - { - logger.LogError($"Sonarr Title for TVDB ID {tvdbId} is null"); - return (false, null, string.Empty); - } - - string? germanTitle = null; - var hasGermanTitle = false; - - var titleApiUrl = $"{_umlautAdaptarrApiHost}/tvshow_german.php?tvdbid={tvdbId}"; - var titleApiResponse = await _httpClient.GetStringAsync(titleApiUrl); - var titleApiResponseData = JsonConvert.DeserializeObject(titleApiResponse); - - if (titleApiResponseData == null) - { - logger.LogError($"Parsing UmlautAdaptarr TitleQuery API response for TVDB ID {tvdbId} resulted in null"); - return (false, null, string.Empty); - } - - if (titleApiResponseData.status == "success" && !string.IsNullOrEmpty((string)titleApiResponseData.germanTitle)) - { - germanTitle = titleApiResponseData.germanTitle; - hasGermanTitle = true; - } - - var hasGermanUmlaut = germanTitle?.HasGermanUmlauts() ?? false; - - var result = (hasGermanUmlaut, germanTitle, expectedTitle); - memoryCache.Set(showCacheKey, result, new MemoryCacheEntryOptions - { - Size = 1, - SlidingExpiration = hasGermanTitle ? TimeSpan.FromDays(30) : TimeSpan.FromDays(7) - }); - - return result; - } - }*/ - - // This method is being used if the *arrs do a search with the "q" parameter (text search) - public async Task<(bool hasGermanUmlaut, string? GermanTitle, string ExpectedTitle)> QueryGermanShowTitleByTitle(string title) - { - // TVDB doesn't use ß - TODO: Determine if this is true - var tvdbCleanTitle = title.Replace("ß", "ss"); - - var cacheKey = $"show_{tvdbCleanTitle}"; - if (memoryCache.TryGetValue(cacheKey, out (bool hasGermanUmlaut, string? GermanTitle, string ExpectedTitle) cachedResult)) - { - return cachedResult; - } - - var titleApiUrl = $"{_umlautAdaptarrApiHost}/tvshow_german.php?title={tvdbCleanTitle}"; - var titleApiResponse = await _httpClient.GetStringAsync(titleApiUrl); - var titleApiResponseData = JsonConvert.DeserializeObject(titleApiResponse); - - if (titleApiResponseData == null) - { - logger.LogError($"Parsing UmlautAdaptarr TitleQuery API response for title {title} resulted in null"); - return (false, null, string.Empty); - } - - if (titleApiResponseData.status == "success" && !string.IsNullOrEmpty((string)titleApiResponseData.germanTitle)) - { - var tvdbId = (string)titleApiResponseData.tvdbId; - if (tvdbId == null) - { - logger.LogError($"Parsing UmlautAdaptarr TitleQuery API response tvdbId {titleApiResponseData} resulted in null"); - return (false, null, string.Empty); - } - - var sonarrUrl = $"{_sonarrHost}/api/v3/series?tvdbId={tvdbId}&includeSeasonImages=false&apikey={_sonarrApiKey}"; - var sonarrApiResponse = await _httpClient.GetStringAsync(sonarrUrl); - var shows = JsonConvert.DeserializeObject(sonarrApiResponse); - - if (shows == null) - { - logger.LogError($"Parsing Sonarr API response for TVDB ID {tvdbId} resulted in null"); - return (false, null, string.Empty); - } - else if (shows.Count == 0) - { - logger.LogWarning($"No results found for TVDB ID {tvdbId}"); - return (false, null, string.Empty); - } - - var expectedTitle = (string)shows[0].title; - if (expectedTitle == null) - { - logger.LogError($"Sonarr Title for TVDB ID {tvdbId} is null"); - return (false, null, string.Empty); - } - - string germanTitle ; - bool hasGermanTitle; - - germanTitle = titleApiResponseData.germanTitle; - hasGermanTitle = true; - - var hasGermanUmlaut = germanTitle?.HasUmlauts() ?? false; - - var result = (hasGermanUmlaut, germanTitle, expectedTitle); - memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions - { - Size = 1, - SlidingExpiration = hasGermanTitle ? TimeSpan.FromDays(30) : TimeSpan.FromDays(7) - }); - - return result; - } - else - { - logger.LogWarning($"UmlautAdaptarr TitleQuery {titleApiUrl} didn't succeed."); - return (false, null, string.Empty); - } - } - } -} diff --git a/UmlautAdaptarr/Utilities/ServicesExtensions.cs b/UmlautAdaptarr/Utilities/ServicesExtensions.cs index 1ac3957..8435828 100644 --- a/UmlautAdaptarr/Utilities/ServicesExtensions.cs +++ b/UmlautAdaptarr/Utilities/ServicesExtensions.cs @@ -1,6 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using UmlautAdaptarr.Options; +using UmlautAdaptarr.Options; using UmlautAdaptarr.Options.ArrOptions; using UmlautAdaptarr.Providers; using UmlautAdaptarr.Services; @@ -91,6 +89,4 @@ namespace UmlautAdaptarr.Utilities return builder.AddServiceWithOptions("Settings"); } } - - } From 6cf87620c32f909e207f15be9d59930a0e4738f4 Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 22:44:51 +0200 Subject: [PATCH 05/24] Add TODO --- UmlautAdaptarr/Providers/ArrClientFactory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UmlautAdaptarr/Providers/ArrClientFactory.cs b/UmlautAdaptarr/Providers/ArrClientFactory.cs index 55a8fec..68f92e5 100644 --- a/UmlautAdaptarr/Providers/ArrClientFactory.cs +++ b/UmlautAdaptarr/Providers/ArrClientFactory.cs @@ -2,6 +2,8 @@ { public static class ArrClientFactory { + // TODO, still uses old IConfiguration + // TODO not used yet public static IEnumerable CreateClients( Func constructor, IConfiguration configuration, string configKey) where TClient : ArrClientBase { From d098d1fd109c5feb780ce22560c89ea15054e03c Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 22:49:50 +0200 Subject: [PATCH 06/24] Change cache time from 5 to 12 minutes --- UmlautAdaptarr/Services/ProxyService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UmlautAdaptarr/Services/ProxyService.cs b/UmlautAdaptarr/Services/ProxyService.cs index e398a91..47239e6 100644 --- a/UmlautAdaptarr/Services/ProxyService.cs +++ b/UmlautAdaptarr/Services/ProxyService.cs @@ -81,7 +81,7 @@ namespace UmlautAdaptarr.Services if (responseMessage.IsSuccessStatusCode) { - _cache.Set(targetUri, responseMessage, TimeSpan.FromMinutes(5)); + _cache.Set(targetUri, responseMessage, TimeSpan.FromMinutes(12)); } return responseMessage; From 881f3b7281169972af455000ffa7f3758e27737f Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 23:15:03 +0200 Subject: [PATCH 07/24] Add IPv6 support --- UmlautAdaptarr/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UmlautAdaptarr/appsettings.json b/UmlautAdaptarr/appsettings.json index 32ae2bb..e075b7f 100644 --- a/UmlautAdaptarr/appsettings.json +++ b/UmlautAdaptarr/appsettings.json @@ -9,7 +9,7 @@ "Kestrel": { "Endpoints": { "Http": { - "Url": "http://*:5005" + "Url": "http://[::]:5005" } } }, From 49193ef12f950dfaa00ebd1465169d6ce000a8bc Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 23:22:40 +0200 Subject: [PATCH 08/24] Rename ProxyService to ProxyRequestService --- UmlautAdaptarr/Controllers/CapsController.cs | 6 +++--- UmlautAdaptarr/Controllers/SearchController.cs | 10 +++++----- UmlautAdaptarr/Program.cs | 2 +- .../{ProxyService.cs => ProxyRequestService.cs} | 8 ++++---- UmlautAdaptarr/Utilities/ServicesExtensions.cs | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) rename UmlautAdaptarr/Services/{ProxyService.cs => ProxyRequestService.cs} (91%) diff --git a/UmlautAdaptarr/Controllers/CapsController.cs b/UmlautAdaptarr/Controllers/CapsController.cs index 0d46837..9dc8fc3 100644 --- a/UmlautAdaptarr/Controllers/CapsController.cs +++ b/UmlautAdaptarr/Controllers/CapsController.cs @@ -6,9 +6,9 @@ using UmlautAdaptarr.Utilities; namespace UmlautAdaptarr.Controllers { - public class CapsController(ProxyService proxyService) : ControllerBase + public class CapsController(ProxyRequestService proxyRequestService) : ControllerBase { - private readonly ProxyService _proxyService = proxyService; + private readonly ProxyRequestService _proxyRequestService = proxyRequestService; [HttpGet] public async Task Caps([FromRoute] string options, [FromRoute] string domain, [FromQuery] string? apikey) @@ -20,7 +20,7 @@ namespace UmlautAdaptarr.Controllers var requestUrl = UrlUtilities.BuildUrl(domain, "caps", apikey); - var responseMessage = await _proxyService.ProxyRequestAsync(HttpContext, requestUrl); + var responseMessage = await _proxyRequestService.ProxyRequestAsync(HttpContext, requestUrl); var content = await responseMessage.Content.ReadAsStringAsync(); var encoding = responseMessage.Content.Headers.ContentType?.CharSet != null ? diff --git a/UmlautAdaptarr/Controllers/SearchController.cs b/UmlautAdaptarr/Controllers/SearchController.cs index 98939c3..90259d5 100644 --- a/UmlautAdaptarr/Controllers/SearchController.cs +++ b/UmlautAdaptarr/Controllers/SearchController.cs @@ -6,7 +6,7 @@ using UmlautAdaptarr.Utilities; namespace UmlautAdaptarr.Controllers { - public abstract class SearchControllerBase(ProxyService proxyService, TitleMatchingService titleMatchingService) : ControllerBase + public abstract class SearchControllerBase(ProxyRequestService proxyRequestService, TitleMatchingService titleMatchingService) : ControllerBase { // TODO evaluate if this should be set to true by default private readonly bool TODO_FORCE_TEXT_SEARCH_ORIGINAL_TITLE = true; @@ -96,7 +96,7 @@ namespace UmlautAdaptarr.Controllers private async Task PerformSingleSearchRequest(string domain, IDictionary queryParameters) { var requestUrl = UrlUtilities.BuildUrl(domain, queryParameters); - var responseMessage = await proxyService.ProxyRequestAsync(HttpContext, requestUrl); + var responseMessage = await proxyRequestService.ProxyRequestAsync(HttpContext, requestUrl); var content = await responseMessage.Content.ReadAsStringAsync(); var encoding = responseMessage.Content.Headers.ContentType?.CharSet != null ? @@ -130,7 +130,7 @@ namespace UmlautAdaptarr.Controllers { queryParameters["q"] = titleVariation; // Replace the "q" parameter for each variation var requestUrl = UrlUtilities.BuildUrl(domain, queryParameters); - var responseMessage = await proxyService.ProxyRequestAsync(HttpContext, requestUrl); + var responseMessage = await proxyRequestService.ProxyRequestAsync(HttpContext, requestUrl); var content = await responseMessage.Content.ReadAsStringAsync(); // Only update encoding from the first response @@ -152,9 +152,9 @@ namespace UmlautAdaptarr.Controllers } } - public class SearchController(ProxyService proxyService, + public class SearchController(ProxyRequestService proxyRequestService, TitleMatchingService titleMatchingService, - SearchItemLookupService searchItemLookupService) : SearchControllerBase(proxyService, titleMatchingService) + SearchItemLookupService searchItemLookupService) : SearchControllerBase(proxyRequestService, titleMatchingService) { public readonly string[] LIDARR_CATEGORY_IDS = ["3000", "3010", "3020", "3040", "3050"]; public readonly string[] READARR_CATEGORY_IDS = ["3030", "3130", "7000", "7010", "7020", "7030", "7100", "7110", "7120", "7130"]; diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index 622a10e..84c3a3f 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -56,7 +56,7 @@ internal class Program builder.AddLidarrSupport(); builder.AddReadarrSupport(); builder.Services.AddSingleton(); - builder.AddProxyService(); + builder.AddProxyRequestService(); var app = builder.Build(); diff --git a/UmlautAdaptarr/Services/ProxyService.cs b/UmlautAdaptarr/Services/ProxyRequestService.cs similarity index 91% rename from UmlautAdaptarr/Services/ProxyService.cs rename to UmlautAdaptarr/Services/ProxyRequestService.cs index 47239e6..0011244 100644 --- a/UmlautAdaptarr/Services/ProxyService.cs +++ b/UmlautAdaptarr/Services/ProxyRequestService.cs @@ -6,17 +6,17 @@ using UmlautAdaptarr.Utilities; namespace UmlautAdaptarr.Services { - public class ProxyService + public class ProxyRequestService { private readonly HttpClient _httpClient; private readonly string _userAgent; - private readonly ILogger _logger; + 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, ILogger logger, IMemoryCache cache, IOptions options) + public ProxyRequestService(IHttpClientFactory clientFactory, ILogger logger, IMemoryCache cache, IOptions options) { _options = options.Value; _httpClient = clientFactory.CreateClient("HttpClient") ?? throw new ArgumentNullException(nameof(clientFactory)); @@ -76,7 +76,7 @@ namespace UmlautAdaptarr.Services try { - _logger.LogInformation($"ProxyService GET {UrlUtilities.RedactApiKey(targetUri)}"); + _logger.LogInformation($"ProxyRequestService GET {UrlUtilities.RedactApiKey(targetUri)}"); var responseMessage = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted); if (responseMessage.IsSuccessStatusCode) diff --git a/UmlautAdaptarr/Utilities/ServicesExtensions.cs b/UmlautAdaptarr/Utilities/ServicesExtensions.cs index 8435828..2cc188d 100644 --- a/UmlautAdaptarr/Utilities/ServicesExtensions.cs +++ b/UmlautAdaptarr/Utilities/ServicesExtensions.cs @@ -80,13 +80,13 @@ namespace UmlautAdaptarr.Utilities } /// - /// Adds a proxy service to the service collection. + /// Adds a proxy request service to the service collection. /// /// The to configure the service collection. /// The configured . - public static WebApplicationBuilder AddProxyService(this WebApplicationBuilder builder) + public static WebApplicationBuilder AddProxyRequestService(this WebApplicationBuilder builder) { - return builder.AddServiceWithOptions("Settings"); + return builder.AddServiceWithOptions("Settings"); } } } From 660c2450697303c9b107f7cd652e0a1ea3f2afd3 Mon Sep 17 00:00:00 2001 From: pcjones Date: Sun, 14 Apr 2024 23:32:21 +0200 Subject: [PATCH 09/24] Remove 5005 port exposing from docker compose --- docker-compose.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2cc732f..1bd5356 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,4 @@ services: - READARR_API_KEY=API_KEY - LIDARR_ENABLED=false - LIDARR_HOST=http://localhost:8686 - - LIDARR_API_KEY=API_KEY - ports: - - "5005":"5005" + - LIDARR_API_KEY=API_KEY \ No newline at end of file From e888a1036620366e6e8db759289446ba737856be Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 00:48:15 +0200 Subject: [PATCH 10/24] Add timestamp to log --- UmlautAdaptarr/appsettings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UmlautAdaptarr/appsettings.json b/UmlautAdaptarr/appsettings.json index e075b7f..56b0dde 100644 --- a/UmlautAdaptarr/appsettings.json +++ b/UmlautAdaptarr/appsettings.json @@ -3,6 +3,9 @@ "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" + }, + "Console": { + "TimestampFormat": "yyyy-MM-dd HH:mm:ss::" } }, "AllowedHosts": "*", From cee3c12daa72d5aae1997c7551223b6790b35cb4 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 00:48:27 +0200 Subject: [PATCH 11/24] Add HttpProxyService --- UmlautAdaptarr/Program.cs | 4 +- UmlautAdaptarr/Services/HttpProxyService.cs | 85 +++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 UmlautAdaptarr/Services/HttpProxyService.cs diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index 84c3a3f..b1b4eae 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -56,13 +56,13 @@ internal class Program builder.AddLidarrSupport(); builder.AddReadarrSupport(); builder.Services.AddSingleton(); - builder.AddProxyRequestService(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); var app = builder.Build(); GlobalStaticLogger.Initialize(app.Services.GetService()!); app.UseHttpsRedirection(); - app.UseAuthorization(); app.MapControllerRoute(name: "caps", diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs new file mode 100644 index 0000000..07be92d --- /dev/null +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -0,0 +1,85 @@ +using System.Net; + +namespace UmlautAdaptarr.Services +{ + public class HttpProxyService : IHostedService + { + private HttpListener _listener; + private readonly IHttpClientFactory _clientFactory; + private readonly ILogger _logger; + private const int PROXY_PORT = 5006; // TODO move to appsettings.json + + public HttpProxyService(IHttpClientFactory clientFactory, ILogger logger) + { + _clientFactory = clientFactory; + _logger = logger; + } + + private async Task HandleRequests() + { + while (_listener.IsListening) + { + try + { + var context = await _listener.GetContextAsync(); + await ProcessRequest(context); + } + catch (Exception ex) + { + _logger.LogError($"Error handling request: {ex.Message}"); + } + } + } + + private async Task ProcessRequest(HttpListenerContext context) + { + var request = context.Request; + var response = context.Response; + + try + { + var originalUri = new Uri(request.RawUrl); + var modifiedUri = "http://localhost:5005/_/" + originalUri.Host + originalUri.PathAndQuery; // TODO read port from appsettings? + + // Act as a proxy and forward the modified request to internal endpoints + using var client = _clientFactory.CreateClient(); + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, modifiedUri); + var result = await client.SendAsync(httpRequestMessage); + + if (result.IsSuccessStatusCode) + { + var responseData = await result.Content.ReadAsByteArrayAsync(); + response.ContentLength64 = responseData.Length; + await response.OutputStream.WriteAsync(responseData); + } + else + { + response.StatusCode = (int)result.StatusCode; + } + } + catch (Exception ex) + { + _logger.LogError($"HTTP Proxy error: {ex.Message}"); + response.StatusCode = 500; + } + finally + { + response.OutputStream.Close(); + } + } + public Task StartAsync(CancellationToken cancellationToken) + { + _listener = new HttpListener(); + _listener.Prefixes.Add($"http://*:{PROXY_PORT}/"); + _listener.Start(); + Task.Run(HandleRequests, cancellationToken); + return Task.CompletedTask; + } + public Task StopAsync(CancellationToken cancellationToken) + { + _listener.Stop(); + _listener.Close(); + return Task.CompletedTask; + } + } +} From f3684d24d31f40b3485cbd4218b6e890bdf01324 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 00:56:13 +0200 Subject: [PATCH 12/24] Temporarily remove API key redaction --- UmlautAdaptarr/Utilities/UrlUtilities.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UmlautAdaptarr/Utilities/UrlUtilities.cs b/UmlautAdaptarr/Utilities/UrlUtilities.cs index 2d016ae..1b383a3 100644 --- a/UmlautAdaptarr/Utilities/UrlUtilities.cs +++ b/UmlautAdaptarr/Utilities/UrlUtilities.cs @@ -43,6 +43,7 @@ namespace UmlautAdaptarr.Utilities public static string RedactApiKey(string targetUri) { + return targetUri; var apiKeyPattern = @"(apikey=)[^&]*"; var redactedUri = Regex.Replace(targetUri, apiKeyPattern, "$1[REDACTED]"); From 08e13db32dbb6dae5a476470489c5ca2686543aa Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 00:59:56 +0200 Subject: [PATCH 13/24] Revert "Temporarily remove API key redaction" This reverts commit f3684d24d31f40b3485cbd4218b6e890bdf01324. --- UmlautAdaptarr/Utilities/UrlUtilities.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/UmlautAdaptarr/Utilities/UrlUtilities.cs b/UmlautAdaptarr/Utilities/UrlUtilities.cs index 1b383a3..2d016ae 100644 --- a/UmlautAdaptarr/Utilities/UrlUtilities.cs +++ b/UmlautAdaptarr/Utilities/UrlUtilities.cs @@ -43,7 +43,6 @@ namespace UmlautAdaptarr.Utilities public static string RedactApiKey(string targetUri) { - return targetUri; var apiKeyPattern = @"(apikey=)[^&]*"; var redactedUri = Regex.Replace(targetUri, apiKeyPattern, "$1[REDACTED]"); From 43717d5fc4557c2a3d1552a5c90baf3269536286 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 03:05:09 +0200 Subject: [PATCH 14/24] HttpProxyService: Forward https, modify http --- UmlautAdaptarr/Services/HttpProxyService.cs | 117 ++++++++++++++------ 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index 07be92d..6eabad2 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -1,47 +1,78 @@ using System.Net; +using System.Net.Sockets; +using System.Text; namespace UmlautAdaptarr.Services { public class HttpProxyService : IHostedService { - private HttpListener _listener; - private readonly IHttpClientFactory _clientFactory; + private TcpListener _listener; private readonly ILogger _logger; - private const int PROXY_PORT = 5006; // TODO move to appsettings.json + private readonly int _proxyPort = 5006; // TODO move to appsettings.json + private readonly IHttpClientFactory _clientFactory; - public HttpProxyService(IHttpClientFactory clientFactory, ILogger logger) + public HttpProxyService(ILogger logger, IHttpClientFactory clientFactory) { - _clientFactory = clientFactory; _logger = logger; + _clientFactory = clientFactory; } - private async Task HandleRequests() + private async Task HandleRequests(CancellationToken stoppingToken) { - while (_listener.IsListening) + while (!stoppingToken.IsCancellationRequested) { - try - { - var context = await _listener.GetContextAsync(); - await ProcessRequest(context); - } - catch (Exception ex) - { - _logger.LogError($"Error handling request: {ex.Message}"); - } + var clientSocket = await _listener.AcceptSocketAsync(); + _ = Task.Run(() => ProcessRequest(clientSocket), stoppingToken); } } - private async Task ProcessRequest(HttpListenerContext context) + private async Task ProcessRequest(Socket clientSocket) { - var request = context.Request; - var response = context.Response; + using var clientStream = new NetworkStream(clientSocket, ownsSocket: true); + var buffer = new byte[8192]; + var bytesRead = await clientStream.ReadAsync(buffer, 0, buffer.Length); + var requestString = Encoding.ASCII.GetString(buffer, 0, bytesRead); + if (requestString.StartsWith("CONNECT")) + { + // Handle HTTPS CONNECT request + await HandleHttpsConnect(requestString, clientStream, clientSocket); + } + else + { + // Handle HTTP request + await HandleHttp(requestString, clientStream, clientSocket); + } + } + + private async Task HandleHttpsConnect(string requestString, NetworkStream clientStream, Socket clientSocket) + { + var targetInfo = ParseTargetInfo(requestString); + if (targetInfo.host != "prowlarr.servarr.com") + { + _logger.LogWarning($"Indexer {targetInfo.host} needs to be set to http:// instead of https://"); + } + using var targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { - var originalUri = new Uri(request.RawUrl); - var modifiedUri = "http://localhost:5005/_/" + originalUri.Host + originalUri.PathAndQuery; // TODO read port from appsettings? + await targetSocket.ConnectAsync(targetInfo.host, targetInfo.port); + await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n")); + using var targetStream = new NetworkStream(targetSocket, ownsSocket: true); + await RelayTraffic(clientStream, targetStream); + } + catch (Exception ex) + { + _logger.LogError($"Failed to connect to target: {ex.Message}"); + clientSocket.Close(); + } + } - // Act as a proxy and forward the modified request to internal endpoints + private async Task HandleHttp(string requestString, NetworkStream clientStream, Socket clientSocket) + { + try + { + var uri = new Uri(requestString.Split(' ')[1]); + var modifiedUri = $"http://localhost:5005/_/{uri.Host}{uri.PathAndQuery}"; // TODO read port from appsettings? using var client = _clientFactory.CreateClient(); var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, modifiedUri); var result = await client.SendAsync(httpRequestMessage); @@ -49,36 +80,60 @@ namespace UmlautAdaptarr.Services if (result.IsSuccessStatusCode) { var responseData = await result.Content.ReadAsByteArrayAsync(); - response.ContentLength64 = responseData.Length; - await response.OutputStream.WriteAsync(responseData); + await clientStream.WriteAsync(Encoding.ASCII.GetBytes($"HTTP/1.1 200 OK\r\nContent-Length: {responseData.Length}\r\n\r\n")); + await clientStream.WriteAsync(responseData); } else { - response.StatusCode = (int)result.StatusCode; + await clientStream.WriteAsync(Encoding.ASCII.GetBytes($"HTTP/1.1 {result.StatusCode}\r\n\r\n")); } } catch (Exception ex) { _logger.LogError($"HTTP Proxy error: {ex.Message}"); - response.StatusCode = 500; + await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 500 Internal Server Error\r\n\r\n")); } finally { - response.OutputStream.Close(); + clientSocket.Close(); } } + + private (string host, int port) ParseTargetInfo(string requestLine) + { + var parts = requestLine.Split(' ')[1].Split(':'); + return (parts[0], int.Parse(parts[1])); + } + + private async Task RelayTraffic(NetworkStream clientStream, NetworkStream targetStream) + { + var clientToTargetTask = RelayStream(clientStream, targetStream); + var targetToClientTask = RelayStream(targetStream, clientStream); + await Task.WhenAll(clientToTargetTask, targetToClientTask); + } + + private async Task RelayStream(NetworkStream input, NetworkStream output) + { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = await input.ReadAsync(buffer.AsMemory(0, buffer.Length))) > 0) + { + await output.WriteAsync(buffer.AsMemory(0, bytesRead)); + await output.FlushAsync(); + } + } + public Task StartAsync(CancellationToken cancellationToken) { - _listener = new HttpListener(); - _listener.Prefixes.Add($"http://*:{PROXY_PORT}/"); + _listener = new TcpListener(IPAddress.Any, _proxyPort); _listener.Start(); - Task.Run(HandleRequests, cancellationToken); + Task.Run(() => HandleRequests(cancellationToken), cancellationToken); return Task.CompletedTask; } + public Task StopAsync(CancellationToken cancellationToken) { _listener.Stop(); - _listener.Close(); return Task.CompletedTask; } } From 45bc7baa4a38c1c8be8022759a4924230ff14e9a Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 03:17:14 +0200 Subject: [PATCH 15/24] Add TODO --- UmlautAdaptarr/Services/HttpProxyService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index 6eabad2..1fe52af 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -50,6 +50,7 @@ namespace UmlautAdaptarr.Services var targetInfo = ParseTargetInfo(requestString); if (targetInfo.host != "prowlarr.servarr.com") { + // TODO check why this is not logging in docker _logger.LogWarning($"Indexer {targetInfo.host} needs to be set to http:// instead of https://"); } using var targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); From 6f743eca017a8816ad20225805ab5c500188c04a Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 03:20:36 +0200 Subject: [PATCH 16/24] Revert "Add TODO" This reverts commit 45bc7baa4a38c1c8be8022759a4924230ff14e9a. --- UmlautAdaptarr/Services/HttpProxyService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index 1fe52af..6eabad2 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -50,7 +50,6 @@ namespace UmlautAdaptarr.Services var targetInfo = ParseTargetInfo(requestString); if (targetInfo.host != "prowlarr.servarr.com") { - // TODO check why this is not logging in docker _logger.LogWarning($"Indexer {targetInfo.host} needs to be set to http:// instead of https://"); } using var targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); From c4069e073284a736bd746696137af11f76c27a67 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 03:21:51 +0200 Subject: [PATCH 17/24] Change warning text --- UmlautAdaptarr/Services/HttpProxyService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index 6eabad2..c3f2285 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -50,7 +50,8 @@ namespace UmlautAdaptarr.Services var targetInfo = ParseTargetInfo(requestString); if (targetInfo.host != "prowlarr.servarr.com") { - _logger.LogWarning($"Indexer {targetInfo.host} needs to be set to http:// instead of https://"); + _logger.LogWarning($"IMPORTANT! {Environment.NewLine} Indexer {targetInfo.host} needs to be set to http:// instead of https:// {Environment.NewLine}" + + $"UmlautAdaptarr will not work for {targetInfo.host}!"); } using var targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try From 94e62cf4dd03a9c02bd03392875f5982d87af04b Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 03:42:20 +0200 Subject: [PATCH 18/24] Forward user-agent when using http proxy --- UmlautAdaptarr/Services/HttpProxyService.cs | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index c3f2285..4131845 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -41,7 +41,7 @@ namespace UmlautAdaptarr.Services else { // Handle HTTP request - await HandleHttp(requestString, clientStream, clientSocket); + await HandleHttp(requestString, clientStream, clientSocket, buffer, bytesRead); } } @@ -68,14 +68,18 @@ namespace UmlautAdaptarr.Services } } - private async Task HandleHttp(string requestString, NetworkStream clientStream, Socket clientSocket) + private async Task HandleHttp(string requestString, NetworkStream clientStream, Socket clientSocket, byte[] buffer, int bytesRead) { try { + var headers = ParseHeaders(buffer, bytesRead); + string userAgent = headers.FirstOrDefault(h => h.Key == "User-Agent").Value; + var uri = new Uri(requestString.Split(' ')[1]); var modifiedUri = $"http://localhost:5005/_/{uri.Host}{uri.PathAndQuery}"; // TODO read port from appsettings? using var client = _clientFactory.CreateClient(); var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, modifiedUri); + httpRequestMessage.Headers.Add("User-Agent", userAgent); var result = await client.SendAsync(httpRequestMessage); if (result.IsSuccessStatusCode) @@ -100,6 +104,24 @@ namespace UmlautAdaptarr.Services } } + private Dictionary ParseHeaders(byte[] buffer, int length) + { + var headers = new Dictionary(); + var headerString = Encoding.ASCII.GetString(buffer, 0, length); + var lines = headerString.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines.Skip(1)) // Skip the request line + { + var colonIndex = line.IndexOf(':'); + if (colonIndex > 0) + { + var key = line.Substring(0, colonIndex).Trim(); + var value = line.Substring(colonIndex + 1).Trim(); + headers[key] = value; + } + } + return headers; + } + private (string host, int port) ParseTargetInfo(string requestLine) { var parts = requestLine.Split(' ')[1].Split(':'); From 530cbed2d378d43b769a30286722b794e8ba2113 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 04:25:21 +0200 Subject: [PATCH 19/24] Update docker compose --- docker-compose.yml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1bd5356..71f32d4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,15 +8,20 @@ services: restart: unless-stopped environment: - TZ=Europe/Berlin - - SONARR_ENABLED=false - - SONARR_HOST=http://localhost:8989 - - SONARR_API_KEY=API_KEY - - RADARR_ENABLED=false - - RADARR_HOST=http://localhost:7878 - - RADARR_API_KEY=API_KEY - - READARR_ENABLED=false - - READARR_HOST=http://localhost:8787 - - READARR_API_KEY=API_KEY - - LIDARR_ENABLED=false - - LIDARR_HOST=http://localhost:8686 - - LIDARR_API_KEY=API_KEY \ No newline at end of file + - SONARR__ENABLED=false + - SONARR__HOST=http://localhost:8989 + - SONARR__APIKEY=APIKEY + - RADARR__ENABLED=false + - RADARR__HOST=http://localhost:7878 + - RADARR__APIKEY=APIKEY + - READARR__ENABLED=false + - READARR__HOST=http://localhost:8787 + - READARR__APIKEY=APIKEY + - LIDARR__ENABLED=false + - LIDARR__HOST=http://localhost:8686 + - LIDARR__APIKEY=APIKEY + #- Proxy__Enabled: false + #- 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) \ No newline at end of file From a4abb31ea1b38da8a4218fc52bdc73557d316856 Mon Sep 17 00:00:00 2001 From: pcjones Date: Mon, 15 Apr 2024 19:25:53 +0200 Subject: [PATCH 20/24] Check for known hosts before logging https warning --- UmlautAdaptarr/Services/HttpProxyService.cs | 27 ++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/UmlautAdaptarr/Services/HttpProxyService.cs b/UmlautAdaptarr/Services/HttpProxyService.cs index 4131845..074a58a 100644 --- a/UmlautAdaptarr/Services/HttpProxyService.cs +++ b/UmlautAdaptarr/Services/HttpProxyService.cs @@ -10,11 +10,15 @@ namespace UmlautAdaptarr.Services private readonly ILogger _logger; private readonly int _proxyPort = 5006; // TODO move to appsettings.json private readonly IHttpClientFactory _clientFactory; + private HashSet _knownHosts = []; + private readonly object _hostsLock = new object(); + public HttpProxyService(ILogger logger, IHttpClientFactory clientFactory) { _logger = logger; _clientFactory = clientFactory; + _knownHosts.Add("prowlarr.servarr.com"); } private async Task HandleRequests(CancellationToken stoppingToken) @@ -47,16 +51,18 @@ namespace UmlautAdaptarr.Services private async Task HandleHttpsConnect(string requestString, NetworkStream clientStream, Socket clientSocket) { - var targetInfo = ParseTargetInfo(requestString); - if (targetInfo.host != "prowlarr.servarr.com") + var (host, port) = ParseTargetInfo(requestString); + + // Prowlarr will send grab requests via https which cannot be changed + if (!_knownHosts.Contains(host)) { - _logger.LogWarning($"IMPORTANT! {Environment.NewLine} Indexer {targetInfo.host} needs to be set to http:// instead of https:// {Environment.NewLine}" + - $"UmlautAdaptarr will not work for {targetInfo.host}!"); + _logger.LogWarning($"IMPORTANT! {Environment.NewLine} Indexer {host} needs to be set to http:// instead of https:// {Environment.NewLine}" + + $"UmlautAdaptarr will not work for {host}!"); } using var targetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { - await targetSocket.ConnectAsync(targetInfo.host, targetInfo.port); + await targetSocket.ConnectAsync(host, port); await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n")); using var targetStream = new NetworkStream(targetSocket, ownsSocket: true); await RelayTraffic(clientStream, targetStream); @@ -74,8 +80,17 @@ namespace UmlautAdaptarr.Services { var headers = ParseHeaders(buffer, bytesRead); string userAgent = headers.FirstOrDefault(h => h.Key == "User-Agent").Value; - var uri = new Uri(requestString.Split(' ')[1]); + + // Add to known hosts if not already present + lock (_hostsLock) + { + if (!_knownHosts.Contains(uri.Host)) + { + _knownHosts.Add(uri.Host); + } + } + var modifiedUri = $"http://localhost:5005/_/{uri.Host}{uri.PathAndQuery}"; // TODO read port from appsettings? using var client = _clientFactory.CreateClient(); var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, modifiedUri); From 0cc0ca98e1e1a7b192282fcc5ab90d859dcb43a5 Mon Sep 17 00:00:00 2001 From: Jonas F Date: Mon, 22 Apr 2024 14:33:50 +0200 Subject: [PATCH 21/24] Update README.md --- README.md | 70 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 316c59e..e5e4f2f 100644 --- a/README.md +++ b/README.md @@ -2,36 +2,21 @@ ## English description coming soon -## Erste Testversion +## Beschreibung Wer möchte kann den UmlautAdaptarr jetzt gerne testen! Über Feedback würde ich mich sehr freuen! Es sollte mit allen *arrs funktionieren, hat aber nur bei Sonarr, Readarr und Lidarr schon Auswirkungen (abgesehen vom Caching). -Momentan ist docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden. - -[Link zum Docker Image](https://hub.docker.com/r/pcjones/umlautadaptarr) - -Zusätzlich müsst ihr in Sonarr oder Prowlarr einen neuen Indexer hinzufügen (für jeden Indexer, bei dem UmlautAdapdarr greifen soll). - -Am Beispiel von sceneNZBs: - -![grafik](https://github.com/PCJones/UmlautAdaptarr/assets/377223/07c7ca45-e0e5-4a82-af63-365bb23c55c9) - -Also alles wie immer, nur dass ihr als API-URL nicht direkt z.B. `https://scenenzbs.com` eingebt, sondern -`http://localhost:5005/_/scenenzbs.com` - -Den API-Key müsst ihr natürlich auch ganz normal setzen. - -## Was macht UmlautAdaptarr überhaupt? UmlautAdaptarr löst mehrere Probleme: - Releases mit Umlauten werden grundsätzlich nicht korrekt von den *Arrs importiert - Releases mit Umlauten werden oft nicht korrekt gefunden (*Arrs suchen nach "o" statt "ö" & es fehlt häufig die korrekte Zuordnung zur Serie/zum Film beim Indexer) - Sonarr & Radarr erwarten immer den englischen Titel von https://thetvdb.com/ bzw. https://www.themoviedb.org/. Das führt bei deutschen Produktionen oder deutschen Übersetzungen oft zu Problemen - falls die *arrs schon mal etwas mit der Meldung `Found matching series/movie via grab history, but release was matched to series by ID. Automatic import is not possible/` nicht importiert haben, dann war das der Grund. +- Zusätzlich werden einige andere Fehler behoben, die häufig dazu führen, dass Titel nicht erfolgreich gefunden, geladen oder importiert werden. -# Wie macht UmlautAdaptarr das? +## Wie macht UmlautAdaptarr das? UmlautAdaptarr tut so, als wäre es ein Indexer. In Wahrheit schaltet sich UmlautAdaptarr aber nur zwischen die *arrs und den echten Indexer und kann somit die Suchen sowie die Ergebnisse abfangen und bearbeiten. Am Ende werden die gefundenen Releases immer so umbenannt, dass die Arrs sie einwandfrei erkennen. -Einige Beispiele findet ihr unter Features. +Einige Beispiele finden sich [weiter unten](https://github.com/PCJones/UmlautAdaptarr/edit/develop/README.md#beispiel-funktionalit%C3%A4t). ## Features @@ -45,7 +30,7 @@ Einige Beispiele findet ihr unter Features. | Releases mit deutschem Titel werden erkannt | ✓ | | Releases mit TVDB-Alias Titel werden erkannt | ✓ | | Korrekte Suche und Erkennung von Titel mit Umlauten | ✓ | -| Anfragen-Caching für 5 Minuten zur Reduzierung der API-Zugriffe | ✓ | +| Anfragen-Caching für 12 Minuten zur Reduzierung der API-Zugriffe | ✓ | | Usenet (newznab) Support |✓| | Torrent (torznab) Support |✓| | Radarr Support | Geplant | @@ -53,6 +38,49 @@ Einige Beispiele findet ihr unter Features. | Unterstützung weiterer Sprachen neben Deutsch | Geplant | | Wünsche? | Vorschläge? | + +## Installation +Momentan ist docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden. Eine Unraid-App gibt es auch, einfach nach `umlautadaptarr` suchen. + +[Link zum Docker Image](https://hub.docker.com/r/pcjones/umlautadaptarr) + +Nicht benötigte Umgebungsvariablen können entfernt werden. + +### Konfiguration in Prowlarr (empfohlen) +Das ist die empfohlene Methode um den UmlautAdaptarr einzurichten. Sie hat den Vorteil, dass es keinen Geschwindigkeitsverlust bei der Suche gibt, sofern man mehrere Indexer hat. + +1) In Prowlarr: Settings>Indexers bzw. Einstellungen>Indexer öffnen +2) Lege einen neuen HTTP-Proxy an: + +![Image](https://github.com/PCJones/UmlautAdaptarr/assets/377223/b97418d8-d972-4e3c-9d2f-3a830a5ac0a3) + +- Port: `5006` +- Tag: `umlautadaptarr` +- Host: Je nachdem, wie deine Docker-Konfiguration ist, kann es sein, dass du entweder `umlautadaptarr` oder `localhost` als Host setzen muss. Probiere es sonst einfach aus, indem du auf Test klickst. +- Die Username- und Passwort-Felder können leergelassen werden. +3) Gehe zur Indexer-Übersichtsseite +4) Für alle Indexer/Tracker, die den UmlautAdaptarr nutzen sollen: + +![grafik](https://github.com/PCJones/UmlautAdaptarr/assets/377223/3daea3f1-7c7b-4982-84e2-ea6a42d90fba) + + - Füge den `umlautadaptarr` Tag hinzu + - **Wichtig:** Ändere die URL von `https` zu `http`. (Für die Experten: Dies ist erforderlich, damit der UmlautAdaptarr die Anfragen lokal abfangen kann. Ausgehende Anfragen an den Indexer verwenden natürlich weiterhin https). +5) Klicke danach auf `Test All Indexers` bzw `Alle Indexer Testen`. Falls du irgendwo noch `https` statt `http` stehen hast, sollte in den UmlautAdaptarr logs eine Warnung auftauchen. Mindestens solltest du aber noch ein zweites Mal alle Indexer durchgehen und überprüfen, ob überall `http` eingestellt ist - Indexer, bei denen noch `https` steht, werden nämlich einwandfrei funktionieren - allerdings ohne, dass der UmlautAdaptarr bei diesen wirken kann. + +### Konfiguration in Sonarr/Radarr oder Prowlarr ohne Proxy +Falls du kein Prowlarr nutzt oder nur 1-3 Indexer nutzt, kannst du diese alternative Konfigurationsmöglichkeit nutzen. + +Dafür musst du einfach nur alle Indexer, bei denen der UmlautAdaptarr greifen soll, bearbeiten: + +Am Beispiel von sceneNZBs: + +![grafik](https://github.com/PCJones/UmlautAdaptarr/assets/377223/07c7ca45-e0e5-4a82-af63-365bb23c55c9) + +Also alles wie immer, nur dass als API-URL nicht direkt z.B. `https://scenenzbs.com` gesetzt wird, sondern +`http://localhost:5005/_/scenenzbs.com` + +Den API-Key muss natürlich auch ganz normal gesetzt werden. + ## Beispiel-Funktionalität In den Klammern am Ende des Releasenamens (Bild 2 & 4) steht zu Anschauungszwecken der deutsche Titel der vorher nicht gefunden bzw. akzeptiert wurde. Das bleibt natürlich nicht so ;) @@ -66,7 +94,7 @@ In den Klammern am Ende des Releasenamens (Bild 2 & 4) steht zu Anschauungszweck **Vorher:** Es werden nur Releases mit dem englischen Titel der Serie gefunden ![Vorherige Suche, englische Titel](https://github.com/PCJones/UmlautAdaptarr/assets/377223/ed7ca0fa-ac36-4584-87ac-b29f32dd9ace) -**Jetzt:** Es werden auch Titel mit dem deutschen Namen gefunden :D (haben nicht alle Suchergebnisse auf den Screenshot gepasst) +**Jetzt:** Es werden auch Titel mit dem deutschen Namen gefunden :D ![Jetzige Suche, deutsche und englische Titel](https://github.com/PCJones/UmlautAdaptarr/assets/377223/1c2dbe1a-5943-4fc4-91ef-29708082900e) From f9a02ae487e1b930381fd2ecfa07b4e23bb4d1b8 Mon Sep 17 00:00:00 2001 From: Br33ce <124933490+Br33ce@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:42:43 +0200 Subject: [PATCH 22/24] Update README.md --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e5e4f2f..c5702be 100644 --- a/README.md +++ b/README.md @@ -40,23 +40,24 @@ Einige Beispiele finden sich [weiter unten](https://github.com/PCJones/UmlautAda ## Installation -Momentan ist docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden. Eine Unraid-App gibt es auch, einfach nach `umlautadaptarr` suchen. +Momentan ist Docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden. Eine Unraid-App gibt es auch, einfach nach `umlautadaptarr` suchen. [Link zum Docker Image](https://hub.docker.com/r/pcjones/umlautadaptarr) -Nicht benötigte Umgebungsvariablen können entfernt werden. +Nicht benötigte Umgebungsvariablen, z.B. wenn Readarr oder Lidarr nicht benötigt werden, können entfernt werden. -### Konfiguration in Prowlarr (empfohlen) -Das ist die empfohlene Methode um den UmlautAdaptarr einzurichten. Sie hat den Vorteil, dass es keinen Geschwindigkeitsverlust bei der Suche gibt, sofern man mehrere Indexer hat. +### Konfiguration in Prowlarr (**empfohlen**) +Das ist die **empfohlene** Methode um den UmlautAdaptarr einzurichten. Sie hat den Vorteil, dass es, sofern man mehrere Indexer nutzt, keinen Geschwindigkeitsverlust bei der Suche geben sollte. 1) In Prowlarr: Settings>Indexers bzw. Einstellungen>Indexer öffnen 2) Lege einen neuen HTTP-Proxy an: ![Image](https://github.com/PCJones/UmlautAdaptarr/assets/377223/b97418d8-d972-4e3c-9d2f-3a830a5ac0a3) -- Port: `5006` +- Name UmlautAdaptarr HTTP Proxy (Beispiel) +- Port: `5006` (Port beachten!) - Tag: `umlautadaptarr` -- Host: Je nachdem, wie deine Docker-Konfiguration ist, kann es sein, dass du entweder `umlautadaptarr` oder `localhost` als Host setzen muss. Probiere es sonst einfach aus, indem du auf Test klickst. +- Host: Je nachdem, wie deine Docker-Konfiguration ist, kann es sein, dass du entweder `umlautadaptarr` oder `localhost`, oder ggf. die IP des Host setzen musst. Probiere es sonst einfach aus, indem du auf Test klickst. - Die Username- und Passwort-Felder können leergelassen werden. 3) Gehe zur Indexer-Übersichtsseite 4) Für alle Indexer/Tracker, die den UmlautAdaptarr nutzen sollen: @@ -64,9 +65,11 @@ Das ist die empfohlene Methode um den UmlautAdaptarr einzurichten. Sie hat den V ![grafik](https://github.com/PCJones/UmlautAdaptarr/assets/377223/3daea3f1-7c7b-4982-84e2-ea6a42d90fba) - Füge den `umlautadaptarr` Tag hinzu - - **Wichtig:** Ändere die URL von `https` zu `http`. (Für die Experten: Dies ist erforderlich, damit der UmlautAdaptarr die Anfragen lokal abfangen kann. Ausgehende Anfragen an den Indexer verwenden natürlich weiterhin https). + - **Wichtig:** Ändere die URL von `https` zu `http`. (Dies ist erforderlich, damit der UmlautAdaptarr die Anfragen **lokal** abfangen kann. **Ausgehende** Anfragen an den Indexer verwenden natürlich weiterhin https). 5) Klicke danach auf `Test All Indexers` bzw `Alle Indexer Testen`. Falls du irgendwo noch `https` statt `http` stehen hast, sollte in den UmlautAdaptarr logs eine Warnung auftauchen. Mindestens solltest du aber noch ein zweites Mal alle Indexer durchgehen und überprüfen, ob überall `http` eingestellt ist - Indexer, bei denen noch `https` steht, werden nämlich einwandfrei funktionieren - allerdings ohne, dass der UmlautAdaptarr bei diesen wirken kann. +6) Je nachdem, wie du das Sync-Level von Prowlarr unter Settings>Apps bzw. Einstellungen>Anwendungen für die verschiedenen *arrs eingerichtet hast (Deaktiviert, Nur hinzufügen und entfernen, Vollständige Synchronisierung), musst du die Änderungen ggf. noch an deine *arrs syncen. + ### Konfiguration in Sonarr/Radarr oder Prowlarr ohne Proxy Falls du kein Prowlarr nutzt oder nur 1-3 Indexer nutzt, kannst du diese alternative Konfigurationsmöglichkeit nutzen. From 9c9c1583f7738ebbf77707f09fccebad4db12862 Mon Sep 17 00:00:00 2001 From: Br33ce <124933490+Br33ce@users.noreply.github.com> Date: Tue, 23 Apr 2024 17:12:14 +0200 Subject: [PATCH 23/24] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index c5702be..315247c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Das ist die **empfohlene** Methode um den UmlautAdaptarr einzurichten. Sie hat d ![Image](https://github.com/PCJones/UmlautAdaptarr/assets/377223/b97418d8-d972-4e3c-9d2f-3a830a5ac0a3) -- Name UmlautAdaptarr HTTP Proxy (Beispiel) +- Name: UmlautAdaptarr HTTP Proxy (Beispiel) - Port: `5006` (Port beachten!) - Tag: `umlautadaptarr` - Host: Je nachdem, wie deine Docker-Konfiguration ist, kann es sein, dass du entweder `umlautadaptarr` oder `localhost`, oder ggf. die IP des Host setzen musst. Probiere es sonst einfach aus, indem du auf Test klickst. @@ -68,8 +68,6 @@ Das ist die **empfohlene** Methode um den UmlautAdaptarr einzurichten. Sie hat d - **Wichtig:** Ändere die URL von `https` zu `http`. (Dies ist erforderlich, damit der UmlautAdaptarr die Anfragen **lokal** abfangen kann. **Ausgehende** Anfragen an den Indexer verwenden natürlich weiterhin https). 5) Klicke danach auf `Test All Indexers` bzw `Alle Indexer Testen`. Falls du irgendwo noch `https` statt `http` stehen hast, sollte in den UmlautAdaptarr logs eine Warnung auftauchen. Mindestens solltest du aber noch ein zweites Mal alle Indexer durchgehen und überprüfen, ob überall `http` eingestellt ist - Indexer, bei denen noch `https` steht, werden nämlich einwandfrei funktionieren - allerdings ohne, dass der UmlautAdaptarr bei diesen wirken kann. -6) Je nachdem, wie du das Sync-Level von Prowlarr unter Settings>Apps bzw. Einstellungen>Anwendungen für die verschiedenen *arrs eingerichtet hast (Deaktiviert, Nur hinzufügen und entfernen, Vollständige Synchronisierung), musst du die Änderungen ggf. noch an deine *arrs syncen. - ### Konfiguration in Sonarr/Radarr oder Prowlarr ohne Proxy Falls du kein Prowlarr nutzt oder nur 1-3 Indexer nutzt, kannst du diese alternative Konfigurationsmöglichkeit nutzen. From aa70c91a8723811d08422e61847cb1f5102f394f Mon Sep 17 00:00:00 2001 From: Jonas F Date: Tue, 23 Apr 2024 21:38:13 +0200 Subject: [PATCH 24/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 315247c..f02cd50 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Am Beispiel von sceneNZBs: Also alles wie immer, nur dass als API-URL nicht direkt z.B. `https://scenenzbs.com` gesetzt wird, sondern `http://localhost:5005/_/scenenzbs.com` -Den API-Key muss natürlich auch ganz normal gesetzt werden. +Der API-Key muss natürlich auch ganz normal gesetzt werden. ## Beispiel-Funktionalität In den Klammern am Ende des Releasenamens (Bild 2 & 4) steht zu Anschauungszwecken der deutsche Titel der vorher nicht gefunden bzw. akzeptiert wurde. Das bleibt natürlich nicht so ;)