diff --git a/API/API.csproj b/API/API.csproj index 1658d7be3..e5276ac0d 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -205,7 +205,6 @@ Always - diff --git a/API/Controllers/PluginController.cs b/API/Controllers/PluginController.cs index 469bb8ebb..3a06fb06c 100644 --- a/API/Controllers/PluginController.cs +++ b/API/Controllers/PluginController.cs @@ -1,11 +1,16 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using API.Data; using API.DTOs; +using API.DTOs.Misc; using API.Entities.Enums; using API.Middleware; using API.Services; +using API.Services.Tasks.Scanner.Parser; using Kavita.Common; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -91,4 +96,85 @@ public class PluginController(IUnitOfWork unitOfWork, ITokenService tokenService return Ok(new { ExpiresAt = exp?.ToUniversalTime() }); } + + + /// + /// Parse a string and return Parsed information from it. Does not support any directory fallback parsing + /// + /// String to parse + /// Determines the set of pattern matching to use + /// + [HttpGet("parse")] + public ActionResult Parse([FromQuery] [StringLength(1000)] string name, [FromQuery] LibraryType libraryType) + { + try + { + var result = ParseText(name, libraryType); + + return Ok(result); + } + catch (RegexMatchTimeoutException) + { + return BadRequest("Input could not be parsed in allowed time"); + } + catch (Exception) + { + return BadRequest("Failed to parse input"); + } + } + + private static ParseResultDto ParseText(string name, LibraryType libraryType) + { + var result = new ParseResultDto + { + SeriesName = Parser.ParseSeries(name, libraryType), + SeriesYear = Parser.ParseYear(name) + }; + var chapterRange = Parser.ParseChapter(name, libraryType); + result.MinChapterNumber = Parser.MinNumberFromRange(chapterRange); + result.MaxChapterNumber = Parser.MaxNumberFromRange(chapterRange); + var volumeRange = Parser.ParseVolume(name, libraryType); + result.MinVolumeNumber = Parser.MinNumberFromRange(volumeRange); + result.MaxVolumeNumber = Parser.MaxNumberFromRange(volumeRange); + return result; + } + + [HttpPost("parse-bulk")] + public ActionResult ParseBulk(ParseBulkRequestDto dto, CancellationToken cancellationToken) + { + if (dto.Names.Count > 100) + { + return BadRequest("Only 100 items can be processed at once"); + } + + var result = new ParseBulkResponseDto(); + + var successfulParses = result.Results; + var errorParses = result.Errors; + foreach (var name in dto.Names) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (name.Length > 1000) + { + errorParses.Add(name, "Length > 1000 characters"); + continue; + } + + try + { + successfulParses.Add(name, ParseText(name, dto.LibraryType)); + } + catch (RegexMatchTimeoutException) + { + errorParses.Add(name, "Input could not be parsed in allowed time"); + } + catch (Exception) + { + errorParses.Add(name, "Failed to parse input"); + } + } + + return Ok(result); + } } diff --git a/API/DTOs/Misc/ParseBulkRequestDto.cs b/API/DTOs/Misc/ParseBulkRequestDto.cs new file mode 100644 index 000000000..7e529e9ed --- /dev/null +++ b/API/DTOs/Misc/ParseBulkRequestDto.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using API.Entities.Enums; + +namespace API.DTOs.Misc; + +public sealed record ParseBulkRequestDto +{ + public ICollection Names { get; set; } + public LibraryType LibraryType { get; set; } +} diff --git a/API/DTOs/Misc/ParseBulkResponseDto.cs b/API/DTOs/Misc/ParseBulkResponseDto.cs new file mode 100644 index 000000000..c46f1b78b --- /dev/null +++ b/API/DTOs/Misc/ParseBulkResponseDto.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace API.DTOs.Misc; + +public record ParseBulkResponseDto +{ + /// + /// The requested name to the parsed result. Does not include errored items + /// + public Dictionary Results { get; set; } = new(); + /// + /// The requested name to parse maps to the Error exception + /// + public Dictionary Errors { get; set; } = new(); + + /// + /// Count of errored items + /// + public int ErrorCounts => Errors.Count; +} diff --git a/API/DTOs/Misc/ParseResultDto.cs b/API/DTOs/Misc/ParseResultDto.cs new file mode 100644 index 000000000..dd2b8771e --- /dev/null +++ b/API/DTOs/Misc/ParseResultDto.cs @@ -0,0 +1,15 @@ +using System; + +namespace API.DTOs.Misc; + +public sealed record ParseResultDto +{ + public string SeriesName { get; set; } + public string SeriesYear { get; set; } + public float MinChapterNumber { get; set; } + public float MaxChapterNumber { get; set; } + public float MinVolumeNumber { get; set; } + public float MaxVolumeNumber { get; set; } + + +}