diff --git a/API.Tests/ParserTest.cs b/API.Tests/ParserTest.cs
index 2aa0f30ff..76a457177 100644
--- a/API.Tests/ParserTest.cs
+++ b/API.Tests/ParserTest.cs
@@ -52,6 +52,7 @@ namespace API.Tests
[InlineData("Mob Psycho 100 v02 (2019) (Digital) (Shizu).cbz", "2")]
[InlineData("Kodomo no Jikan vol. 1.cbz", "1")]
[InlineData("Kodomo no Jikan vol. 10.cbz", "10")]
+ [InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 12 [Dametrans][v2]", "0")]
public void ParseVolumeTest(string filename, string expected)
{
Assert.Equal(expected, ParseVolume(filename));
@@ -103,11 +104,12 @@ namespace API.Tests
[InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 09", "Kedouin Makoto - Corpse Party Musume")]
[InlineData("Goblin Slayer Side Story - Year One 025.5", "Goblin Slayer Side Story - Year One")]
[InlineData("Goblin Slayer - Brand New Day 006.5 (2019) (Digital) (danke-Empire)", "Goblin Slayer - Brand New Day")]
+ [InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 01 [Dametrans][v2]", "Kedouin Makoto - Corpse Party Musume")]
public void ParseSeriesTest(string filename, string expected)
{
Assert.Equal(expected, ParseSeries(filename));
}
-
+
[Theory]
[InlineData("Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)", "1")]
[InlineData("My Girlfriend Is Shobitch v01 - ch. 09 - pg. 008.png", "9")]
@@ -143,6 +145,7 @@ namespace API.Tests
[InlineData("Vol 1", "0")]
[InlineData("VanDread-v01-c001[MD].zip", "1")]
[InlineData("Goblin Slayer Side Story - Year One 025.5", "25.5")]
+ [InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 01", "1")]
public void ParseChaptersTest(string filename, string expected)
{
Assert.Equal(expected, ParseChapter(filename));
@@ -218,6 +221,73 @@ namespace API.Tests
{
Assert.Equal(expected, Normalize(input));
}
+
+ [Theory]
+ [InlineData("01 Spider-Man & Wolverine 01.cbr", "Spider-Man & Wolverine")]
+ [InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "Asterix the Gladiator")]
+ [InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "The First Asterix Frieze")]
+ [InlineData("Batman & Catwoman - Trail of the Gun 01", "Batman & Catwoman - Trail of the Gun")]
+ [InlineData("Batman & Daredevil - King of New York", "Batman & Daredevil - King of New York")]
+ [InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "Batman & Grendel")]
+ [InlineData("Batman & Robin the Teen Wonder #0", "Batman & Robin the Teen Wonder")]
+ [InlineData("Batman & Wildcat (1 of 3)", "Batman & Wildcat")]
+ [InlineData("Batman And Superman World's Finest #01", "Batman And Superman World's Finest")]
+ [InlineData("Babe 01", "Babe")]
+ [InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "Scott Pilgrim")]
+ [InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "Teen Titans")]
+ [InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "Scott Pilgrim")]
+ public void ParseComicSeriesTest(string filename, string expected)
+ {
+ Assert.Equal(expected, ParseComicSeries(filename));
+ }
+
+ [Theory]
+ [InlineData("01 Spider-Man & Wolverine 01.cbr", "1")]
+ [InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "4")]
+ [InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
+ [InlineData("Batman & Catwoman - Trail of the Gun 01", "1")]
+ [InlineData("Batman & Daredevil - King of New York", "0")]
+ [InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
+ [InlineData("Batman & Robin the Teen Wonder #0", "0")]
+ [InlineData("Batman & Wildcat (1 of 3)", "0")]
+ [InlineData("Batman And Superman World's Finest #01", "1")]
+ [InlineData("Babe 01", "1")]
+ [InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "1")]
+ [InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
+ [InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "2")]
+ public void ParseComicVolumeTest(string filename, string expected)
+ {
+ Assert.Equal(expected, ParseComicVolume(filename));
+ }
+
+ [Theory]
+ [InlineData("01 Spider-Man & Wolverine 01.cbr", "0")]
+ [InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "0")]
+ [InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
+ [InlineData("Batman & Catwoman - Trail of the Gun 01", "0")]
+ [InlineData("Batman & Daredevil - King of New York", "0")]
+ [InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "0")]
+ [InlineData("Batman & Robin the Teen Wonder #0", "0")]
+ [InlineData("Batman & Wildcat (1 of 3)", "1")]
+ [InlineData("Batman & Wildcat (2 of 3)", "2")]
+ [InlineData("Batman And Superman World's Finest #01", "0")]
+ [InlineData("Babe 01", "0")]
+ [InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "0")]
+ [InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
+ public void ParseComicChapterTest(string filename, string expected)
+ {
+ Assert.Equal(expected, ParseComicChapter(filename));
+ }
+
+ [Theory]
+ [InlineData("test.jpg", true)]
+ [InlineData("test.jpeg", true)]
+ [InlineData("test.png", true)]
+ [InlineData(".test.jpg", false)]
+ public void IsImageTest(string filename, bool expected)
+ {
+ Assert.Equal(expected, IsImage(filename));
+ }
[Fact]
diff --git a/API/API.csproj b/API/API.csproj
index 0a8cfeb3b..33fdc7cf5 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -16,8 +16,8 @@
-
-
+
+
all
diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs
index af0383a81..c089a688e 100644
--- a/API/Parser/Parser.cs
+++ b/API/Parser/Parser.cs
@@ -9,7 +9,7 @@ namespace API.Parser
public static class Parser
{
public static readonly string MangaFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|.tar.gz|.7zip";
- public static readonly string ImageFileExtensions = @"\.png|\.jpeg|\.jpg";
+ public static readonly string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)";
private static readonly string XmlRegexExtensions = @"\.xml";
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex MangaFileRegex = new Regex(MangaFileExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -24,7 +24,7 @@ namespace API.Parser
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Historys Strongest Disciple Kenichi_v11_c90-98.zip or Dance in the Vampire Bund v16-17
new Regex(
- @"(?.*)(\b|_)v(?\d+(-\d+)?)",
+ @"(?.*)(\b|_)(?!\[)v(?\d+(-\d+)?)(?!\])",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Kodomo no Jikan vol. 10
new Regex(
@@ -60,6 +60,10 @@ namespace API.Parser
new Regex(
@"(?.*) (\b|_|-)v",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip
+ new Regex(
+ @"(?.*)(?:, Chapter )(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Tonikaku Cawaii [Volume 11], Darling in the FranXX - Volume 01.cbz
new Regex(
@"(?.*)(?: _|-|\[|\() ?v",
@@ -81,10 +85,6 @@ namespace API.Parser
new Regex(
@"(?.*) (?\d+) (?:\(\d{4}\)) ",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
- // Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip
- new Regex(
- @"(?.*)(?:, Chapter )(?\d+)",
- RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Goblin Slayer - Brand New Day 006.5 (2019) (Digital) (danke-Empire)
new Regex(
@"(?.*) (?\d+(?:.\d+|-\d+)?) \(\d{4}\)",
@@ -110,6 +110,106 @@ namespace API.Parser
@"(?.*)( |_)(c)\d+",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
+
+ private static readonly Regex[] ComicSeriesRegex = new[]
+ {
+ // 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
+ new Regex(
+ @"^(?\d+) (- |_)?(?.*(\d{4})?)( |_)(\(|\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // 01 Spider-Man & Wolverine 01.cbr
+ new Regex(
+ @"^(?\d+) (?:- )?(?.*) (\d+)?",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Wildcat (1 of 3)
+ new Regex(
+ @"(?.*(\d{4})?)( |_)(?:\(\d+ of \d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: |_)v\d+",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Catwoman - Trail of the Gun 01, Batman & Grendel (1996) 01 - Devil's Bones, Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: \d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Robin the Teen Wonder #0
+ new Regex(
+ @"^(?.*)(?: |_)#\d+",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
+ new Regex(
+ @"^(?.*)(?: |_)(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // The First Asterix Frieze (WebP by Doc MaKS)
+ new Regex(
+ @"^(?.*)(?: |_)(?!\(\d{4}|\d{4}-\d{2}\))\(",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // MUST BE LAST: Batman & Daredevil - King of New York
+ new Regex(
+ @"^(?.*)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ };
+
+ private static readonly Regex[] ComicVolumeRegex = new[]
+ {
+ // 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
+ new Regex(
+ @"^(?\d+) (- |_)?(?.*(\d{4})?)( |_)(\(|\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // 01 Spider-Man & Wolverine 01.cbr
+ new Regex(
+ @"^(?\d+) (?:- )?(?.*) (\d+)?",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Wildcat (1 of 3)
+ new Regex(
+ @"(?.*(\d{4})?)( |_)(?:\((?\d+) of \d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: |_)v(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
+ new Regex(
+ @"^(?.*)(?: |_)(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Catwoman - Trail of the Gun 01, Batman & Grendel (1996) 01 - Devil's Bones, Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: (?\d+))",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Robin the Teen Wonder #0
+ new Regex(
+ @"^(?.*)(?: |_)#(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ };
+
+ private static readonly Regex[] ComicChapterRegex = new[]
+ {
+ // 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
+ new Regex(
+ @"^(?\d+) (- |_)?(?.*(\d{4})?)( |_)(\(|\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // 01 Spider-Man & Wolverine 01.cbr
+ new Regex(
+ @"^(?\d+) (?:- )?(?.*) (\d+)?",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Wildcat (1 of 3)
+ new Regex(
+ @"(?.*(\d{4})?)( |_)(?:\((?\d+) of \d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: |_)v(?\d+)(?: |_)(c? ?)(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Catwoman - Trail of the Gun 01, Batman & Grendel (1996) 01 - Devil's Bones, Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
+ new Regex(
+ @"^(?.*)(?: (?\d+))",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ // Batman & Robin the Teen Wonder #0
+ new Regex(
+ @"^(?.*)(?: |_)#(?\d+)",
+ RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ };
private static readonly Regex[] ReleaseGroupRegex = new[]
{
@@ -184,8 +284,9 @@ namespace API.Parser
///
///
/// Root folder
+ /// Defaults to Manga. Allows different Regex to be used for parsing.
/// or null if Series was empty
- public static ParserInfo Parse(string filePath, string rootPath)
+ public static ParserInfo Parse(string filePath, string rootPath, LibraryType type = LibraryType.Manga)
{
var fileName = Path.GetFileName(filePath);
var directoryName = (new FileInfo(filePath)).Directory?.Name;
@@ -193,9 +294,9 @@ namespace API.Parser
var ret = new ParserInfo()
{
- Chapters = ParseChapter(fileName),
- Series = ParseSeries(fileName),
- Volumes = ParseVolume(fileName),
+ Chapters = type == LibraryType.Manga ? ParseChapter(fileName) : ParseComicChapter(fileName),
+ Series = type == LibraryType.Manga ? ParseSeries(fileName) : ParseComicSeries(fileName),
+ Volumes = type == LibraryType.Manga ? ParseVolume(fileName) : ParseComicVolume(fileName),
Filename = fileName,
Format = ParseFormat(filePath),
FullFilePath = filePath
@@ -261,6 +362,22 @@ namespace API.Parser
return string.Empty;
}
+ public static string ParseComicSeries(string filename)
+ {
+ foreach (var regex in ComicSeriesRegex)
+ {
+ var matches = regex.Matches(filename);
+ foreach (Match match in matches)
+ {
+ if (match.Groups["Series"].Success && match.Groups["Series"].Value != string.Empty)
+ {
+ return CleanTitle(match.Groups["Series"].Value);
+ }
+ }
+ }
+
+ return string.Empty;
+ }
public static string ParseVolume(string filename)
{
@@ -283,6 +400,28 @@ namespace API.Parser
return "0";
}
+
+ public static string ParseComicVolume(string filename)
+ {
+ foreach (var regex in ComicVolumeRegex)
+ {
+ var matches = regex.Matches(filename);
+ foreach (Match match in matches)
+ {
+ if (match.Groups["Volume"] == Match.Empty) continue;
+
+ var value = match.Groups["Volume"].Value;
+ if (!value.Contains("-")) return RemoveLeadingZeroes(match.Groups["Volume"].Value);
+ var tokens = value.Split("-");
+ var from = RemoveLeadingZeroes(tokens[0]);
+ var to = RemoveLeadingZeroes(tokens[1]);
+ return $"{@from}-{to}";
+
+ }
+ }
+
+ return "0";
+ }
public static string ParseChapter(string filename)
{
@@ -311,6 +450,34 @@ namespace API.Parser
return "0";
}
+
+ public static string ParseComicChapter(string filename)
+ {
+ foreach (var regex in ComicChapterRegex)
+ {
+ var matches = regex.Matches(filename);
+ foreach (Match match in matches)
+ {
+ if (match.Groups["Chapter"] != Match.Empty)
+ {
+ var value = match.Groups["Chapter"].Value;
+
+ if (value.Contains("-"))
+ {
+ var tokens = value.Split("-");
+ var from = RemoveLeadingZeroes(tokens[0]);
+ var to = RemoveLeadingZeroes(tokens[1]);
+ return $"{from}-{to}";
+ }
+
+ return RemoveLeadingZeroes(match.Groups["Chapter"].Value);
+ }
+
+ }
+ }
+
+ return "0";
+ }
private static string RemoveEditionTagHolders(string title)
{
@@ -408,6 +575,7 @@ namespace API.Parser
public static bool IsImage(string filePath)
{
+ if (filePath.StartsWith(".")) return false;
return ImageRegex.IsMatch(Path.GetExtension(filePath));
}
diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs
index 4e4101982..4be3d749d 100644
--- a/API/Services/Tasks/ScannerService.cs
+++ b/API/Services/Tasks/ScannerService.cs
@@ -98,7 +98,7 @@ namespace API.Services.Tasks
{
try
{
- ProcessFile(f, folderPath.Path);
+ ProcessFile(f, folderPath.Path, library.Type);
}
catch (FileNotFoundException exception)
{
@@ -333,9 +333,10 @@ namespace API.Services.Tasks
///
/// Path of a file
///
- private void ProcessFile(string path, string rootPath)
+ /// Library type to determine parsing to perform
+ private void ProcessFile(string path, string rootPath, LibraryType type)
{
- var info = Parser.Parser.Parse(path, rootPath);
+ var info = Parser.Parser.Parse(path, rootPath, type);
if (info == null)
{