v0.8.4.1 - Hotfix (#3419)

Co-authored-by: Weblate (bot) <hosted@weblate.org>
Co-authored-by: Adam Beneš <toohka@protonmail.com>
Co-authored-by: Dark77 <Dark77@pobox.sk>
Co-authored-by: Frozehunter <frozehunter@me.com>
Co-authored-by: Havokdan <havokdan@yahoo.com.br>
Co-authored-by: Yoan Jacquemin <yoanjacquemin@gmail.com>
Co-authored-by: aleixcox <18121624@qq.com>
Co-authored-by: mag37 <robin.ivehult@gmail.com>
This commit is contained in:
Joe Milazzo 2024-11-27 11:04:18 -06:00 committed by GitHub
parent d4028a8d68
commit ac47cbd75f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 300 additions and 128 deletions

View file

@ -96,7 +96,7 @@
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
<PackageReference Include="SharpCompress" Version="0.38.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.3.0.106239">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View file

@ -20,6 +20,7 @@ using API.SignalR;
using AutoMapper;
using EasyCaching.Core;
using Hangfire;
using Kavita.Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@ -447,12 +448,58 @@ public class LibraryController : BaseApiController
return Ok();
}
/// <summary>
/// Deletes the library and all series within it.
/// </summary>
/// <remarks>This does not touch any files</remarks>
/// <param name="libraryId"></param>
/// <returns></returns>
[Authorize(Policy = "RequireAdminRole")]
[HttpDelete("delete")]
public async Task<ActionResult<bool>> DeleteLibrary(int libraryId)
{
_logger.LogInformation("Library {LibraryId} is being deleted by {UserName}", libraryId, User.GetUsername());
try
{
return Ok(await DeleteLibrary(libraryId, User.GetUserId()));
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
/// <summary>
/// Deletes multiple libraries and all series within it.
/// </summary>
/// <remarks>This does not touch any files</remarks>
/// <param name="libraryIds"></param>
/// <returns></returns>
[Authorize(Policy = "RequireAdminRole")]
[HttpDelete("delete-multiple")]
public async Task<ActionResult<bool>> DeleteMultipleLibraries([FromQuery] List<int> libraryIds)
{
var username = User.GetUsername();
_logger.LogInformation("Library {LibraryId} is being deleted by {UserName}", libraryId, username);
_logger.LogInformation("Libraries {LibraryIds} are being deleted by {UserName}", libraryIds, username);
foreach (var libraryId in libraryIds)
{
try
{
await DeleteLibrary(libraryId, User.GetUserId());
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
return Ok();
}
private async Task<bool> DeleteLibrary(int libraryId, int userId)
{
var series = await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(libraryId);
var seriesIds = series.Select(x => x.Id).ToArray();
var chapterIds =
@ -463,16 +510,19 @@ public class LibraryController : BaseApiController
if (TaskScheduler.HasScanTaskRunningForLibrary(libraryId))
{
_logger.LogInformation("User is attempting to delete a library while a scan is in progress");
return BadRequest(await _localizationService.Translate(User.GetUserId(), "delete-library-while-scan"));
throw new KavitaException(await _localizationService.Translate(userId, "delete-library-while-scan"));
}
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId);
if (library == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "library-doesnt-exist"));
if (library == null)
{
throw new KavitaException(await _localizationService.Translate(userId, "library-doesnt-exist"));
}
// Due to a bad schema that I can't figure out how to fix, we need to erase all RelatedSeries before we delete the library
// Aka SeriesRelation has an invalid foreign key
foreach (var s in await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(library.Id,
SeriesIncludes.Related))
foreach (var s in await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(library.Id, SeriesIncludes.Related))
{
s.Relations = new List<SeriesRelation>();
_unitOfWork.SeriesRepository.Update(s);
@ -489,7 +539,7 @@ public class LibraryController : BaseApiController
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
await _eventHub.SendMessageAsync(MessageFactory.SideNavUpdate,
MessageFactory.SideNavUpdateEvent(User.GetUserId()), false);
MessageFactory.SideNavUpdateEvent(userId), false);
if (chapterIds.Any())
{
@ -508,13 +558,13 @@ public class LibraryController : BaseApiController
await _eventHub.SendMessageAsync(MessageFactory.LibraryModified,
MessageFactory.LibraryModifiedEvent(libraryId, "delete"), false);
return Ok(true);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "There was a critical issue. Please try again");
await _unitOfWork.RollbackAsync();
return Ok(false);
return false;
}
}

View file

@ -540,6 +540,8 @@ public class ReaderController : BaseApiController
public async Task<ActionResult<ProgressDto>> GetProgress(int chapterId)
{
var progress = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(chapterId, User.GetUserId());
_logger.LogDebug("Get Progress for {ChapterId} is {Pages}", chapterId, progress?.PageNum ?? 0);
if (progress == null) return Ok(new ProgressDto()
{
PageNum = 0,

View file

@ -2,6 +2,7 @@
using System.Linq;
using System.Threading.Tasks;
using API.Entities;
using API.Extensions;
using API.Helpers.Builders;
using API.Services.Tasks.Scanner.Parser;
using Kavita.Common.EnvironmentInfo;
@ -28,7 +29,7 @@ public static class MigrateChapterRange
var chapters = await dataContext.Chapter.ToListAsync();
foreach (var chapter in chapters)
{
if (Parser.MinNumberFromRange(chapter.Range) == 0.0f)
if (Parser.MinNumberFromRange(chapter.Range).Is(0.0f))
{
chapter.Range = chapter.GetNumberTitle();
}

View file

@ -192,22 +192,32 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
/// <returns></returns>
public string GetNumberTitle()
{
if (MinNumber.Is(MaxNumber))
// BUG: TODO: On non-english locales, for floats, the range will be 20,5 but the NumberTitle will return 20.5
// Have I fixed this with TryParse CultureInvariant
try
{
if (MinNumber.Is(Parser.DefaultChapterNumber) && IsSpecial)
if (MinNumber.Is(MaxNumber))
{
return Parser.RemoveExtensionIfSupported(Title);
if (MinNumber.Is(Parser.DefaultChapterNumber) && IsSpecial)
{
return Parser.RemoveExtensionIfSupported(Title);
}
if (MinNumber.Is(0f) && !float.TryParse(Range, CultureInfo.InvariantCulture, out _))
{
return $"{Range.ToString(CultureInfo.InvariantCulture)}";
}
return $"{MinNumber.ToString(CultureInfo.InvariantCulture)}";
}
if (MinNumber.Is(0) && !float.TryParse(Range, CultureInfo.InvariantCulture, out _))
{
return $"{Range}";
}
return $"{MinNumber}";
return $"{MinNumber.ToString(CultureInfo.InvariantCulture)}-{MaxNumber.ToString(CultureInfo.InvariantCulture)}";
}
catch (Exception)
{
return MinNumber.ToString(CultureInfo.InvariantCulture);
}
return $"{MinNumber}-{MaxNumber}";
}
/// <summary>

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using API.Entities.Interfaces;
using API.Extensions;
using API.Services.Tasks.Scanner.Parser;
@ -67,11 +68,12 @@ public class Volume : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
/// <returns></returns>
public string GetNumberTitle()
{
if (MinNumber.Is(MaxNumber))
if (MinNumber.Equals(MaxNumber))
{
return $"{MinNumber}";
return MinNumber.ToString(CultureInfo.InvariantCulture);
}
return $"{MinNumber}-{MaxNumber}";
return $"{MinNumber.ToString(CultureInfo.InvariantCulture)}-{MaxNumber.ToString(CultureInfo.InvariantCulture)}";
}
public void ResetColorScape()

View file

@ -38,7 +38,7 @@ public static class ChapterListExtensions
fakeChapter.UpdateFrom(info);
return specialTreatment
? chapters.FirstOrDefault(c => c.Range == Parser.RemoveExtensionIfSupported(info.Filename) || c.Files.Select(f => Parser.NormalizePath(f.FilePath)).Contains(normalizedPath))
: chapters.FirstOrDefault(c => c.Range == fakeChapter.GetNumberTitle()); // BUG: TODO: On non-english locales, for floats, the range will be 20,5 but the NumberTitle will return 20.5
: chapters.FirstOrDefault(c => c.Range == fakeChapter.GetNumberTitle());
}
/// <summary>

View file

@ -885,7 +885,7 @@ public class BookService : IBookService
}
/// <summary>
/// Extracts a pdf into images to a target directory. Uses multi-threaded implementation since docnet is slow normally.
/// Extracts a pdf into images to a target directory. Uses multithreaded implementation since docnet is slow normally.
/// </summary>
/// <param name="fileFilePath"></param>
/// <param name="targetDirectory"></param>

View file

@ -708,7 +708,7 @@ public class DirectoryService : IDirectoryService
if (!FileSystem.Directory.Exists(folderPath)) return ImmutableArray<string>.Empty;
var directories = new List<string>();
var foundDirs = GetDirectories(folderPath);
var foundDirs = GetDirectories(folderPath, matcher);
foreach (var foundDir in foundDirs)
{
directories.Add(foundDir);

View file

@ -818,11 +818,11 @@ public class ParseScannedFiles
chapter.IssueOrder = counter;
// Increment for next chapter (unless the next has a similar value, then add 0.1)
if (!string.IsNullOrEmpty(prevIssue) && float.TryParse(prevIssue, CultureInfo.InvariantCulture, out var prevIssueFloat) && parsedChapter.Is(prevIssueFloat))
if (!string.IsNullOrEmpty(prevIssue) && float.TryParse(prevIssue, NumberStyles.Any, CultureInfo.InvariantCulture, out var prevIssueFloat) && parsedChapter.Is(prevIssueFloat))
{
counter += 0.1f; // bump if same value as the previous issue
}
prevIssue = $"{parsedChapter}";
prevIssue = $"{parsedChapter.ToString(CultureInfo.InvariantCulture)}";
}
else
{

View file

@ -10,7 +10,7 @@ using API.Extensions;
namespace API.Services.Tasks.Scanner.Parser;
#nullable enable
public static class Parser
public static partial class Parser
{
// NOTE: If you change this, don't forget to change in the UI (see Series Detail)
public const string DefaultChapter = "-100000"; // -2147483648
@ -1054,7 +1054,7 @@ public static class Parser
}
// Check if there is a range or not
if (Regex.IsMatch(range, @"\d-{1}\d"))
if (NumberRangeRegex().IsMatch(range))
{
var tokens = range.Replace("_", string.Empty).Split("-", StringSplitOptions.RemoveEmptyEntries);
@ -1081,7 +1081,7 @@ public static class Parser
}
// Check if there is a range or not
if (Regex.IsMatch(range, @"\d-{1}\d"))
if (NumberRangeRegex().IsMatch(range))
{
var tokens = range.Replace("_", string.Empty).Split("-", StringSplitOptions.RemoveEmptyEntries);
@ -1244,10 +1244,15 @@ public static class Parser
{
if (string.IsNullOrEmpty(filename)) return filename;
if (Regex.IsMatch(filename, SupportedExtensions))
if (SupportedExtensionsRegex().IsMatch(filename))
{
return Regex.Replace(filename, SupportedExtensions, string.Empty);
return SupportedExtensionsRegex().Replace(filename, string.Empty);
}
return filename;
}
[GeneratedRegex(SupportedExtensions)]
private static partial Regex SupportedExtensionsRegex();
[GeneratedRegex(@"\d-{1}\d")]
private static partial Regex NumberRangeRegex();
}