Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Proxy Support, Add IOptions Pattern, Add Extensions Method #15

Merged
merged 1 commit into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions UmlautAdaptarr/Options/ArrOptions/ArrApplicationBaseOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace UmlautAdaptarr.Options.ArrOptions
{
/// <summary>
/// Base Options for ARR applications
/// </summary>
public class ArrApplicationBaseOptions
{
/// <summary>
/// Indicates whether the Arr application is enabled.
/// </summary>
public bool Enabled { get; set; }

/// <summary>
/// The host of the ARR application.
/// </summary>
public string Host { get; set; }

/// <summary>
/// The API key of the ARR application.
/// </summary>
public string ApiKey { get; set; }
}
}
9 changes: 9 additions & 0 deletions UmlautAdaptarr/Options/ArrOptions/LidarrInstanceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace UmlautAdaptarr.Options.ArrOptions
{
/// <summary>
/// Lidarr Options
/// </summary>
public class LidarrInstanceOptions : ArrApplicationBaseOptions
{
}
}
9 changes: 9 additions & 0 deletions UmlautAdaptarr/Options/ArrOptions/ReadarrInstanceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace UmlautAdaptarr.Options.ArrOptions
{
/// <summary>
/// Readarr Options
/// </summary>
public class ReadarrInstanceOptions : ArrApplicationBaseOptions
{
}
}
9 changes: 9 additions & 0 deletions UmlautAdaptarr/Options/ArrOptions/SonarrInstanceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace UmlautAdaptarr.Options.ArrOptions
{
/// <summary>
/// Sonarr Options
/// </summary>
public class SonarrInstanceOptions : ArrApplicationBaseOptions
{
}
}
18 changes: 18 additions & 0 deletions UmlautAdaptarr/Options/GlobalOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace UmlautAdaptarr.Options
{
/// <summary>
/// Global options for the UmlautAdaptarr application.
/// </summary>
public class GlobalOptions
{
/// <summary>
/// The host of the UmlautAdaptarr API.
/// </summary>
public string UmlautAdaptarrApiHost { get; set; }

/// <summary>
/// The User-Agent string used in HTTP requests.
/// </summary>
public string UserAgent { get; set; }
}
}
27 changes: 27 additions & 0 deletions UmlautAdaptarr/Options/Proxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace UmlautAdaptarr.Options;

/// <summary>
/// Represents options for proxy configuration.
/// </summary>
public class Proxy
{
/// <summary>
/// Gets or sets a value indicating whether to use a proxy.
/// </summary>
public bool Enabled { get; set; }

/// <summary>
/// Gets or sets the address of the proxy.
/// </summary>
public string? Address { get; set; }

/// <summary>
/// Gets or sets the username for proxy authentication.
/// </summary>
public string? Username { get; set; }

/// <summary>
/// Gets or sets the password for proxy authentication.
/// </summary>
public string? Password { get; set; }
}
32 changes: 32 additions & 0 deletions UmlautAdaptarr/Options/ProxyOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace UmlautAdaptarr.Options;

/// <summary>
/// Represents options for proxy configuration.
/// </summary>
public class ProxyOptions
{
/// <summary>
/// Gets or sets a value indicating whether to use a proxy.
/// </summary>
public bool Enabled { get; set; }

/// <summary>
/// Gets or sets the address of the proxy.
/// </summary>
public string? Address { get; set; }

/// <summary>
/// Gets or sets the username for proxy authentication.
/// </summary>
public string? Username { get; set; }

/// <summary>
/// Gets or sets the password for proxy authentication.
/// </summary>
public string? Password { get; set; }

/// <summary>
/// Bypass Local Ip Addresses , Proxy will ignore local Ip Addresses
/// </summary>
public bool BypassOnLocal { get; set; }
}
15 changes: 10 additions & 5 deletions UmlautAdaptarr/Program.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -24,6 +26,8 @@ private static void Main(string[] args)
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli
};

var proxyOptions = configuration.GetSection("Proxy").Get<ProxyOptions>();
handler.ConfigureProxy(proxyOptions);
return handler;
});

Expand All @@ -46,17 +50,18 @@ private static void Main(string[] args)

builder.Services.AddControllers();
builder.Services.AddHostedService<ArrSyncBackgroundService>();
builder.Services.AddSingleton<TitleApiService>();
builder.AddTitleLookupService();
builder.Services.AddSingleton<SearchItemLookupService>();
builder.Services.AddSingleton<TitleMatchingService>();
builder.Services.AddSingleton<SonarrClient>();
builder.Services.AddSingleton<LidarrClient>();
builder.Services.AddSingleton<ReadarrClient>();
builder.AddSonarrSupport();
builder.AddLidarrSupport();
builder.AddReadarrSupport();
builder.Services.AddSingleton<CacheService>();
builder.Services.AddSingleton<ProxyService>();
builder.AddProxyService();

var app = builder.Build();

GlobalStaticLogger.Initialize(app.Services.GetService<ILoggerFactory>()!);
app.UseHttpsRedirection();

app.UseAuthorization();
Expand Down
11 changes: 6 additions & 5 deletions UmlautAdaptarr/Providers/LidarrClient.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -12,10 +14,9 @@ public class LidarrClient(
IConfiguration configuration,
CacheService cacheService,
IMemoryCache cache,
ILogger<LidarrClient> logger) : ArrClientBase()
ILogger<LidarrClient> logger, IOptions<LidarrInstanceOptions> options) : ArrClientBase()
{
private readonly string _lidarrHost = configuration.GetValue<string>("LIDARR_HOST") ?? throw new ArgumentException("LIDARR_HOST environment variable must be set");
private readonly string _lidarrApiKey = configuration.GetValue<string>("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<IEnumerable<SearchItem>> FetchAllItemsAsync()
Expand All @@ -25,7 +26,7 @@ public override async Task<IEnumerable<SearchItem>> 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<List<dynamic>>(artistsApiResponse);
Expand All @@ -40,7 +41,7 @@ public override async Task<IEnumerable<SearchItem>> 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
Expand Down
11 changes: 7 additions & 4 deletions UmlautAdaptarr/Providers/ReadarrClient.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -12,10 +14,11 @@ public class ReadarrClient(
IConfiguration configuration,
CacheService cacheService,
IMemoryCache cache,
IOptions<ReadarrInstanceOptions> options,
ILogger<ReadarrClient> logger) : ArrClientBase()
{
private readonly string _readarrHost = configuration.GetValue<string>("READARR_HOST") ?? throw new ArgumentException("READARR_HOST environment variable must be set");
private readonly string _readarrApiKey = configuration.GetValue<string>("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<IEnumerable<SearchItem>> FetchAllItemsAsync()
Expand All @@ -25,7 +28,7 @@ public override async Task<IEnumerable<SearchItem>> 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<List<dynamic>>(authorApiResponse);
Expand All @@ -40,7 +43,7 @@ public override async Task<IEnumerable<SearchItem>> 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)}");
Expand Down
16 changes: 10 additions & 6 deletions UmlautAdaptarr/Providers/SonarrClient.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -9,10 +11,12 @@ public class SonarrClient(
IHttpClientFactory clientFactory,
IConfiguration configuration,
TitleApiService titleService,
IOptions<SonarrInstanceOptions> options,
ILogger<SonarrClient> logger) : ArrClientBase()
{
private readonly string _sonarrHost = configuration.GetValue<string>("SONARR_HOST") ?? throw new ArgumentException("SONARR_HOST environment variable must be set");
private readonly string _sonarrApiKey = configuration.GetValue<string>("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<string>("SONARR_HOST") ?? throw new ArgumentException("SONARR_HOST environment variable must be set");
//private readonly string _sonarrApiKey = configuration.GetValue<string>("SONARR_API_KEY") ?? throw new ArgumentException("SONARR_API_KEY environment variable must be set");
private readonly string _mediaType = "tv";

public override async Task<IEnumerable<SearchItem>> FetchAllItemsAsync()
Expand All @@ -22,7 +26,7 @@ public override async Task<IEnumerable<SearchItem>> 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<List<dynamic>>(response);
Expand Down Expand Up @@ -71,7 +75,7 @@ public override async Task<IEnumerable<SearchItem>> 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<dynamic>(response);
Expand Down Expand Up @@ -123,7 +127,7 @@ public override async Task<IEnumerable<SearchItem>> 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<dynamic>(sonarrApiResponse);

Expand Down
9 changes: 3 additions & 6 deletions UmlautAdaptarr/Services/ArrSyncBackgroundService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ public class ArrSyncBackgroundService(
IConfiguration configuration,
ILogger<ArrSyncBackgroundService> logger) : BackgroundService
{
private readonly bool _sonarrEnabled = configuration.GetValue<bool>("SONARR_ENABLED");
private readonly bool _lidarrEnabled = configuration.GetValue<bool>("LIDARR_ENABLED");
private readonly bool _readarrEnabled = configuration.GetValue<bool>("READARR_ENABLED");
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("ArrSyncBackgroundService is starting.");
Expand Down Expand Up @@ -62,17 +59,17 @@ private async Task<bool> 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;
Expand Down
9 changes: 7 additions & 2 deletions UmlautAdaptarr/Services/ProxyService.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,15 +12,18 @@ public class ProxyService
private readonly string _userAgent;
private readonly ILogger<ProxyService> _logger;
private readonly IMemoryCache _cache;
private readonly GlobalOptions _options;
private static readonly ConcurrentDictionary<string, DateTimeOffset> _lastRequestTimes = new();
private static readonly TimeSpan MINIMUM_DELAY_FOR_SAME_HOST = new(0, 0, 0, 1);

public ProxyService(IHttpClientFactory clientFactory, IConfiguration configuration, ILogger<ProxyService> logger, IMemoryCache cache)
public ProxyService(IHttpClientFactory clientFactory, ILogger<ProxyService> logger, IMemoryCache cache, IOptions<GlobalOptions> 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)
Expand Down
Loading