mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
.NET 8 Update (#2496)
This commit is contained in:
parent
6d4d2d4a7f
commit
b838fd53e5
2
.github/workflows/build-and-test.yml
vendored
2
.github/workflows/build-and-test.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install Swashbuckle CLI
|
||||
shell: powershell
|
||||
|
4
.github/workflows/canary-workflow.yml
vendored
4
.github/workflows/canary-workflow.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Bump versions
|
||||
uses: SiqiLu/dotnet-bump-version@2.0.0
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
- name: Compile dotnet app
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install Swashbuckle CLI
|
||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
||||
|
4
.github/workflows/develop-workflow.yml
vendored
4
.github/workflows/develop-workflow.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Bump versions
|
||||
uses: majora2007/dotnet-bump-version@v0.0.10
|
||||
@ -131,7 +131,7 @@ jobs:
|
||||
- name: Compile dotnet app
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Install Swashbuckle CLI
|
||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
||||
|
2
.github/workflows/release-workflow.yml
vendored
2
.github/workflows/release-workflow.yml
vendored
@ -119,7 +119,7 @@ jobs:
|
||||
- name: Compile dotnet app
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Install Swashbuckle CLI
|
||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
||||
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.10" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
|
||||
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.11" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="19.2.69" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="19.2.69" />
|
||||
<PackageReference Include="xunit" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="20.0.4" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="20.0.4" />
|
||||
<PackageReference Include="xunit" Version="2.6.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<AnalysisMode>Default</AnalysisMode>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
@ -53,7 +53,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@ -72,38 +72,37 @@
|
||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
||||
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
|
||||
<PackageReference Include="NetVips" Version="2.3.1" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.14.5" />
|
||||
<PackageReference Include="NetVips" Version="2.4.0" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.15.0" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.7" />
|
||||
<PackageReference Include="Serilog" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.AspNetCore.SignalR" Version="0.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||
<PackageReference Include="SharpCompress" Version="0.34.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.12.0.78982">
|
||||
<PackageReference Include="SharpCompress" Version="0.34.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.1" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.69" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="20.0.4" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
||||
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -40,15 +40,13 @@ public class LibraryController : BaseApiController
|
||||
private readonly IEventHub _eventHub;
|
||||
private readonly ILibraryWatcher _libraryWatcher;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IStreamService _streamService;
|
||||
private readonly IEasyCachingProvider _libraryCacheProvider;
|
||||
private const string CacheKey = "library_";
|
||||
|
||||
public LibraryController(IDirectoryService directoryService,
|
||||
ILogger<LibraryController> logger, IMapper mapper, ITaskScheduler taskScheduler,
|
||||
IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher,
|
||||
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService,
|
||||
IStreamService streamService)
|
||||
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService)
|
||||
{
|
||||
_directoryService = directoryService;
|
||||
_logger = logger;
|
||||
@ -58,7 +56,6 @@ public class LibraryController : BaseApiController
|
||||
_eventHub = eventHub;
|
||||
_libraryWatcher = libraryWatcher;
|
||||
_localizationService = localizationService;
|
||||
_streamService = streamService;
|
||||
|
||||
_libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library);
|
||||
}
|
||||
@ -157,7 +154,7 @@ public class LibraryController : BaseApiController
|
||||
}));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(path)) return Ok(_directoryService.ListDirectory(Path.GetDirectoryName(path)));
|
||||
if (!Directory.Exists(path)) return Ok(_directoryService.ListDirectory(Path.GetDirectoryName(path)!));
|
||||
|
||||
return Ok(_directoryService.ListDirectory(path));
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public class OpdsController : BaseApiController
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var (_, prefix) = await GetPrefix();
|
||||
|
||||
var feed = CreateFeed("Kavita", string.Empty, apiKey, prefix);
|
||||
SetFeedId(feed, "root");
|
||||
@ -141,10 +141,37 @@ public class OpdsController : BaseApiController
|
||||
});
|
||||
break;
|
||||
case DashboardStreamType.RecentlyUpdated:
|
||||
// TODO: See if we can implement this and use (count) on series name for number of updates
|
||||
feed.Entries.Add(new FeedEntry()
|
||||
{
|
||||
Id = "recentlyUpdated",
|
||||
Title = await _localizationService.Translate(userId, "recently-updated"),
|
||||
Content = new FeedEntryContent()
|
||||
{
|
||||
Text = await _localizationService.Translate(userId, "browse-recently-updated")
|
||||
},
|
||||
Links = new List<FeedLink>()
|
||||
{
|
||||
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/recently-updated"),
|
||||
}
|
||||
});
|
||||
break;
|
||||
case DashboardStreamType.MoreInGenre:
|
||||
// TODO: See if we can implement this
|
||||
var randomGenre = await _unitOfWork.GenreRepository.GetRandomGenre();
|
||||
if (randomGenre == null) break;
|
||||
|
||||
feed.Entries.Add(new FeedEntry()
|
||||
{
|
||||
Id = "moreInGenre",
|
||||
Title = await _localizationService.Translate(userId, "more-in-genre", randomGenre.Title),
|
||||
Content = new FeedEntryContent()
|
||||
{
|
||||
Text = await _localizationService.Translate(userId, "browse-more-in-genre", randomGenre.Title)
|
||||
},
|
||||
Links = new List<FeedLink>()
|
||||
{
|
||||
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/more-in-genre?genreId={randomGenre.Id}"),
|
||||
}
|
||||
});
|
||||
break;
|
||||
case DashboardStreamType.SmartFilter:
|
||||
|
||||
@ -632,6 +659,61 @@ public class OpdsController : BaseApiController
|
||||
return CreateXmlResult(SerializeXml(feed));
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/more-in-genre")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<IActionResult> GetMoreInGenre(string apiKey, [FromQuery] int genreId, [FromQuery] int pageNumber = 1)
|
||||
{
|
||||
var userId = await GetUser(apiKey);
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var genre = await _unitOfWork.GenreRepository.GetGenreById(genreId);
|
||||
var seriesDtos = await _unitOfWork.SeriesRepository.GetMoreIn(userId, 0, genreId, GetUserParams(pageNumber));
|
||||
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.Id));
|
||||
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "more-in-genre", genre.Title), $"{prefix}{apiKey}/more-in-genre", apiKey, prefix);
|
||||
SetFeedId(feed, "more-in-genre");
|
||||
AddPagination(feed, seriesDtos, $"{prefix}{apiKey}/more-in-genre");
|
||||
|
||||
foreach (var seriesDto in seriesDtos)
|
||||
{
|
||||
feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl));
|
||||
}
|
||||
|
||||
return CreateXmlResult(SerializeXml(feed));
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/recently-updated")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<IActionResult> GetRecentlyUpdated(string apiKey, [FromQuery] int pageNumber = 1)
|
||||
{
|
||||
var userId = await GetUser(apiKey);
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var seriesDtos = (await _unitOfWork.SeriesRepository.GetRecentlyUpdatedSeries(userId, PageSize)).ToList();
|
||||
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(seriesDtos.Select(s => s.SeriesId));
|
||||
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "recently-updated"), $"{prefix}{apiKey}/recently-updated", apiKey, prefix);
|
||||
SetFeedId(feed, "recently-updated");
|
||||
//AddPagination(feed, seriesDtos, $"{prefix}{apiKey}/recently-updated");
|
||||
|
||||
foreach (var groupedSeries in seriesDtos)
|
||||
{
|
||||
var seriesDto = new SeriesDto()
|
||||
{
|
||||
Name = $"{groupedSeries.SeriesName} ({groupedSeries.Count})",
|
||||
Id = groupedSeries.SeriesId,
|
||||
Format = groupedSeries.Format,
|
||||
LibraryId = groupedSeries.LibraryId,
|
||||
};
|
||||
var metadata = seriesMetadatas.First(s => s.SeriesId == seriesDto.Id);
|
||||
feed.Entries.Add(CreateSeries(seriesDto, metadata, apiKey, prefix, baseUrl));
|
||||
}
|
||||
|
||||
return CreateXmlResult(SerializeXml(feed));
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/on-deck")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<IActionResult> GetOnDeck(string apiKey, [FromQuery] int pageNumber = 1)
|
||||
@ -1161,7 +1243,7 @@ public class OpdsController : BaseApiController
|
||||
var userId = await GetUser(apiKey);
|
||||
var progress = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(chapterId, userId);
|
||||
|
||||
// TODO: Type could be wrong
|
||||
// NOTE: Type could be wrong, there is nothing I can do in the spec
|
||||
var link = CreateLink(FeedLinkRelation.Stream, "image/jpeg",
|
||||
$"{prefix}{apiKey}/image?libraryId={libraryId}&seriesId={seriesId}&volumeId={volumeId}&chapterId={chapterId}&pageNumber=" + "{pageNumber}");
|
||||
link.TotalPages = mangaFile.Pages;
|
||||
|
@ -19,13 +19,11 @@ public class StreamController : BaseApiController
|
||||
{
|
||||
private readonly IStreamService _streamService;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ILogger<StreamController> _logger;
|
||||
|
||||
public StreamController(IStreamService streamService, IUnitOfWork unitOfWork, ILogger<StreamController> logger)
|
||||
public StreamController(IStreamService streamService, IUnitOfWork unitOfWork)
|
||||
{
|
||||
_streamService = streamService;
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -53,7 +53,7 @@ public class ThemeController : BaseApiController
|
||||
{
|
||||
await _themeService.UpdateDefault(dto.ThemeId);
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
catch (KavitaException)
|
||||
{
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "theme-doesnt-exist"));
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
namespace API.DTOs.Account;
|
||||
#nullable enable
|
||||
|
||||
public class LoginDto
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Filtering;
|
||||
#nullable enable
|
||||
|
||||
public class FilterDto
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace API.DTOs.Filtering;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range between two int/float/double
|
||||
/// </summary>
|
||||
|
@ -1,9 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace API.DTOs.Filtering.v2;
|
||||
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Metadata filtering for v2 API only
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs;
|
||||
#nullable enable
|
||||
|
||||
public class LibraryDto
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using API.DTOs.Account;
|
||||
|
||||
namespace API.DTOs;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Represents a member of a Kavita server.
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Metadata;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Exclusively metadata about a given chapter
|
||||
|
@ -1,6 +1,7 @@
|
||||
using API.Services.Plus;
|
||||
|
||||
namespace API.DTOs;
|
||||
#nullable enable
|
||||
|
||||
public class RatingDto
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace API.DTOs.Reader;
|
||||
#nullable enable
|
||||
|
||||
public class BookmarkDto
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Reader;
|
||||
#nullable enable
|
||||
|
||||
public class BookmarkInfoDto
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Reader;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Information about the Chapter for the Reader to render
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace API.DTOs.Reader;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class PersonalToCDto
|
||||
{
|
||||
public required int ChapterId { get; set; }
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.DTOs.Scrobbling;
|
||||
|
||||
namespace API.DTOs.Recommendation;
|
||||
#nullable enable
|
||||
|
||||
public class ExternalSeriesDetailDto
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
namespace API.DTOs.Recommendation;
|
||||
#nullable enable
|
||||
|
||||
public class SeriesStaffDto
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
namespace API.DTOs.Scrobbling;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Response from Kavita+ Scrobble API
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace API.DTOs.SeriesDetail;
|
||||
#nullable enable
|
||||
|
||||
public class UpdateUserReviewDto
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using API.Services.Plus;
|
||||
|
||||
namespace API.DTOs.SeriesDetail;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Represents a User Review for a given Series
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Statistics;
|
||||
#nullable enable
|
||||
|
||||
public class FileExtensionDto
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
namespace API.DTOs.Statistics;
|
||||
#nullable enable
|
||||
|
||||
public class TopReadDto
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Stats;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about a Kavita Installation
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs;
|
||||
#nullable enable
|
||||
|
||||
public class UpdateRbsDto
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using API.DTOs.Account;
|
||||
|
||||
namespace API.DTOs;
|
||||
#nullable enable
|
||||
|
||||
public class UserDto
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ using Kavita.Common.Extensions;
|
||||
using Nager.ArticleNumber;
|
||||
|
||||
namespace API.Data.Metadata;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// A representation of a ComicInfo.xml file
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.Metadata;
|
||||
@ -22,6 +23,8 @@ public interface IGenreRepository
|
||||
Task RemoveAllGenreNoLongerAssociated(bool removeExternal = false);
|
||||
Task<IList<GenreTagDto>> GetAllGenreDtosForLibrariesAsync(IList<int> libraryIds, int userId);
|
||||
Task<int> GetCountAsync();
|
||||
Task<GenreTagDto> GetRandomGenre();
|
||||
Task<GenreTagDto> GetGenreById(int id);
|
||||
}
|
||||
|
||||
public class GenreRepository : IGenreRepository
|
||||
@ -92,6 +95,27 @@ public class GenreRepository : IGenreRepository
|
||||
return await _context.Genre.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<GenreTagDto> GetRandomGenre()
|
||||
{
|
||||
var genreCount = await GetCountAsync();
|
||||
if (genreCount == 0) return null;
|
||||
|
||||
var randomIndex = new Random().Next(0, genreCount);
|
||||
return await _context.Genre
|
||||
.Skip(randomIndex)
|
||||
.Take(1)
|
||||
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<GenreTagDto> GetGenreById(int id)
|
||||
{
|
||||
return await _context.Genre
|
||||
.Where(g => g.Id == id)
|
||||
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<Genre>> GetAllGenresAsync()
|
||||
{
|
||||
return await _context.Genre.ToListAsync();
|
||||
|
@ -17,7 +17,7 @@ public static class FileTypeGroupExtensions
|
||||
case FileTypeGroup.Pdf:
|
||||
return Parser.PdfFileExtension;
|
||||
case FileTypeGroup.Images:
|
||||
return Parser.ImageFileExtensions;;
|
||||
return Parser.ImageFileExtensions;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(fileTypeGroup), fileTypeGroup, null);
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ public static class HttpExtensions
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
response.Headers.Add("Pagination", JsonSerializer.Serialize(paginationHeader, options));
|
||||
response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
|
||||
response.Headers.Append("Pagination", JsonSerializer.Serialize(paginationHeader, options));
|
||||
response.Headers.Append("Access-Control-Expose-Headers", "Pagination");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -33,7 +33,7 @@ public static class HttpExtensions
|
||||
public static void AddCacheHeader(this HttpResponse response, byte[] content)
|
||||
{
|
||||
if (content is not {Length: > 0}) return;
|
||||
response.Headers.Add(HeaderNames.ETag, string.Concat(SHA256.HashData(content).Select(x => x.ToString("X2"))));
|
||||
response.Headers.Append(HeaderNames.ETag, string.Concat(SHA256.HashData(content).Select(x => x.ToString("X2"))));
|
||||
response.Headers.CacheControl = $"private,max-age=100";
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ public static class HttpExtensions
|
||||
{
|
||||
if (filename is not {Length: > 0}) return;
|
||||
var hashContent = filename + File.GetLastWriteTimeUtc(filename);
|
||||
response.Headers.Add("ETag", string.Concat(SHA256.HashData(Encoding.UTF8.GetBytes(hashContent)).Select(x => x.ToString("X2"))));
|
||||
response.Headers.Append("ETag", string.Concat(SHA256.HashData(Encoding.UTF8.GetBytes(hashContent)).Select(x => x.ToString("X2"))));
|
||||
if (maxAge != 10)
|
||||
{
|
||||
response.Headers.CacheControl = $"max-age={maxAge}";
|
||||
|
@ -22,7 +22,7 @@ public static class SeriesFilter
|
||||
switch (comparison)
|
||||
{
|
||||
case FilterComparison.Equal:
|
||||
return queryable.Where(s => s.Metadata.Language.Equals(languages.First()));
|
||||
return queryable.Where(s => s.Metadata.Language.Equals(languages[0]));
|
||||
case FilterComparison.Contains:
|
||||
return queryable.Where(s => languages.Contains(s.Metadata.Language));
|
||||
case FilterComparison.MustContains:
|
||||
@ -30,9 +30,9 @@ public static class SeriesFilter
|
||||
case FilterComparison.NotContains:
|
||||
return queryable.Where(s => !languages.Contains(s.Metadata.Language));
|
||||
case FilterComparison.NotEqual:
|
||||
return queryable.Where(s => !s.Metadata.Language.Equals(languages.First()));
|
||||
return queryable.Where(s => !s.Metadata.Language.Equals(languages[0]));
|
||||
case FilterComparison.Matches:
|
||||
return queryable.Where(s => EF.Functions.Like(s.Metadata.Language, $"{languages.First()}%"));
|
||||
return queryable.Where(s => EF.Functions.Like(s.Metadata.Language, $"{languages[0]}%"));
|
||||
case FilterComparison.GreaterThan:
|
||||
case FilterComparison.GreaterThanEqual:
|
||||
case FilterComparison.LessThan:
|
||||
|
@ -6,8 +6,9 @@ namespace API.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
private static readonly Regex SentenceCaseRegex = new Regex(@"(^[a-z])|\.\s+(.)",
|
||||
RegexOptions.ExplicitCapture | RegexOptions.Compiled, Services.Tasks.Scanner.Parser.Parser.RegexTimeout);
|
||||
private static readonly Regex SentenceCaseRegex = new(@"(^[a-z])|\.\s+(.)",
|
||||
RegexOptions.ExplicitCapture | RegexOptions.Compiled,
|
||||
Services.Tasks.Scanner.Parser.Parser.RegexTimeout);
|
||||
|
||||
public static string SentenceCase(this string value)
|
||||
{
|
||||
@ -21,17 +22,16 @@ public static class StringExtensions
|
||||
/// <returns></returns>
|
||||
public static string ToNormalized(this string? value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return string.Empty;
|
||||
return Services.Tasks.Scanner.Parser.Parser.Normalize(value);
|
||||
return string.IsNullOrEmpty(value) ? string.Empty : Services.Tasks.Scanner.Parser.Parser.Normalize(value);
|
||||
}
|
||||
|
||||
public static float AsFloat(this string value)
|
||||
public static float AsFloat(this string? value, float defaultValue = 0.0f)
|
||||
{
|
||||
return float.Parse(value, CultureInfo.InvariantCulture);
|
||||
return string.IsNullOrEmpty(value) ? defaultValue : float.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static double AsDouble(this string value)
|
||||
public static double AsDouble(this string? value, double defaultValue = 0.0f)
|
||||
{
|
||||
return double.Parse(value, CultureInfo.InvariantCulture);
|
||||
return string.IsNullOrEmpty(value) ? defaultValue : double.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using API.Entities;
|
||||
using Kavita.Common;
|
||||
|
||||
namespace API.Helpers.Builders;
|
||||
#nullable enable
|
||||
|
||||
public class AppUserBuilder : IEntityBuilder<AppUser>
|
||||
{
|
||||
|
@ -6,6 +6,7 @@ using API.Entities.Enums;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
|
||||
namespace API.Helpers.Builders;
|
||||
#nullable enable
|
||||
|
||||
public class ChapterBuilder : IEntityBuilder<Chapter>
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using API.Entities.Scrobble;
|
||||
|
||||
namespace API.Helpers.Builders;
|
||||
#nullable enable
|
||||
|
||||
public class ScrobbleHoldBuilder : IEntityBuilder<ScrobbleHold>
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ public static class ParserInfoHelpers
|
||||
}
|
||||
}
|
||||
|
||||
if (series.Format == MangaFormat.Unknown && format != MangaFormat.Unknown)
|
||||
if (series.Format == MangaFormat.Unknown)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -130,8 +130,9 @@ public static class SmartFilterHelper
|
||||
private static SortOptions DecodeSortOptions(string encodedSortOptions)
|
||||
{
|
||||
var parts = Uri.UnescapeDataString(encodedSortOptions).Split(InnerStatementSeparator);
|
||||
var sortFieldPart = parts.FirstOrDefault(part => part.StartsWith(SortFieldKey));
|
||||
var isAscendingPart = parts.FirstOrDefault(part => part.StartsWith(IsAscendingKey));
|
||||
|
||||
var sortFieldPart = Array.Find(parts, part => part.StartsWith(SortFieldKey));
|
||||
var isAscendingPart = Array.Find(parts, part => part.StartsWith(IsAscendingKey));
|
||||
|
||||
var isAscending = isAscendingPart?.Trim().Replace(IsAscendingKey, string.Empty).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
if (sortFieldPart == null)
|
||||
|
@ -151,6 +151,10 @@
|
||||
"browse-libraries": "Browse by Libraries",
|
||||
"collections": "All Collections",
|
||||
"browse-collections": "Browse by Collections",
|
||||
"more-in-genre": "More in Genre {0}",
|
||||
"browse-more-in-genre": "Browse more in {0}",
|
||||
"recently-updated": "Recently Updated",
|
||||
"browse-recently-updated": "Browse Recently Updated",
|
||||
"smart-filters": "Smart Filters",
|
||||
"external-sources": "External Sources",
|
||||
"browse-external-sources": "Browse External Sources",
|
||||
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
|
||||
namespace API.Middleware.RateLimit;
|
||||
#nullable enable
|
||||
|
||||
public class AuthenticationRateLimiterPolicy : IRateLimiterPolicy<string>
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ using Serilog.Events;
|
||||
using Serilog.Sinks.AspNetCore.SignalR.Extensions;
|
||||
|
||||
namespace API;
|
||||
#nullable enable
|
||||
|
||||
public class Program
|
||||
{
|
||||
|
@ -199,7 +199,6 @@ public class BookService : IBookService
|
||||
}
|
||||
if (!book.Content.AllFiles.TryGetLocalFileRefByKey(key, out var bookFile)) continue;
|
||||
|
||||
//var bookFile = book.Content.AllFiles.Local[key];
|
||||
var content = await bookFile.ReadContentAsBytesAsync();
|
||||
importBuilder.Append(Encoding.UTF8.GetString(content));
|
||||
}
|
||||
@ -555,7 +554,6 @@ public class BookService : IBookService
|
||||
// If this is a single book and not a collection, set publication status to Completed
|
||||
if (string.IsNullOrEmpty(info.Volume) && Parser.ParseVolume(filePath).Equals(Parser.DefaultVolume))
|
||||
{
|
||||
//info.Number = "1";
|
||||
info.Count = 1;
|
||||
}
|
||||
|
||||
@ -938,8 +936,9 @@ public class BookService : IBookService
|
||||
/// <param name="mappings"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
private static string CoalesceKey(EpubBookRef book, IReadOnlyDictionary<string, int> mappings, string key)
|
||||
private static string? CoalesceKey(EpubBookRef book, IReadOnlyDictionary<string, int> mappings, string? key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key)) return key;
|
||||
if (mappings.ContainsKey(CleanContentKeys(key))) return key;
|
||||
|
||||
// Fallback to searching for key (bad epub metadata)
|
||||
@ -949,7 +948,7 @@ public class BookService : IBookService
|
||||
key = correctedKey;
|
||||
}
|
||||
|
||||
var stepsBack = CountParentDirectory(book.Content.NavigationHtmlFile?.FilePath); // FileName -> FilePath
|
||||
var stepsBack = CountParentDirectory(book.Content.NavigationHtmlFile?.FilePath);
|
||||
if (mappings.TryGetValue(key, out _))
|
||||
{
|
||||
return key;
|
||||
|
@ -644,10 +644,10 @@ public class DirectoryService : IDirectoryService
|
||||
/// Scans a directory by utilizing a recursive folder search. If a .kavitaignore file is found, will ignore matching patterns
|
||||
/// </summary>
|
||||
/// <param name="folderPath"></param>
|
||||
/// <param name="supportedExtensions"></param>
|
||||
/// <param name="fileTypes"></param>
|
||||
/// <param name="matcher"></param>
|
||||
/// <returns></returns>
|
||||
public IList<string> ScanFiles(string folderPath, string supportedExtensions, GlobMatcher? matcher = null)
|
||||
public IList<string> ScanFiles(string folderPath, string fileTypes, GlobMatcher? matcher = null)
|
||||
{
|
||||
_logger.LogDebug("[ScanFiles] called on {Path}", folderPath);
|
||||
var files = new List<string>();
|
||||
@ -668,19 +668,19 @@ public class DirectoryService : IDirectoryService
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
files.AddRange(ScanFiles(directory, supportedExtensions, matcher));
|
||||
files.AddRange(ScanFiles(directory, fileTypes, matcher));
|
||||
}
|
||||
|
||||
|
||||
// Get the matcher from either ignore or global (default setup)
|
||||
if (matcher == null)
|
||||
{
|
||||
files.AddRange(GetFilesWithCertainExtensions(folderPath, supportedExtensions));
|
||||
files.AddRange(GetFilesWithCertainExtensions(folderPath, fileTypes));
|
||||
}
|
||||
else
|
||||
{
|
||||
var foundFiles = GetFilesWithCertainExtensions(folderPath,
|
||||
supportedExtensions)
|
||||
fileTypes)
|
||||
.Where(file => !matcher.ExcludeMatches(FileSystem.FileInfo.New(file).Name));
|
||||
files.AddRange(foundFiles);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services;
|
||||
#nullable enable
|
||||
|
||||
public interface IEmailService
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using Hangfire;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services;
|
||||
#nullable enable
|
||||
|
||||
public interface IMetadataService
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ internal class ExternalMetadataIdsDto
|
||||
|
||||
public interface IExternalMetadataService
|
||||
{
|
||||
Task<ExternalSeriesDetailDto> GetExternalSeriesDetail(int? aniListId, long? malId, int? seriesId);
|
||||
Task<ExternalSeriesDetailDto?> GetExternalSeriesDetail(int? aniListId, long? malId, int? seriesId);
|
||||
}
|
||||
|
||||
public class ExternalMetadataService : IExternalMetadataService
|
||||
|
@ -22,6 +22,7 @@ using Kavita.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services;
|
||||
#nullable enable
|
||||
|
||||
public interface IReaderService
|
||||
{
|
||||
|
@ -21,6 +21,7 @@ using Kavita.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services;
|
||||
#nullable enable
|
||||
|
||||
public interface IReadingListService
|
||||
{
|
||||
|
@ -399,7 +399,7 @@ public class ParseScannedFiles
|
||||
/// World of Acceleration v02.cbz having Series "Accel World" and Localized Series of "World of Acceleration"
|
||||
/// </example>
|
||||
/// <param name="infos">A collection of ParserInfos</param>
|
||||
private void MergeLocalizedSeriesWithSeries(IReadOnlyCollection<ParserInfo> infos)
|
||||
private void MergeLocalizedSeriesWithSeries(IReadOnlyCollection<ParserInfo?> infos)
|
||||
{
|
||||
var hasLocalizedSeries = infos.Any(i => !string.IsNullOrEmpty(i.LocalizedSeries));
|
||||
if (!hasLocalizedSeries) return;
|
||||
|
@ -34,7 +34,7 @@ public class DefaultParser : IDefaultParser
|
||||
public ParserInfo? Parse(string filePath, string rootPath, LibraryType type = LibraryType.Manga)
|
||||
{
|
||||
var fileName = _directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
||||
// TODO: Potential Bug: This will return null, but on Image libraries, if all images, we would want to include this. (we can probably remove this and have users use kavitaignore)
|
||||
// TODO: Potential Bug: This will return null, but on Image libraries, if all images, we would want to include this.
|
||||
if (type != LibraryType.Image && Parser.IsCoverImage(_directoryService.FileSystem.Path.GetFileName(filePath))) return null;
|
||||
|
||||
var ret = new ParserInfo()
|
||||
|
@ -501,7 +501,6 @@ public class ScannerService : IScannerService
|
||||
// {
|
||||
// await task();
|
||||
// }
|
||||
// TODO: We might be able to do Task.WhenAll
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.FileScanProgressEvent(string.Empty, library.Name, ProgressEventType.Ended));
|
||||
|
@ -37,7 +37,7 @@ public class StatsService : IStatsService
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly DataContext _context;
|
||||
private readonly IStatisticService _statisticService;
|
||||
private const string ApiUrl = "https://stats.kavitareader.com"; // ""
|
||||
private const string ApiUrl = "https://stats.kavitareader.com";
|
||||
|
||||
public StatsService(ILogger<StatsService> logger, IUnitOfWork unitOfWork, DataContext context, IStatisticService statisticService)
|
||||
{
|
||||
|
@ -13,7 +13,6 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services.Tasks;
|
||||
|
||||
#nullable enable
|
||||
|
||||
internal class GithubReleaseMetadata
|
||||
@ -76,7 +75,7 @@ public class VersionUpdaterService : IVersionUpdaterService
|
||||
/// Fetches the latest release from Github
|
||||
/// </summary>
|
||||
/// <returns>Latest update</returns>
|
||||
public async Task<UpdateNotificationDto> CheckForUpdate()
|
||||
public async Task<UpdateNotificationDto?> CheckForUpdate()
|
||||
{
|
||||
var update = await GetGithubRelease();
|
||||
return CreateDto(update);
|
||||
|
@ -17,6 +17,7 @@ using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegiste
|
||||
|
||||
|
||||
namespace API.Services;
|
||||
#nullable enable
|
||||
|
||||
public interface ITokenService
|
||||
{
|
||||
|
@ -21,13 +21,11 @@ public class EventHub : IEventHub
|
||||
{
|
||||
private readonly IHubContext<MessageHub> _messageHub;
|
||||
private readonly IPresenceTracker _presenceTracker;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public EventHub(IHubContext<MessageHub> messageHub, IPresenceTracker presenceTracker, IUnitOfWork unitOfWork)
|
||||
public EventHub(IHubContext<MessageHub> messageHub, IPresenceTracker presenceTracker)
|
||||
{
|
||||
_messageHub = messageHub;
|
||||
_presenceTracker = presenceTracker;
|
||||
_unitOfWork = unitOfWork;
|
||||
|
||||
// TODO: When sending a message, queue the message up and on re-connect, reply the queued messages. Queue messages expire on a rolling basis (rolling array)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace API.SignalR;
|
||||
#nullable enable
|
||||
|
||||
public interface ILogHub : Serilog.Sinks.AspNetCore.SignalR.Interfaces.IHub
|
||||
{
|
||||
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace API.SignalR;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Generic hub for sending messages to UI
|
||||
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
|
||||
namespace API.SignalR.Presence;
|
||||
#nullable enable
|
||||
|
||||
public interface IPresenceTracker
|
||||
{
|
||||
@ -22,7 +23,6 @@ internal class ConnectionDetail
|
||||
public bool IsAdmin { get; set; }
|
||||
}
|
||||
|
||||
// TODO: This can respond to UserRoleUpdate events to handle online users
|
||||
/// <summary>
|
||||
/// This is a singleton service for tracking what users have a SignalR connection and their difference connectionIds
|
||||
/// </summary>
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace API.SignalR;
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// Payload for SignalR messages to Frontend
|
||||
|
@ -232,7 +232,6 @@ public class Startup
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// Apply all migrations on startup
|
||||
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
|
||||
var dataContext = serviceProvider.GetRequiredService<DataContext>();
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Company>kavitareader.com</Company>
|
||||
<Product>Kavita</Product>
|
||||
<AssemblyVersion>0.7.11.2</AssemblyVersion>
|
||||
@ -12,9 +12,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.12.0.78982">
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -55,7 +55,7 @@ install methods and platforms.
|
||||
**Note: Kavita is under heavy development and is being updated all the time, so the tag for bleeding edge builds is `:nightly`. The `:latest` tag will be the latest stable release.**
|
||||
|
||||
## Feature Requests
|
||||
Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects?type=classic) first for a list of planned features before you submit an idea.
|
||||
Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/), [Feature Discord Channel](https://discord.com/channels/821879810934439936/1164375153493422122) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects?type=classic) first for a list of planned features before you submit an idea.
|
||||
|
||||
## Notice
|
||||
Kavita is being actively developed and should be considered beta software until the 1.0 release.
|
||||
@ -107,7 +107,7 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="" width="32"> JetBrains](http:
|
||||
Thank you to [Weblate](https://hosted.weblate.org/engage/kavita/) who hosts our localization infrastructure pro-bono. If you want to see Kavita in your language, please help us localize.
|
||||
|
||||
<a href="https://hosted.weblate.org/engage/kavita/">
|
||||
<img src="https://hosted.weblate.org/widgets/kavita/-/horizontal-blue.svg" alt="Translation status" />
|
||||
<img src="https://hosted.weblate.org/widget/kavita/horizontal-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
## PikaPods
|
||||
|
540
UI/Web/package-lock.json
generated
540
UI/Web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,26 +13,26 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.0.4",
|
||||
"@angular/cdk": "^17.0.1",
|
||||
"@angular/common": "^17.0.4",
|
||||
"@angular/compiler": "^17.0.4",
|
||||
"@angular/core": "^17.0.4",
|
||||
"@angular/forms": "^17.0.4",
|
||||
"@angular/localize": "^17.0.4",
|
||||
"@angular/platform-browser": "^17.0.4",
|
||||
"@angular/platform-browser-dynamic": "^17.0.4",
|
||||
"@angular/router": "^17.0.4",
|
||||
"@angular/animations": "^17.0.6",
|
||||
"@angular/cdk": "^17.0.4",
|
||||
"@angular/common": "^17.0.6",
|
||||
"@angular/compiler": "^17.0.6",
|
||||
"@angular/core": "^17.0.6",
|
||||
"@angular/forms": "^17.0.6",
|
||||
"@angular/localize": "^17.0.6",
|
||||
"@angular/platform-browser": "^17.0.6",
|
||||
"@angular/platform-browser-dynamic": "^17.0.6",
|
||||
"@angular/router": "^17.0.6",
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
"@iharbeck/ngx-virtual-scroller": "^17.0.0",
|
||||
"@iplab/ngx-file-upload": "^17.0.0",
|
||||
"@microsoft/signalr": "^7.0.12",
|
||||
"@microsoft/signalr": "^8.0.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
|
||||
"@ngneat/transloco": "^6.0.0",
|
||||
"@ngneat/transloco": "^6.0.4",
|
||||
"@ngneat/transloco-locale": "^5.1.1",
|
||||
"@ngneat/transloco-persist-lang": "^5.0.0",
|
||||
"@ngneat/transloco-persist-translations": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.1",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"@tweenjs/tween.js": "^21.0.0",
|
||||
@ -58,17 +58,17 @@
|
||||
"zone.js": "^0.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.0.3",
|
||||
"@angular-eslint/builder": "^17.1.0",
|
||||
"@angular-eslint/eslint-plugin": "^17.1.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^17.1.0",
|
||||
"@angular-eslint/schematics": "^17.1.0",
|
||||
"@angular-eslint/template-parser": "^17.1.0",
|
||||
"@angular/cli": "^17.0.3",
|
||||
"@angular/compiler-cli": "^17.0.4",
|
||||
"@angular-devkit/build-angular": "^17.0.7",
|
||||
"@angular-eslint/builder": "^17.1.1",
|
||||
"@angular-eslint/eslint-plugin": "^17.1.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^17.1.1",
|
||||
"@angular-eslint/schematics": "^17.1.1",
|
||||
"@angular-eslint/template-parser": "^17.1.1",
|
||||
"@angular/cli": "^17.0.7",
|
||||
"@angular/compiler-cli": "^17.0.6",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/luxon": "^3.3.5",
|
||||
"@types/luxon": "^3.3.7",
|
||||
"@types/node": "^20.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||
"@typescript-eslint/parser": "^6.13.0",
|
||||
|
@ -141,6 +141,7 @@ export class MessageHubService {
|
||||
accessTokenFactory: () => user.token
|
||||
})
|
||||
.withAutomaticReconnect()
|
||||
.withStatefulReconnect()
|
||||
.build();
|
||||
|
||||
this.hubConnection
|
||||
|
@ -21,7 +21,6 @@ Build()
|
||||
|
||||
slnFile=Kavita.sln
|
||||
|
||||
dotnet clean $slnFile -c Debug
|
||||
dotnet clean $slnFile -c Release
|
||||
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform="Any CPU" -p:RuntimeIdentifiers=$RID
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"version": "8.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
|
@ -53,9 +53,6 @@ Package()
|
||||
echo "Copying LICENSE"
|
||||
cp ../LICENSE "$lOutputFolder"/LICENSE.txt
|
||||
|
||||
echo "Show API structure"
|
||||
find
|
||||
|
||||
echo "Copying appsettings.json"
|
||||
cp ./config/appsettings.Development.json $lOutputFolder/config/appsettings.json
|
||||
|
||||
|
72
openapi.json
72
openapi.json
@ -7,7 +7,7 @@
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||
},
|
||||
"version": "0.7.11.1"
|
||||
"version": "0.7.11.2"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
@ -3917,6 +3917,76 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/Opds/{apiKey}/more-in-genre": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Opds"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiKey",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "genreId",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageNumber",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/Opds/{apiKey}/recently-updated": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Opds"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apiKey",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageNumber",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/Opds/{apiKey}/on-deck": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
Loading…
x
Reference in New Issue
Block a user