15 Commits

Author SHA1 Message Date
pcjones
96f8ff9332 Refactor TItleMatchingService 2024-02-14 21:00:24 +01:00
pcjones
e739affb39 Use TitleMatchVariations instead of TitleSearchVariations in SearchItemByTitle 2024-02-14 20:55:13 +01:00
pcjones
92bdf14618 Remove test variable 2024-02-14 20:40:13 +01:00
pcjones
4260b07bc4 Merge branch 'master' of https://github.com/PCJones/UmlautAdaptarr 2024-02-14 11:15:51 +01:00
pcjones
4d2ac194aa Ignore case when filtering distinct title match variations 2024-02-14 11:15:44 +01:00
pcjones
a6f332fd99 Fix hyphen in indexer url not being accepted 2024-02-14 11:14:26 +01:00
Jonas F
9c364cb652 Update README.md 2024-02-13 22:34:49 +01:00
pcjones
7e7ff15f75 Workaround for weird lidarr album title parsing 2024-02-13 01:47:08 +01:00
pcjones
4ee55fc14a Merge branch 'master' of https://github.com/PCJones/UmlautAdaptarr 2024-02-13 01:38:11 +01:00
pcjones
2ae236b68c Add Lidarr album matching workaround 2024-02-13 01:38:06 +01:00
Jonas F
5fe257f5d6 Update README.md 2024-02-13 01:26:58 +01:00
pcjones
525036e08f Merge branch 'master' of https://github.com/PCJones/UmlautAdaptarr 2024-02-13 01:22:03 +01:00
pcjones
687ba9b924 Add workaround for (DE) titles 2024-02-13 01:21:59 +01:00
Jonas F
0a048c92b8 Update README.md 2024-02-13 00:14:45 +01:00
Jonas F
eef0822ce7 Update README.md 2024-02-13 00:13:35 +01:00
7 changed files with 80 additions and 36 deletions

View File

@@ -2,10 +2,10 @@
## English description coming soon ## English description coming soon
## 12.02.2024: Erste Testversion ## Erste Testversion
Wer möchte kann den UmlautAdaptarr jetzt gerne testen! Über Feedback würde ich mich sehr freuen! 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 schon Auswirkungen (abgesehen vom Caching). Es sollte mit allen *arrs funktionieren, hat aber nur bei Sonarr und Lidarr schon Auswirkungen (abgesehen vom Caching).
Momentan ist docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden. Momentan ist docker dafür nötig, wer kein Docker nutzt muss sich noch etwas gedulden.
@@ -38,8 +38,9 @@ Einige Beispiele findet ihr unter Features.
| Feature | Status | | Feature | Status |
|-------------------------------------------------------------------|---------------| |-------------------------------------------------------------------|---------------|
| Sonarr & Prowlarr Support | ✓ | | Prowlarr Support | ✓|
| Lidarr Support | (✓) kein RSS Sync| | Sonarr Support | ✓ |
| Lidarr Support | ✓|
| Releases mit deutschem Titel werden erkannt | ✓ | | Releases mit deutschem Titel werden erkannt | ✓ |
| Releases mit TVDB-Alias Titel werden erkannt | ✓ | | Releases mit TVDB-Alias Titel werden erkannt | ✓ |
| Korrekte Suche und Erkennung von Titel mit Umlauten | ✓ | | Korrekte Suche und Erkennung von Titel mit Umlauten | ✓ |
@@ -54,16 +55,18 @@ Einige Beispiele findet ihr unter Features.
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 ;) 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 ;)
**Vorher:** Release wird zwar gefunden, kann aber kann nicht zu geordnet werden. **Vorher:** Release wird zwar gefunden, kann aber kann nicht zu geordnet werden.
![Vorherige Suche ohne deutsche Titel](https://i.imgur.com/7pfRzgH.png) ![Vorherige Suche ohne deutsche Titel](https://github.com/PCJones/UmlautAdaptarr/assets/377223/1fce2909-a36c-4f1b-8497-85903357fee3)
**Jetzt:** 2-3 weitere Releases werden gefunden, außerdem meckert Sonarr nicht mehr über den Namen und würde es bei einer automatischen Suche ohne Probleme importieren. **Jetzt:** 2-3 weitere Releases werden gefunden, außerdem meckert Sonarr nicht mehr über den Namen und würde es bei einer automatischen Suche ohne Probleme importieren.
![Jetzige Suche mit deutschen Titeln](https://i.imgur.com/k55YIN9.png) ![Jetzige Suche mit deutschen Titeln](https://github.com/PCJones/UmlautAdaptarr/assets/377223/0edf43ba-2beb-4f22-aaf4-30f9a619dbd6)
**Vorher:** Es werden nur Releases mit dem englischen Titel der Serie gefunden **Vorher:** Es werden nur Releases mit dem englischen Titel der Serie gefunden
![Vorherige Suche, englische Titel](https://i.imgur.com/pbRlOeX.png) ![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 (haben nicht alle Suchergebnisse auf den Screenshot gepasst)
![Jetzige Suche, deutsche und englische Titel](https://i.imgur.com/eeq0Voj.png) ![Jetzige Suche, deutsche und englische Titel](https://github.com/PCJones/UmlautAdaptarr/assets/377223/1c2dbe1a-5943-4fc4-91ef-29708082900e)
**Vorher:** Die deutsche Produktion `Alone - Überlebe die Wildnis` hat auf [TheTVDB](https://thetvdb.com/series/alone-uberlebe-die-wildnis) den Englischen Namen `Alone Germany`. **Vorher:** Die deutsche Produktion `Alone - Überlebe die Wildnis` hat auf [TheTVDB](https://thetvdb.com/series/alone-uberlebe-die-wildnis) den Englischen Namen `Alone Germany`.

View File

@@ -54,6 +54,7 @@ namespace UmlautAdaptarr.Models
else else
{ {
TitleSearchVariations = GenerateVariations(germanTitle, mediaType).ToArray(); TitleSearchVariations = GenerateVariations(germanTitle, mediaType).ToArray();
var allTitleVariations = new List<string>(TitleSearchVariations); var allTitleVariations = new List<string>(TitleSearchVariations);
// If aliases are not null, generate variations for each and add them to the list // If aliases are not null, generate variations for each and add them to the list
@@ -66,8 +67,22 @@ namespace UmlautAdaptarr.Models
} }
} }
TitleMatchVariations = allTitleVariations.Distinct().ToArray();
AuthorMatchVariations = []; AuthorMatchVariations = [];
// if a german title ends with (DE) also add a search string that replaces (DE) with GERMAN
// also add a matching title without (DE)
if (germanTitle?.EndsWith("(DE)") ?? false)
{
TitleSearchVariations = [.. TitleSearchVariations, ..
GenerateVariations(
germanTitle.Replace("(DE)", " GERMAN").RemoveExtraWhitespaces(),
mediaType)];
allTitleVariations.AddRange(GenerateVariations(germanTitle.Replace("(DE)", "").Trim(), mediaType));
}
TitleMatchVariations = allTitleVariations.Distinct(StringComparer.InvariantCultureIgnoreCase).ToArray();
} }
} }

View File

@@ -39,6 +39,7 @@ namespace UmlautAdaptarr.Providers
logger.LogWarning($"Sonarr Show {show.id} doesn't have a tvdbId."); logger.LogWarning($"Sonarr Show {show.id} doesn't have a tvdbId.");
continue; continue;
} }
(var germanTitle, var aliases) = await titleService.FetchGermanTitleAndAliasesByExternalIdAsync(_mediaType, tvdbId); (var germanTitle, var aliases) = await titleService.FetchGermanTitleAndAliasesByExternalIdAsync(_mediaType, tvdbId);
var searchItem = new SearchItem var searchItem = new SearchItem
( (

View File

@@ -84,7 +84,7 @@ namespace UmlautAdaptarr.Services
continue; continue;
} }
// After finding a potential item, compare normalizedTitle with each German title variation // After finding a potential item, compare normalizedTitle with each German title variation
foreach (var variation in item?.TitleSearchVariations ?? []) foreach (var variation in item?.TitleMatchVariations ?? [])
{ {
var normalizedVariation = variation.RemoveAccentButKeepGermanUmlauts().ToLower(); var normalizedVariation = variation.RemoveAccentButKeepGermanUmlauts().ToLower();
if (normalizedTitle.StartsWith(variation, StringComparison.OrdinalIgnoreCase)) if (normalizedTitle.StartsWith(variation, StringComparison.OrdinalIgnoreCase))

View File

@@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using Microsoft.Extensions.FileSystemGlobbing.Internal;
using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using UmlautAdaptarr.Models; using UmlautAdaptarr.Models;
using UmlautAdaptarr.Utilities; using UmlautAdaptarr.Utilities;
@@ -19,7 +20,7 @@ namespace UmlautAdaptarr.Services
if (titleElement != null) if (titleElement != null)
{ {
var originalTitle = titleElement.Value; var originalTitle = titleElement.Value;
var normalizedOriginalTitle = NormalizeTitle(originalTitle); var cleanTitleSeperatedBySpace = ReplaceSeperatorsWithSpace(originalTitle.RemoveAccentButKeepGermanUmlauts());
var categoryElement = item.Element("category"); var categoryElement = item.Element("category");
var category = categoryElement?.Value; var category = categoryElement?.Value;
@@ -33,7 +34,7 @@ namespace UmlautAdaptarr.Services
if (useCacheService) if (useCacheService)
{ {
// Use CacheService to find a matching SearchItem by title // Use CacheService to find a matching SearchItem by title
searchItem = cacheService.SearchItemByTitle(mediaType, normalizedOriginalTitle); searchItem = cacheService.SearchItemByTitle(mediaType, cleanTitleSeperatedBySpace);
} }
if (searchItem == null) if (searchItem == null)
@@ -45,10 +46,10 @@ namespace UmlautAdaptarr.Services
switch (mediaType) switch (mediaType)
{ {
case "tv": case "tv":
FindAndReplaceForMoviesAndTV(logger, searchItem, titleElement, originalTitle, normalizedOriginalTitle!); FindAndReplaceForMoviesAndTV(logger, searchItem, titleElement, originalTitle, cleanTitleSeperatedBySpace!);
break; break;
case "movie": case "movie":
FindAndReplaceForMoviesAndTV(logger, searchItem, titleElement, originalTitle, normalizedOriginalTitle!); FindAndReplaceForMoviesAndTV(logger, searchItem, titleElement, originalTitle, cleanTitleSeperatedBySpace!);
break; break;
case "audio": case "audio":
FindAndReplaceForAudio(searchItem, titleElement, originalTitle!); FindAndReplaceForAudio(searchItem, titleElement, originalTitle!);
@@ -67,23 +68,22 @@ namespace UmlautAdaptarr.Services
var authorMatch = FindBestMatch(searchItem.AuthorMatchVariations, originalTitle.NormalizeForComparison(), originalTitle); var authorMatch = FindBestMatch(searchItem.AuthorMatchVariations, originalTitle.NormalizeForComparison(), originalTitle);
var titleMatch = FindBestMatch(searchItem.TitleMatchVariations, originalTitle.NormalizeForComparison(), originalTitle); var titleMatch = FindBestMatch(searchItem.TitleMatchVariations, originalTitle.NormalizeForComparison(), originalTitle);
if (authorMatch.Item1 && titleMatch.Item1) if (authorMatch.foundMatch && titleMatch.foundMatch)
{ {
int matchEndPositionInOriginal = Math.Max(authorMatch.Item3, titleMatch.Item3); int matchEndPositionInOriginal = Math.Max(authorMatch.bestEndInOriginal, titleMatch.bestEndInOriginal);
var test = originalTitle[matchEndPositionInOriginal];
// Check and adjust for immediate following delimiter // Check and adjust for immediate following delimiter
if (matchEndPositionInOriginal < originalTitle.Length && new char[] { ' ', '-', '_', '.' }.Contains(originalTitle[matchEndPositionInOriginal])) char[] delimiters = [' ', '-', '_', '.'];
if (matchEndPositionInOriginal < originalTitle.Length && delimiters.Contains(originalTitle[matchEndPositionInOriginal]))
{ {
matchEndPositionInOriginal++; // Skip the delimiter if it's immediately after the match matchEndPositionInOriginal++; // Skip the delimiter if it's immediately after the match
} }
// Ensure we trim any leading delimiters from the suffix // Ensure we trim any leading delimiters from the suffix
string suffix = originalTitle[matchEndPositionInOriginal..].TrimStart([' ', '-', '_', '.']).Trim(); string suffix = originalTitle[matchEndPositionInOriginal..].TrimStart([' ', '-', '_', '.']).Trim();
suffix = suffix.Replace("-", ".");
// Concatenate the expected title with the remaining suffix // Concatenate the expected title with the remaining suffix
var updatedTitle = $"{searchItem.ExpectedAuthor} - {searchItem.ExpectedTitle}-{suffix}"; var updatedTitle = $"{searchItem.ExpectedAuthor} - {searchItem.ExpectedTitle}-[{suffix}]";
// Update the title element // Update the title element
titleElement.Value = updatedTitle; titleElement.Value = updatedTitle;
@@ -96,7 +96,7 @@ namespace UmlautAdaptarr.Services
} }
private Tuple<bool, int, int> FindBestMatch(string[] variations, string normalizedOriginal, string originalTitle) private (bool foundMatch, int bestStart, int bestEndInOriginal) FindBestMatch(string[] variations, string normalizedOriginal, string originalTitle)
{ {
bool found = false; bool found = false;
int bestStart = int.MaxValue; int bestStart = int.MaxValue;
@@ -119,8 +119,8 @@ namespace UmlautAdaptarr.Services
} }
} }
if (!found) return Tuple.Create(false, 0, 0); if (!found) return (false, 0, 0);
return Tuple.Create(found, bestStart, bestEndInOriginal); return (found, bestStart, bestEndInOriginal);
} }
// Maps an index from the normalized string back to a corresponding index in the original string // Maps an index from the normalized string back to a corresponding index in the original string
@@ -159,6 +159,7 @@ namespace UmlautAdaptarr.Services
var titleMatchVariations = searchItem.TitleMatchVariations; var titleMatchVariations = searchItem.TitleMatchVariations;
var expectedTitle = searchItem.ExpectedTitle; var expectedTitle = searchItem.ExpectedTitle;
var variationsOrderedByLength = titleMatchVariations!.OrderByDescending(variation => variation.Length); var variationsOrderedByLength = titleMatchVariations!.OrderByDescending(variation => variation.Length);
// Attempt to find a variation that matches the start of the original title // Attempt to find a variation that matches the start of the original title
foreach (var variation in variationsOrderedByLength) foreach (var variation in variationsOrderedByLength)
{ {
@@ -174,12 +175,6 @@ namespace UmlautAdaptarr.Services
// Check if the originalTitle starts with the variation (ignoring case and separators) // Check if the originalTitle starts with the variation (ignoring case and separators)
if (Regex.IsMatch(normalizedOriginalTitle, variationMatchPattern, RegexOptions.IgnoreCase)) if (Regex.IsMatch(normalizedOriginalTitle, variationMatchPattern, RegexOptions.IgnoreCase))
{ {
// Workaround for the rare case of e.g. "Frieren: Beyond Journey's End" that also has the alias "Frieren"
if (expectedTitle!.StartsWith(variation, StringComparison.OrdinalIgnoreCase))
{
logger.LogWarning($"TitleMatchingService - Didn't rename: '{originalTitle}' because the expected title '{expectedTitle}' starts with the variation '{variation}'");
continue;
}
var originalTitleMatchPattern = "^" + Regex.Escape(variation).Replace("\\ ", "[._ ]"); var originalTitleMatchPattern = "^" + Regex.Escape(variation).Replace("\\ ", "[._ ]");
// Find the first separator used in the original title for consistent replacement // Find the first separator used in the original title for consistent replacement
@@ -191,8 +186,21 @@ namespace UmlautAdaptarr.Services
var variationLength = variation.Length; var variationLength = variation.Length;
var suffix = originalTitle[Math.Min(variationLength, originalTitle.Length)..]; var suffix = originalTitle[Math.Min(variationLength, originalTitle.Length)..];
// Clean up any leading separators from the suffix // Workaround for the rare case of e.g. "Frieren: Beyond Journey's End" that also has the alias "Frieren"
suffix = Regex.Replace(suffix, "^[._ ]+", ""); if (expectedTitle!.StartsWith(variation, StringComparison.OrdinalIgnoreCase))
{
// See if we already matched the whole title by checking if S01E01 pattern is coming next to avoid false positives
// - that won't help with movies but with tv shows
var seasonMatchingPattern = $"^{separator}S\\d{{1,2}}E\\d{{1,2}}";
if (!Regex.IsMatch(suffix, seasonMatchingPattern))
{
logger.LogWarning($"TitleMatchingService - Didn't rename: '{originalTitle}' because the expected title '{expectedTitle}' starts with the variation '{variation}'");
continue;
}
}
// Clean up any leading separator from the suffix
suffix = Regex.Replace(suffix, "^ +", "");
// TODO EVALUTE! definitely make this optional - this adds GERMAN to the title is the title is german to make sure it's recognized as german // TODO EVALUTE! definitely make this optional - this adds GERMAN to the title is the title is german to make sure it's recognized as german
// can lead to problems with shows such as "dark" that have international dubs // can lead to problems with shows such as "dark" that have international dubs
@@ -218,9 +226,8 @@ namespace UmlautAdaptarr.Services
} }
} }
private static string NormalizeTitle(string title) private static string ReplaceSeperatorsWithSpace(string title)
{ {
title = title.RemoveAccentButKeepGermanUmlauts();
// Replace all known separators with space for normalization // Replace all known separators with space for normalization
return WordSeperationCharRegex().Replace(title, " ".ToString()); return WordSeperationCharRegex().Replace(title, " ".ToString());
} }

View File

@@ -5,7 +5,7 @@ namespace UmlautAdaptarr.Utilities
{ {
public partial class UrlUtilities public partial class UrlUtilities
{ {
[GeneratedRegex(@"^(?!http:\/\/)([a-zA-Z0-9]+(\.[a-zA-Z0-9]+)+.*)$")] [GeneratedRegex(@"^(?!http:\/\/)([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+.*)$")]
private static partial Regex UrlMatchingRegex(); private static partial Regex UrlMatchingRegex();
public static bool IsValidDomain(string domain) public static bool IsValidDomain(string domain)
{ {

View File

@@ -0,0 +1,18 @@
@echo off
SET IMAGE_NAME=pcjones/umlautadaptarr
echo Enter the version number for the Docker image:
set /p VERSION="Version: "
echo Building Docker image with version %VERSION%...
docker build -t %IMAGE_NAME%:%VERSION% .
docker tag %IMAGE_NAME%:%VERSION% %IMAGE_NAME%:latest
echo Pushing Docker image with version %VERSION%...
docker push %IMAGE_NAME%:%VERSION%
echo Pushing Docker image with tag latest...
docker push %IMAGE_NAME%:latest
echo Done.
pause