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
This commit is contained in:
Joseph Milazzo 2021-06-14 21:12:37 -05:00 committed by GitHub
parent 46b60405b1
commit 584348c6ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 18 deletions

View File

@ -145,6 +145,7 @@ namespace API.Tests.Parser
[InlineData("X-Men v1 #201 (September 2007).cbz", "X-Men")] [InlineData("X-Men v1 #201 (September 2007).cbz", "X-Men")]
[InlineData("Kodoja #001 (March 2016)", "Kodoja")] [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("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) public void ParseSeriesTest(string filename, string expected)
{ {
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename)); 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("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
[InlineData("A Town Where You Live - Bonus Chapter.zip", true)] [InlineData("A Town Where You Live - Bonus Chapter.zip", true)]
[InlineData("Yuki Merry - 4-Komga Anthology", false)] [InlineData("Yuki Merry - 4-Komga Anthology", false)]
[InlineData("Beastars - SP01", true)] [InlineData("Beastars - SP01", false)]
[InlineData("Beastars SP01", true)] [InlineData("Beastars SP01", false)]
public void ParseMangaSpecialTest(string input, bool expected) public void ParseMangaSpecialTest(string input, bool expected)
{ {
Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input))); Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input)));

View File

@ -5,6 +5,16 @@ namespace API.Tests.Parser
{ {
public class ParserTests 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] [Theory]
[InlineData("0001", "1")] [InlineData("0001", "1")]

View File

@ -9,9 +9,12 @@ namespace API.Parser
{ {
public static class Parser public static class Parser
{ {
public static readonly string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z|.cb7"; public const string DefaultChapter = "0";
public static readonly string BookFileExtensions = @"\.epub"; public const string DefaultVolume = "0";
public static readonly string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)";
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 FontSrcUrlRegex = new Regex("(src:url\\(\"?'?)([a-z0-9/\\._]+)(\"?'?\\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)", RegexOptions.IgnoreCase | RegexOptions.Compiled); public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
@ -92,7 +95,7 @@ namespace API.Parser
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb) // Historys Strongest Disciple Kenichi_v11_c90-98.zip, Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)
new Regex( new Regex(
@"(?<Series>.*) (\b|_|-)v", @"(?<Series>.*) (\b|_|-)(v|ch\.?|c)\d+",
RegexOptions.IgnoreCase | RegexOptions.Compiled), 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 //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. // due to duplicate version identifiers in file.
@ -374,12 +377,14 @@ namespace API.Parser
new Regex( new Regex(
@"(?<Special>Specials?|OneShot|One\-Shot|Omake|Extra( Chapter)?|Art Collection|Side( |_)Stories|Bonus)", @"(?<Special>Specials?|OneShot|One\-Shot|Omake|Extra( Chapter)?|Art Collection|Side( |_)Stories|Bonus)",
RegexOptions.IgnoreCase | RegexOptions.Compiled), 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(
@"(?<Special>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(
@"(?<Special>SP\d+)",
RegexOptions.IgnoreCase | RegexOptions.Compiled
);
/// <summary> /// <summary>
/// Parses information out of a file path. Will fallback to using directory name if Series couldn't be parsed /// 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]; var folder = fallbackFolders[i];
if (!string.IsNullOrEmpty(ParseMangaSpecial(folder))) continue; 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); var series = ParseSeries(folder);
@ -457,10 +462,17 @@ namespace API.Parser
var isSpecial = ParseMangaSpecial(fileName); 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 // 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. // 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; 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; return string.Empty;
} }
/// <summary>
/// If the file has SP marker.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
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) public static string ParseMangaSpecial(string filePath)
{ {
foreach (var regex in MangaSpecialRegex) foreach (var regex in MangaSpecialRegex)
@ -564,7 +595,7 @@ namespace API.Parser
} }
} }
return "0"; return DefaultVolume;
} }
public static string ParseComicVolume(string filename) public static string ParseComicVolume(string filename)
@ -586,7 +617,7 @@ namespace API.Parser
} }
} }
return "0"; return DefaultVolume;
} }
public static string ParseChapter(string filename) public static string ParseChapter(string filename)
@ -614,7 +645,7 @@ namespace API.Parser
} }
} }
return "0"; return DefaultChapter;
} }
private static string AddChapterPart(string value) private static string AddChapterPart(string value)
@ -652,7 +683,7 @@ namespace API.Parser
} }
} }
return "0"; return DefaultChapter;
} }
private static string RemoveEditionTagHolders(string title) private static string RemoveEditionTagHolders(string title)

View File

@ -3,7 +3,7 @@
namespace API.Parser namespace API.Parser
{ {
/// <summary> /// <summary>
/// This represents a single file /// This represents all parsed information from a single file
/// </summary> /// </summary>
public class ParserInfo public class ParserInfo
{ {

View File

@ -466,7 +466,7 @@ namespace API.Services.Tasks
return; 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); info = Parser.Parser.Parse(path, rootPath, type);
var info2 = _bookService.ParseInfo(path); var info2 = _bookService.ParseInfo(path);