mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Feature/performance pdf (#426)
# Added - Added: Added series format information to the search typeahead to help identify duplicate series in libraries # Fixed - Fixed: Fixed accent color not looking well on light theme - Fixed: Attempted to fix the memory issues with PDF reading on Docker. Uses a Memory Pool for streams and removes a bitmap operation for fixing books with transparent backgrounds (#424) # Changed - Changed: Refactored download logs to use the same download code as rest of Kavita # Dev stuff - Added timeout for Regex's to make sure during matching, malicious filenames doesn't crash user system - Refactored a missing GetCoverImage to use Series Format rather than old Library Type ================================================== * Added Timeout for Regex matching to ensure malicious filenames don't crash system * Refactored GetCoverImage to use series format rather than library type * Refactored download logs to use the download service * Fixed accent color not looking well on light theme * Refactored series format into dedicated component and added to search results * Switch to using MemoryManager for Streams to attempt to minimize GC pressure and reduced bitmap manipulation for transparency hack.
This commit is contained in:
parent
78ad01f5ae
commit
81dfd63250
@ -1,4 +1,6 @@
|
|||||||
namespace API.DTOs
|
using API.Entities.Enums;
|
||||||
|
|
||||||
|
namespace API.DTOs
|
||||||
{
|
{
|
||||||
public class SearchResultDto
|
public class SearchResultDto
|
||||||
{
|
{
|
||||||
@ -7,6 +9,7 @@
|
|||||||
public string OriginalName { get; init; }
|
public string OriginalName { get; init; }
|
||||||
public string SortName { get; init; }
|
public string SortName { get; init; }
|
||||||
public string LocalizedName { get; init; }
|
public string LocalizedName { get; init; }
|
||||||
|
public MangaFormat Format { get; init; }
|
||||||
|
|
||||||
// Grouping information
|
// Grouping information
|
||||||
public string LibraryName { get; set; }
|
public string LibraryName { get; set; }
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Comparators;
|
using API.Comparators;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
|
||||||
using API.Parser;
|
using API.Parser;
|
||||||
using API.Services.Tasks.Scanner;
|
using API.Services.Tasks.Scanner;
|
||||||
|
|
||||||
|
@ -19,12 +19,11 @@ namespace API.Extensions
|
|||||||
/// If there are both specials and non-specials, then the first non-special will be returned.
|
/// If there are both specials and non-specials, then the first non-special will be returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="volumes"></param>
|
/// <param name="volumes"></param>
|
||||||
/// <param name="libraryType"></param>
|
/// <param name="seriesFormat"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Volume GetCoverImage(this IList<Volume> volumes, LibraryType libraryType)
|
public static Volume GetCoverImage(this IList<Volume> volumes, MangaFormat seriesFormat)
|
||||||
{
|
{
|
||||||
// TODO: Refactor this to use MangaFormat Epub instead
|
if (seriesFormat is MangaFormat.Epub or MangaFormat.Pdf)
|
||||||
if (libraryType == LibraryType.Book)
|
|
||||||
{
|
{
|
||||||
return volumes.OrderBy(x => x.Number).FirstOrDefault();
|
return volumes.OrderBy(x => x.Number).FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
@ -11,24 +11,38 @@ namespace API.Parser
|
|||||||
{
|
{
|
||||||
public const string DefaultChapter = "0";
|
public const string DefaultChapter = "0";
|
||||||
public const string DefaultVolume = "0";
|
public const string DefaultVolume = "0";
|
||||||
|
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(250);
|
||||||
|
|
||||||
public const string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)";
|
public const string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)";
|
||||||
public const string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z|\.cb7|\.cbt";
|
public const string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z|\.cb7|\.cbt";
|
||||||
public const string BookFileExtensions = @"\.epub|\.pdf";
|
public const string BookFileExtensions = @"\.epub|\.pdf";
|
||||||
public const string MangaComicFileExtensions = ArchiveFileExtensions + "|" + ImageFileExtensions + @"|\.pdf";
|
|
||||||
|
|
||||||
public const string SupportedExtensions =
|
public const string SupportedExtensions =
|
||||||
ArchiveFileExtensions + "|" + ImageFileExtensions + "|" + BookFileExtensions;
|
ArchiveFileExtensions + "|" + ImageFileExtensions + "|" + BookFileExtensions;
|
||||||
|
|
||||||
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))",
|
||||||
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
|
||||||
private static readonly string XmlRegexExtensions = @"\.xml";
|
private static readonly string XmlRegexExtensions = @"\.xml";
|
||||||
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions,
|
||||||
private static readonly Regex ArchiveFileRegex = new Regex(ArchiveFileExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
private static readonly Regex XmlRegex = new Regex(XmlRegexExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
RegexTimeout);
|
||||||
private static readonly Regex BookFileRegex = new Regex(BookFileExtensions, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
private static readonly Regex ArchiveFileRegex = new Regex(ArchiveFileExtensions,
|
||||||
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(cover|folder)(?![\w\d])", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
private static readonly Regex XmlRegex = new Regex(XmlRegexExtensions,
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
private static readonly Regex BookFileRegex = new Regex(BookFileExtensions,
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(cover|folder)(?![\w\d])",
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout);
|
||||||
|
|
||||||
|
|
||||||
private static readonly Regex[] MangaVolumeRegex = new[]
|
private static readonly Regex[] MangaVolumeRegex = new[]
|
||||||
@ -36,31 +50,38 @@ namespace API.Parser
|
|||||||
// Dance in the Vampire Bund v16-17
|
// Dance in the Vampire Bund v16-17
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)v(?<Volume>\d+-?\d+)( |_)",
|
@"(?<Series>.*)(\b|_)v(?<Volume>\d+-?\d+)( |_)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar
|
// NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)(?!\[)(vol\.?)(?<Volume>\d+(-\d+)?)(?!\])",
|
@"(?<Series>.*)(\b|_)(?!\[)(vol\.?)(?<Volume>\d+(-\d+)?)(?!\])",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Historys Strongest Disciple Kenichi_v11_c90-98.zip or Dance in the Vampire Bund v16-17
|
// Historys Strongest Disciple Kenichi_v11_c90-98.zip or Dance in the Vampire Bund v16-17
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)(?!\[)v(?<Volume>\d+(-\d+)?)(?!\])",
|
@"(?<Series>.*)(\b|_)(?!\[)v(?<Volume>\d+(-\d+)?)(?!\])",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Kodomo no Jikan vol. 10
|
// Kodomo no Jikan vol. 10
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)(vol\.? ?)(?<Volume>\d+(-\d+)?)",
|
@"(?<Series>.*)(\b|_)(vol\.? ?)(?<Volume>\d+(-\d+)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)
|
// Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(vol\.? ?)(?<Volume>\d+)",
|
@"(vol\.? ?)(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Tonikaku Cawaii [Volume 11].cbz
|
// Tonikaku Cawaii [Volume 11].cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(volume )(?<Volume>\d+)",
|
@"(volume )(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Tower Of God S01 014 (CBT) (digital).cbz
|
// Tower Of God S01 014 (CBT) (digital).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_|)(S(?<Volume>\d+))",
|
@"(?<Series>.*)(\b|_|)(S(?<Volume>\d+))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] MangaSeriesRegex = new[]
|
private static readonly Regex[] MangaSeriesRegex = new[]
|
||||||
@ -68,130 +89,161 @@ namespace API.Parser
|
|||||||
// Grand Blue Dreaming - SP02
|
// Grand Blue Dreaming - SP02
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_|-|\s)(?:sp)\d",
|
@"(?<Series>.*)(\b|_|-|\s)(?:sp)\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz
|
// [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)( |_)Vol\.?(\d+|tbd)",
|
@"^(?<Series>.*)( |_)Vol\.?(\d+|tbd)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Ichiban_Ushiro_no_Daimaou_v04_ch34_[VISCANS].zip, VanDread-v01-c01.zip
|
// Ichiban_Ushiro_no_Daimaou_v04_ch34_[VISCANS].zip, VanDread-v01-c01.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)v(?<Volume>\d+-?\d*)(\s|_|-)",
|
@"(?<Series>.*)(\b|_)v(?<Volume>\d+-?\d*)(\s|_|-)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Gokukoku no Brynhildr - c001-008 (v01) [TrinityBAKumA], Black Bullet - v4 c17 [batoto]
|
// Gokukoku no Brynhildr - c001-008 (v01) [TrinityBAKumA], Black Bullet - v4 c17 [batoto]
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)( - )(?:v|vo|c)\d",
|
@"(?<Series>.*)( - )(?:v|vo|c)\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip
|
// Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(?:, Chapter )(?<Chapter>\d+)",
|
@"(?<Series>.*)(?:, Chapter )(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Mad Chimera World - Volume 005 - Chapter 026.cbz (couldn't figure out how to get Volume negative lookaround working on below regex)
|
// Mad Chimera World - Volume 005 - Chapter 026.cbz (couldn't figure out how to get Volume negative lookaround working on below regex)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_|-)(?:Volume(\s|_|-)+\d+)(\s|_|-)+(?:Chapter)(\s|_|-)(?<Chapter>\d+)",
|
@"(?<Series>.*)(\s|_|-)(?:Volume(\s|_|-)+\d+)(\s|_|-)+(?:Chapter)(\s|_|-)(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Please Go Home, Akutsu-San! - Chapter 038.5 - Volume Announcement.cbz
|
// Please Go Home, Akutsu-San! - Chapter 038.5 - Volume Announcement.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_|-)(?!Vol)(\s|_|-)(?:Chapter)(\s|_|-)(?<Chapter>\d+)",
|
@"(?<Series>.*)(\s|_|-)(?!Vol)(\s|_|-)(?:Chapter)(\s|_|-)(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [dmntsf.net] One Piece - Digital Colored Comics Vol. 20 Ch. 177 - 30 Million vs 81 Million.cbz
|
// [dmntsf.net] One Piece - Digital Colored Comics Vol. 20 Ch. 177 - 30 Million vs 81 Million.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*) (\b|_|-)(vol)\.?",
|
@"(?<Series>.*) (\b|_|-)(vol)\.?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
//Knights of Sidonia c000 (S2 LE BD Omake - BLAME!) [Habanero Scans]
|
//Knights of Sidonia c000 (S2 LE BD Omake - BLAME!) [Habanero Scans]
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\bc\d+\b)",
|
@"(?<Series>.*)(\bc\d+\b)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
//Tonikaku Cawaii [Volume 11], Darling in the FranXX - Volume 01.cbz
|
//Tonikaku Cawaii [Volume 11], Darling in the FranXX - Volume 01.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(?: _|-|\[|\()\s?vol(ume)?",
|
@"(?<Series>.*)(?: _|-|\[|\()\s?vol(ume)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Momo The Blood Taker - Chapter 027 Violent Emotion.cbz, Grand Blue Dreaming - SP02 Extra (2019) (Digital) (danke-Empire).cbz
|
// Momo The Blood Taker - Chapter 027 Violent Emotion.cbz, Grand Blue Dreaming - SP02 Extra (2019) (Digital) (danke-Empire).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_|-|\s)(?:(chapter(\b|_|-|\s))|sp)\d",
|
@"(?<Series>.*)(\b|_|-|\s)(?:(chapter(\b|_|-|\s))|sp)\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 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|ch\.?|c)\d+",
|
@"(?<Series>.*) (\b|_|-)(v|ch\.?|c)\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
//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.
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(v|s)\d+(-\d+)?(_|\s)",
|
@"(?<Series>.*)(v|s)\d+(-\d+)?(_|\s)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
//[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
//[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(v|s)\d+(-\d+)?",
|
@"(?<Series>.*)(v|s)\d+(-\d+)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz
|
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*) (?<Chapter>\d+) (?:\(\d{4}\)) ",
|
@"(?<Series>.*) (?<Chapter>\d+) (?:\(\d{4}\)) ",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Goblin Slayer - Brand New Day 006.5 (2019) (Digital) (danke-Empire)
|
// Goblin Slayer - Brand New Day 006.5 (2019) (Digital) (danke-Empire)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*) (?<Chapter>\d+(?:.\d+|-\d+)?) \(\d{4}\)",
|
@"(?<Series>.*) (?<Chapter>\d+(?:.\d+|-\d+)?) \(\d{4}\)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Noblesse - Episode 429 (74 Pages).7z
|
// Noblesse - Episode 429 (74 Pages).7z
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_)(?:Episode|Ep\.?)(\s|_)(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
@"(?<Series>.*)(\s|_)(?:Episode|Ep\.?)(\s|_)(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)
|
// Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)\(\d",
|
@"(?<Series>.*)\(\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Tonikaku Kawaii (Ch 59-67) (Ongoing)
|
// Tonikaku Kawaii (Ch 59-67) (Ongoing)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_)\((c\s|ch\s|chapter\s)",
|
@"(?<Series>.*)(\s|_)\((c\s|ch\s|chapter\s)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Black Bullet (This is very loose, keep towards bottom)
|
// Black Bullet (This is very loose, keep towards bottom)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(_)(v|vo|c|volume)( |_)\d+",
|
@"(?<Series>.*)(_)(v|vo|c|volume)( |_)\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [Hidoi]_Amaenaideyo_MS_vol01_chp02.rar
|
// [Hidoi]_Amaenaideyo_MS_vol01_chp02.rar
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)( |_)(vol\d+)?( |_)(?:Chp\.? ?\d+)",
|
@"(?<Series>.*)( |_)(vol\d+)?( |_)(?:Chp\.? ?\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Mahoutsukai to Deshi no Futekisetsu na Kankei Chp. 1
|
// Mahoutsukai to Deshi no Futekisetsu na Kankei Chp. 1
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)( |_)(?:Chp.? ?\d+)",
|
@"(?<Series>.*)( |_)(?:Chp.? ?\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Chapter 01
|
// Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Chapter 01
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)( |_)Chapter( |_)(\d+)",
|
@"^(?!Vol)(?<Series>.*)( |_)Chapter( |_)(\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
|
|
||||||
// Fullmetal Alchemist chapters 101-108.cbz
|
// Fullmetal Alchemist chapters 101-108.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!vol)(?<Series>.*)( |_)(chapters( |_)?)\d+-?\d*",
|
@"^(?!vol)(?<Series>.*)( |_)(chapters( |_)?)\d+-?\d*",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Umineko no Naku Koro ni - Episode 1 - Legend of the Golden Witch #1
|
// Umineko no Naku Koro ni - Episode 1 - Legend of the Golden Witch #1
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol\.?)(?<Series>.*)( |_|-)(?<!-)(episode|chapter|(ch\.?) ?)\d+-?\d*",
|
@"^(?!Vol\.?)(?<Series>.*)( |_|-)(?<!-)(episode|chapter|(ch\.?) ?)\d+-?\d*",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
|
|
||||||
// Baketeriya ch01-05.zip
|
// Baketeriya ch01-05.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)ch\d+-?\d?",
|
@"^(?!Vol)(?<Series>.*)ch\d+-?\d?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Magi - Ch.252-005.cbz
|
// Magi - Ch.252-005.cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)( ?- ?)Ch\.\d+-?\d*",
|
@"(?<Series>.*)( ?- ?)Ch\.\d+-?\d*",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [BAA]_Darker_than_Black_Omake-1.zip
|
// [BAA]_Darker_than_Black_Omake-1.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)(-)\d+-?\d*", // This catches a lot of stuff ^(?!Vol)(?<Series>.*)( |_)(\d+)
|
@"^(?!Vol)(?<Series>.*)(-)\d+-?\d*", // This catches a lot of stuff ^(?!Vol)(?<Series>.*)( |_)(\d+)
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Kodoja #001 (March 2016)
|
// Kodoja #001 (March 2016)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_|-)#",
|
@"(?<Series>.*)(\s|_|-)#",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Baketeriya ch01-05.zip, Akiiro Bousou Biyori - 01.jpg, Beelzebub_172_RHS.zip, Cynthia the Mission 29.rar
|
// Baketeriya ch01-05.zip, Akiiro Bousou Biyori - 01.jpg, Beelzebub_172_RHS.zip, Cynthia the Mission 29.rar
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol\.?)(?<Series>.*)( |_|-)(?<!-)(ch)?\d+-?\d*",
|
@"^(?!Vol\.?)(?<Series>.*)( |_|-)(?<!-)(ch)?\d+-?\d*",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [BAA]_Darker_than_Black_c1 (This is very greedy, make sure it's close to last)
|
// [BAA]_Darker_than_Black_c1 (This is very greedy, make sure it's close to last)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)( |_|-)(ch?)\d+",
|
@"^(?!Vol)(?<Series>.*)( |_|-)(ch?)\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] ComicSeriesRegex = new[]
|
private static readonly Regex[] ComicSeriesRegex = new[]
|
||||||
@ -199,51 +251,63 @@ namespace API.Parser
|
|||||||
// Invincible Vol 01 Family matters (2005) (Digital)
|
// Invincible Vol 01 Family matters (2005) (Digital)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\b|_)(vol\.?)( |_)(?<Volume>\d+(-\d+)?)",
|
@"(?<Series>.*)(\b|_)(vol\.?)( |_)(?<Volume>\d+(-\d+)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
|
// 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Volume>\d+) (- |_)?(?<Series>.*(\d{4})?)( |_)(\(|\d+)",
|
@"^(?<Volume>\d+) (- |_)?(?<Series>.*(\d{4})?)( |_)(\(|\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 01 Spider-Man & Wolverine 01.cbr
|
// 01 Spider-Man & Wolverine 01.cbr
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Volume>\d+) (?:- )?(?<Series>.*) (\d+)?",
|
@"^(?<Volume>\d+) (?:- )?(?<Series>.*) (\d+)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Batman & Wildcat (1 of 3)
|
// Batman & Wildcat (1 of 3)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Volume>\d+) of \d+)",
|
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Volume>\d+) of \d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)v\d+",
|
@"^(?<Series>.*)(?: |_)v\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Amazing Man Comics chapter 25
|
// Amazing Man Comics chapter 25
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)c(hapter) \d+",
|
@"^(?<Series>.*)(?: |_)c(hapter) \d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Amazing Man Comics issue #25
|
// Amazing Man Comics issue #25
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)i(ssue) #\d+",
|
@"^(?<Series>.*)(?: |_)i(ssue) #\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 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)
|
// 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(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: \d+)",
|
@"^(?<Series>.*)(?: \d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Batman & Robin the Teen Wonder #0
|
// Batman & Robin the Teen Wonder #0
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)#\d+",
|
@"^(?<Series>.*)(?: |_)#\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)(?<Volume>\d+)",
|
@"^(?<Series>.*)(?: |_)(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// The First Asterix Frieze (WebP by Doc MaKS)
|
// The First Asterix Frieze (WebP by Doc MaKS)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)(?!\(\d{4}|\d{4}-\d{2}\))\(",
|
@"^(?<Series>.*)(?: |_)(?!\(\d{4}|\d{4}-\d{2}\))\(",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// MUST BE LAST: Batman & Daredevil - King of New York
|
// MUST BE LAST: Batman & Daredevil - King of New York
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)",
|
@"^(?<Series>.*)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] ComicVolumeRegex = new[]
|
private static readonly Regex[] ComicVolumeRegex = new[]
|
||||||
@ -251,31 +315,38 @@ namespace API.Parser
|
|||||||
// 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
|
// 04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Volume>\d+) (- |_)?(?<Series>.*(\d{4})?)( |_)(\(|\d+)",
|
@"^(?<Volume>\d+) (- |_)?(?<Series>.*(\d{4})?)( |_)(\(|\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 01 Spider-Man & Wolverine 01.cbr
|
// 01 Spider-Man & Wolverine 01.cbr
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Volume>\d+) (?:- )?(?<Series>.*) (\d+)?",
|
@"^(?<Volume>\d+) (?:- )?(?<Series>.*) (\d+)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Batman & Wildcat (1 of 3)
|
// Batman & Wildcat (1 of 3)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Chapter>\d+) of \d+)",
|
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Chapter>\d+) of \d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)v(?<Volume>\d+)",
|
@"^(?<Series>.*)(?: |_)v(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?<!c(hapter)|i(ssue))(?<!of)(?: |_)(?<!of )(?<Volume>\d+)",
|
@"^(?<Series>.*)(?<!c(hapter)|i(ssue))(?<!of)(?: |_)(?<!of )(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 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)
|
// 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(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?<!c(hapter)|i(ssue))(?<!of)(?: (?<Volume>\d+))",
|
@"^(?<Series>.*)(?<!c(hapter)|i(ssue))(?<!of)(?: (?<Volume>\d+))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Batman & Robin the Teen Wonder #0
|
// Batman & Robin the Teen Wonder #0
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)#(?<Volume>\d+)",
|
@"^(?<Series>.*)(?: |_)#(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] ComicChapterRegex = new[]
|
private static readonly Regex[] ComicChapterRegex = new[]
|
||||||
@ -283,38 +354,46 @@ namespace API.Parser
|
|||||||
// Batman & Wildcat (1 of 3)
|
// Batman & Wildcat (1 of 3)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Chapter>\d+) of \d+)",
|
@"(?<Series>.*(\d{4})?)( |_)(?:\((?<Chapter>\d+) of \d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
// Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)v(?<Volume>\d+)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)(c? ?)",
|
@"^(?<Series>.*)(?: |_)v(?<Volume>\d+)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)(c? ?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// 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)
|
// 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(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: (?<Volume>\d+))",
|
@"^(?<Series>.*)(?: (?<Volume>\d+))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Batman & Robin the Teen Wonder #0
|
// Batman & Robin the Teen Wonder #0
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)#(?<Volume>\d+)",
|
@"^(?<Series>.*)(?: |_)#(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Invincible 070.5 - Invincible Returns 1 (2010) (digital) (Minutemen-InnerDemons).cbr
|
// Invincible 070.5 - Invincible Returns 1 (2010) (digital) (Minutemen-InnerDemons).cbr
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)(c? ?)-",
|
@"^(?<Series>.*)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)(c? ?)-",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Amazing Man Comics chapter 25
|
// Amazing Man Comics chapter 25
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)( |_)c(hapter)( |_)(?<Chapter>\d*)",
|
@"^(?!Vol)(?<Series>.*)( |_)c(hapter)( |_)(?<Chapter>\d*)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Amazing Man Comics issue #25
|
// Amazing Man Comics issue #25
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)( |_)i(ssue)( |_) #(?<Chapter>\d*)",
|
@"^(?!Vol)(?<Series>.*)( |_)i(ssue)( |_) #(?<Chapter>\d*)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] ReleaseGroupRegex = new[]
|
private static readonly Regex[] ReleaseGroupRegex = new[]
|
||||||
{
|
{
|
||||||
// [TrinityBAKumA Finella&anon], [BAA]_, [SlowManga&OverloadScans], [batoto]
|
// [TrinityBAKumA Finella&anon], [BAA]_, [SlowManga&OverloadScans], [batoto]
|
||||||
new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// (Shadowcat-Empire),
|
// (Shadowcat-Empire),
|
||||||
// new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
// new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
||||||
// RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
// RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
||||||
@ -325,62 +404,76 @@ namespace API.Parser
|
|||||||
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, ...c90.5-100.5
|
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, ...c90.5-100.5
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)",
|
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
// [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"v\d+\.(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
@"v\d+\.(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Umineko no Naku Koro ni - Episode 3 - Banquet of the Golden Witch #02.cbz (Rare case, if causes issue remove)
|
// Umineko no Naku Koro ni - Episode 3 - Banquet of the Golden Witch #02.cbz (Rare case, if causes issue remove)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.*)(?: |_)#(?<Chapter>\d+)",
|
@"^(?<Series>.*)(?: |_)#(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Green Worldz - Chapter 027
|
// Green Worldz - Chapter 027
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)\s?(?<!vol\. )\sChapter\s(?<Chapter>\d+(?:\.?[\d-])?)",
|
@"^(?!Vol)(?<Series>.*)\s?(?<!vol\. )\sChapter\s(?<Chapter>\d+(?:\.?[\d-])?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz
|
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol)(?<Series>.*)\s(?<!vol\. )(?<Chapter>\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
@"^(?!Vol)(?<Series>.*)\s(?<!vol\. )(?<Chapter>\d+(?:.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Tower Of God S01 014 (CBT) (digital).cbz
|
// Tower Of God S01 014 (CBT) (digital).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)\sS(?<Volume>\d+)\s(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
@"(?<Series>.*)\sS(?<Volume>\d+)\s(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Beelzebub_01_[Noodles].zip, Beelzebub_153b_RHS.zip
|
// Beelzebub_01_[Noodles].zip, Beelzebub_153b_RHS.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^((?!v|vo|vol|Volume).)*(\s|_)(?<Chapter>\.?\d+(?:.\d+|-\d+)?)(?<ChapterPart>b)?(\s|_|\[|\()",
|
@"^((?!v|vo|vol|Volume).)*(\s|_)(?<Chapter>\.?\d+(?:.\d+|-\d+)?)(?<ChapterPart>b)?(\s|_|\[|\()",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Yumekui-Merry_DKThias_Chapter21.zip
|
// Yumekui-Merry_DKThias_Chapter21.zip
|
||||||
new Regex(
|
new Regex(
|
||||||
@"Chapter(?<Chapter>\d+(-\d+)?)", //(?:.\d+|-\d+)?
|
@"Chapter(?<Chapter>\d+(-\d+)?)", //(?:.\d+|-\d+)?
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// [Hidoi]_Amaenaideyo_MS_vol01_chp02.rar
|
// [Hidoi]_Amaenaideyo_MS_vol01_chp02.rar
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_)(vol\d+)?(\s|_)Chp\.? ?(?<Chapter>\d+)",
|
@"(?<Series>.*)(\s|_)(vol\d+)?(\s|_)Chp\.? ?(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Vol 1 Chapter 2
|
// Vol 1 Chapter 2
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Volume>((vol|volume|v))?(\s|_)?\.?\d+)(\s|_)(Chp|Chapter)\.?(\s|_)?(?<Chapter>\d+)",
|
@"(?<Volume>((vol|volume|v))?(\s|_)?\.?\d+)(\s|_)(Chp|Chapter)\.?(\s|_)?(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
|
|
||||||
};
|
};
|
||||||
private static readonly Regex[] MangaEditionRegex = {
|
private static readonly Regex[] MangaEditionRegex = {
|
||||||
// Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz
|
// Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Edition>({|\(|\[).* Edition(}|\)|\]))",
|
@"(?<Edition>({|\(|\[).* Edition(}|\)|\]))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz
|
// Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(\b|_)(?<Edition>Omnibus(( |_)?Edition)?)(\b|_)?",
|
@"(\b|_)(?<Edition>Omnibus(( |_)?Edition)?)(\b|_)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// To Love Ru v01 Uncensored (Ch.001-007)
|
// To Love Ru v01 Uncensored (Ch.001-007)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(\b|_)(?<Edition>Uncensored)(\b|_)",
|
@"(\b|_)(?<Edition>Uncensored)(\b|_)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// AKIRA - c003 (v01) [Full Color] [Darkhorse].cbz
|
// AKIRA - c003 (v01) [Full Color] [Darkhorse].cbz
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(\b|_)(?<Edition>Full(?: |_)Color)(\b|_)?",
|
@"(\b|_)(?<Edition>Full(?: |_)Color)(\b|_)?",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] CleanupRegex =
|
private static readonly Regex[] CleanupRegex =
|
||||||
@ -388,15 +481,18 @@ namespace API.Parser
|
|||||||
// (), {}, []
|
// (), {}, []
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Cleanup>(\{\}|\[\]|\(\)))",
|
@"(?<Cleanup>(\{\}|\[\]|\(\)))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// (Complete)
|
// (Complete)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Cleanup>(\{Complete\}|\[Complete\]|\(Complete\)))",
|
@"(?<Cleanup>(\{Complete\}|\[Complete\]|\(Complete\)))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
// Anything in parenthesis
|
// Anything in parenthesis
|
||||||
new Regex(
|
new Regex(
|
||||||
@"\(.*\)",
|
@"\(.*\)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] MangaSpecialRegex =
|
private static readonly Regex[] MangaSpecialRegex =
|
||||||
@ -404,7 +500,8 @@ namespace API.Parser
|
|||||||
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
// All Keywords, does not account for checking if contains volume/chapter identification. Parser.Parse() will handle.
|
||||||
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,
|
||||||
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If SP\d+ is in the filename, we force treat it as a special regardless if volume or chapter might have been found.
|
// If SP\d+ is in the filename, we force treat it as a special regardless if volume or chapter might have been found.
|
||||||
|
@ -387,12 +387,11 @@ namespace API.Services
|
|||||||
if (!archive.HasFiles() && !needsFlattening) return;
|
if (!archive.HasFiles() && !needsFlattening) return;
|
||||||
|
|
||||||
archive.ExtractToDirectory(extractPath, true);
|
archive.ExtractToDirectory(extractPath, true);
|
||||||
if (needsFlattening)
|
if (!needsFlattening) return;
|
||||||
{
|
|
||||||
_logger.LogDebug("Extracted archive is nested in root folder, flattening...");
|
_logger.LogDebug("Extracted archive is nested in root folder, flattening...");
|
||||||
new DirectoryInfo(extractPath).Flatten();
|
new DirectoryInfo(extractPath).Flatten();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts an archive to a temp cache directory. Returns path to new directory. If temp cache directory already exists,
|
/// Extracts an archive to a temp cache directory. Returns path to new directory. If temp cache directory already exists,
|
||||||
|
@ -13,11 +13,13 @@ using API.Entities.Enums;
|
|||||||
using API.Interfaces.Services;
|
using API.Interfaces.Services;
|
||||||
using API.Parser;
|
using API.Parser;
|
||||||
using Docnet.Core;
|
using Docnet.Core;
|
||||||
|
using Docnet.Core.Converters;
|
||||||
using Docnet.Core.Models;
|
using Docnet.Core.Models;
|
||||||
using Docnet.Core.Readers;
|
using Docnet.Core.Readers;
|
||||||
using ExCSS;
|
using ExCSS;
|
||||||
using HtmlAgilityPack;
|
using HtmlAgilityPack;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.IO;
|
||||||
using VersOne.Epub;
|
using VersOne.Epub;
|
||||||
using Image = NetVips.Image;
|
using Image = NetVips.Image;
|
||||||
using Point = System.Drawing.Point;
|
using Point = System.Drawing.Point;
|
||||||
@ -28,6 +30,7 @@ namespace API.Services
|
|||||||
{
|
{
|
||||||
private readonly ILogger<BookService> _logger;
|
private readonly ILogger<BookService> _logger;
|
||||||
private readonly StylesheetParser _cssParser = new ();
|
private readonly StylesheetParser _cssParser = new ();
|
||||||
|
private static readonly RecyclableMemoryStreamManager StreamManager = new ();
|
||||||
|
|
||||||
public BookService(ILogger<BookService> logger)
|
public BookService(ILogger<BookService> logger)
|
||||||
{
|
{
|
||||||
@ -375,7 +378,6 @@ namespace API.Services
|
|||||||
var pages = docReader.GetPageCount();
|
var pages = docReader.GetPageCount();
|
||||||
for (var pageNumber = 0; pageNumber < pages; pageNumber++)
|
for (var pageNumber = 0; pageNumber < pages; pageNumber++)
|
||||||
{
|
{
|
||||||
using var pageReader = docReader.GetPageReader(pageNumber);
|
|
||||||
using var stream = GetPdfPage(docReader, pageNumber);
|
using var stream = GetPdfPage(docReader, pageNumber);
|
||||||
File.WriteAllBytes(Path.Combine(targetDirectory, "Page-" + pageNumber + ".png"), stream.ToArray());
|
File.WriteAllBytes(Path.Combine(targetDirectory, "Page-" + pageNumber + ".png"), stream.ToArray());
|
||||||
}
|
}
|
||||||
@ -405,7 +407,7 @@ namespace API.Services
|
|||||||
|
|
||||||
if (!createThumbnail) return coverImageContent.ReadContent();
|
if (!createThumbnail) return coverImageContent.ReadContent();
|
||||||
|
|
||||||
using var stream = new MemoryStream(coverImageContent.ReadContent());
|
using var stream = StreamManager.GetStream("BookService.GetCoverImage", coverImageContent.ReadContent());
|
||||||
using var thumbnail = Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
|
using var thumbnail = Image.ThumbnailStream(stream, MetadataService.ThumbnailWidth);
|
||||||
return thumbnail.WriteToBuffer(".jpg");
|
return thumbnail.WriteToBuffer(".jpg");
|
||||||
|
|
||||||
@ -447,23 +449,19 @@ namespace API.Services
|
|||||||
private static MemoryStream GetPdfPage(IDocReader docReader, int pageNumber)
|
private static MemoryStream GetPdfPage(IDocReader docReader, int pageNumber)
|
||||||
{
|
{
|
||||||
using var pageReader = docReader.GetPageReader(pageNumber);
|
using var pageReader = docReader.GetPageReader(pageNumber);
|
||||||
var rawBytes = pageReader.GetImage();
|
var rawBytes = pageReader.GetImage(new NaiveTransparencyRemover());
|
||||||
var width = pageReader.GetPageWidth();
|
var width = pageReader.GetPageWidth();
|
||||||
var height = pageReader.GetPageHeight();
|
var height = pageReader.GetPageHeight();
|
||||||
using var doc = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
|
||||||
using var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
using var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||||
AddBytesToBitmap(bmp, rawBytes);
|
AddBytesToBitmap(bmp, rawBytes);
|
||||||
for (int y = 0; y < bmp.Height; y++)
|
// Removes 1px margin on left/right side after bitmap is copied out
|
||||||
|
for (var y = 0; y < bmp.Height; y++)
|
||||||
{
|
{
|
||||||
bmp.SetPixel(bmp.Width - 1, y, bmp.GetPixel(bmp.Width - 2, y));
|
bmp.SetPixel(bmp.Width - 1, y, bmp.GetPixel(bmp.Width - 2, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var g = Graphics.FromImage(doc);
|
var stream = StreamManager.GetStream("BookService.GetPdfPage");
|
||||||
g.FillRegion(Brushes.White, new Region(new Rectangle(0, 0, width, height)));
|
bmp.Save(stream, ImageFormat.Jpeg);
|
||||||
g.DrawImage(bmp, new Point(0, 0));
|
|
||||||
g.Save();
|
|
||||||
var stream = new MemoryStream();
|
|
||||||
doc.Save(stream, ImageFormat.Jpeg);
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ namespace API.Services
|
|||||||
if (ShouldFindCoverImage(series.CoverImage, forceUpdate))
|
if (ShouldFindCoverImage(series.CoverImage, forceUpdate))
|
||||||
{
|
{
|
||||||
series.Volumes ??= new List<Volume>();
|
series.Volumes ??= new List<Volume>();
|
||||||
var firstCover = series.Volumes.GetCoverImage(series.Library.Type);
|
var firstCover = series.Volumes.GetCoverImage(series.Format);
|
||||||
byte[] coverImage = null;
|
byte[] coverImage = null;
|
||||||
if (firstCover == null && series.Volumes.Any())
|
if (firstCover == null && series.Volumes.Any())
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { MangaFormat } from "./manga-format";
|
||||||
|
|
||||||
export interface SearchResult {
|
export interface SearchResult {
|
||||||
seriesId: number;
|
seriesId: number;
|
||||||
libraryId: number;
|
libraryId: number;
|
||||||
@ -6,4 +8,5 @@ export interface SearchResult {
|
|||||||
originalName: string;
|
originalName: string;
|
||||||
sortName: string;
|
sortName: string;
|
||||||
coverImage: string; // byte64 encoded
|
coverImage: string; // byte64 encoded
|
||||||
|
format: MangaFormat;
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,6 @@ export class ServerService {
|
|||||||
return this.httpClient.post(this.baseUrl + 'server/restart', {});
|
return this.httpClient.post(this.baseUrl + 'server/restart', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchLogs() {
|
|
||||||
return this.httpClient.get(this.baseUrl + 'server/logs', {responseType: 'blob' as 'text'});
|
|
||||||
}
|
|
||||||
|
|
||||||
getServerInfo() {
|
getServerInfo() {
|
||||||
return this.httpClient.get<ServerInfo>(this.baseUrl + 'server/server-info');
|
return this.httpClient.get<ServerInfo>(this.baseUrl + 'server/server-info');
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
|||||||
import { ServerService } from 'src/app/_services/server.service';
|
import { ServerService } from 'src/app/_services/server.service';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { DownloadService } from 'src/app/shared/_services/download.service';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -23,7 +24,8 @@ export class DashboardComponent implements OnInit {
|
|||||||
counter = this.tabs.length + 1;
|
counter = this.tabs.length + 1;
|
||||||
active = this.tabs[0];
|
active = this.tabs[0];
|
||||||
|
|
||||||
constructor(public route: ActivatedRoute, private serverService: ServerService, private toastr: ToastrService, private titleService: Title) {
|
constructor(public route: ActivatedRoute, private serverService: ServerService,
|
||||||
|
private toastr: ToastrService, private titleService: Title, private downloadService: DownloadService) {
|
||||||
this.route.fragment.subscribe(frag => {
|
this.route.fragment.subscribe(frag => {
|
||||||
const tab = this.tabs.filter(item => item.fragment === frag);
|
const tab = this.tabs.filter(item => item.fragment === frag);
|
||||||
if (tab.length > 0) {
|
if (tab.length > 0) {
|
||||||
@ -46,10 +48,7 @@ export class DashboardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchLogs() {
|
fetchLogs() {
|
||||||
this.serverService.fetchLogs().subscribe(res => {
|
this.downloadService.downloadLogs();
|
||||||
const blob = new Blob([res], {type: 'text/plain;charset=utf-8'});
|
|
||||||
saveAs(blob, 'kavita.zip');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
@import '../../../assets/themes/dark.scss';
|
|
||||||
|
|
||||||
.accent {
|
.accent {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
background-color: $dark-form-background;
|
background-color: lightgray;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
color: lightgray;
|
color: black;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: inset 0px 0px 8px 1px $dark-form-background
|
|
||||||
}
|
}
|
@ -34,6 +34,7 @@
|
|||||||
<img class="mr-3 search-result" src="{{imageService.getSeriesCoverImage(item.seriesId)}}">
|
<img class="mr-3 search-result" src="{{imageService.getSeriesCoverImage(item.seriesId)}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
|
<app-series-format [format]="item.format"></app-series-format>
|
||||||
<span *ngIf="item.name.toLowerCase().indexOf(searchTerm) >= 0; else localizedName" [innerHTML]="item.name"></span>
|
<span *ngIf="item.name.toLowerCase().indexOf(searchTerm) >= 0; else localizedName" [innerHTML]="item.name"></span>
|
||||||
<ng-template #localizedName>
|
<ng-template #localizedName>
|
||||||
<span [innerHTML]="item.localizedName"></span>
|
<span [innerHTML]="item.localizedName"></span>
|
||||||
|
@ -3,6 +3,7 @@ import { Component, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { UtilityService } from '../shared/_services/utility.service';
|
||||||
import { SearchResult } from '../_models/search-result';
|
import { SearchResult } from '../_models/search-result';
|
||||||
import { AccountService } from '../_services/account.service';
|
import { AccountService } from '../_services/account.service';
|
||||||
import { ImageService } from '../_services/image.service';
|
import { ImageService } from '../_services/image.service';
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
<h5>Type</h5>
|
<h5>Type</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<app-tag-badge><i class="fa {{utilityService.mangaFormatIcon(series.format)}}" aria-hidden="true" title="{{utilityService.mangaFormat(series.format)}}"></i> {{utilityService.mangaFormat(series.format)}} </app-tag-badge>
|
<app-tag-badge><app-series-format [format]="series.format">{{utilityService.mangaFormat(series.format)}}</app-series-format></app-tag-badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
|
import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Series } from 'src/app/_models/series';
|
import { Series } from 'src/app/_models/series';
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from 'src/environments/environment';
|
||||||
@ -46,14 +46,18 @@ export class DownloadService {
|
|||||||
return this.httpClient.get(this.baseUrl + 'download/chapter?chapterId=' + chapterId, {observe: 'response', responseType: 'blob' as 'text'});
|
return this.httpClient.get(this.baseUrl + 'download/chapter?chapterId=' + chapterId, {observe: 'response', responseType: 'blob' as 'text'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadLogs() {
|
||||||
|
this.httpClient.get(this.baseUrl + 'server/logs', {observe: 'response', responseType: 'blob' as 'text'}).subscribe(resp => {
|
||||||
|
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, 'logs'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
downloadSeries(series: Series) {
|
downloadSeries(series: Series) {
|
||||||
this.downloadSeriesSize(series.id).subscribe(async size => {
|
this.downloadSeriesSize(series.id).subscribe(async size => {
|
||||||
if (size >= this.SIZE_WARNING && !await this.confirmService.confirm('The series is ' + this.humanFileSize(size) + '. Are you sure you want to continue?')) {
|
if (size >= this.SIZE_WARNING && !await this.confirmService.confirm('The series is ' + this.humanFileSize(size) + '. Are you sure you want to continue?')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.downloadSeriesAPI(series.id).subscribe(resp => {
|
this.downloadSeriesAPI(series.id).subscribe(resp => {
|
||||||
//const filename = series.name + '.zip';
|
|
||||||
//this.preformSave(res, filename);
|
|
||||||
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, series.name));
|
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, series.name));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<ng-container *ngIf="format != MangaFormat.UNKNOWN">
|
||||||
|
<i class="fa {{utilityService.mangaFormatIcon(format)}}" aria-hidden="true" title="{{utilityService.mangaFormat(format)}}"></i>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</ng-container>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||||
|
import { UtilityService } from '../_services/utility.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-series-format',
|
||||||
|
templateUrl: './series-format.component.html',
|
||||||
|
styleUrls: ['./series-format.component.scss']
|
||||||
|
})
|
||||||
|
export class SeriesFormatComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() format: MangaFormat = MangaFormat.UNKNOWN;
|
||||||
|
|
||||||
|
get MangaFormat(): typeof MangaFormat {
|
||||||
|
return MangaFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public utilityService: UtilityService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,7 @@ import { TagBadgeComponent } from './tag-badge/tag-badge.component';
|
|||||||
import { CardDetailLayoutComponent } from './card-detail-layout/card-detail-layout.component';
|
import { CardDetailLayoutComponent } from './card-detail-layout/card-detail-layout.component';
|
||||||
import { ShowIfScrollbarDirective } from './show-if-scrollbar.directive';
|
import { ShowIfScrollbarDirective } from './show-if-scrollbar.directive';
|
||||||
import { A11yClickDirective } from './a11y-click.directive';
|
import { A11yClickDirective } from './a11y-click.directive';
|
||||||
|
import { SeriesFormatComponent } from './series-format/series-format.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -31,7 +32,8 @@ import { A11yClickDirective } from './a11y-click.directive';
|
|||||||
TagBadgeComponent,
|
TagBadgeComponent,
|
||||||
CardDetailLayoutComponent,
|
CardDetailLayoutComponent,
|
||||||
ShowIfScrollbarDirective,
|
ShowIfScrollbarDirective,
|
||||||
A11yClickDirective
|
A11yClickDirective,
|
||||||
|
SeriesFormatComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -54,7 +56,8 @@ import { A11yClickDirective } from './a11y-click.directive';
|
|||||||
TagBadgeComponent,
|
TagBadgeComponent,
|
||||||
CardDetailLayoutComponent,
|
CardDetailLayoutComponent,
|
||||||
ShowIfScrollbarDirective,
|
ShowIfScrollbarDirective,
|
||||||
A11yClickDirective
|
A11yClickDirective,
|
||||||
|
SeriesFormatComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
export class SharedModule { }
|
||||||
|
@ -23,6 +23,12 @@ $dark-item-accent-bg: #292d32;
|
|||||||
color: #4ac694;
|
color: #4ac694;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accent {
|
||||||
|
background-color: $dark-form-background !important;
|
||||||
|
color: lightgray !important;
|
||||||
|
box-shadow: inset 0px 0px 8px 1px $dark-form-background !important;
|
||||||
|
}
|
||||||
|
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
background-color: $dark-item-accent-bg;
|
background-color: $dark-item-accent-bg;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user