mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-23 15:30:34 -04:00
Scanner Bugfixes (#2818)
This commit is contained in:
parent
829a610579
commit
93a8883fe4
6
.github/workflows/build-and-test.yml
vendored
6
.github/workflows/build-and-test.yml
vendored
@ -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
|
||||||
|
28
.github/workflows/canary-workflow.yml
vendored
28
.github/workflows/canary-workflow.yml
vendored
@ -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
|
||||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -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
|
||||||
|
28
.github/workflows/develop-workflow.yml
vendored
28
.github/workflows/develop-workflow.yml
vendored
@ -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
|
||||||
|
26
.github/workflows/release-workflow.yml
vendored
26
.github/workflows/release-workflow.yml
vendored
@ -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
|
||||||
|
@ -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));
|
||||||
|
@ -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" />
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -14,6 +14,6 @@ export class FilterPipe implements PipeTransform {
|
|||||||
const ret = items.filter(item => callback(item));
|
const ret = items.filter(item => callback(item));
|
||||||
if (ret.length === items.length) return items; // This will prevent a re-render
|
if (ret.length === items.length) return items; // This will prevent a re-render
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user