mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-05 14:45:28 -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
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Install Swashbuckle CLI
|
- name: Install Swashbuckle CLI
|
||||||
shell: powershell
|
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
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Bump versions
|
- name: Bump versions
|
||||||
uses: SiqiLu/dotnet-bump-version@2.0.0
|
uses: SiqiLu/dotnet-bump-version@2.0.0
|
||||||
@ -98,7 +98,7 @@ jobs:
|
|||||||
- name: Compile dotnet app
|
- name: Compile dotnet app
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Install Swashbuckle CLI
|
- name: Install Swashbuckle CLI
|
||||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.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
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Bump versions
|
- name: Bump versions
|
||||||
uses: majora2007/dotnet-bump-version@v0.0.10
|
uses: majora2007/dotnet-bump-version@v0.0.10
|
||||||
@ -131,7 +131,7 @@ jobs:
|
|||||||
- name: Compile dotnet app
|
- name: Compile dotnet app
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Install Swashbuckle CLI
|
- name: Install Swashbuckle CLI
|
||||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.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
|
- name: Compile dotnet app
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Install Swashbuckle CLI
|
- name: Install Swashbuckle CLI
|
||||||
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@ -10,8 +10,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
|
||||||
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.10" />
|
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.11" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="19.2.69" />
|
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="20.0.4" />
|
||||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="19.2.69" />
|
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="20.0.4" />
|
||||||
<PackageReference Include="xunit" Version="2.6.1" />
|
<PackageReference Include="xunit" Version="2.6.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AnalysisMode>Default</AnalysisMode>
|
<AnalysisMode>Default</AnalysisMode>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<TieredPGO>true</TieredPGO>
|
<TieredPGO>true</TieredPGO>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@ -72,38 +72,37 @@
|
|||||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
|
||||||
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
||||||
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
|
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
|
||||||
<PackageReference Include="NetVips" Version="2.3.1" />
|
<PackageReference Include="NetVips" Version="2.4.0" />
|
||||||
<PackageReference Include="NetVips.Native" Version="8.14.5" />
|
<PackageReference Include="NetVips.Native" Version="8.15.0" />
|
||||||
<PackageReference Include="NReco.Logging.File" Version="1.1.7" />
|
<PackageReference Include="NReco.Logging.File" Version="1.1.7" />
|
||||||
<PackageReference Include="Serilog" Version="3.1.0" />
|
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
|
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.AspNetCore.SignalR" Version="0.4.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.File" Version="5.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.34.1" />
|
<PackageReference Include="SharpCompress" Version="0.34.2" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.1" />
|
||||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.12.0.78982">
|
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.69" />
|
<PackageReference Include="System.IO.Abstractions" Version="20.0.4" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
|
||||||
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
|
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -40,15 +40,13 @@ public class LibraryController : BaseApiController
|
|||||||
private readonly IEventHub _eventHub;
|
private readonly IEventHub _eventHub;
|
||||||
private readonly ILibraryWatcher _libraryWatcher;
|
private readonly ILibraryWatcher _libraryWatcher;
|
||||||
private readonly ILocalizationService _localizationService;
|
private readonly ILocalizationService _localizationService;
|
||||||
private readonly IStreamService _streamService;
|
|
||||||
private readonly IEasyCachingProvider _libraryCacheProvider;
|
private readonly IEasyCachingProvider _libraryCacheProvider;
|
||||||
private const string CacheKey = "library_";
|
private const string CacheKey = "library_";
|
||||||
|
|
||||||
public LibraryController(IDirectoryService directoryService,
|
public LibraryController(IDirectoryService directoryService,
|
||||||
ILogger<LibraryController> logger, IMapper mapper, ITaskScheduler taskScheduler,
|
ILogger<LibraryController> logger, IMapper mapper, ITaskScheduler taskScheduler,
|
||||||
IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher,
|
IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher,
|
||||||
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService,
|
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService)
|
||||||
IStreamService streamService)
|
|
||||||
{
|
{
|
||||||
_directoryService = directoryService;
|
_directoryService = directoryService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -58,7 +56,6 @@ public class LibraryController : BaseApiController
|
|||||||
_eventHub = eventHub;
|
_eventHub = eventHub;
|
||||||
_libraryWatcher = libraryWatcher;
|
_libraryWatcher = libraryWatcher;
|
||||||
_localizationService = localizationService;
|
_localizationService = localizationService;
|
||||||
_streamService = streamService;
|
|
||||||
|
|
||||||
_libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library);
|
_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));
|
return Ok(_directoryService.ListDirectory(path));
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ public class OpdsController : BaseApiController
|
|||||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
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);
|
var feed = CreateFeed("Kavita", string.Empty, apiKey, prefix);
|
||||||
SetFeedId(feed, "root");
|
SetFeedId(feed, "root");
|
||||||
@ -141,10 +141,37 @@ public class OpdsController : BaseApiController
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case DashboardStreamType.RecentlyUpdated:
|
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;
|
break;
|
||||||
case DashboardStreamType.MoreInGenre:
|
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;
|
break;
|
||||||
case DashboardStreamType.SmartFilter:
|
case DashboardStreamType.SmartFilter:
|
||||||
|
|
||||||
@ -632,6 +659,61 @@ public class OpdsController : BaseApiController
|
|||||||
return CreateXmlResult(SerializeXml(feed));
|
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")]
|
[HttpGet("{apiKey}/on-deck")]
|
||||||
[Produces("application/xml")]
|
[Produces("application/xml")]
|
||||||
public async Task<IActionResult> GetOnDeck(string apiKey, [FromQuery] int pageNumber = 1)
|
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 userId = await GetUser(apiKey);
|
||||||
var progress = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(chapterId, userId);
|
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",
|
var link = CreateLink(FeedLinkRelation.Stream, "image/jpeg",
|
||||||
$"{prefix}{apiKey}/image?libraryId={libraryId}&seriesId={seriesId}&volumeId={volumeId}&chapterId={chapterId}&pageNumber=" + "{pageNumber}");
|
$"{prefix}{apiKey}/image?libraryId={libraryId}&seriesId={seriesId}&volumeId={volumeId}&chapterId={chapterId}&pageNumber=" + "{pageNumber}");
|
||||||
link.TotalPages = mangaFile.Pages;
|
link.TotalPages = mangaFile.Pages;
|
||||||
|
@ -19,13 +19,11 @@ public class StreamController : BaseApiController
|
|||||||
{
|
{
|
||||||
private readonly IStreamService _streamService;
|
private readonly IStreamService _streamService;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
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;
|
_streamService = streamService;
|
||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
_logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -53,7 +53,7 @@ public class ThemeController : BaseApiController
|
|||||||
{
|
{
|
||||||
await _themeService.UpdateDefault(dto.ThemeId);
|
await _themeService.UpdateDefault(dto.ThemeId);
|
||||||
}
|
}
|
||||||
catch (KavitaException ex)
|
catch (KavitaException)
|
||||||
{
|
{
|
||||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "theme-doesnt-exist"));
|
return BadRequest(await _localizationService.Translate(User.GetUserId(), "theme-doesnt-exist"));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Account;
|
namespace API.DTOs.Account;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class LoginDto
|
public class LoginDto
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ using API.Entities;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Filtering;
|
namespace API.DTOs.Filtering;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class FilterDto
|
public class FilterDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace API.DTOs.Filtering;
|
namespace API.DTOs.Filtering;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a range between two int/float/double
|
/// Represents a range between two int/float/double
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
namespace API.DTOs.Filtering.v2;
|
namespace API.DTOs.Filtering.v2;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Metadata filtering for v2 API only
|
/// Metadata filtering for v2 API only
|
||||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class LibraryDto
|
public class LibraryDto
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using API.DTOs.Account;
|
using API.DTOs.Account;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a member of a Kavita server.
|
/// Represents a member of a Kavita server.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Metadata;
|
namespace API.DTOs.Metadata;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exclusively metadata about a given chapter
|
/// Exclusively metadata about a given chapter
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class RatingDto
|
public class RatingDto
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace API.DTOs.Reader;
|
namespace API.DTOs.Reader;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class BookmarkDto
|
public class BookmarkDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Reader;
|
namespace API.DTOs.Reader;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class BookmarkInfoDto
|
public class BookmarkInfoDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Reader;
|
namespace API.DTOs.Reader;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Information about the Chapter for the Reader to render
|
/// Information about the Chapter for the Reader to render
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
namespace API.DTOs.Reader;
|
namespace API.DTOs.Reader;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class PersonalToCDto
|
public class PersonalToCDto
|
||||||
{
|
{
|
||||||
public required int ChapterId { get; set; }
|
public required int ChapterId { get; set; }
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.DTOs.Scrobbling;
|
using API.DTOs.Scrobbling;
|
||||||
|
|
||||||
namespace API.DTOs.Recommendation;
|
namespace API.DTOs.Recommendation;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ExternalSeriesDetailDto
|
public class ExternalSeriesDetailDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Recommendation;
|
namespace API.DTOs.Recommendation;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class SeriesStaffDto
|
public class SeriesStaffDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Scrobbling;
|
namespace API.DTOs.Scrobbling;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Response from Kavita+ Scrobble API
|
/// Response from Kavita+ Scrobble API
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace API.DTOs.SeriesDetail;
|
namespace API.DTOs.SeriesDetail;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UpdateUserReviewDto
|
public class UpdateUserReviewDto
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using API.Services.Plus;
|
using API.Services.Plus;
|
||||||
|
|
||||||
namespace API.DTOs.SeriesDetail;
|
namespace API.DTOs.SeriesDetail;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a User Review for a given Series
|
/// Represents a User Review for a given Series
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Statistics;
|
namespace API.DTOs.Statistics;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class FileExtensionDto
|
public class FileExtensionDto
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace API.DTOs.Statistics;
|
namespace API.DTOs.Statistics;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class TopReadDto
|
public class TopReadDto
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
|
|
||||||
namespace API.DTOs.Stats;
|
namespace API.DTOs.Stats;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents information about a Kavita Installation
|
/// Represents information about a Kavita Installation
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UpdateRbsDto
|
public class UpdateRbsDto
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using API.DTOs.Account;
|
using API.DTOs.Account;
|
||||||
|
|
||||||
namespace API.DTOs;
|
namespace API.DTOs;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class UserDto
|
public class UserDto
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ using Kavita.Common.Extensions;
|
|||||||
using Nager.ArticleNumber;
|
using Nager.ArticleNumber;
|
||||||
|
|
||||||
namespace API.Data.Metadata;
|
namespace API.Data.Metadata;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A representation of a ComicInfo.xml file
|
/// 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.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.DTOs.Metadata;
|
using API.DTOs.Metadata;
|
||||||
@ -22,6 +23,8 @@ public interface IGenreRepository
|
|||||||
Task RemoveAllGenreNoLongerAssociated(bool removeExternal = false);
|
Task RemoveAllGenreNoLongerAssociated(bool removeExternal = false);
|
||||||
Task<IList<GenreTagDto>> GetAllGenreDtosForLibrariesAsync(IList<int> libraryIds, int userId);
|
Task<IList<GenreTagDto>> GetAllGenreDtosForLibrariesAsync(IList<int> libraryIds, int userId);
|
||||||
Task<int> GetCountAsync();
|
Task<int> GetCountAsync();
|
||||||
|
Task<GenreTagDto> GetRandomGenre();
|
||||||
|
Task<GenreTagDto> GetGenreById(int id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GenreRepository : IGenreRepository
|
public class GenreRepository : IGenreRepository
|
||||||
@ -92,6 +95,27 @@ public class GenreRepository : IGenreRepository
|
|||||||
return await _context.Genre.CountAsync();
|
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()
|
public async Task<IList<Genre>> GetAllGenresAsync()
|
||||||
{
|
{
|
||||||
return await _context.Genre.ToListAsync();
|
return await _context.Genre.ToListAsync();
|
||||||
|
@ -17,7 +17,7 @@ public static class FileTypeGroupExtensions
|
|||||||
case FileTypeGroup.Pdf:
|
case FileTypeGroup.Pdf:
|
||||||
return Parser.PdfFileExtension;
|
return Parser.PdfFileExtension;
|
||||||
case FileTypeGroup.Images:
|
case FileTypeGroup.Images:
|
||||||
return Parser.ImageFileExtensions;;
|
return Parser.ImageFileExtensions;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(fileTypeGroup), fileTypeGroup, null);
|
throw new ArgumentOutOfRangeException(nameof(fileTypeGroup), fileTypeGroup, null);
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ public static class HttpExtensions
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
};
|
};
|
||||||
|
|
||||||
response.Headers.Add("Pagination", JsonSerializer.Serialize(paginationHeader, options));
|
response.Headers.Append("Pagination", JsonSerializer.Serialize(paginationHeader, options));
|
||||||
response.Headers.Add("Access-Control-Expose-Headers", "Pagination");
|
response.Headers.Append("Access-Control-Expose-Headers", "Pagination");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -33,7 +33,7 @@ public static class HttpExtensions
|
|||||||
public static void AddCacheHeader(this HttpResponse response, byte[] content)
|
public static void AddCacheHeader(this HttpResponse response, byte[] content)
|
||||||
{
|
{
|
||||||
if (content is not {Length: > 0}) return;
|
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";
|
response.Headers.CacheControl = $"private,max-age=100";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ public static class HttpExtensions
|
|||||||
{
|
{
|
||||||
if (filename is not {Length: > 0}) return;
|
if (filename is not {Length: > 0}) return;
|
||||||
var hashContent = filename + File.GetLastWriteTimeUtc(filename);
|
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)
|
if (maxAge != 10)
|
||||||
{
|
{
|
||||||
response.Headers.CacheControl = $"max-age={maxAge}";
|
response.Headers.CacheControl = $"max-age={maxAge}";
|
||||||
|
@ -22,7 +22,7 @@ public static class SeriesFilter
|
|||||||
switch (comparison)
|
switch (comparison)
|
||||||
{
|
{
|
||||||
case FilterComparison.Equal:
|
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:
|
case FilterComparison.Contains:
|
||||||
return queryable.Where(s => languages.Contains(s.Metadata.Language));
|
return queryable.Where(s => languages.Contains(s.Metadata.Language));
|
||||||
case FilterComparison.MustContains:
|
case FilterComparison.MustContains:
|
||||||
@ -30,9 +30,9 @@ public static class SeriesFilter
|
|||||||
case FilterComparison.NotContains:
|
case FilterComparison.NotContains:
|
||||||
return queryable.Where(s => !languages.Contains(s.Metadata.Language));
|
return queryable.Where(s => !languages.Contains(s.Metadata.Language));
|
||||||
case FilterComparison.NotEqual:
|
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:
|
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.GreaterThan:
|
||||||
case FilterComparison.GreaterThanEqual:
|
case FilterComparison.GreaterThanEqual:
|
||||||
case FilterComparison.LessThan:
|
case FilterComparison.LessThan:
|
||||||
|
@ -6,8 +6,9 @@ namespace API.Extensions;
|
|||||||
|
|
||||||
public static class StringExtensions
|
public static class StringExtensions
|
||||||
{
|
{
|
||||||
private static readonly Regex SentenceCaseRegex = new Regex(@"(^[a-z])|\.\s+(.)",
|
private static readonly Regex SentenceCaseRegex = new(@"(^[a-z])|\.\s+(.)",
|
||||||
RegexOptions.ExplicitCapture | RegexOptions.Compiled, Services.Tasks.Scanner.Parser.Parser.RegexTimeout);
|
RegexOptions.ExplicitCapture | RegexOptions.Compiled,
|
||||||
|
Services.Tasks.Scanner.Parser.Parser.RegexTimeout);
|
||||||
|
|
||||||
public static string SentenceCase(this string value)
|
public static string SentenceCase(this string value)
|
||||||
{
|
{
|
||||||
@ -21,17 +22,16 @@ public static class StringExtensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string ToNormalized(this string? value)
|
public static string ToNormalized(this string? value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value)) return string.Empty;
|
return string.IsNullOrEmpty(value) ? string.Empty : Services.Tasks.Scanner.Parser.Parser.Normalize(value);
|
||||||
return 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;
|
using Kavita.Common;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class AppUserBuilder : IEntityBuilder<AppUser>
|
public class AppUserBuilder : IEntityBuilder<AppUser>
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ using API.Entities.Enums;
|
|||||||
using API.Services.Tasks.Scanner.Parser;
|
using API.Services.Tasks.Scanner.Parser;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ChapterBuilder : IEntityBuilder<Chapter>
|
public class ChapterBuilder : IEntityBuilder<Chapter>
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using API.Entities.Scrobble;
|
using API.Entities.Scrobble;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class ScrobbleHoldBuilder : IEntityBuilder<ScrobbleHold>
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,9 @@ public static class SmartFilterHelper
|
|||||||
private static SortOptions DecodeSortOptions(string encodedSortOptions)
|
private static SortOptions DecodeSortOptions(string encodedSortOptions)
|
||||||
{
|
{
|
||||||
var parts = Uri.UnescapeDataString(encodedSortOptions).Split(InnerStatementSeparator);
|
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;
|
var isAscending = isAscendingPart?.Trim().Replace(IsAscendingKey, string.Empty).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||||
if (sortFieldPart == null)
|
if (sortFieldPart == null)
|
||||||
|
@ -151,6 +151,10 @@
|
|||||||
"browse-libraries": "Browse by Libraries",
|
"browse-libraries": "Browse by Libraries",
|
||||||
"collections": "All Collections",
|
"collections": "All Collections",
|
||||||
"browse-collections": "Browse by 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",
|
"smart-filters": "Smart Filters",
|
||||||
"external-sources": "External Sources",
|
"external-sources": "External Sources",
|
||||||
"browse-external-sources": "Browse External Sources",
|
"browse-external-sources": "Browse External Sources",
|
||||||
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
|
|
||||||
namespace API.Middleware.RateLimit;
|
namespace API.Middleware.RateLimit;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class AuthenticationRateLimiterPolicy : IRateLimiterPolicy<string>
|
public class AuthenticationRateLimiterPolicy : IRateLimiterPolicy<string>
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,7 @@ using Serilog.Events;
|
|||||||
using Serilog.Sinks.AspNetCore.SignalR.Extensions;
|
using Serilog.Sinks.AspNetCore.SignalR.Extensions;
|
||||||
|
|
||||||
namespace API;
|
namespace API;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
|
@ -199,7 +199,6 @@ public class BookService : IBookService
|
|||||||
}
|
}
|
||||||
if (!book.Content.AllFiles.TryGetLocalFileRefByKey(key, out var bookFile)) continue;
|
if (!book.Content.AllFiles.TryGetLocalFileRefByKey(key, out var bookFile)) continue;
|
||||||
|
|
||||||
//var bookFile = book.Content.AllFiles.Local[key];
|
|
||||||
var content = await bookFile.ReadContentAsBytesAsync();
|
var content = await bookFile.ReadContentAsBytesAsync();
|
||||||
importBuilder.Append(Encoding.UTF8.GetString(content));
|
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 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))
|
if (string.IsNullOrEmpty(info.Volume) && Parser.ParseVolume(filePath).Equals(Parser.DefaultVolume))
|
||||||
{
|
{
|
||||||
//info.Number = "1";
|
|
||||||
info.Count = 1;
|
info.Count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,8 +936,9 @@ public class BookService : IBookService
|
|||||||
/// <param name="mappings"></param>
|
/// <param name="mappings"></param>
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <returns></returns>
|
/// <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;
|
if (mappings.ContainsKey(CleanContentKeys(key))) return key;
|
||||||
|
|
||||||
// Fallback to searching for key (bad epub metadata)
|
// Fallback to searching for key (bad epub metadata)
|
||||||
@ -949,7 +948,7 @@ public class BookService : IBookService
|
|||||||
key = correctedKey;
|
key = correctedKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stepsBack = CountParentDirectory(book.Content.NavigationHtmlFile?.FilePath); // FileName -> FilePath
|
var stepsBack = CountParentDirectory(book.Content.NavigationHtmlFile?.FilePath);
|
||||||
if (mappings.TryGetValue(key, out _))
|
if (mappings.TryGetValue(key, out _))
|
||||||
{
|
{
|
||||||
return key;
|
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
|
/// Scans a directory by utilizing a recursive folder search. If a .kavitaignore file is found, will ignore matching patterns
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="folderPath"></param>
|
/// <param name="folderPath"></param>
|
||||||
/// <param name="supportedExtensions"></param>
|
/// <param name="fileTypes"></param>
|
||||||
/// <param name="matcher"></param>
|
/// <param name="matcher"></param>
|
||||||
/// <returns></returns>
|
/// <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);
|
_logger.LogDebug("[ScanFiles] called on {Path}", folderPath);
|
||||||
var files = new List<string>();
|
var files = new List<string>();
|
||||||
@ -668,19 +668,19 @@ public class DirectoryService : IDirectoryService
|
|||||||
|
|
||||||
foreach (var directory in directories)
|
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)
|
// Get the matcher from either ignore or global (default setup)
|
||||||
if (matcher == null)
|
if (matcher == null)
|
||||||
{
|
{
|
||||||
files.AddRange(GetFilesWithCertainExtensions(folderPath, supportedExtensions));
|
files.AddRange(GetFilesWithCertainExtensions(folderPath, fileTypes));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var foundFiles = GetFilesWithCertainExtensions(folderPath,
|
var foundFiles = GetFilesWithCertainExtensions(folderPath,
|
||||||
supportedExtensions)
|
fileTypes)
|
||||||
.Where(file => !matcher.ExcludeMatches(FileSystem.FileInfo.New(file).Name));
|
.Where(file => !matcher.ExcludeMatches(FileSystem.FileInfo.New(file).Name));
|
||||||
files.AddRange(foundFiles);
|
files.AddRange(foundFiles);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IEmailService
|
public interface IEmailService
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ using Hangfire;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IMetadataService
|
public interface IMetadataService
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,7 @@ internal class ExternalMetadataIdsDto
|
|||||||
|
|
||||||
public interface IExternalMetadataService
|
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
|
public class ExternalMetadataService : IExternalMetadataService
|
||||||
|
@ -22,6 +22,7 @@ using Kavita.Common;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IReaderService
|
public interface IReaderService
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,7 @@ using Kavita.Common;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IReadingListService
|
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"
|
/// World of Acceleration v02.cbz having Series "Accel World" and Localized Series of "World of Acceleration"
|
||||||
/// </example>
|
/// </example>
|
||||||
/// <param name="infos">A collection of ParserInfos</param>
|
/// <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));
|
var hasLocalizedSeries = infos.Any(i => !string.IsNullOrEmpty(i.LocalizedSeries));
|
||||||
if (!hasLocalizedSeries) return;
|
if (!hasLocalizedSeries) return;
|
||||||
|
@ -34,7 +34,7 @@ public class DefaultParser : IDefaultParser
|
|||||||
public ParserInfo? Parse(string filePath, string rootPath, LibraryType type = LibraryType.Manga)
|
public ParserInfo? Parse(string filePath, string rootPath, LibraryType type = LibraryType.Manga)
|
||||||
{
|
{
|
||||||
var fileName = _directoryService.FileSystem.Path.GetFileNameWithoutExtension(filePath);
|
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;
|
if (type != LibraryType.Image && Parser.IsCoverImage(_directoryService.FileSystem.Path.GetFileName(filePath))) return null;
|
||||||
|
|
||||||
var ret = new ParserInfo()
|
var ret = new ParserInfo()
|
||||||
|
@ -501,7 +501,6 @@ public class ScannerService : IScannerService
|
|||||||
// {
|
// {
|
||||||
// await task();
|
// await task();
|
||||||
// }
|
// }
|
||||||
// TODO: We might be able to do Task.WhenAll
|
|
||||||
|
|
||||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||||
MessageFactory.FileScanProgressEvent(string.Empty, library.Name, ProgressEventType.Ended));
|
MessageFactory.FileScanProgressEvent(string.Empty, library.Name, ProgressEventType.Ended));
|
||||||
|
@ -37,7 +37,7 @@ public class StatsService : IStatsService
|
|||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
private readonly DataContext _context;
|
private readonly DataContext _context;
|
||||||
private readonly IStatisticService _statisticService;
|
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)
|
public StatsService(ILogger<StatsService> logger, IUnitOfWork unitOfWork, DataContext context, IStatisticService statisticService)
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services.Tasks;
|
namespace API.Services.Tasks;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
internal class GithubReleaseMetadata
|
internal class GithubReleaseMetadata
|
||||||
@ -76,7 +75,7 @@ public class VersionUpdaterService : IVersionUpdaterService
|
|||||||
/// Fetches the latest release from Github
|
/// Fetches the latest release from Github
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Latest update</returns>
|
/// <returns>Latest update</returns>
|
||||||
public async Task<UpdateNotificationDto> CheckForUpdate()
|
public async Task<UpdateNotificationDto?> CheckForUpdate()
|
||||||
{
|
{
|
||||||
var update = await GetGithubRelease();
|
var update = await GetGithubRelease();
|
||||||
return CreateDto(update);
|
return CreateDto(update);
|
||||||
|
@ -17,6 +17,7 @@ using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegiste
|
|||||||
|
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface ITokenService
|
public interface ITokenService
|
||||||
{
|
{
|
||||||
|
@ -21,13 +21,11 @@ public class EventHub : IEventHub
|
|||||||
{
|
{
|
||||||
private readonly IHubContext<MessageHub> _messageHub;
|
private readonly IHubContext<MessageHub> _messageHub;
|
||||||
private readonly IPresenceTracker _presenceTracker;
|
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;
|
_messageHub = messageHub;
|
||||||
_presenceTracker = presenceTracker;
|
_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)
|
// 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;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace API.SignalR;
|
namespace API.SignalR;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface ILogHub : Serilog.Sinks.AspNetCore.SignalR.Interfaces.IHub
|
public interface ILogHub : Serilog.Sinks.AspNetCore.SignalR.Interfaces.IHub
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace API.SignalR;
|
namespace API.SignalR;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic hub for sending messages to UI
|
/// Generic hub for sending messages to UI
|
||||||
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using API.Data;
|
using API.Data;
|
||||||
|
|
||||||
namespace API.SignalR.Presence;
|
namespace API.SignalR.Presence;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IPresenceTracker
|
public interface IPresenceTracker
|
||||||
{
|
{
|
||||||
@ -22,7 +23,6 @@ internal class ConnectionDetail
|
|||||||
public bool IsAdmin { get; set; }
|
public bool IsAdmin { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This can respond to UserRoleUpdate events to handle online users
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is a singleton service for tracking what users have a SignalR connection and their difference connectionIds
|
/// This is a singleton service for tracking what users have a SignalR connection and their difference connectionIds
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace API.SignalR;
|
namespace API.SignalR;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Payload for SignalR messages to Frontend
|
/// Payload for SignalR messages to Frontend
|
||||||
|
@ -232,7 +232,6 @@ public class Startup
|
|||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
// Apply all migrations on startup
|
// Apply all migrations on startup
|
||||||
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
|
|
||||||
var dataContext = serviceProvider.GetRequiredService<DataContext>();
|
var dataContext = serviceProvider.GetRequiredService<DataContext>();
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Company>kavitareader.com</Company>
|
<Company>kavitareader.com</Company>
|
||||||
<Product>Kavita</Product>
|
<Product>Kavita</Product>
|
||||||
<AssemblyVersion>0.7.11.2</AssemblyVersion>
|
<AssemblyVersion>0.7.11.2</AssemblyVersion>
|
||||||
@ -12,9 +12,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.12.0.78982">
|
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</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.**
|
**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
|
## 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
|
## Notice
|
||||||
Kavita is being actively developed and should be considered beta software until the 1.0 release.
|
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.
|
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/">
|
<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>
|
</a>
|
||||||
|
|
||||||
## PikaPods
|
## 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,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.0.4",
|
"@angular/animations": "^17.0.6",
|
||||||
"@angular/cdk": "^17.0.1",
|
"@angular/cdk": "^17.0.4",
|
||||||
"@angular/common": "^17.0.4",
|
"@angular/common": "^17.0.6",
|
||||||
"@angular/compiler": "^17.0.4",
|
"@angular/compiler": "^17.0.6",
|
||||||
"@angular/core": "^17.0.4",
|
"@angular/core": "^17.0.6",
|
||||||
"@angular/forms": "^17.0.4",
|
"@angular/forms": "^17.0.6",
|
||||||
"@angular/localize": "^17.0.4",
|
"@angular/localize": "^17.0.6",
|
||||||
"@angular/platform-browser": "^17.0.4",
|
"@angular/platform-browser": "^17.0.6",
|
||||||
"@angular/platform-browser-dynamic": "^17.0.4",
|
"@angular/platform-browser-dynamic": "^17.0.6",
|
||||||
"@angular/router": "^17.0.4",
|
"@angular/router": "^17.0.6",
|
||||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||||
"@iharbeck/ngx-virtual-scroller": "^17.0.0",
|
"@iharbeck/ngx-virtual-scroller": "^17.0.0",
|
||||||
"@iplab/ngx-file-upload": "^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",
|
"@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-locale": "^5.1.1",
|
||||||
"@ngneat/transloco-persist-lang": "^5.0.0",
|
"@ngneat/transloco-persist-lang": "^5.0.0",
|
||||||
"@ngneat/transloco-persist-translations": "^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",
|
"@popperjs/core": "^2.11.7",
|
||||||
"@swimlane/ngx-charts": "^20.5.0",
|
"@swimlane/ngx-charts": "^20.5.0",
|
||||||
"@tweenjs/tween.js": "^21.0.0",
|
"@tweenjs/tween.js": "^21.0.0",
|
||||||
@ -58,17 +58,17 @@
|
|||||||
"zone.js": "^0.14.2"
|
"zone.js": "^0.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^17.0.3",
|
"@angular-devkit/build-angular": "^17.0.7",
|
||||||
"@angular-eslint/builder": "^17.1.0",
|
"@angular-eslint/builder": "^17.1.1",
|
||||||
"@angular-eslint/eslint-plugin": "^17.1.0",
|
"@angular-eslint/eslint-plugin": "^17.1.1",
|
||||||
"@angular-eslint/eslint-plugin-template": "^17.1.0",
|
"@angular-eslint/eslint-plugin-template": "^17.1.1",
|
||||||
"@angular-eslint/schematics": "^17.1.0",
|
"@angular-eslint/schematics": "^17.1.1",
|
||||||
"@angular-eslint/template-parser": "^17.1.0",
|
"@angular-eslint/template-parser": "^17.1.1",
|
||||||
"@angular/cli": "^17.0.3",
|
"@angular/cli": "^17.0.7",
|
||||||
"@angular/compiler-cli": "^17.0.4",
|
"@angular/compiler-cli": "^17.0.6",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/luxon": "^3.3.5",
|
"@types/luxon": "^3.3.7",
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||||
"@typescript-eslint/parser": "^6.13.0",
|
"@typescript-eslint/parser": "^6.13.0",
|
||||||
|
@ -141,6 +141,7 @@ export class MessageHubService {
|
|||||||
accessTokenFactory: () => user.token
|
accessTokenFactory: () => user.token
|
||||||
})
|
})
|
||||||
.withAutomaticReconnect()
|
.withAutomaticReconnect()
|
||||||
|
.withStatefulReconnect()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.hubConnection
|
this.hubConnection
|
||||||
|
@ -21,7 +21,6 @@ Build()
|
|||||||
|
|
||||||
slnFile=Kavita.sln
|
slnFile=Kavita.sln
|
||||||
|
|
||||||
dotnet clean $slnFile -c Debug
|
|
||||||
dotnet clean $slnFile -c Release
|
dotnet clean $slnFile -c Release
|
||||||
|
|
||||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform="Any CPU" -p:RuntimeIdentifiers=$RID
|
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform="Any CPU" -p:RuntimeIdentifiers=$RID
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "7.0.0",
|
"version": "8.0.0",
|
||||||
"rollForward": "latestMajor",
|
"rollForward": "latestMajor",
|
||||||
"allowPrerelease": false
|
"allowPrerelease": false
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,6 @@ Package()
|
|||||||
echo "Copying LICENSE"
|
echo "Copying LICENSE"
|
||||||
cp ../LICENSE "$lOutputFolder"/LICENSE.txt
|
cp ../LICENSE "$lOutputFolder"/LICENSE.txt
|
||||||
|
|
||||||
echo "Show API structure"
|
|
||||||
find
|
|
||||||
|
|
||||||
echo "Copying appsettings.json"
|
echo "Copying appsettings.json"
|
||||||
cp ./config/appsettings.Development.json $lOutputFolder/config/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",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.7.11.1"
|
"version": "0.7.11.2"
|
||||||
},
|
},
|
||||||
"servers": [
|
"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": {
|
"/api/Opds/{apiKey}/on-deck": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user