11 Commits
v0.3 ... v0.3.5

9 changed files with 80 additions and 26 deletions

View File

@@ -38,15 +38,15 @@ Einige Beispiele findet ihr unter Features.
| Feature | Status |
|-------------------------------------------------------------------|---------------|
| Prowlarr Support | ✓|
| Prowlarr & NZB Hydra Support | ✓|
| Sonarr Support | ✓ |
| Lidarr Support | ✓|
| Readarr Support | ✓ |
| 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 | ✓ |
| Radarr Support | Geplant |
| Readarr Support | Geplant |
| Prowlarr Unterstützung für "DE" SceneNZBs Kategorien | Geplant |
| Unterstützung weiterer Sprachen neben Deutsch | Geplant |
| Wünsche? | Vorschläge? |
@@ -87,6 +87,12 @@ Sonarr erwartet immer den Englischen Namen, der hier natürlich nicht gegeben is
- [Telegram](https://t.me/pc_jones)
- Discord: pcjones1 - oder komm in den UsenetDE Discord Server: [https://discord.gg/pZrrMcJMQM](https://discord.gg/pZrrMcJMQM)
## Spenden
Über eine Spende freue ich mich natürlich immer :D
PayPal: https://paypal.me/pcjones1
Für andere Spendenmöglichkeiten gerne auf Discord oder Telegram melden - danke!
### Licenses & Metadata source
- TV Metadata source: https://thetvdb.com
- Movie Metadata source: https://themoviedb.org

View File

@@ -8,7 +8,8 @@ namespace UmlautAdaptarr.Controllers
{
public abstract class SearchControllerBase(ProxyService proxyService, TitleMatchingService titleMatchingService) : ControllerBase
{
private readonly bool TODO_FORCE_TEXT_SEARCH_ORIGINAL_TITLE = false;
// TODO evaluate if this should be set to true by default
private readonly bool TODO_FORCE_TEXT_SEARCH_ORIGINAL_TITLE = true;
private readonly bool TODO_FORCE_TEXT_SEARCH_GERMAN_TITLE = false;
protected async Task<IActionResult> BaseSearch(string options,
string domain,
@@ -52,7 +53,7 @@ namespace UmlautAdaptarr.Controllers
var titleSearchVariations = new List<string>(searchItem?.TitleSearchVariations);
string searchQuery = string.Empty;
var searchQuery = string.Empty;
if (queryParameters.TryGetValue("q", out string? q))
{
searchQuery = q ?? string.Empty;
@@ -185,7 +186,6 @@ namespace UmlautAdaptarr.Controllers
if (categories.Split(',').Any(category => READARR_CATEGORY_IDS.Contains(category)))
{
var mediaType = "book";
// TODO rename function or use own
searchItem = await searchItemLookupService.GetOrFetchSearchItemByExternalId(mediaType, title.GetReadarrTitleForExternalId());
}

View File

@@ -43,7 +43,30 @@ namespace UmlautAdaptarr.Models
}
else
{
GenerateVariationsForTV(germanTitle, mediaType, aliases);
// if mediatype is movie/tv and the Expected Title ends with a year but the german title doesn't then append the year to the german title and to aliases
// example: https://thetvdb.com/series/385925-avatar-the-last-airbender -> german Title is without 2024
var yearAtEndOfTitleMatch = YearAtEndOfTitleRegex().Match(expectedTitle);
if (yearAtEndOfTitleMatch.Success)
{
string year = yearAtEndOfTitleMatch.Value[1..^1];
if (GermanTitle != null && !GermanTitle.Contains(year))
{
GermanTitle = $"{germanTitle} {year}";
}
if (aliases != null)
{
for (int i = 0; i < aliases.Length; i++)
{
if (!aliases[i].Contains(year))
{
aliases[i] = $"{aliases[i]} {year}";
}
}
}
}
GenerateVariationsForTV(GermanTitle, mediaType, aliases);
}
}
@@ -60,6 +83,12 @@ namespace UmlautAdaptarr.Models
foreach (var alias in aliases)
{
allTitleVariations.AddRange(GenerateVariations(alias, mediaType));
// If title contains ":" also match for "-"
if (alias.Contains(':'))
{
allTitleVariations.Add(alias.Replace(":", " -"));
}
}
}
@@ -79,6 +108,12 @@ namespace UmlautAdaptarr.Models
}
// If title contains ":" also match for "-"
if (germanTitle?.Contains(':') ?? false)
{
allTitleVariations.Add(germanTitle.Replace(":", " -"));
}
TitleMatchVariations = allTitleVariations.Distinct(StringComparer.InvariantCultureIgnoreCase).ToArray();
}
@@ -123,6 +158,7 @@ namespace UmlautAdaptarr.Models
{
return [];
}
var cleanTitle = title.GetCleanTitle();
if (cleanTitle?.Length == 0)
@@ -180,5 +216,8 @@ namespace UmlautAdaptarr.Models
return cleanedVariations.Distinct();
}
[GeneratedRegex(@"\(\d{4}\)$")]
private static partial Regex YearAtEndOfTitleRegex();
}
}

View File

@@ -22,7 +22,6 @@ namespace UmlautAdaptarr.Providers
try
{
var sonarrUrl = $"{_sonarrHost}/api/v3/series?includeSeasonImages=false&apikey={_sonarrApiKey}";
logger.LogInformation($"Fetching all items from Sonarr: {UrlUtilities.RedactApiKey(sonarrUrl)}");
var response = await httpClient.GetStringAsync(sonarrUrl);

View File

@@ -64,15 +64,18 @@ namespace UmlautAdaptarr.Services
var success = true;
if (_readarrEnabled)
{
success = success && await FetchItemsFromReadarrAsync();
var syncSuccess = await FetchItemsFromReadarrAsync();
success = success && syncSuccess;
}
if (_sonarrEnabled)
{
success = success && await FetchItemsFromSonarrAsync();
var syncSuccess = await FetchItemsFromSonarrAsync();
success = success && syncSuccess;
}
if (_lidarrEnabled)
{
success = success && await FetchItemsFromLidarrAsync();
var syncSuccess = await FetchItemsFromLidarrAsync();
success = success && syncSuccess;
}
return success;
}

View File

@@ -20,6 +20,20 @@ namespace UmlautAdaptarr.Services
_cache = cache;
}
private static async Task EnsureMinimumDelayAsync(string targetUri)
{
var host = new Uri(targetUri).Host;
if (_lastRequestTimes.TryGetValue(host, out var lastRequestTime))
{
var timeSinceLastRequest = DateTimeOffset.Now - lastRequestTime;
if (timeSinceLastRequest < TimeSpan.FromMilliseconds(1500))
{
await Task.Delay(TimeSpan.FromMilliseconds(1500) - timeSinceLastRequest);
}
}
_lastRequestTimes[host] = DateTimeOffset.Now;
}
public async Task<HttpResponseMessage> ProxyRequestAsync(HttpContext context, string targetUri)
{
if (!HttpMethods.IsGet(context.Request.Method))
@@ -27,18 +41,6 @@ namespace UmlautAdaptarr.Services
throw new ArgumentException("Only GET requests are supported", context.Request.Method);
}
// Throttling mechanism
var host = new Uri(targetUri).Host;
if (_lastRequestTimes.TryGetValue(host, out var lastRequestTime))
{
var timeSinceLastRequest = DateTimeOffset.Now - lastRequestTime;
if (timeSinceLastRequest < TimeSpan.FromSeconds(3))
{
await Task.Delay(TimeSpan.FromSeconds(3) - timeSinceLastRequest);
}
}
_lastRequestTimes[host] = DateTimeOffset.Now;
// Check cache
if (_cache.TryGetValue(targetUri, out HttpResponseMessage cachedResponse))
{
@@ -46,6 +48,8 @@ namespace UmlautAdaptarr.Services
return cachedResponse!;
}
await EnsureMinimumDelayAsync(targetUri);
var requestMessage = new HttpRequestMessage
{
RequestUri = new Uri(targetUri),

View File

@@ -35,12 +35,14 @@ namespace UmlautAdaptarr.Services
if (_lidarrEnabled)
{
fetchedItem = await lidarrClient.FetchItemByExternalIdAsync(externalId);
fetchedItem = cacheService.GetSearchItemByExternalId(mediaType, externalId);
}
break;
case "book":
if (_readarrEnabled)
{
fetchedItem = await readarrClient.FetchItemByExternalIdAsync(externalId);
await readarrClient.FetchItemByExternalIdAsync(externalId);
fetchedItem = cacheService.GetSearchItemByExternalId(mediaType, externalId);
}
break;
}

View File

@@ -13,13 +13,14 @@ namespace UmlautAdaptarr.Services
private async Task EnsureMinimumDelayAsync()
{
var sinceLastRequest = DateTime.Now - lastRequestTime;
if (sinceLastRequest < TimeSpan.FromSeconds(2))
if (sinceLastRequest < TimeSpan.FromSeconds(1))
{
await Task.Delay(TimeSpan.FromSeconds(2) - sinceLastRequest);
await Task.Delay(TimeSpan.FromSeconds(1) - sinceLastRequest);
}
lastRequestTime = DateTime.Now;
}
// TODO add cache, TODO add bulk request
public async Task<(string? germanTitle, string[]? aliases)> FetchGermanTitleAndAliasesByExternalIdAsync(string mediaType, string externalId)
{
try

View File

@@ -221,7 +221,7 @@ namespace UmlautAdaptarr.Services
*/
// Construct the new title with the original suffix
var newTitle = newTitlePrefix + (string.IsNullOrEmpty(suffix) ? "" : separator + suffix);
var newTitle = newTitlePrefix + (string.IsNullOrEmpty(suffix) ? "" : suffix.StartsWith(separator) ? suffix : $"{separator}{suffix}");
// Update the title element's value with the new title
//titleElement.Value = newTitle + $"({originalTitle.Substring(0, variationLength)})";