mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-08-11 09:13:42 -04:00
A ton of random bugs and polish (#3668)
This commit is contained in:
parent
b45d92ea5c
commit
de651215f5
@ -1,6 +1,7 @@
|
|||||||
# Editor configuration, see https://editorconfig.org
|
# Editor configuration, see https://editorconfig.org
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
@ -22,3 +23,7 @@ indent_size = 2
|
|||||||
|
|
||||||
[*.csproj]
|
[*.csproj]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
# Disable SonarLint warning S1075 (Don't use hardcoded url)
|
||||||
|
dotnet_diagnostic.S1075.severity = none
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
@ -13,7 +10,6 @@ using API.Helpers.Builders;
|
|||||||
using API.Services;
|
using API.Services;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using API.Helpers.Converters;
|
using API.Helpers.Converters;
|
||||||
using Hangfire;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace API.Tests.Converters;
|
namespace API.Tests.Converters;
|
||||||
|
@ -7,7 +7,6 @@ using API.Extensions;
|
|||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
using API.Tests.Helpers;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.Data;
|
|
||||||
using API.Data.Misc;
|
using API.Data.Misc;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
|
||||||
using API.Extensions.QueryExtensions;
|
using API.Extensions.QueryExtensions;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -3,7 +3,6 @@ using API.Entities;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Tests.Helpers;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace API.Tests.Extensions;
|
namespace API.Tests.Extensions;
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Threading;
|
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Extensions;
|
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services.Tasks.Scanner;
|
using API.Services.Tasks.Scanner;
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
|
||||||
using API.DTOs;
|
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
|
||||||
using API.Helpers;
|
|
||||||
using API.Helpers.Builders;
|
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace API.Tests.Helpers;
|
namespace API.Tests.Helpers;
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ using NSubstitute;
|
|||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace API.Tests.Helpers;
|
namespace API.Tests.Helpers;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ScannerHelper
|
public class ScannerHelper
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.Data;
|
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using API.Helpers;
|
||||||
using API.Helpers;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace API.Tests.Helpers;
|
namespace API.Tests.Helpers;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using API.Data.Metadata;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
namespace API.Tests.Parsing;
|
namespace API.Tests.Parsing;
|
||||||
|
|
||||||
public class MangaParsingTests
|
public class MangaParsingTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _testOutputHelper;
|
|
||||||
|
|
||||||
public MangaParsingTests(ITestOutputHelper testOutputHelper)
|
|
||||||
{
|
|
||||||
_testOutputHelper = testOutputHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)", "1")]
|
[InlineData("Killing Bites Vol. 0001 Ch. 0001 - Galactica Scanlations (gb)", "1")]
|
||||||
[InlineData("My Girlfriend Is Shobitch v01 - ch. 09 - pg. 008.png", "1")]
|
[InlineData("My Girlfriend Is Shobitch v01 - ch. 09 - pg. 008.png", "1")]
|
||||||
@ -84,6 +76,7 @@ public class MangaParsingTests
|
|||||||
[InlineData("Accel World Chapter 001 Volume 002", "2")]
|
[InlineData("Accel World Chapter 001 Volume 002", "2")]
|
||||||
[InlineData("Accel World Volume 2", "2")]
|
[InlineData("Accel World Volume 2", "2")]
|
||||||
[InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake", "30")]
|
[InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake", "30")]
|
||||||
|
[InlineData("Zom 100 - Bucket List of the Dead v01", "1")]
|
||||||
public void ParseVolumeTest(string filename, string expected)
|
public void ParseVolumeTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseVolume(filename, LibraryType.Manga));
|
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseVolume(filename, LibraryType.Manga));
|
||||||
@ -212,6 +205,8 @@ public class MangaParsingTests
|
|||||||
[InlineData("不安の種\uff0b - 01", "不安の種\uff0b")]
|
[InlineData("不安の種\uff0b - 01", "不安の種\uff0b")]
|
||||||
[InlineData("Giant Ojou-sama - Ch. 33.5 - Volume 04 Bonus Chapter", "Giant Ojou-sama")]
|
[InlineData("Giant Ojou-sama - Ch. 33.5 - Volume 04 Bonus Chapter", "Giant Ojou-sama")]
|
||||||
[InlineData("[218565]-(C92) [BRIO (Puyocha)] Mika-nee no Tanryoku Shidou - Mika s Guide to Self-Confidence (THE IDOLM@STE", "")]
|
[InlineData("[218565]-(C92) [BRIO (Puyocha)] Mika-nee no Tanryoku Shidou - Mika s Guide to Self-Confidence (THE IDOLM@STE", "")]
|
||||||
|
[InlineData("Monster #8 Ch. 001", "Monster #8")]
|
||||||
|
[InlineData("Zom 100 - Bucket List of the Dead v01", "Zom 100 - Bucket List of the Dead")]
|
||||||
public void ParseSeriesTest(string filename, string expected)
|
public void ParseSeriesTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename, LibraryType.Manga));
|
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename, LibraryType.Manga));
|
||||||
@ -304,6 +299,7 @@ public class MangaParsingTests
|
|||||||
[InlineData("เด็กคนนี้ขอลาออกจากการเป็นเจ้าของปราสาท เล่ม 1 ตอนที่ 3", "3")]
|
[InlineData("เด็กคนนี้ขอลาออกจากการเป็นเจ้าของปราสาท เล่ม 1 ตอนที่ 3", "3")]
|
||||||
[InlineData("Max Level Returner ตอนที่ 5", "5")]
|
[InlineData("Max Level Returner ตอนที่ 5", "5")]
|
||||||
[InlineData("หนึ่งความคิด นิจนิรันดร์ บทที่ 112", "112")]
|
[InlineData("หนึ่งความคิด นิจนิรันดร์ บทที่ 112", "112")]
|
||||||
|
[InlineData("Monster #8 Ch. 001", "1")]
|
||||||
public void ParseChaptersTest(string filename, string expected)
|
public void ParseChaptersTest(string filename, string expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseChapter(filename, LibraryType.Manga));
|
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseChapter(filename, LibraryType.Manga));
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static API.Services.Tasks.Scanner.Parser.Parser;
|
using static API.Services.Tasks.Scanner.Parser.Parser;
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace API.Tests.Repository;
|
namespace API.Tests.Repository;
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
|||||||
using API.Archive;
|
using API.Archive;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using EasyCaching.Core;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NetVips;
|
using NetVips;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Data.Common;
|
||||||
using System.Data.Common;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
using System.IO.Abstractions;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using EasyCaching.Core;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -92,18 +91,17 @@ public class BookServiceTests
|
|||||||
Assert.Equal("Georges Bizet \\(1838-1875\\)", comicInfo.Writer);
|
Assert.Equal("Georges Bizet \\(1838-1875\\)", comicInfo.Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get the file from microtherion
|
|
||||||
//[Fact]
|
//[Fact]
|
||||||
// public void ShouldUsePdfInfoDict()
|
public void ShouldUsePdfInfoDict()
|
||||||
// {
|
{
|
||||||
// var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Library/Books/PDFs");
|
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Library/Books/PDFs");
|
||||||
// var document = Path.Join(testDirectory, "Rollo at Work SP01.pdf");
|
var document = Path.Join(testDirectory, "Rollo at Work SP01.pdf");
|
||||||
// var comicInfo = _bookService.GetComicInfo(document);
|
var comicInfo = _bookService.GetComicInfo(document);
|
||||||
// Assert.NotNull(comicInfo);
|
Assert.NotNull(comicInfo);
|
||||||
// Assert.Equal("Rollo at Work", comicInfo.Title);
|
Assert.Equal("Rollo at Work", comicInfo.Title);
|
||||||
// Assert.Equal("Jacob Abbott", comicInfo.Writer);
|
Assert.Equal("Jacob Abbott", comicInfo.Writer);
|
||||||
// Assert.Equal(2008, comicInfo.Year);
|
Assert.Equal(2008, comicInfo.Year);
|
||||||
// }
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldHandleIndirectPdfObjects()
|
public void ShouldHandleIndirectPdfObjects()
|
||||||
|
@ -9,12 +9,9 @@ using API.Data.Repositories;
|
|||||||
using API.DTOs.Reader;
|
using API.DTOs.Reader;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Extensions;
|
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.SignalR;
|
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Data.Common;
|
||||||
using System.Data.Common;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Data.Metadata;
|
using API.Data.Metadata;
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
|
||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.DTOs.Filtering;
|
using API.DTOs.Filtering;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
|
@ -13,7 +13,6 @@ using API.Services;
|
|||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using Kavita.Common;
|
using Kavita.Common;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Constants;
|
using API.Constants;
|
||||||
@ -12,6 +11,7 @@ using API.Entities;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
using API.Entities.MetadataMatching;
|
using API.Entities.MetadataMatching;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.Services.Tasks.Metadata;
|
using API.Services.Tasks.Metadata;
|
||||||
@ -21,8 +21,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
|
||||||
using YamlDotNet.Serialization;
|
|
||||||
|
|
||||||
namespace API.Tests.Services;
|
namespace API.Tests.Services;
|
||||||
|
|
||||||
@ -31,17 +29,14 @@ namespace API.Tests.Services;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExternalMetadataServiceTests : AbstractDbTest
|
public class ExternalMetadataServiceTests : AbstractDbTest
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _testOutputHelper;
|
|
||||||
private readonly ExternalMetadataService _externalMetadataService;
|
private readonly ExternalMetadataService _externalMetadataService;
|
||||||
private readonly Dictionary<string, Genre> _genreLookup = new Dictionary<string, Genre>();
|
private readonly Dictionary<string, Genre> _genreLookup = new Dictionary<string, Genre>();
|
||||||
private readonly Dictionary<string, Tag> _tagLookup = new Dictionary<string, Tag>();
|
private readonly Dictionary<string, Tag> _tagLookup = new Dictionary<string, Tag>();
|
||||||
private readonly Dictionary<string, Person> _personLookup = new Dictionary<string, Person>();
|
private readonly Dictionary<string, Person> _personLookup = new Dictionary<string, Person>();
|
||||||
|
|
||||||
|
|
||||||
public ExternalMetadataServiceTests(ITestOutputHelper testOutputHelper)
|
public ExternalMetadataServiceTests()
|
||||||
{
|
{
|
||||||
_testOutputHelper = testOutputHelper;
|
|
||||||
|
|
||||||
// Set up Hangfire to use in-memory storage for testing
|
// Set up Hangfire to use in-memory storage for testing
|
||||||
GlobalConfiguration.Configuration.UseInMemoryStorage();
|
GlobalConfiguration.Configuration.UseInMemoryStorage();
|
||||||
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
using System.Drawing;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using EasyCaching.Core;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NetVips;
|
using NetVips;
|
||||||
using NSubstitute;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Image = NetVips.Image;
|
using Image = NetVips.Image;
|
||||||
|
|
||||||
@ -28,6 +23,7 @@ public class ImageServiceTests
|
|||||||
public void GenerateBaseline()
|
public void GenerateBaseline()
|
||||||
{
|
{
|
||||||
GenerateFiles(BaselinePattern);
|
GenerateFiles(BaselinePattern);
|
||||||
|
Assert.True(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -38,6 +34,7 @@ public class ImageServiceTests
|
|||||||
{
|
{
|
||||||
GenerateFiles(OutputPattern);
|
GenerateFiles(OutputPattern);
|
||||||
GenerateHtmlFile();
|
GenerateHtmlFile();
|
||||||
|
Assert.True(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateFiles(string outputExtension)
|
private void GenerateFiles(string outputExtension)
|
||||||
@ -159,7 +156,7 @@ public class ImageServiceTests
|
|||||||
|
|
||||||
// Step 4: Generate HTML file
|
// Step 4: Generate HTML file
|
||||||
GenerateHtmlFileForColorScape();
|
GenerateHtmlFileForColorScape();
|
||||||
|
Assert.True(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateColorImage(string hexColor, string outputPath)
|
private static void GenerateColorImage(string hexColor, string outputPath)
|
||||||
|
@ -1,29 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.Common;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
using System.IO.Abstractions;
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
|
||||||
using API.Data.Metadata;
|
using API.Data.Metadata;
|
||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
|
||||||
using API.Helpers.Builders;
|
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Tasks.Scanner;
|
using API.Services.Tasks.Scanner;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using API.Tests.Helpers;
|
using API.Tests.Helpers;
|
||||||
using AutoMapper;
|
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -391,7 +381,7 @@ public class ParseScannedFilesTests : AbstractDbTest
|
|||||||
var executionerAndHerWayOfLife = postLib.Series.First(x => x.Name == "The Executioner and Her Way of Life");
|
var executionerAndHerWayOfLife = postLib.Series.First(x => x.Name == "The Executioner and Her Way of Life");
|
||||||
Assert.Equal(2, executionerAndHerWayOfLife.Volumes.Count);
|
Assert.Equal(2, executionerAndHerWayOfLife.Volumes.Count);
|
||||||
|
|
||||||
Thread.Sleep(1100); // Ensure at least one second has passed since library scan
|
await Task.Delay(1100); // Ensure at least one second has passed since library scan
|
||||||
|
|
||||||
// Add a new chapter to a volume of the series, and scan. Validate that only, and all directories of this
|
// Add a new chapter to a volume of the series, and scan. Validate that only, and all directories of this
|
||||||
// series are marked as HasChanged
|
// series are marked as HasChanged
|
||||||
@ -440,7 +430,7 @@ public class ParseScannedFilesTests : AbstractDbTest
|
|||||||
var frieren = postLib.Series.First(x => x.Name == "Frieren - Beyond Journey's End");
|
var frieren = postLib.Series.First(x => x.Name == "Frieren - Beyond Journey's End");
|
||||||
Assert.Equal(2, frieren.Volumes.Count);
|
Assert.Equal(2, frieren.Volumes.Count);
|
||||||
|
|
||||||
Thread.Sleep(1100); // Ensure at least one second has passed since library scan
|
await Task.Delay(1100); // Ensure at least one second has passed since library scan
|
||||||
|
|
||||||
// Add a volume to a series, and scan. Ensure only this series is marked as HasChanged
|
// Add a volume to a series, and scan. Ensure only this series is marked as HasChanged
|
||||||
var executionerCopyDir = Path.Join(Path.Join(testDirectoryPath, "YenPress"), "The Executioner and Her Way of Life");
|
var executionerCopyDir = Path.Join(Path.Join(testDirectoryPath, "YenPress"), "The Executioner and Her Way of Life");
|
||||||
@ -483,7 +473,7 @@ public class ParseScannedFilesTests : AbstractDbTest
|
|||||||
|
|
||||||
// Needs to be actual time as the write time is now, so if we set LastFolderChecked in the past
|
// Needs to be actual time as the write time is now, so if we set LastFolderChecked in the past
|
||||||
// it'll always a scan as it was changed since the last scan.
|
// it'll always a scan as it was changed since the last scan.
|
||||||
Thread.Sleep(1100); // Ensure at least one second has passed since library scan
|
await Task.Delay(1100); // Ensure at least one second has passed since library scan
|
||||||
|
|
||||||
var res = await psf.ScanFiles(testDirectoryPath, true,
|
var res = await psf.ScanFiles(testDirectoryPath, true,
|
||||||
await _unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
|
await _unitOfWork.SeriesRepository.GetFolderPathMap(postLib.Id), postLib);
|
||||||
|
@ -1,19 +1,4 @@
|
|||||||
using System.IO;
|
namespace API.Tests.Services;
|
||||||
using API.Data;
|
|
||||||
using API.Data.Metadata;
|
|
||||||
using API.Entities;
|
|
||||||
using API.Entities.Enums;
|
|
||||||
using API.Helpers;
|
|
||||||
using API.Helpers.Builders;
|
|
||||||
using API.Services;
|
|
||||||
using API.Services.Tasks.Metadata;
|
|
||||||
using API.Services.Tasks.Scanner;
|
|
||||||
using API.SignalR;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NSubstitute;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace API.Tests.Services;
|
|
||||||
|
|
||||||
public class ProcessSeriesTests
|
public class ProcessSeriesTests
|
||||||
{
|
{
|
||||||
|
@ -1,25 +1,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.DTOs;
|
|
||||||
using API.DTOs.Progress;
|
using API.DTOs.Progress;
|
||||||
using API.DTOs.Reader;
|
using API.DTOs.Reader;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.Services.Tasks;
|
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using API.Tests.Helpers;
|
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.InMemory;
|
using Hangfire.InMemory;
|
||||||
|
@ -11,15 +11,11 @@ using API.DTOs.ReadingLists;
|
|||||||
using API.DTOs.ReadingLists.CBL;
|
using API.DTOs.ReadingLists.CBL;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Extensions;
|
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.Services.Tasks;
|
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using API.Tests.Helpers;
|
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -1,34 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using API.Data;
|
|
||||||
using API.Data.Metadata;
|
using API.Data.Metadata;
|
||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
|
||||||
using API.Helpers.Builders;
|
|
||||||
using API.Services;
|
|
||||||
using API.Services.Plus;
|
|
||||||
using API.Services.Tasks;
|
|
||||||
using API.Services.Tasks.Metadata;
|
|
||||||
using API.Services.Tasks.Scanner;
|
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
using API.SignalR;
|
|
||||||
using API.Tests.Helpers;
|
using API.Tests.Helpers;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NSubstitute;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using API.DTOs.SeriesDetail;
|
|||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
@ -809,6 +810,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.True(series.Metadata.Genres.Select(g1 => g1.Title).All(g2 => g2 == "New Genre".SentenceCase()));
|
Assert.True(series.Metadata.Genres.Select(g1 => g1.Title).All(g2 => g2 == "New Genre".SentenceCase()));
|
||||||
Assert.False(series.Metadata.GenresLocked); // GenreLocked is false unless the UI Explicitly says it should be locked
|
Assert.False(series.Metadata.GenresLocked); // GenreLocked is false unless the UI Explicitly says it should be locked
|
||||||
@ -847,6 +849,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
|
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
|
||||||
Assert.False(series.Metadata.PublisherLocked); // PublisherLocked is false unless the UI Explicitly says it should be locked
|
Assert.False(series.Metadata.PublisherLocked); // PublisherLocked is false unless the UI Explicitly says it should be locked
|
||||||
@ -887,6 +890,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
|
Assert.True(series.Metadata.People.Select(g => g.Person.Name).All(personName => personName == "Existing Person"));
|
||||||
Assert.True(series.Metadata.PublisherLocked);
|
Assert.True(series.Metadata.PublisherLocked);
|
||||||
@ -976,6 +980,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.False(series.Metadata.People.Any());
|
Assert.False(series.Metadata.People.Any());
|
||||||
}
|
}
|
||||||
@ -1010,6 +1015,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.True(series.Metadata.Genres.Select(g => g.Title).All(g => g == "Existing Genre".SentenceCase()));
|
Assert.True(series.Metadata.Genres.Select(g => g.Title).All(g => g == "Existing Genre".SentenceCase()));
|
||||||
Assert.True(series.Metadata.GenresLocked);
|
Assert.True(series.Metadata.GenresLocked);
|
||||||
@ -1039,6 +1045,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.Equal(0, series.Metadata.ReleaseYear);
|
Assert.Equal(0, series.Metadata.ReleaseYear);
|
||||||
Assert.False(series.Metadata.ReleaseYearLocked);
|
Assert.False(series.Metadata.ReleaseYearLocked);
|
||||||
@ -1071,6 +1078,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.Contains("New Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
Assert.Contains("New Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
||||||
Assert.False(series.Metadata.GenresLocked); // Ensure the lock is not activated unless specified.
|
Assert.False(series.Metadata.GenresLocked); // Ensure the lock is not activated unless specified.
|
||||||
@ -1104,6 +1112,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.DoesNotContain("Existing Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
Assert.DoesNotContain("Existing Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
||||||
Assert.Contains("New Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
Assert.Contains("New Genre".SentenceCase(), series.Metadata.Genres.Select(g => g.Title));
|
||||||
@ -1137,6 +1146,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.Empty(series.Metadata.Genres);
|
Assert.Empty(series.Metadata.Genres);
|
||||||
}
|
}
|
||||||
@ -1168,6 +1178,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.Contains("New Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
Assert.Contains("New Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
||||||
}
|
}
|
||||||
@ -1200,6 +1211,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.DoesNotContain("Existing Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
Assert.DoesNotContain("Existing Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
||||||
Assert.Contains("New Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
Assert.Contains("New Tag".SentenceCase(), series.Metadata.Tags.Select(t => t.Title));
|
||||||
@ -1233,6 +1245,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
Assert.True(success);
|
Assert.True(success);
|
||||||
|
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(s.Id);
|
||||||
|
Assert.NotNull(series);
|
||||||
Assert.NotNull(series.Metadata);
|
Assert.NotNull(series.Metadata);
|
||||||
Assert.Empty(series.Metadata.Tags);
|
Assert.Empty(series.Metadata.Tags);
|
||||||
}
|
}
|
||||||
@ -1431,6 +1444,7 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
addRelationDto.Sequels.Add(2);
|
addRelationDto.Sequels.Add(2);
|
||||||
await _seriesService.UpdateRelatedSeries(addRelationDto);
|
await _seriesService.UpdateRelatedSeries(addRelationDto);
|
||||||
Assert.NotNull(series1);
|
Assert.NotNull(series1);
|
||||||
|
Assert.NotNull(series2);
|
||||||
Assert.Equal(2, series1.Relations.Single(s => s.TargetSeriesId == 2).TargetSeriesId);
|
Assert.Equal(2, series1.Relations.Single(s => s.TargetSeriesId == 2).TargetSeriesId);
|
||||||
Assert.Equal(1, series2.Relations.Single(s => s.TargetSeriesId == 1).TargetSeriesId);
|
Assert.Equal(1, series2.Relations.Single(s => s.TargetSeriesId == 1).TargetSeriesId);
|
||||||
}
|
}
|
||||||
@ -1473,8 +1487,9 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
// Remove relations
|
// Remove relations
|
||||||
var removeRelationDto = CreateRelationsDto(series1);
|
var removeRelationDto = CreateRelationsDto(series1);
|
||||||
await _seriesService.UpdateRelatedSeries(removeRelationDto);
|
await _seriesService.UpdateRelatedSeries(removeRelationDto);
|
||||||
Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 1));
|
Assert.NotNull(series1);
|
||||||
Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 2));
|
Assert.DoesNotContain(series1.Relations, s => s.TargetSeriesId == 1);
|
||||||
|
Assert.DoesNotContain(series1.Relations, s => s.TargetSeriesId == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1507,6 +1522,8 @@ public class SeriesServiceTests : AbstractDbTest
|
|||||||
var addRelationDto = CreateRelationsDto(series1);
|
var addRelationDto = CreateRelationsDto(series1);
|
||||||
addRelationDto.Adaptations.Add(2);
|
addRelationDto.Adaptations.Add(2);
|
||||||
await _seriesService.UpdateRelatedSeries(addRelationDto);
|
await _seriesService.UpdateRelatedSeries(addRelationDto);
|
||||||
|
|
||||||
|
Assert.NotNull(series1);
|
||||||
Assert.Equal(2, series1.Relations.Single(s => s.TargetSeriesId == 2).TargetSeriesId);
|
Assert.Equal(2, series1.Relations.Single(s => s.TargetSeriesId == 2).TargetSeriesId);
|
||||||
|
|
||||||
_context.Series.Remove(await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(2));
|
_context.Series.Remove(await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(2));
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using API.Extensions;
|
using API.Helpers.Builders;
|
||||||
using API.Helpers.Builders;
|
|
||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.Services.Tasks;
|
|
||||||
|
|
||||||
namespace API.Tests.Services;
|
namespace API.Tests.Services;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -16,7 +14,6 @@ using API.Entities.Enums;
|
|||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using SignalR;
|
using SignalR;
|
||||||
using Helpers;
|
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
BIN
API.Tests/Services/Test Data/BookService/Rollo at Work SP01.pdf
Normal file
BIN
API.Tests/Services/Test Data/BookService/Rollo at Work SP01.pdf
Normal file
Binary file not shown.
@ -1,16 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.DTOs.Update;
|
using API.DTOs.Update;
|
||||||
using API.Extensions;
|
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Tasks;
|
using API.Services.Tasks;
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using Flurl.Http;
|
|
||||||
using Flurl.Http.Testing;
|
using Flurl.Http.Testing;
|
||||||
using Kavita.Common.EnvironmentInfo;
|
using Kavita.Common.EnvironmentInfo;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Abstractions;
|
|
||||||
using System.IO.Abstractions.TestingHelpers;
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -11,10 +10,8 @@ using API.Helpers;
|
|||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
using API.Services.Tasks;
|
|
||||||
using API.Services.Tasks.Metadata;
|
using API.Services.Tasks.Metadata;
|
||||||
using API.SignalR;
|
using API.SignalR;
|
||||||
using API.Tests.Helpers;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -98,10 +98,10 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.6.1" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.7.0" />
|
||||||
<PackageReference Include="System.IO.Abstractions" Version="22.0.12" />
|
<PackageReference Include="System.IO.Abstractions" Version="22.0.12" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="9.0.3" />
|
<PackageReference Include="System.Drawing.Common" Version="9.0.3" />
|
||||||
<PackageReference Include="VersOne.Epub" Version="3.3.2" />
|
<PackageReference Include="VersOne.Epub" Version="3.3.3" />
|
||||||
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using API.Data.Repositories;
|
|||||||
using API.DTOs;
|
using API.DTOs;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
|
@ -351,27 +351,6 @@ public class LibraryController : BaseApiController
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "RequireAdminRole")]
|
|
||||||
[HttpPost("analyze")]
|
|
||||||
public ActionResult Analyze(int libraryId)
|
|
||||||
{
|
|
||||||
_taskScheduler.AnalyzeFilesForLibrary(libraryId, true);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize(Policy = "RequireAdminRole")]
|
|
||||||
[HttpPost("analyze-multiple")]
|
|
||||||
public ActionResult AnalyzeMultiple(BulkActionDto dto)
|
|
||||||
{
|
|
||||||
foreach (var libraryId in dto.Ids)
|
|
||||||
{
|
|
||||||
_taskScheduler.AnalyzeFilesForLibrary(libraryId, dto.Force ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy the library settings (adv tab + optional type) to a set of other libraries.
|
/// Copy the library settings (adv tab + optional type) to a set of other libraries.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -9,7 +9,6 @@ using API.DTOs.ReadingLists;
|
|||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
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;
|
||||||
@ -39,7 +38,7 @@ public class ReadingListController : BaseApiController
|
|||||||
/// <param name="readingListId"></param>
|
/// <param name="readingListId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<ReadingListDto?>> GetList(int readingListId)
|
public async Task<ActionResult<ReadingListDto>> GetList(int readingListId)
|
||||||
{
|
{
|
||||||
var readingList = await _unitOfWork.ReadingListRepository.GetReadingListDtoByIdAsync(readingListId, User.GetUserId());
|
var readingList = await _unitOfWork.ReadingListRepository.GetReadingListDtoByIdAsync(readingListId, User.GetUserId());
|
||||||
if (readingList == null)
|
if (readingList == null)
|
||||||
@ -268,7 +267,7 @@ public class ReadingListController : BaseApiController
|
|||||||
var readingList = user.ReadingLists.SingleOrDefault(l => l.Id == dto.ReadingListId);
|
var readingList = user.ReadingLists.SingleOrDefault(l => l.Id == dto.ReadingListId);
|
||||||
if (readingList == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "reading-list-doesnt-exist"));
|
if (readingList == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "reading-list-doesnt-exist"));
|
||||||
var chapterIdsForSeries =
|
var chapterIdsForSeries =
|
||||||
await _unitOfWork.SeriesRepository.GetChapterIdsForSeriesAsync(new [] {dto.SeriesId});
|
await _unitOfWork.SeriesRepository.GetChapterIdsForSeriesAsync([dto.SeriesId]);
|
||||||
|
|
||||||
// If there are adds, tell tracking this has been modified
|
// If there are adds, tell tracking this has been modified
|
||||||
if (await _readingListService.AddChaptersToReadingList(dto.SeriesId, chapterIdsForSeries, readingList))
|
if (await _readingListService.AddChaptersToReadingList(dto.SeriesId, chapterIdsForSeries, readingList))
|
||||||
|
@ -54,7 +54,7 @@ public class ScrobblingController : BaseApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current user's MAL token & username
|
/// Get the current user's MAL token and username
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("mal-token")]
|
[HttpGet("mal-token")]
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace API.DTOs.Account;
|
namespace API.DTOs.Account;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public record UpdateUserDto
|
public record UpdateUserDto
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using API.Entities.Enums;
|
|||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Chapter is the lowest grouping of a reading medium. A Chapter contains a set of MangaFiles which represents the underlying
|
/// A Chapter is the lowest grouping of a reading medium. A Chapter contains a set of MangaFiles which represents the underlying
|
||||||
@ -188,8 +189,8 @@ public class ChapterDto : IHasReadTimeEstimate, IHasCoverImage
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public string CoverImage { get; set; }
|
public string CoverImage { get; set; }
|
||||||
public string PrimaryColor { get; set; }
|
public string PrimaryColor { get; set; } = string.Empty;
|
||||||
public string SecondaryColor { get; set; }
|
public string SecondaryColor { get; set; } = string.Empty;
|
||||||
|
|
||||||
public void ResetColorScape()
|
public void ResetColorScape()
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Collection;
|
namespace API.DTOs.Collection;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an Interest Stack from MAL
|
/// Represents an Interest Stack from MAL
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A primary and secondary color
|
/// A primary and secondary color
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using API.DTOs.Scrobbling;
|
using API.DTOs.Scrobbling;
|
||||||
|
|
||||||
namespace API.DTOs.KavitaPlus.ExternalMetadata;
|
namespace API.DTOs.KavitaPlus.ExternalMetadata;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for matching and fetching metadata on a series
|
/// Used for matching and fetching metadata on a series
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.DTOs.Scrobbling;
|
using API.DTOs.Scrobbling;
|
||||||
|
|
||||||
namespace API.DTOs.KavitaPlus.ExternalMetadata;
|
namespace API.DTOs.KavitaPlus.ExternalMetadata;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
internal class MatchSeriesRequestDto
|
internal class MatchSeriesRequestDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.KavitaPlus.License;
|
namespace API.DTOs.KavitaPlus.License;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class EncryptLicenseDto
|
public class EncryptLicenseDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.KavitaPlus.License;
|
namespace API.DTOs.KavitaPlus.License;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UpdateLicenseDto
|
public class UpdateLicenseDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.KavitaPlus.Metadata;
|
namespace API.DTOs.KavitaPlus.Metadata;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public enum CharacterRole
|
public enum CharacterRole
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class MangaFileDto
|
public class MangaFileDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class PersonDto
|
public class PersonDto
|
||||||
{
|
{
|
||||||
@ -6,12 +9,12 @@ public class PersonDto
|
|||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
|
|
||||||
public bool CoverImageLocked { get; set; }
|
public bool CoverImageLocked { get; set; }
|
||||||
public string PrimaryColor { get; set; }
|
public string? PrimaryColor { get; set; }
|
||||||
public string SecondaryColor { get; set; }
|
public string? SecondaryColor { get; set; }
|
||||||
|
|
||||||
public string? CoverImage { get; set; }
|
public string? CoverImage { get; set; }
|
||||||
|
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ASIN for person
|
/// ASIN for person
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UpdatePersonDto
|
public class UpdatePersonDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Reader;
|
namespace API.DTOs.Reader;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class CreatePersonalToCDto
|
public class CreatePersonalToCDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
|
|
||||||
namespace API.DTOs.Scrobbling;
|
namespace API.DTOs.Scrobbling;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public record MediaRecommendationDto
|
public record MediaRecommendationDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Scrobbling;
|
namespace API.DTOs.Scrobbling;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents information about a potential Series for Kavita+
|
/// Represents information about a potential Series for Kavita+
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace API.DTOs.Scrobbling;
|
namespace API.DTOs.Scrobbling;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ScrobbleEventDto
|
public class ScrobbleEventDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.DTOs.Recommendation;
|
using API.DTOs.Recommendation;
|
||||||
|
|
||||||
namespace API.DTOs.SeriesDetail;
|
namespace API.DTOs.SeriesDetail;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the data from Kavita+ for Series Detail
|
/// All the data from Kavita+ for Series Detail
|
||||||
|
@ -79,8 +79,8 @@ public class SeriesDto : IHasReadTimeEstimate, IHasCoverImage
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public string? CoverImage { get; set; }
|
public string? CoverImage { get; set; }
|
||||||
public string PrimaryColor { get; set; }
|
public string PrimaryColor { get; set; } = string.Empty;
|
||||||
public string SecondaryColor { get; set; }
|
public string SecondaryColor { get; set; } = string.Empty;
|
||||||
|
|
||||||
public void ResetColorScape()
|
public void ResetColorScape()
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ using API.Entities.Enums;
|
|||||||
using API.Services;
|
using API.Services;
|
||||||
|
|
||||||
namespace API.DTOs.Settings;
|
namespace API.DTOs.Settings;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ServerSettingDto
|
public class ServerSettingDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.SideNav;
|
namespace API.DTOs.SideNav;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class SideNavStreamDto
|
public class SideNavStreamDto
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used on Person Profile page
|
/// Used on Person Profile page
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace API.DTOs.Statistics;
|
namespace API.DTOs.Statistics;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a single User's reading event
|
/// Represents a single User's reading event
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace API.DTOs.Statistics;
|
namespace API.DTOs.Statistics;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UserReadStatistics
|
public class UserReadStatistics
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace API.DTOs.Stats;
|
namespace API.DTOs.Stats;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is just for the Server tab on UI
|
/// This is just for the Server tab on UI
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is explicitly for Tachiyomi. Number field was removed in v0.8.0, but Tachiyomi needs it for the hacks.
|
/// This is explicitly for Tachiyomi. Number field was removed in v0.8.0, but Tachiyomi needs it for the hacks.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UpdateSeriesDto
|
public class UpdateSeriesDto
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using API.Entities.Enums;
|
|||||||
using API.Entities.Enums.UserPreferences;
|
using API.Entities.Enums.UserPreferences;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UserPreferencesDto
|
public class UserPreferencesDto
|
||||||
{
|
{
|
||||||
|
@ -66,8 +66,8 @@ public class VolumeDto : IHasReadTimeEstimate, IHasCoverImage
|
|||||||
|
|
||||||
public string CoverImage { get; set; }
|
public string CoverImage { get; set; }
|
||||||
private bool CoverImageLocked { get; set; }
|
private bool CoverImageLocked { get; set; }
|
||||||
public string PrimaryColor { get; set; }
|
public string PrimaryColor { get; set; } = string.Empty;
|
||||||
public string SecondaryColor { get; set; }
|
public string SecondaryColor { get; set; } = string.Empty;
|
||||||
|
|
||||||
public void ResetColorScape()
|
public void ResetColorScape()
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@ using API.Entities.History;
|
|||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
using API.Entities.MetadataMatching;
|
using API.Entities.MetadataMatching;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Entities.Scrobble;
|
using API.Entities.Scrobble;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
@ -16,6 +16,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
public interface IAppUserProgressRepository
|
public interface IAppUserProgressRepository
|
||||||
{
|
{
|
||||||
void Update(AppUserProgress userProgress);
|
void Update(AppUserProgress userProgress);
|
||||||
@ -41,7 +42,7 @@ public interface IAppUserProgressRepository
|
|||||||
Task UpdateAllProgressThatAreMoreThanChapterPages();
|
Task UpdateAllProgressThatAreMoreThanChapterPages();
|
||||||
Task<IList<FullProgressDto>> GetUserProgressForChapter(int chapterId, int userId = 0);
|
Task<IList<FullProgressDto>> GetUserProgressForChapter(int chapterId, int userId = 0);
|
||||||
}
|
}
|
||||||
#nullable disable
|
|
||||||
public class AppUserProgressRepository : IAppUserProgressRepository
|
public class AppUserProgressRepository : IAppUserProgressRepository
|
||||||
{
|
{
|
||||||
private readonly DataContext _context;
|
private readonly DataContext _context;
|
||||||
|
@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.DTOs.CoverDb;
|
using API.DTOs.CoverDb;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
|
using API.Entities.Person;
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IGenreRepository
|
public interface IGenreRepository
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IMediaErrorRepository
|
public interface IMediaErrorRepository
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using API.DTOs;
|
using API.DTOs;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Extensions.QueryExtensions;
|
using API.Extensions.QueryExtensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
@ -14,6 +15,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IPersonRepository
|
public interface IPersonRepository
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ReadingListIncludes
|
public enum ReadingListIncludes
|
||||||
|
@ -12,6 +12,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IScrobbleRepository
|
public interface IScrobbleRepository
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum SeriesIncludes
|
public enum SeriesIncludes
|
||||||
|
@ -13,6 +13,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface ISettingsRepository
|
public interface ISettingsRepository
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface ISiteThemeRepository
|
public interface ISiteThemeRepository
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface ITagRepository
|
public interface ITagRepository
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum AppUserIncludes
|
public enum AppUserIncludes
|
||||||
|
@ -15,6 +15,7 @@ using Kavita.Common;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Data.Repositories;
|
namespace API.Data.Repositories;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum VolumeIncludes
|
public enum VolumeIncludes
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ public enum LibraryType
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses Comic regex for filename parsing
|
/// Uses Comic regex for filename parsing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("Comic")]
|
[Description("Comic (Legacy)")]
|
||||||
Comic = 1,
|
Comic = 1,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses Manga regex for filename parsing also uses epub metadata
|
/// Uses Manga regex for filename parsing also uses epub metadata
|
||||||
@ -30,8 +30,8 @@ public enum LibraryType
|
|||||||
[Description("Light Novel")]
|
[Description("Light Novel")]
|
||||||
LightNovel = 4,
|
LightNovel = 4,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses Comic regex for filename parsing, uses Comic Vine type of Parsing. Will replace Comic type in future
|
/// Uses Comic regex for filename parsing, uses Comic Vine type of Parsing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("Comic (Comic Vine)")]
|
[Description("Comic")]
|
||||||
ComicVine = 5,
|
ComicVine = 5,
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
|
using API.Entities.Person;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Entities.Metadata;
|
namespace API.Entities.Metadata;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.Entities;
|
namespace API.Entities.Person;
|
||||||
|
|
||||||
public class ChapterPeople
|
public class ChapterPeople
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using API.Entities.Enums;
|
|
||||||
using API.Entities.Interfaces;
|
using API.Entities.Interfaces;
|
||||||
using API.Entities.Metadata;
|
|
||||||
using API.Services.Plus;
|
|
||||||
|
|
||||||
namespace API.Entities;
|
namespace API.Entities.Person;
|
||||||
|
|
||||||
public class Person : IHasCoverImage
|
public class Person : IHasCoverImage
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
using API.Services.Plus;
|
|
||||||
|
|
||||||
namespace API.Entities;
|
namespace API.Entities.Person;
|
||||||
|
|
||||||
public class SeriesMetadataPeople
|
public class SeriesMetadataPeople
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@ using Kavita.Common;
|
|||||||
using Kavita.Common.EnvironmentInfo;
|
using Kavita.Common.EnvironmentInfo;
|
||||||
|
|
||||||
namespace API.Extensions;
|
namespace API.Extensions;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public static class FlurlExtensions
|
public static class FlurlExtensions
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ using API.Data.Misc;
|
|||||||
using API.Data.Repositories;
|
using API.Data.Repositories;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
using AutoMapper.QueryableExtensions;
|
using API.Entities.Person;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace API.Extensions.QueryExtensions.Filtering;
|
namespace API.Extensions.QueryExtensions.Filtering;
|
||||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using API.Data.Misc;
|
using API.Data.Misc;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
using API.Entities.Person;
|
||||||
|
|
||||||
namespace API.Extensions.QueryExtensions;
|
namespace API.Extensions.QueryExtensions;
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
@ -30,6 +30,7 @@ using API.Entities;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
using API.Entities.MetadataMatching;
|
using API.Entities.MetadataMatching;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Entities.Scrobble;
|
using API.Entities.Scrobble;
|
||||||
using API.Extensions.QueryExtensions.Filtering;
|
using API.Extensions.QueryExtensions.Filtering;
|
||||||
using API.Helpers.Converters;
|
using API.Helpers.Converters;
|
||||||
|
@ -4,6 +4,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
|
using API.Entities.Person;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Data;
|
using API.Data;
|
||||||
@ -7,6 +6,7 @@ using API.DTOs;
|
|||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
|
|
||||||
|
@ -56,9 +56,6 @@ public class Program
|
|||||||
Configuration.JwtToken = Convert.ToBase64String(rBytes).Replace("/", string.Empty);
|
Configuration.JwtToken = Convert.ToBase64String(rBytes).Replace("/", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration.KavitaPlusApiUrl = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == Environments.Development
|
|
||||||
? "http://localhost:5020" : "https://plus.kavitareader.com";
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var host = CreateHostBuilder(args).Build();
|
var host = CreateHostBuilder(args).Build();
|
||||||
|
@ -73,12 +73,27 @@ public class BookService : IBookService
|
|||||||
private const string BookApiUrl = "book-resources?file=";
|
private const string BookApiUrl = "book-resources?file=";
|
||||||
private readonly PdfComicInfoExtractor _pdfComicInfoExtractor;
|
private readonly PdfComicInfoExtractor _pdfComicInfoExtractor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setup the most lenient book parsing options possible as people have some really bad epubs
|
||||||
|
/// </summary>
|
||||||
public static readonly EpubReaderOptions BookReaderOptions = new()
|
public static readonly EpubReaderOptions BookReaderOptions = new()
|
||||||
{
|
{
|
||||||
PackageReaderOptions = new PackageReaderOptions
|
PackageReaderOptions = new PackageReaderOptions
|
||||||
{
|
{
|
||||||
IgnoreMissingToc = true,
|
IgnoreMissingToc = true,
|
||||||
SkipInvalidManifestItems = true
|
SkipInvalidManifestItems = true,
|
||||||
|
},
|
||||||
|
Epub2NcxReaderOptions = new Epub2NcxReaderOptions
|
||||||
|
{
|
||||||
|
IgnoreMissingContentForNavigationPoints = true
|
||||||
|
},
|
||||||
|
SpineReaderOptions = new SpineReaderOptions
|
||||||
|
{
|
||||||
|
IgnoreMissingManifestItems = true
|
||||||
|
},
|
||||||
|
BookCoverReaderOptions = new BookCoverReaderOptions
|
||||||
|
{
|
||||||
|
Epub2MetadataIgnoreMissingManifestItem = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using API.DTOs.SeriesDetail;
|
|||||||
using API.Entities;
|
using API.Entities;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Entities.Metadata;
|
using API.Entities.Metadata;
|
||||||
|
using API.Entities.Person;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
@ -73,7 +74,7 @@ public class SeriesService : ISeriesService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the first chapter for a series to extract metadata from (ie Summary, etc)
|
/// Returns the first chapter for a series to extract metadata from (ie Summary, etc.)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="series">The full series with all volumes and chapters on it</param>
|
/// <param name="series">The full series with all volumes and chapters on it</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@ -915,19 +916,19 @@ public class SeriesService : ISeriesService
|
|||||||
// Calculate the time differences between consecutive chapters
|
// Calculate the time differences between consecutive chapters
|
||||||
var timeDifferences = new List<TimeSpan>();
|
var timeDifferences = new List<TimeSpan>();
|
||||||
DateTime? previousChapterTime = null;
|
DateTime? previousChapterTime = null;
|
||||||
foreach (var chapter in chapters)
|
foreach (var chapterCreatedUtc in chapters.Select(c => c.CreatedUtc))
|
||||||
{
|
{
|
||||||
if (previousChapterTime.HasValue && (chapter.CreatedUtc - previousChapterTime.Value) <= TimeSpan.FromHours(1))
|
if (previousChapterTime.HasValue && (chapterCreatedUtc - previousChapterTime.Value) <= TimeSpan.FromHours(1))
|
||||||
{
|
{
|
||||||
continue; // Skip this chapter if it's within an hour of the previous one
|
continue; // Skip this chapter if it's within an hour of the previous one
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((chapter.CreatedUtc - previousChapterTime ?? TimeSpan.Zero) != TimeSpan.Zero)
|
if ((chapterCreatedUtc - previousChapterTime ?? TimeSpan.Zero) != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
timeDifferences.Add(chapter.CreatedUtc - previousChapterTime ?? TimeSpan.Zero);
|
timeDifferences.Add(chapterCreatedUtc - previousChapterTime ?? TimeSpan.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
previousChapterTime = chapter.CreatedUtc;
|
previousChapterTime = chapterCreatedUtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeDifferences.Count < minimumTimeDeltas)
|
if (timeDifferences.Count < minimumTimeDeltas)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user