mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-02 21:24:18 -04:00
Polishing and Bugfixes (#613)
* Added a new field to Library for showing Last Scan time for a library. Manage library page now shows last scan for each library. Tweaked the websocket code to make scan progress a bit more reliable. * Updated docnet to provide ARM support from our recent PR. Pi users can now have a version for PDF support out of the box. * Parser is now culture invariant to hopefully fix an issue on Italian systems not detecting . correctly * Added the ability for the collection detail page to update when a new series is added. * Fixed an issue where multiple chapters stacked in a volume and reading in incognito, wouldn't sort the chapters and would open the wrong one. * Code smell
This commit is contained in:
parent
1ff1be2eb9
commit
ce2f4b6a9f
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
|
||||||
<PackageReference Include="Docnet.Core" Version="2.3.1" />
|
<PackageReference Include="Docnet.Core" Version="2.4.0-alpha.1" />
|
||||||
<PackageReference Include="ExCSS" Version="4.1.0" />
|
<PackageReference Include="ExCSS" Version="4.1.0" />
|
||||||
<PackageReference Include="Flurl" Version="3.0.2" />
|
<PackageReference Include="Flurl" Version="3.0.2" />
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.0" />
|
<PackageReference Include="Flurl.Http" Version="3.2.0" />
|
||||||
|
@ -10,9 +10,11 @@ using API.Entities;
|
|||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Interfaces;
|
using API.Interfaces;
|
||||||
|
using API.SignalR;
|
||||||
using Kavita.Common;
|
using Kavita.Common;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Controllers
|
namespace API.Controllers
|
||||||
@ -22,12 +24,14 @@ namespace API.Controllers
|
|||||||
private readonly ILogger<SeriesController> _logger;
|
private readonly ILogger<SeriesController> _logger;
|
||||||
private readonly ITaskScheduler _taskScheduler;
|
private readonly ITaskScheduler _taskScheduler;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IHubContext<MessageHub> _messageHub;
|
||||||
|
|
||||||
public SeriesController(ILogger<SeriesController> logger, ITaskScheduler taskScheduler, IUnitOfWork unitOfWork)
|
public SeriesController(ILogger<SeriesController> logger, ITaskScheduler taskScheduler, IUnitOfWork unitOfWork, IHubContext<MessageHub> messageHub)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_taskScheduler = taskScheduler;
|
_taskScheduler = taskScheduler;
|
||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
|
_messageHub = messageHub;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -296,6 +300,12 @@ namespace API.Controllers
|
|||||||
|
|
||||||
if (await _unitOfWork.CommitAsync())
|
if (await _unitOfWork.CommitAsync())
|
||||||
{
|
{
|
||||||
|
foreach (var tag in updateSeriesMetadataDto.Tags)
|
||||||
|
{
|
||||||
|
await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesAddedToCollection,
|
||||||
|
MessageFactory.SeriesAddedToCollection(tag.Id,
|
||||||
|
updateSeriesMetadataDto.SeriesMetadata.SeriesId));
|
||||||
|
}
|
||||||
return Ok("Successfully updated");
|
return Ok("Successfully updated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs
|
namespace API.DTOs
|
||||||
@ -7,8 +8,11 @@ namespace API.DTOs
|
|||||||
{
|
{
|
||||||
public int Id { get; init; }
|
public int Id { get; init; }
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public string CoverImage { get; init; }
|
/// <summary>
|
||||||
|
/// Last time Library was scanned
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastScanned { get; init; }
|
||||||
public LibraryType Type { get; init; }
|
public LibraryType Type { get; init; }
|
||||||
public ICollection<string> Folders { get; init; }
|
public ICollection<string> Folders { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1045
API/Data/Migrations/20211001113608_LastScannedLibrary.Designer.cs
generated
Normal file
1045
API/Data/Migrations/20211001113608_LastScannedLibrary.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
API/Data/Migrations/20211001113608_LastScannedLibrary.cs
Normal file
25
API/Data/Migrations/20211001113608_LastScannedLibrary.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace API.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class LastScannedLibrary : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastScanned",
|
||||||
|
table: "Library",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastScanned",
|
||||||
|
table: "Library");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -397,6 +397,9 @@ namespace API.Data.Migrations
|
|||||||
b.Property<DateTime>("LastModified")
|
b.Property<DateTime>("LastModified")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastScanned")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
@ -237,8 +237,8 @@ namespace API.Data.Repositories
|
|||||||
Libraries = u.Libraries.Select(l => new LibraryDto
|
Libraries = u.Libraries.Select(l => new LibraryDto
|
||||||
{
|
{
|
||||||
Name = l.Name,
|
Name = l.Name,
|
||||||
CoverImage = l.CoverImage,
|
|
||||||
Type = l.Type,
|
Type = l.Type,
|
||||||
|
LastScanned = l.LastScanned,
|
||||||
Folders = l.Folders.Select(x => x.Path).ToList()
|
Folders = l.Folders.Select(x => x.Path).ToList()
|
||||||
}).ToList()
|
}).ToList()
|
||||||
})
|
})
|
||||||
|
@ -13,9 +13,13 @@ namespace API.Entities
|
|||||||
public LibraryType Type { get; set; }
|
public LibraryType Type { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
public DateTime LastModified { get; set; }
|
public DateTime LastModified { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Last time Library was scanned
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastScanned { get; set; }
|
||||||
public ICollection<FolderPath> Folders { get; set; }
|
public ICollection<FolderPath> Folders { get; set; }
|
||||||
public ICollection<AppUser> AppUsers { get; set; }
|
public ICollection<AppUser> AppUsers { get; set; }
|
||||||
public ICollection<Series> Series { get; set; }
|
public ICollection<Series> Series { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,32 +21,35 @@ namespace API.Parser
|
|||||||
public const string SupportedExtensions =
|
public const string SupportedExtensions =
|
||||||
ArchiveFileExtensions + "|" + ImageFileExtensions + "|" + BookFileExtensions;
|
ArchiveFileExtensions + "|" + ImageFileExtensions + "|" + BookFileExtensions;
|
||||||
|
|
||||||
|
private const RegexOptions MatchOptions =
|
||||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant;
|
||||||
|
|
||||||
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))",
|
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)",
|
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
|
|
||||||
private static readonly string XmlRegexExtensions = @"\.xml";
|
private static readonly string XmlRegexExtensions = @"\.xml";
|
||||||
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions,
|
private static readonly Regex ImageRegex = new Regex(ImageFileExtensions,
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
private static readonly Regex ArchiveFileRegex = new Regex(ArchiveFileExtensions,
|
private static readonly Regex ArchiveFileRegex = new Regex(ArchiveFileExtensions,
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
private static readonly Regex XmlRegex = new Regex(XmlRegexExtensions,
|
private static readonly Regex XmlRegex = new Regex(XmlRegexExtensions,
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
private static readonly Regex BookFileRegex = new Regex(BookFileExtensions,
|
private static readonly Regex BookFileRegex = new Regex(BookFileExtensions,
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(cover|folder)(?![\w\d])",
|
private static readonly Regex CoverImageRegex = new Regex(@"(?<![[a-z]\d])(?:!?)(cover|folder)(?![\w\d])",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
|
|
||||||
private static readonly Regex NormalizeRegex = new Regex(@"[^a-zA-Z0-9]",
|
private static readonly Regex NormalizeRegex = new Regex(@"[^a-zA-Z0-9]",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout);
|
RegexTimeout);
|
||||||
|
|
||||||
|
|
||||||
@ -55,42 +58,42 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// vol_001-1.cbz for MangaPy default naming convention
|
// vol_001-1.cbz for MangaPy default naming convention
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(vol_)(?<Volume>\d+)",
|
@"(vol_)(?<Volume>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,166 +102,166 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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),
|
||||||
// The Duke of Death and His Black Maid - Vol. 04 Ch. 054.5 - V4 Omake
|
// The Duke of Death and His Black Maid - Vol. 04 Ch. 054.5 - V4 Omake
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.+?)(\s|_|-)+(?:Vol(ume|\.)?(\s|_|-)+\d+)(\s|_|-)+(?:(Ch|Chapter|Ch)\.?)(\s|_|-)+(?<Chapter>\d+)",
|
@"(?<Series>.+?)(\s|_|-)+(?:Vol(ume|\.)?(\s|_|-)+\d+)(\s|_|-)+(?:(Ch|Chapter|Ch)\.?)(\s|_|-)+(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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)\.?(\s|-|_)?\d+",
|
@"(?<Series>.*) (\b|_|-)(vol)\.?(\s|-|_)?\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// [xPearse] Kyochuu Rettou Volume 1 [English] [Manga] [Volume Scans]
|
// [xPearse] Kyochuu Rettou Volume 1 [English] [Manga] [Volume Scans]
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*) (\b|_|-)(vol)(ume)",
|
@"(?<Series>.*) (\b|_|-)(vol)(ume)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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>(?!Vol).+?)(?:(ch(apter|\.)(\b|_|-|\s))|sp)\d",
|
@"^(?<Series>(?!Vol).+?)(?:(ch(apter|\.)(\b|_|-|\s))|sp)\d",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Kodoja #001 (March 2016)
|
// Kodoja #001 (March 2016)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.*)(\s|_|-)#",
|
@"(?<Series>.*)(\s|_|-)#",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Baketeriya ch01-05.zip, Akiiro Bousou Biyori - 01.jpg, Beelzebub_172_RHS.zip, Cynthia the Mission 29.rar, A Compendium of Ghosts - 031 - The Third Story_ Part 12 (Digital) (Cobalt001)
|
// Baketeriya ch01-05.zip, Akiiro Bousou Biyori - 01.jpg, Beelzebub_172_RHS.zip, Cynthia the Mission 29.rar, A Compendium of Ghosts - 031 - The Third Story_ Part 12 (Digital) (Cobalt001)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?!Vol\.?)(?<Series>.+?)( |_|-)(?<!-)(ch)?\d+-?\d*",
|
@"^(?!Vol\.?)(?<Series>.+?)( |_|-)(?<!-)(ch)?\d+-?\d*",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -267,67 +270,67 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Batman Wayne Family Adventures - Ep. 001 - Moving In
|
// Batman Wayne Family Adventures - Ep. 001 - Moving In
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.+?)(\s|_|-)?(?:Ep\.?)(\s|_|-)+\d+",
|
@"^(?<Series>.+?)(\s|_|-)?(?:Ep\.?)(\s|_|-)+\d+",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -336,40 +339,40 @@ 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
// Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)
|
||||||
// BUG: Negative lookbehind has to be fixed width
|
// BUG: Negative lookbehind has to be fixed width
|
||||||
// NOTE: The case this is built for does not make much sense.
|
// NOTE: The case this is built for does not make much sense.
|
||||||
// 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// 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,
|
// MatchOptions,
|
||||||
// RegexTimeout),
|
// RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -378,47 +381,47 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Batman Beyond 04 (of 6) (1999)
|
// Batman Beyond 04 (of 6) (1999)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.+?)(?<Chapter>\d+)(\s|_|-)?\(of",
|
@"(?<Series>.+?)(?<Chapter>\d+)(\s|_|-)?\(of",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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>.+?)(?: (?<Chapter>\d+))",
|
@"^(?<Series>.+?)(?: (?<Chapter>\d+))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Batman & Robin the Teen Wonder #0
|
// Batman & Robin the Teen Wonder #0
|
||||||
new Regex(
|
new Regex(
|
||||||
@"^(?<Series>.+?)(?:\s|_)#(?<Chapter>\d+)",
|
@"^(?<Series>.+?)(?:\s|_)#(?<Chapter>\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Saga 001 (2012) (Digital) (Empire-Zone)
|
// Saga 001 (2012) (Digital) (Empire-Zone)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Series>.+?)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)\s\(\d{4}",
|
@"(?<Series>.+?)(?: |_)(c? ?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)\s\(\d{4}",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -426,11 +429,11 @@ namespace API.Parser
|
|||||||
{
|
{
|
||||||
// [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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// (Shadowcat-Empire),
|
// (Shadowcat-Empire),
|
||||||
// new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
// new Regex(@"(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
|
||||||
// RegexOptions.IgnoreCase | RegexOptions.Compiled),
|
// MatchOptions),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Regex[] MangaChapterRegex = new[]
|
private static readonly Regex[] MangaChapterRegex = new[]
|
||||||
@ -438,52 +441,52 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Green Worldz - Chapter 027, Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 11-10
|
// Green Worldz - Chapter 027, Kimi no Koto ga Daidaidaidaidaisuki na 100-nin no Kanojo Chapter 11-10
|
||||||
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -491,22 +494,22 @@ namespace API.Parser
|
|||||||
// 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -515,17 +518,17 @@ namespace API.Parser
|
|||||||
// (), {}, []
|
// (), {}, []
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Cleanup>(\{\}|\[\]|\(\)))",
|
@"(?<Cleanup>(\{\}|\[\]|\(\)))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// (Complete)
|
// (Complete)
|
||||||
new Regex(
|
new Regex(
|
||||||
@"(?<Cleanup>(\{Complete\}|\[Complete\]|\(Complete\)))",
|
@"(?<Cleanup>(\{Complete\}|\[Complete\]|\(Complete\)))",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
// Anything in parenthesis
|
// Anything in parenthesis
|
||||||
new Regex(
|
new Regex(
|
||||||
@"\(.*\)",
|
@"\(.*\)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout),
|
RegexTimeout),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -534,14 +537,14 @@ 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,
|
MatchOptions,
|
||||||
RegexTimeout),
|
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.
|
||||||
private static readonly Regex SpecialMarkerRegex = new Regex(
|
private static readonly Regex SpecialMarkerRegex = new Regex(
|
||||||
@"(?<Special>SP\d+)",
|
@"(?<Special>SP\d+)",
|
||||||
RegexOptions.IgnoreCase | RegexOptions.Compiled,
|
MatchOptions,
|
||||||
RegexTimeout
|
RegexTimeout
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -199,6 +199,7 @@ namespace API.Services.Tasks
|
|||||||
|
|
||||||
await UpdateLibrary(library, series);
|
await UpdateLibrary(library, series);
|
||||||
|
|
||||||
|
library.LastScanned = DateTime.Now;
|
||||||
_unitOfWork.LibraryRepository.Update(library);
|
_unitOfWork.LibraryRepository.Update(library);
|
||||||
if (await _unitOfWork.CommitAsync())
|
if (await _unitOfWork.CommitAsync())
|
||||||
{
|
{
|
||||||
@ -296,6 +297,9 @@ namespace API.Services.Tasks
|
|||||||
{
|
{
|
||||||
await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesRemoved, MessageFactory.SeriesRemovedEvent(missing.Id, missing.Name, library.Id));
|
await _messageHub.Clients.All.SendAsync(SignalREvents.SeriesRemoved, MessageFactory.SeriesRemovedEvent(missing.Id, missing.Name, library.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress,
|
||||||
|
MessageFactory.ScanLibraryProgressEvent(library.Id, ((chunk + 1F) * chunkInfo.ChunkSize) / chunkInfo.TotalSize, string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ namespace API.SignalR
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SignalRMessage ScanLibraryProgressEvent(int libraryId, int progress, string seriesName)
|
public static SignalRMessage ScanLibraryProgressEvent(int libraryId, float progress, string seriesName)
|
||||||
{
|
{
|
||||||
return new SignalRMessage()
|
return new SignalRMessage()
|
||||||
{
|
{
|
||||||
@ -97,5 +97,17 @@ namespace API.SignalR
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SignalRMessage SeriesAddedToCollection(int tagId, int seriesId)
|
||||||
|
{
|
||||||
|
return new SignalRMessage
|
||||||
|
{
|
||||||
|
Name = SignalREvents.UpdateVersion,
|
||||||
|
Body = new
|
||||||
|
{
|
||||||
|
TagId = tagId,
|
||||||
|
SeriesId = seriesId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using API.Extensions;
|
|
||||||
using API.SignalR.Presence;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
|
|
||||||
namespace API.SignalR
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Keeps track of who is logged into the app
|
|
||||||
/// </summary>
|
|
||||||
public class PresenceHub : Hub
|
|
||||||
{
|
|
||||||
private readonly IPresenceTracker _tracker;
|
|
||||||
|
|
||||||
public PresenceHub(IPresenceTracker tracker)
|
|
||||||
{
|
|
||||||
_tracker = tracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task OnConnectedAsync()
|
|
||||||
{
|
|
||||||
await _tracker.UserConnected(Context.User.GetUsername(), Context.ConnectionId);
|
|
||||||
|
|
||||||
var currentUsers = await PresenceTracker.GetOnlineUsers();
|
|
||||||
await Clients.All.SendAsync("GetOnlineUsers", currentUsers);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task OnDisconnectedAsync(Exception exception)
|
|
||||||
{
|
|
||||||
await _tracker.UserDisconnected(Context.User.GetUsername(), Context.ConnectionId);
|
|
||||||
|
|
||||||
var currentUsers = await PresenceTracker.GetOnlineUsers();
|
|
||||||
await Clients.All.SendAsync("GetOnlineUsers", currentUsers);
|
|
||||||
|
|
||||||
await base.OnDisconnectedAsync(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,5 +10,6 @@
|
|||||||
public const string SeriesRemoved = "SeriesRemoved";
|
public const string SeriesRemoved = "SeriesRemoved";
|
||||||
public const string ScanLibraryProgress = "ScanLibraryProgress";
|
public const string ScanLibraryProgress = "ScanLibraryProgress";
|
||||||
public const string OnlineUsers = "OnlineUsers";
|
public const string OnlineUsers = "OnlineUsers";
|
||||||
|
public const string SeriesAddedToCollection = "SeriesAddedToCollection";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
export interface SeriesAddedToCollectionEvent {
|
||||||
|
tagId: number;
|
||||||
|
seriesId: number;
|
||||||
|
}
|
@ -7,7 +7,7 @@ export enum LibraryType {
|
|||||||
export interface Library {
|
export interface Library {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
coverImage: string;
|
lastScanned: string;
|
||||||
type: LibraryType;
|
type: LibraryType;
|
||||||
folders: string[];
|
folders: string[];
|
||||||
}
|
}
|
@ -17,7 +17,8 @@ export enum EVENTS {
|
|||||||
RefreshMetadata = 'RefreshMetadata',
|
RefreshMetadata = 'RefreshMetadata',
|
||||||
SeriesAdded = 'SeriesAdded',
|
SeriesAdded = 'SeriesAdded',
|
||||||
ScanLibraryProgress = 'ScanLibraryProgress',
|
ScanLibraryProgress = 'ScanLibraryProgress',
|
||||||
OnlineUsers = 'OnlineUsers'
|
OnlineUsers = 'OnlineUsers',
|
||||||
|
SeriesAddedToCollection = 'SeriesAddedToCollection'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Message<T> {
|
export interface Message<T> {
|
||||||
@ -85,6 +86,13 @@ export class MessageHubService {
|
|||||||
this.scanLibrary.emit(resp.body);
|
this.scanLibrary.emit(resp.body);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.hubConnection.on(EVENTS.SeriesAddedToCollection, resp => {
|
||||||
|
this.messagesSource.next({
|
||||||
|
event: EVENTS.SeriesAddedToCollection,
|
||||||
|
payload: resp.body
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.hubConnection.on(EVENTS.SeriesAdded, resp => {
|
this.hubConnection.on(EVENTS.SeriesAdded, resp => {
|
||||||
this.messagesSource.next({
|
this.messagesSource.next({
|
||||||
event: EVENTS.SeriesAdded,
|
event: EVENTS.SeriesAdded,
|
||||||
|
@ -20,6 +20,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>Type: {{libraryType(library.type)}}</div>
|
<div>Type: {{libraryType(library.type)}}</div>
|
||||||
<div>Shared Folders: {{library.folders.length + ' folders'}}</div>
|
<div>Shared Folders: {{library.folders.length + ' folders'}}</div>
|
||||||
|
<div>
|
||||||
|
Last Scanned:
|
||||||
|
<span *ngIf="library.lastScanned == '0001-01-01T00:00:00'; else activeDate">Never</span>
|
||||||
|
<ng-template #activeDate>
|
||||||
|
{{library.lastScanned | date: 'MM/dd/yyyy'}}
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="loading" class="list-group-item">
|
<li *ngIf="loading" class="list-group-item">
|
||||||
<div class="spinner-border text-secondary" role="status">
|
<div class="spinner-border text-secondary" role="status">
|
||||||
|
@ -2,12 +2,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
|
|||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { take, takeUntil } from 'rxjs/operators';
|
import { take, takeUntil, takeWhile } from 'rxjs/operators';
|
||||||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||||
import { ScanLibraryProgressEvent } from 'src/app/_models/events/scan-library-progress-event';
|
import { ScanLibraryProgressEvent } from 'src/app/_models/events/scan-library-progress-event';
|
||||||
import { Library, LibraryType } from 'src/app/_models/library';
|
import { Library, LibraryType } from 'src/app/_models/library';
|
||||||
import { LibraryService } from 'src/app/_services/library.service';
|
import { LibraryService } from 'src/app/_services/library.service';
|
||||||
import { MessageHubService } from 'src/app/_services/message-hub.service';
|
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
|
||||||
import { LibraryEditorModalComponent } from '../_modals/library-editor-modal/library-editor-modal.component';
|
import { LibraryEditorModalComponent } from '../_modals/library-editor-modal/library-editor-modal.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -35,9 +35,10 @@ export class ManageLibraryComponent implements OnInit, OnDestroy {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getLibraries();
|
this.getLibraries();
|
||||||
|
|
||||||
this.hubService.scanLibrary.subscribe((event: ScanLibraryProgressEvent) => {
|
// when a progress event comes in, show it on the UI next to library
|
||||||
|
this.hubService.messages$.pipe(takeWhile(event => event.event === EVENTS.ScanLibraryProgress)).subscribe((event) => {
|
||||||
this.scanInProgress[event.libraryId] = event.progress !== 100;
|
const scanEvent = event.payload as ScanLibraryProgressEvent;
|
||||||
|
this.scanInProgress[scanEvent.libraryId] = scanEvent.progress !== 100;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { Component, HostListener, OnInit } from '@angular/core';
|
import { Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { take } from 'rxjs/operators';
|
import { Subject } from 'rxjs';
|
||||||
|
import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
|
||||||
import { BulkSelectionService } from 'src/app/cards/bulk-selection.service';
|
import { BulkSelectionService } from 'src/app/cards/bulk-selection.service';
|
||||||
import { UpdateFilterEvent } from 'src/app/cards/card-detail-layout/card-detail-layout.component';
|
import { UpdateFilterEvent } from 'src/app/cards/card-detail-layout/card-detail-layout.component';
|
||||||
import { EditCollectionTagsComponent } from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
|
import { EditCollectionTagsComponent } from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
|
||||||
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
|
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
|
||||||
import { CollectionTag } from 'src/app/_models/collection-tag';
|
import { CollectionTag } from 'src/app/_models/collection-tag';
|
||||||
|
import { SeriesAddedToCollectionEvent } from 'src/app/_models/events/series-added-to-collection-event';
|
||||||
import { Pagination } from 'src/app/_models/pagination';
|
import { Pagination } from 'src/app/_models/pagination';
|
||||||
import { Series } from 'src/app/_models/series';
|
import { Series } from 'src/app/_models/series';
|
||||||
import { FilterItem, mangaFormatFilters, SeriesFilter } from 'src/app/_models/series-filter';
|
import { FilterItem, mangaFormatFilters, SeriesFilter } from 'src/app/_models/series-filter';
|
||||||
@ -17,6 +19,7 @@ import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/acti
|
|||||||
import { ActionService } from 'src/app/_services/action.service';
|
import { ActionService } from 'src/app/_services/action.service';
|
||||||
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
import { CollectionTagService } from 'src/app/_services/collection-tag.service';
|
||||||
import { ImageService } from 'src/app/_services/image.service';
|
import { ImageService } from 'src/app/_services/image.service';
|
||||||
|
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
|
||||||
import { SeriesService } from 'src/app/_services/series.service';
|
import { SeriesService } from 'src/app/_services/series.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -24,7 +27,7 @@ import { SeriesService } from 'src/app/_services/series.service';
|
|||||||
templateUrl: './collection-detail.component.html',
|
templateUrl: './collection-detail.component.html',
|
||||||
styleUrls: ['./collection-detail.component.scss']
|
styleUrls: ['./collection-detail.component.scss']
|
||||||
})
|
})
|
||||||
export class CollectionDetailComponent implements OnInit {
|
export class CollectionDetailComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
collectionTag!: CollectionTag;
|
collectionTag!: CollectionTag;
|
||||||
tagImage: string = '';
|
tagImage: string = '';
|
||||||
@ -40,6 +43,8 @@ export class CollectionDetailComponent implements OnInit {
|
|||||||
mangaFormat: null
|
mangaFormat: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onDestory: Subject<void> = new Subject<void>();
|
||||||
|
|
||||||
bulkActionCallback = (action: Action, data: any) => {
|
bulkActionCallback = (action: Action, data: any) => {
|
||||||
const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series');
|
const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series');
|
||||||
const selectedSeries = this.series.filter((series, index: number) => selectedSeriesIndexies.includes(index + ''));
|
const selectedSeries = this.series.filter((series, index: number) => selectedSeriesIndexies.includes(index + ''));
|
||||||
@ -68,7 +73,7 @@ export class CollectionDetailComponent implements OnInit {
|
|||||||
constructor(public imageService: ImageService, private collectionService: CollectionTagService, private router: Router, private route: ActivatedRoute,
|
constructor(public imageService: ImageService, private collectionService: CollectionTagService, private router: Router, private route: ActivatedRoute,
|
||||||
private seriesService: SeriesService, private toastr: ToastrService, private actionFactoryService: ActionFactoryService,
|
private seriesService: SeriesService, private toastr: ToastrService, private actionFactoryService: ActionFactoryService,
|
||||||
private modalService: NgbModal, private titleService: Title, private accountService: AccountService,
|
private modalService: NgbModal, private titleService: Title, private accountService: AccountService,
|
||||||
public bulkSelectionService: BulkSelectionService, private actionService: ActionService) {
|
public bulkSelectionService: BulkSelectionService, private actionService: ActionService, private messageHub: MessageHubService) {
|
||||||
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
|
||||||
|
|
||||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||||
@ -88,6 +93,18 @@ export class CollectionDetailComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.collectionTagActions = this.actionFactoryService.getCollectionTagActions(this.handleCollectionActionCallback.bind(this));
|
this.collectionTagActions = this.actionFactoryService.getCollectionTagActions(this.handleCollectionActionCallback.bind(this));
|
||||||
|
|
||||||
|
this.messageHub.messages$.pipe(takeWhile(event => event.event === EVENTS.SeriesAddedToCollection), takeUntil(this.onDestory), debounceTime(2000)).subscribe(event => {
|
||||||
|
const collectionEvent = event.payload as SeriesAddedToCollectionEvent;
|
||||||
|
if (collectionEvent.tagId === this.collectionTag.id) {
|
||||||
|
this.loadPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestory.next();
|
||||||
|
this.onDestory.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:keydown.shift', ['$event'])
|
@HostListener('document:keydown.shift', ['$event'])
|
||||||
|
@ -259,7 +259,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
|
|||||||
break;
|
break;
|
||||||
case(Action.IncognitoRead):
|
case(Action.IncognitoRead):
|
||||||
if (volume.chapters != undefined && volume.chapters?.length >= 1) {
|
if (volume.chapters != undefined && volume.chapters?.length >= 1) {
|
||||||
this.openChapter(volume.chapters[0], true);
|
this.openChapter(volume.chapters.sort(this.utilityService.sortChapters)[0], true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user