From 584348c6ad68d4ff478fe7a594d31ead4701637f Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Mon, 14 Jun 2021 21:12:37 -0500 Subject: [PATCH] Special Marker Changes (#306) * SP# is now a way to force the file to be a special rather than pushing it into a Specials folder. * Made it so if there is a Special (for any Parse call), volume and chapters will be ignored. * Fixed a unit test missing Theory and fixed a regex case --- API.Tests/Parser/MangaParserTests.cs | 5 ++- API.Tests/Parser/ParserTest.cs | 10 +++++ API/Parser/Parser.cs | 59 +++++++++++++++++++++------- API/Parser/ParserInfo.cs | 2 +- API/Services/Tasks/ScannerService.cs | 2 +- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/API.Tests/Parser/MangaParserTests.cs b/API.Tests/Parser/MangaParserTests.cs index 2ec34318a..1123404b0 100644 --- a/API.Tests/Parser/MangaParserTests.cs +++ b/API.Tests/Parser/MangaParserTests.cs @@ -145,6 +145,7 @@ namespace API.Tests.Parser [InlineData("X-Men v1 #201 (September 2007).cbz", "X-Men")] [InlineData("Kodoja #001 (March 2016)", "Kodoja")] [InlineData("Boku No Kokoro No Yabai Yatsu - Chapter 054 I Prayed At The Shrine (V0).cbz", "Boku No Kokoro No Yabai Yatsu")] + [InlineData("Kiss x Sis - Ch.36 - A Cold Home Visit.cbz", "Kiss x Sis")] public void ParseSeriesTest(string filename, string expected) { Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename)); @@ -242,8 +243,8 @@ namespace API.Tests.Parser [InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)] [InlineData("A Town Where You Live - Bonus Chapter.zip", true)] [InlineData("Yuki Merry - 4-Komga Anthology", false)] - [InlineData("Beastars - SP01", true)] - [InlineData("Beastars SP01", true)] + [InlineData("Beastars - SP01", false)] + [InlineData("Beastars SP01", false)] public void ParseMangaSpecialTest(string input, bool expected) { Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input))); diff --git a/API.Tests/Parser/ParserTest.cs b/API.Tests/Parser/ParserTest.cs index 314c7cd11..4a1a1babd 100644 --- a/API.Tests/Parser/ParserTest.cs +++ b/API.Tests/Parser/ParserTest.cs @@ -5,6 +5,16 @@ namespace API.Tests.Parser { public class ParserTests { + + [Theory] + [InlineData("Beastars - SP01", true)] + [InlineData("Beastars SP01", true)] + [InlineData("Beastars Special 01", false)] + [InlineData("Beastars Extra 01", false)] + public void HasSpecialTest(string input, bool expected) + { + Assert.Equal(expected, HasSpecialMarker(input)); + } [Theory] [InlineData("0001", "1")] diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index d453a7586..149d5a940 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -9,9 +9,12 @@ namespace API.Parser { public static class Parser { - public static readonly string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z|.cb7"; - public static readonly string BookFileExtensions = @"\.epub"; - public static readonly string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)"; + public const string DefaultChapter = "0"; + public const string DefaultVolume = "0"; + + public const string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z|.cb7"; + public const string BookFileExtensions = @"\.epub"; + public const string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)"; public static readonly Regex FontSrcUrlRegex = new Regex("(src:url\\(\"?'?)([a-z0-9/\\._]+)(\"?'?\\))", RegexOptions.IgnoreCase | RegexOptions.Compiled); public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?[\\w\\d/\\._-]+)([\"|'];?)", RegexOptions.IgnoreCase | RegexOptions.Compiled); @@ -92,7 +95,7 @@ namespace API.Parser RegexOptions.IgnoreCase | RegexOptions.Compiled), // Historys Strongest Disciple Kenichi_v11_c90-98.zip, Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb) new Regex( - @"(?.*) (\b|_|-)v", + @"(?.*) (\b|_|-)(v|ch\.?|c)\d+", RegexOptions.IgnoreCase | RegexOptions.Compiled), //Ichinensei_ni_Nacchattara_v01_ch01_[Taruby]_v1.1.zip must be before [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip // due to duplicate version identifiers in file. @@ -374,12 +377,14 @@ namespace API.Parser new Regex( @"(?Specials?|OneShot|One\-Shot|Omake|Extra( Chapter)?|Art Collection|Side( |_)Stories|Bonus)", RegexOptions.IgnoreCase | RegexOptions.Compiled), - // If SP\d+ is in the filename, we force treat it as a special regardless if volume or chapter might have been found. - new Regex( - @"(?SP\d+)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), }; + // If SP\d+ is in the filename, we force treat it as a special regardless if volume or chapter might have been found. + private static readonly Regex SpecialMarkerRegex = new Regex( + @"(?SP\d+)", + RegexOptions.IgnoreCase | RegexOptions.Compiled + ); + /// /// Parses information out of a file path. Will fallback to using directory name if Series couldn't be parsed @@ -428,7 +433,7 @@ namespace API.Parser { var folder = fallbackFolders[i]; if (!string.IsNullOrEmpty(ParseMangaSpecial(folder))) continue; - if (ParseVolume(folder) != "0" || ParseChapter(folder) != "0") continue; + if (ParseVolume(folder) != DefaultVolume || ParseChapter(folder) != DefaultChapter) continue; var series = ParseSeries(folder); @@ -457,10 +462,17 @@ namespace API.Parser var isSpecial = ParseMangaSpecial(fileName); // We must ensure that we can only parse a special out. As some files will have v20 c171-180+Omake and that // could cause a problem as Omake is a special term, but there is valid volume/chapter information. - if (ret.Chapters == "0" && ret.Volumes == "0" && !string.IsNullOrEmpty(isSpecial)) + if (ret.Chapters == DefaultChapter && ret.Volumes == DefaultVolume && !string.IsNullOrEmpty(isSpecial)) { ret.IsSpecial = true; } + + if (HasSpecialMarker(fileName)) + { + ret.IsSpecial = true; + ret.Chapters = DefaultChapter; + ret.Volumes = DefaultVolume; + } @@ -495,6 +507,25 @@ namespace API.Parser return string.Empty; } + /// + /// If the file has SP marker. + /// + /// + /// + public static bool HasSpecialMarker(string filePath) + { + var matches = SpecialMarkerRegex.Matches(filePath); + foreach (Match match in matches) + { + if (match.Groups["Special"].Success && match.Groups["Special"].Value != string.Empty) + { + return true; + } + } + + return false; + } + public static string ParseMangaSpecial(string filePath) { foreach (var regex in MangaSpecialRegex) @@ -564,7 +595,7 @@ namespace API.Parser } } - return "0"; + return DefaultVolume; } public static string ParseComicVolume(string filename) @@ -586,7 +617,7 @@ namespace API.Parser } } - return "0"; + return DefaultVolume; } public static string ParseChapter(string filename) @@ -614,7 +645,7 @@ namespace API.Parser } } - return "0"; + return DefaultChapter; } private static string AddChapterPart(string value) @@ -652,7 +683,7 @@ namespace API.Parser } } - return "0"; + return DefaultChapter; } private static string RemoveEditionTagHolders(string title) diff --git a/API/Parser/ParserInfo.cs b/API/Parser/ParserInfo.cs index e49d87e74..a2c4a9c51 100644 --- a/API/Parser/ParserInfo.cs +++ b/API/Parser/ParserInfo.cs @@ -3,7 +3,7 @@ namespace API.Parser { /// - /// This represents a single file + /// This represents all parsed information from a single file /// public class ParserInfo { diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index e22803c4b..91e873d13 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -466,7 +466,7 @@ namespace API.Services.Tasks return; } - if (type == LibraryType.Book && Parser.Parser.IsEpub(path) && Parser.Parser.ParseVolume(info.Series) != "0") + if (type == LibraryType.Book && Parser.Parser.IsEpub(path) && Parser.Parser.ParseVolume(info.Series) != Parser.Parser.DefaultVolume) { info = Parser.Parser.Parse(path, rootPath, type); var info2 = _bookService.ParseInfo(path);