diff --git a/src/Jackett.Common/Indexers/EraiRaws.cs b/src/Jackett.Common/Indexers/EraiRaws.cs index 21e229149..fc4e2216c 100644 --- a/src/Jackett.Common/Indexers/EraiRaws.cs +++ b/src/Jackett.Common/Indexers/EraiRaws.cs @@ -147,12 +147,15 @@ namespace Jackett.Common.Indexers continue; } - if (releaseInfo.Link == null) + if (releaseInfo.MagnetLink == null) { logger.Warn($"Failed to parse {DisplayName} RSS feed item '{fi.Title}' due to malformed link URI."); continue; } + // Run the title parser for the details link + releaseInfo.DetailsLink = new Uri(string.Format("{0}anime-list/{1}", SiteLink, titleParser.GetUrlSlug(releaseInfo.Title))); + // If enabled, perform detailed title parsing if (IsTitleDetailParsingEnabled) { @@ -181,8 +184,9 @@ namespace Jackett.Common.Indexers yield return new ReleaseInfo { Title = string.Concat(fi.Title, " - ", fi.Quality), - Guid = fi.Link, - MagnetUri = fi.Link, + Guid = fi.MagnetLink, + MagnetUri = fi.MagnetLink, + Details = fi.DetailsLink, PublishDate = fi.PublishDate.Value.ToLocalTime().DateTime, Category = MapTrackerCatToNewznab("1"), @@ -278,7 +282,7 @@ namespace Jackett.Common.Indexers if (Uri.TryCreate(feedItem.Link, UriKind.Absolute, out Uri magnetUri)) { - Link = magnetUri; + MagnetLink = magnetUri; } if (DateTimeOffset.TryParse(feedItem.PublishDate, out DateTimeOffset publishDate)) @@ -302,7 +306,9 @@ namespace Jackett.Common.Indexers public string Title { get; set; } - public Uri Link { get; } + public Uri MagnetLink { get; } + + public Uri DetailsLink { get; set; } public DateTimeOffset? PublishDate { get; } } @@ -321,6 +327,8 @@ namespace Jackett.Common.Indexers { " – (?[0-9]+) ", " – " } // " – <episode> ..." - NOT A HYPHEN! }; + private const string TITLE_URL_SLUG_REGEX = @"^(?<url_slug>.+) –"; + public string Parse(string title) { var results = SearchTitleForDetails(title, new Dictionary<string, Dictionary<string, string>> { @@ -353,6 +361,25 @@ namespace Jackett.Common.Indexers ).Trim(); } + public string GetUrlSlug(string title) + { + var match = Regex.Match(title, TITLE_URL_SLUG_REGEX, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(0.5)); + if (!match.Success) + { + return null; + } + + var urlSlug = match.Groups["url_slug"].Value.ToLowerInvariant(); + urlSlug = Regex.Replace(urlSlug, "[^a-zA-Z0-9]", "-"); + urlSlug = urlSlug.Trim('-'); + while (urlSlug.Contains("--")) + { + urlSlug = urlSlug.Replace("--", "-"); + } + + return urlSlug; + } + private static (string strippedTitle, Dictionary<string, string> details) SearchTitleForDetails(string title, Dictionary<string, Dictionary<string, string>> definition) { Dictionary<string, string> details = new Dictionary<string, string>(); @@ -370,7 +397,7 @@ namespace Jackett.Common.Indexers { foreach (var srp in searchReplacePatterns) { - var match = Regex.Match(title, srp.Key, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(0.5)); + var match = Regex.Match(title, srp.Key, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(0.5)); if (match.Success) { string detail = match.Groups["detail"].Value; diff --git a/src/Jackett.Test/Common/Indexers/EraiRawsTests.cs b/src/Jackett.Test/Common/Indexers/EraiRawsTests.cs index 374ac7095..a32bfb2c5 100644 --- a/src/Jackett.Test/Common/Indexers/EraiRawsTests.cs +++ b/src/Jackett.Test/Common/Indexers/EraiRawsTests.cs @@ -15,6 +15,13 @@ namespace Jackett.Test.Common.Indexers var titleParser = new EraiRaws.TitleParser(); return titleParser.Parse(title); } + + [TestCaseSource(typeof(UrlSlugTestData), nameof(UrlSlugTestData.TestCases))] + public string TestTitleParsing_GetUrlSlug(string title) + { + var titleParser = new EraiRaws.TitleParser(); + return titleParser.GetUrlSlug(title); + } } public class TitleParserTestData @@ -31,4 +38,21 @@ namespace Jackett.Test.Common.Indexers } } } + + public class UrlSlugTestData + { + public static IEnumerable TestCases + { + get + { + yield return new TestCaseData("Tokyo Revengers – 02").Returns("tokyo-revengers"); + yield return new TestCaseData("Mairimashita! Iruma-kun 2nd Season – 01").Returns("mairimashita-iruma-kun-2nd-season"); + yield return new TestCaseData("Seijo no Maryoku wa Bannou Desu – 02 v2 (Multi)").Returns("seijo-no-maryoku-wa-bannou-desu"); + yield return new TestCaseData("Yuukoku no Moriarty Part 2 – 01 (Multi)").Returns("yuukoku-no-moriarty-part-2"); + yield return new TestCaseData("Maou-sama Retry! – 12 END ").Returns("maou-sama-retry"); + yield return new TestCaseData("Baki (2018) – 01 ~ 26 ").Returns("baki-2018"); + yield return new TestCaseData("Free!: Dive to the Future – 01 ~ 26 ").Returns("free-dive-to-the-future"); + } + } + } }