Scanner Bugfixes (#2818)

This commit is contained in:
Joe Milazzo 2024-03-25 16:28:10 -05:00 committed by GitHub
parent 829a610579
commit 93a8883fe4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 180 additions and 99 deletions

View File

@ -10,12 +10,12 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
@ -26,7 +26,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: csproj name: csproj
path: Kavita.Common/Kavita.Common.csproj path: Kavita.Common/Kavita.Common.csproj

View File

@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: csproj name: csproj
path: Kavita.Common/Kavita.Common.csproj path: Kavita.Common/Kavita.Common.csproj
@ -26,12 +26,12 @@ jobs:
needs: [ build ] needs: [ build ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
@ -59,14 +59,14 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check Out Repo - name: Check Out Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: canary ref: canary
- name: NodeJS to Compile WebUI - name: NodeJS to Compile WebUI
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: '18.13.x' node-version: 20
- run: | - run: |
cd UI/Web || exit cd UI/Web || exit
echo 'Installing web dependencies' echo 'Installing web dependencies'
@ -81,7 +81,7 @@ jobs:
cd ../ || exit cd ../ || exit
- name: Get csproj Version - name: Get csproj Version
uses: kzrnm/get-net-sdk-project-versions-action@v1 uses: kzrnm/get-net-sdk-project-versions-action@v2
id: get-version id: get-version
with: with:
proj-path: Kavita.Common/Kavita.Common.csproj proj-path: Kavita.Common/Kavita.Common.csproj
@ -96,7 +96,7 @@ jobs:
run: echo "${{steps.get-version.outputs.assembly-version}}" run: echo "${{steps.get-version.outputs.assembly-version}}"
- name: Compile dotnet app - name: Compile dotnet app
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
@ -106,28 +106,28 @@ jobs:
- run: ./monorepo-build.sh - run: ./monorepo-build.sh
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Build and push - name: Build and push
id: docker_build id: docker_build
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64 platforms: linux/amd64,linux/arm/v7,linux/arm64

View File

@ -46,7 +46,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install Swashbuckle CLI - name: Install Swashbuckle CLI
shell: bash shell: bash

View File

@ -21,11 +21,11 @@ jobs:
if: github.ref == 'refs/heads/develop' if: github.ref == 'refs/heads/develop'
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: csproj name: csproj
path: Kavita.Common/Kavita.Common.csproj path: Kavita.Common/Kavita.Common.csproj
@ -36,12 +36,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop' if: github.ref == 'refs/heads/develop'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
@ -89,14 +89,14 @@ jobs:
echo "BODY=$body" >> $GITHUB_OUTPUT echo "BODY=$body" >> $GITHUB_OUTPUT
- name: Check Out Repo - name: Check Out Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: develop ref: develop
- name: NodeJS to Compile WebUI - name: NodeJS to Compile WebUI
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: '18.13.x' node-version: 20
- run: | - run: |
cd UI/Web || exit cd UI/Web || exit
echo 'Installing web dependencies' echo 'Installing web dependencies'
@ -111,7 +111,7 @@ jobs:
cd ../ || exit cd ../ || exit
- name: Get csproj Version - name: Get csproj Version
uses: kzrnm/get-net-sdk-project-versions-action@v1 uses: kzrnm/get-net-sdk-project-versions-action@v2
id: get-version id: get-version
with: with:
proj-path: Kavita.Common/Kavita.Common.csproj proj-path: Kavita.Common/Kavita.Common.csproj
@ -126,7 +126,7 @@ jobs:
run: echo "${{steps.get-version.outputs.assembly-version}}" run: echo "${{steps.get-version.outputs.assembly-version}}"
- name: Compile dotnet app - name: Compile dotnet app
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
@ -136,28 +136,28 @@ jobs:
- run: ./monorepo-build.sh - run: ./monorepo-build.sh
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Build and push - name: Build and push
id: docker_build id: docker_build
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64 platforms: linux/amd64,linux/arm/v7,linux/arm64

View File

@ -30,11 +30,11 @@ jobs:
if: github.event.pull_request.merged == true && contains(github.head_ref, 'release') if: github.event.pull_request.merged == true && contains(github.head_ref, 'release')
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: csproj name: csproj
path: Kavita.Common/Kavita.Common.csproj path: Kavita.Common/Kavita.Common.csproj
@ -77,14 +77,14 @@ jobs:
echo "BODY=$body" >> $GITHUB_OUTPUT echo "BODY=$body" >> $GITHUB_OUTPUT
- name: Check Out Repo - name: Check Out Repo
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
ref: develop ref: develop
- name: NodeJS to Compile WebUI - name: NodeJS to Compile WebUI
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: '18.13.x' node-version: 20
- run: | - run: |
cd UI/Web || exit cd UI/Web || exit
@ -100,7 +100,7 @@ jobs:
cd ../ || exit cd ../ || exit
- name: Get csproj Version - name: Get csproj Version
uses: kzrnm/get-net-sdk-project-versions-action@v1 uses: kzrnm/get-net-sdk-project-versions-action@v2
id: get-version id: get-version
with: with:
proj-path: Kavita.Common/Kavita.Common.csproj proj-path: Kavita.Common/Kavita.Common.csproj
@ -117,7 +117,7 @@ jobs:
id: parse-version id: parse-version
- name: Compile dotnet app - name: Compile dotnet app
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
- name: Install Swashbuckle CLI - name: Install Swashbuckle CLI
@ -126,28 +126,28 @@ jobs:
- run: ./monorepo-build.sh - run: ./monorepo-build.sh
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Build and push stable - name: Build and push stable
id: docker_build_stable id: docker_build_stable
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64 platforms: linux/amd64,linux/arm/v7,linux/arm64
@ -156,7 +156,7 @@ jobs:
- name: Build and push nightly - name: Build and push nightly
id: docker_build_nightly id: docker_build_nightly
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64 platforms: linux/amd64,linux/arm/v7,linux/arm64

View File

@ -206,6 +206,7 @@ public class MangaParsingTests
[InlineData("test 2 years 1권", "test 2 years")] [InlineData("test 2 years 1권", "test 2 years")]
[InlineData("test 2 years 1화", "test 2 years")] [InlineData("test 2 years 1화", "test 2 years")]
[InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake", "Nagasarete Airantou")] [InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake", "Nagasarete Airantou")]
[InlineData("Cynthia The Mission - c000 - c006 (v06)", "Cynthia The Mission")]
public void ParseSeriesTest(string filename, string expected) public void ParseSeriesTest(string filename, string expected)
{ {
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename)); Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename));

View File

@ -66,7 +66,7 @@
<PackageReference Include="Flurl" Version="3.0.7" /> <PackageReference Include="Flurl" Version="3.0.7" />
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Hangfire" Version="1.8.11" /> <PackageReference Include="Hangfire" Version="1.8.11" />
<PackageReference Include="Hangfire.InMemory" Version="0.8.0" /> <PackageReference Include="Hangfire.InMemory" Version="0.8.1" />
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" /> <PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.4.1" /> <PackageReference Include="Hangfire.Storage.SQLite" Version="0.4.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.59" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.59" />

View File

@ -113,7 +113,35 @@ public static class MigrateLooseLeafChapters
//UpdateCoverImage(directoryService, logger, chapter, extension, newVolume); //UpdateCoverImage(directoryService, logger, chapter, extension, newVolume);
} }
// Update the progress table with the new VolumeId
var oldVolumeBookmarks = await dataContext.AppUserBookmark
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Bookmarks from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumeBookmarks.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var bookmark in oldVolumeBookmarks)
{
bookmark.VolumeId = newVolume.Id;
}
var oldVolumePersonalToC = await dataContext.AppUserTableOfContent
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumePersonalToC.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var pToc in oldVolumePersonalToC)
{
pToc.VolumeId = newVolume.Id;
}
var oldVolumeReadingListItems = await dataContext.ReadingListItem
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumeReadingListItems.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var readingListItem in oldVolumeReadingListItems)
{
readingListItem.VolumeId = newVolume.Id;
}
await dataContext.SaveChangesAsync(); await dataContext.SaveChangesAsync();
} }

View File

@ -129,6 +129,35 @@ public static class MigrateMixedSpecials
//UpdateCoverImage(directoryService, logger, specialChapter, extension, newVolume); //UpdateCoverImage(directoryService, logger, specialChapter, extension, newVolume);
} }
var oldVolumeBookmarks = await dataContext.AppUserBookmark
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Bookmarks from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumeBookmarks.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var bookmark in oldVolumeBookmarks)
{
bookmark.VolumeId = newVolume.Id;
}
var oldVolumePersonalToC = await dataContext.AppUserTableOfContent
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumePersonalToC.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var pToc in oldVolumePersonalToC)
{
pToc.VolumeId = newVolume.Id;
}
var oldVolumeReadingListItems = await dataContext.ReadingListItem
.Where(p => p.VolumeId == distinctVolume.Volume.Id).ToListAsync();
logger.LogInformation("Moving {Count} existing Personal ToC from Volume Id {OldVolumeId} to New Volume {NewVolumeId}",
oldVolumeReadingListItems.Count, distinctVolume.Volume.Id, newVolume.Id);
foreach (var readingListItem in oldVolumeReadingListItems)
{
readingListItem.VolumeId = newVolume.Id;
}
await dataContext.SaveChangesAsync(); await dataContext.SaveChangesAsync();
} }

View File

@ -7,7 +7,7 @@ namespace API.Entities;
/// <summary> /// <summary>
/// Represents the progress a single user has on a given Chapter. /// Represents the progress a single user has on a given Chapter.
/// </summary> /// </summary>
public class AppUserProgress : IEntityDate public class AppUserProgress
{ {
/// <summary> /// <summary>
/// Id of Entity /// Id of Entity
@ -59,4 +59,10 @@ public class AppUserProgress : IEntityDate
/// User this progress belongs to /// User this progress belongs to
/// </summary> /// </summary>
public int AppUserId { get; set; } public int AppUserId { get; set; }
public void MarkModified()
{
LastModified = DateTime.Now;
LastModifiedUtc = DateTime.UtcNow;
}
} }

View File

@ -31,7 +31,7 @@ public static class SeriesSort
SortField.TimeToRead => query.DoOrderBy(s => s.AvgHoursToRead, sortOptions), SortField.TimeToRead => query.DoOrderBy(s => s.AvgHoursToRead, sortOptions),
SortField.ReleaseYear => query.DoOrderBy(s => s.Metadata.ReleaseYear, sortOptions), SortField.ReleaseYear => query.DoOrderBy(s => s.Metadata.ReleaseYear, sortOptions),
SortField.ReadProgress => query.DoOrderBy(s => s.Progress.Where(p => p.SeriesId == s.Id && p.AppUserId == userId) SortField.ReadProgress => query.DoOrderBy(s => s.Progress.Where(p => p.SeriesId == s.Id && p.AppUserId == userId)
.Select(p => p.LastModified) .Select(p => p.LastModified) // TODO: Migrate this to UTC
.Max(), sortOptions), .Max(), sortOptions),
SortField.AverageRating => query.DoOrderBy(s => s.ExternalSeriesMetadata.ExternalRatings SortField.AverageRating => query.DoOrderBy(s => s.ExternalSeriesMetadata.ExternalRatings
.Where(p => p.SeriesId == s.Id).Average(p => p.AverageScore), sortOptions), .Where(p => p.SeriesId == s.Id).Average(p => p.AverageScore), sortOptions),

View File

@ -134,7 +134,11 @@ public class ReaderService : IReaderService
VolumeId = chapter.VolumeId, VolumeId = chapter.VolumeId,
SeriesId = seriesId, SeriesId = seriesId,
ChapterId = chapter.Id, ChapterId = chapter.Id,
LibraryId = series.LibraryId LibraryId = series.LibraryId,
Created = DateTime.Now,
CreatedUtc = DateTime.UtcNow,
LastModified = DateTime.Now,
LastModifiedUtc = DateTime.UtcNow
}); });
} }
else else
@ -144,6 +148,8 @@ public class ReaderService : IReaderService
userProgress.VolumeId = chapter.VolumeId; userProgress.VolumeId = chapter.VolumeId;
} }
userProgress?.MarkModified();
await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate, await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate,
MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName!, seriesId, chapter.VolumeId, chapter.Id, chapter.Pages)); MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName!, seriesId, chapter.VolumeId, chapter.Id, chapter.Pages));
@ -177,6 +183,7 @@ public class ReaderService : IReaderService
userProgress.PagesRead = 0; userProgress.PagesRead = 0;
userProgress.SeriesId = seriesId; userProgress.SeriesId = seriesId;
userProgress.VolumeId = chapter.VolumeId; userProgress.VolumeId = chapter.VolumeId;
userProgress.MarkModified();
await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate, await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate,
MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName!, userProgress.SeriesId, userProgress.VolumeId, userProgress.ChapterId, 0)); MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName!, userProgress.SeriesId, userProgress.VolumeId, userProgress.ChapterId, 0));
@ -266,7 +273,11 @@ public class ReaderService : IReaderService
SeriesId = progressDto.SeriesId, SeriesId = progressDto.SeriesId,
ChapterId = progressDto.ChapterId, ChapterId = progressDto.ChapterId,
LibraryId = progressDto.LibraryId, LibraryId = progressDto.LibraryId,
BookScrollId = progressDto.BookScrollId BookScrollId = progressDto.BookScrollId,
Created = DateTime.Now,
CreatedUtc = DateTime.UtcNow,
LastModified = DateTime.Now,
LastModifiedUtc = DateTime.UtcNow
}); });
_unitOfWork.UserRepository.Update(userWithProgress); _unitOfWork.UserRepository.Update(userWithProgress);
} }
@ -280,6 +291,8 @@ public class ReaderService : IReaderService
_unitOfWork.AppUserProgressRepository.Update(userProgress); _unitOfWork.AppUserProgressRepository.Update(userProgress);
} }
userProgress?.MarkModified();
if (!_unitOfWork.HasChanges() || await _unitOfWork.CommitAsync()) if (!_unitOfWork.HasChanges() || await _unitOfWork.CommitAsync())
{ {
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId); var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);

View File

@ -31,7 +31,7 @@ public class BookParser(IDirectoryService directoryService, IBookService bookSer
{ {
var info2 = basicParser.Parse(filePath, rootPath, libraryRoot, LibraryType.Book, comicInfo); var info2 = basicParser.Parse(filePath, rootPath, libraryRoot, LibraryType.Book, comicInfo);
info.Merge(info2); info.Merge(info2);
if (type == LibraryType.LightNovel && hasVolumeInSeries && info2 != null && Parser.ParseVolume(info2.Series) if (hasVolumeInSeries && info2 != null && Parser.ParseVolume(info2.Series)
.Equals(Parser.LooseLeafVolume)) .Equals(Parser.LooseLeafVolume))
{ {
// Override the Series name so it groups appropriately // Override the Series name so it groups appropriately

View File

@ -107,11 +107,11 @@ public abstract class DefaultParser(IDirectoryService directoryService) : IDefau
{ {
info.Volumes = info.ComicInfo.Volume; info.Volumes = info.ComicInfo.Volume;
} }
if (string.IsNullOrEmpty(info.Series) && !string.IsNullOrEmpty(info.ComicInfo.Series)) if (!string.IsNullOrEmpty(info.ComicInfo.Series))
{ {
info.Series = info.ComicInfo.Series.Trim(); info.Series = info.ComicInfo.Series.Trim();
} }
if (string.IsNullOrEmpty(info.LocalizedSeries) && !string.IsNullOrEmpty(info.ComicInfo.LocalizedSeries)) if (!string.IsNullOrEmpty(info.ComicInfo.LocalizedSeries))
{ {
info.LocalizedSeries = info.ComicInfo.LocalizedSeries.Trim(); info.LocalizedSeries = info.ComicInfo.LocalizedSeries.Trim();
} }

View File

@ -232,7 +232,7 @@ public static class Parser
RegexTimeout), RegexTimeout),
// Gokukoku no Brynhildr - c001-008 (v01) [TrinityBAKumA], Black Bullet - v4 c17 [batoto] // Gokukoku no Brynhildr - c001-008 (v01) [TrinityBAKumA], Black Bullet - v4 c17 [batoto]
new Regex( new Regex(
@"(?<Series>.*)( - )(?:v|vo|c|chapters)\d", @"(?<Series>.+?)( - )(?:v|vo|c|chapters)\d",
MatchOptions, RegexTimeout), MatchOptions, RegexTimeout),
// Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip // Kedouin Makoto - Corpse Party Musume, Chapter 19 [Dametrans].zip
new Regex( new Regex(

View File

@ -22,6 +22,11 @@ public class PdfParser(IDirectoryService directoryService) : DefaultParser(direc
: Parser.ParseChapter(fileName) : Parser.ParseChapter(fileName)
}; };
if (type == LibraryType.Book)
{
ret.Chapters = Parser.DefaultChapter;
}
ret.Series = type == LibraryType.Comic ? Parser.ParseComicSeries(fileName) : Parser.ParseSeries(fileName); ret.Series = type == LibraryType.Comic ? Parser.ParseComicSeries(fileName) : Parser.ParseSeries(fileName);
ret.Volumes = type == LibraryType.Comic ? Parser.ParseComicVolume(fileName) : Parser.ParseVolume(fileName); ret.Volumes = type == LibraryType.Comic ? Parser.ParseComicVolume(fileName) : Parser.ParseVolume(fileName);

View File

@ -339,6 +339,9 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
get ShowStorylineTab() { get ShowStorylineTab() {
if (this.libraryType === LibraryType.ComicVine) return false; if (this.libraryType === LibraryType.ComicVine) return false;
// Edge case for bad pdf parse
if (this.libraryType === LibraryType.Book && (this.volumes.length === 0 && this.chapters.length === 0 && this.storyChapters.length > 0)) return true;
return (this.libraryType !== LibraryType.Book && this.libraryType !== LibraryType.LightNovel && this.libraryType !== LibraryType.Comic) return (this.libraryType !== LibraryType.Book && this.libraryType !== LibraryType.LightNovel && this.libraryType !== LibraryType.Comic)
&& (this.volumes.length > 0 || this.chapters.length > 0); && (this.volumes.length > 0 || this.chapters.length > 0);
} }
@ -727,7 +730,12 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
// Book libraries only have Volumes or Specials enabled // Book libraries only have Volumes or Specials enabled
if (this.libraryType === LibraryType.Book || this.libraryType === LibraryType.LightNovel) { if (this.libraryType === LibraryType.Book || this.libraryType === LibraryType.LightNovel) {
if (this.volumes.length === 0) { if (this.volumes.length === 0) {
if (this.specials.length === 0 && this.storyChapters.length > 0) {
// NOTE: This is an edge case caused by bad parsing of pdf files. Once the new pdf parser is in place, this should be removed
this.activeTabId = TabID.Storyline;
} else {
this.activeTabId = TabID.Specials; this.activeTabId = TabID.Specials;
}
} else { } else {
this.activeTabId = TabID.Volumes; this.activeTabId = TabID.Volumes;
} }

View File

@ -258,7 +258,10 @@ export class LibrarySettingsModalComponent implements OnInit {
forceScan() { forceScan() {
this.libraryService.scan(this.library!.id, true) this.libraryService.scan(this.library!.id, true)
.subscribe(() => this.toastr.info(translate('toasts.forced-scan-queued', {name: this.library!.name}))); .subscribe(() => {
this.toastr.info(translate('toasts.forced-scan-queued', {name: this.library!.name}));
this.close();
});
} }
async save() { async save() {

View File

@ -1,37 +1,23 @@
<ng-container *transloco="let t; read: 'kavitaplus-metadata-breakdown-stats'"> <ng-container *transloco="let t; read: 'kavitaplus-metadata-breakdown-stats'">
<div class="dashboard-card-content"> <div class="dashboard-card-content">
<h4>{{t('title')}}</h4> <h4>{{t('title')}}</h4>
<p>{{t('description')}}</p>
@if(breakdown) { @if(breakdown) {
@if(breakdown.totalSeries === 0 || breakdown.seriesCompleted === 0) { @if(breakdown.totalSeries === 0 || breakdown.seriesCompleted === 0) {
<div>{{t('no-data')}}</div> <div>{{t('no-data')}}</div>
}
@if (percentDone >= 1) {
<p>{{t('complete') }}</p>
} @else { } @else {
<p>{{t('total-series-progress-label', {percent: percentDone * 100 | percent}) }}</p> <ngb-progressbar-stacked>
<ngb-progressbar type="danger" [value]="errorPercent" [ngbTooltip]="t('errored-series-label') + ' ' + breakdown.erroredSeries"></ngb-progressbar>
<div class="day-breakdown-chart"> <ngb-progressbar type="success" [value]="completedPercent" [ngbTooltip]="t('completed-series-label') + ' ' + breakdown.seriesCompleted"></ngb-progressbar>
<table class="charts-css pie show-labels"> </ngb-progressbar-stacked>
<tbody> @if (breakdown.seriesCompleted >= breakdown.totalSeries) {
<p>{{t('complete') }}
<tr> @if (breakdown.erroredSeries > 0) {
<th scope="row">{{t('completed-series-label')}}</th> {{t('errored-series-label') }} {{breakdown.erroredSeries}}
<td class="completed" style="--start: 0; --end: ' + {{ completedEnd }}"> }
<span class="data">{{ breakdown.seriesCompleted }}</span> </p>
</td> }
</tr>
<tr>
<th scope="row">{{t('errored-series-label')}}</th>
<td class="error" style="--start: ' + {{ errorStart }}; --end: {{ errorEnd }}'">
<span class="data">{{ breakdown.erroredSeries }}</span>
</td>
</tr>
</tbody>
</table>
</div>
} }
} }
</div> </div>

View File

@ -3,13 +3,17 @@ import {StatisticsService} from "../../../_services/statistics.service";
import {KavitaPlusMetadataBreakdown} from "../../_models/kavitaplus-metadata-breakdown"; import {KavitaPlusMetadataBreakdown} from "../../_models/kavitaplus-metadata-breakdown";
import {TranslocoDirective} from "@ngneat/transloco"; import {TranslocoDirective} from "@ngneat/transloco";
import {PercentPipe} from "@angular/common"; import {PercentPipe} from "@angular/common";
import {NgbProgressbar, NgbProgressbarStacked, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
selector: 'app-kavitaplus-metadata-breakdown-stats', selector: 'app-kavitaplus-metadata-breakdown-stats',
standalone: true, standalone: true,
imports: [ imports: [
TranslocoDirective, TranslocoDirective,
PercentPipe PercentPipe,
NgbProgressbarStacked,
NgbProgressbar,
NgbTooltip
], ],
templateUrl: './kavitaplus-metadata-breakdown-stats.component.html', templateUrl: './kavitaplus-metadata-breakdown-stats.component.html',
styleUrl: './kavitaplus-metadata-breakdown-stats.component.scss', styleUrl: './kavitaplus-metadata-breakdown-stats.component.scss',
@ -20,20 +24,18 @@ export class KavitaplusMetadataBreakdownStatsComponent {
private readonly statsService = inject(StatisticsService); private readonly statsService = inject(StatisticsService);
breakdown: KavitaPlusMetadataBreakdown | undefined; breakdown: KavitaPlusMetadataBreakdown | undefined;
completedStart!: number;
completedEnd!: number; errorPercent!: number;
errorStart!: number; completedPercent!: number;
errorEnd!: number;
percentDone!: number;
constructor() { constructor() {
this.statsService.getKavitaPlusMetadataBreakdown().subscribe(res => { this.statsService.getKavitaPlusMetadataBreakdown().subscribe(res => {
this.breakdown = res; this.breakdown = res;
this.completedStart = 0;
this.completedEnd = ((res.seriesCompleted - res.erroredSeries) / res.totalSeries); this.errorPercent = (res.erroredSeries / res.totalSeries) * 100;
this.errorStart = this.completedEnd; this.completedPercent = ((res.seriesCompleted - res.erroredSeries) / res.totalSeries) * 100;
this.errorEnd = Math.max(1, ((res.seriesCompleted) / res.totalSeries));
this.percentDone = res.seriesCompleted / res.totalSeries;
this.cdRef.markForCheck(); this.cdRef.markForCheck();
}); });
} }

View File

@ -579,7 +579,7 @@
"published-label": "Published: ", "published-label": "Published: ",
"available": "Available", "available": "Available",
"description": "If you do not see an {{installed}}", "description": "If you do not see an {{installed}}",
"description-continued": "tag, you are on a nightly release. Only major versions will show as available." "description-continued": "tag, you are on a nightly release. Only major versions will show as available. A nightly tag will show when on a nightly from that stable."
}, },
"invite-user": { "invite-user": {
@ -1786,10 +1786,10 @@
"kavitaplus-metadata-breakdown-stats": { "kavitaplus-metadata-breakdown-stats": {
"title": "Kavita+ Metadata Breakdown", "title": "Kavita+ Metadata Breakdown",
"description": "Kavita fetches metadata (ratings, reviews, recommendations, etc) slowly over time for eligible series.",
"no-data": "No data", "no-data": "No data",
"errored-series-label": "Errored Series", "errored-series-label": "Errored Series",
"completed-series-label": "Completed Series", "completed-series-label": "Completed Series",
"total-series-progress-label": "Series Processed: {{percent}}",
"complete": "All Series have metadata" "complete": "All Series have metadata"
}, },

View File

@ -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.14.8" "version": "0.7.14.9"
}, },
"servers": [ "servers": [
{ {