Compare commits

...

46 commits

Author SHA1 Message Date
github-actions[bot]
361d20df2c build(webui): update Browserslist db 2025-12-01 11:23:06 +08:00
github-actions
ced89c5c54 chore(release): 1.23.6 [skip ci] 2025-11-28 03:43:03 +00:00
Hosted Weblate
a5548a5429 i18n(komga-tray): translated using Weblate (Arabic)
Currently translated at 100.0% (9 of 9 strings)

i18n(komga-tray): translated using Weblate (Arabic)

Currently translated at 33.3% (3 of 9 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: redaloe <farahks@proton.me>
Co-authored-by: redaloe <redaloe@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/desktop/ar/
Translation: komga/desktop
2025-11-28 11:11:17 +08:00
Hosted Weblate
8f8d20a324 i18n(komga-tray): translated using Weblate (Russian)
Currently translated at 77.7% (7 of 9 strings)

Co-authored-by: Aleksey <mitin_aleksey@mail.ru>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/desktop/ru/
Translation: komga/desktop
2025-11-28 11:11:17 +08:00
Hosted Weblate
0f69a3a4cb i18n(komga-tray): translated using Weblate (Galician)
Currently translated at 100.0% (9 of 9 strings)

Co-authored-by: Francisco José Aquino García <fj.aquino@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/komga/desktop/gl/
Translation: komga/desktop
2025-11-28 11:11:17 +08:00
Hosted Weblate
9d10ed31a7 chore: update translation files
Updated by "Remove blank strings" hook in Weblate.

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

i18n(webui): translated using Weblate (Arabic)

Currently translated at 91.6% (770 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: redaloe <redaloe@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/
Translate-URL: https://hosted.weblate.org/projects/komga/webui/ar/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Hosted Weblate
dde0169f2a i18n(webui): translated using Weblate (Croatian)
Currently translated at 100.0% (840 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/hr/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Hosted Weblate
a2ed7d319d i18n(webui): translated using Weblate (Russian)
Currently translated at 64.6% (543 of 840 strings)

i18n(webui): translated using Weblate (Russian)

Currently translated at 67.0% (563 of 840 strings)

Co-authored-by: Aleksey <mitin_aleksey@mail.ru>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/ru/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Hosted Weblate
475f026749 i18n(webui): translated using Weblate (Portuguese (Brazil))
Currently translated at 79.5% (668 of 840 strings)

i18n(webui): translated using Weblate (Portuguese (Brazil))

Currently translated at 79.5% (668 of 840 strings)

i18n(webui): translated using Weblate (Portuguese (Brazil))

Currently translated at 79.5% (668 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Luiz Henrique Moreira de Souza <cloud.5623tumacacori@gmail.com>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/pt_BR/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Hosted Weblate
a03f1bdf7b i18n(webui): translated using Weblate (Thai)
Currently translated at 100.0% (840 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: altinat <al@altqx.com>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/th/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Hosted Weblate
b43046fbeb chore: update translation files
Updated by "Remove blank strings" hook in Weblate.

i18n(webui): translated using Weblate (Galician)

Currently translated at 20.1% (169 of 840 strings)

Co-authored-by: Francisco José Aquino García <fj.aquino@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/
Translate-URL: https://hosted.weblate.org/projects/komga/webui/gl/
Translation: komga/webui
2025-11-28 11:10:47 +08:00
Jason
3739951b36
fix(kobo): proxy 401 errors on initialization
Co-authored-by: Gauthier Roebroeck <gauthier.roebroeck@gmail.com>
2025-11-28 11:10:01 +08:00
dependabot[bot]
0f25453949 deps(webui): bump node-forge from 1.3.1 to 1.3.2 in /komga-webui
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.1 to 1.3.2.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-27 11:02:30 +08:00
dependabot[bot]
cd47fc777a deps(webui): bump js-yaml from 3.14.1 to 3.14.2 in /komga-webui
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.14.1 to 3.14.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 16:07:21 +08:00
dependabot[bot]
f138fe31e7 deps(ci): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 15:39:11 +08:00
Jason
454c6c7202
refactor(kobo): log error responses 2025-11-13 13:32:16 +08:00
Jason
ce3ad4c1c7
fix(kobo): prevent double URL encoding when proxying
Closes: #2130
2025-11-11 15:52:32 +08:00
Jason
b925f3e19d
fix(kobo): proxy Content-Type headers for kobo
Closes: #2074
2025-11-10 15:26:07 +08:00
Gauthier Roebroeck
9a56b30b6c ci: fix svu install 2025-11-03 16:00:14 +08:00
dependabot[bot]
6b07fda273 deps(ci): bump mikepenz/action-junit-report from 5 to 6
Bumps [mikepenz/action-junit-report](https://github.com/mikepenz/action-junit-report) from 5 to 6.
- [Release notes](https://github.com/mikepenz/action-junit-report/releases)
- [Commits](https://github.com/mikepenz/action-junit-report/compare/v5...v6)

---
updated-dependencies:
- dependency-name: mikepenz/action-junit-report
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 15:12:21 +08:00
github-actions[bot]
727fe39e6d build(webui): update Browserslist db 2025-11-03 10:39:12 +08:00
Gauthier Roebroeck
f8ca936ee7 fix: properly decode cover href when generating epub cover
Closes: #2118
2025-11-03 10:38:43 +08:00
dependabot[bot]
fe40ede34a deps(ci): bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 10:46:45 +08:00
dependabot[bot]
c23f2d3810 deps(ci): bump actions/setup-node from 5 to 6
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 15:51:56 +08:00
Gauthier Roebroeck
af66144060 docs(api): fix mediatype 2025-10-14 14:00:34 +08:00
Gauthier Roebroeck
ba7b82631f build(docker): use old-releases apt repo 2025-10-08 16:42:12 +08:00
github-actions
a166f96bdf chore(release): 1.23.5 [skip ci] 2025-10-08 07:21:06 +00:00
Hosted Weblate
2259e4bf1c i18n(komga-tray): translated using Weblate (Portuguese (Brazil))
Currently translated at 55.5% (5 of 9 strings)

Co-authored-by: lucas philippe <lucas.philippe.nunes@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/komga/desktop/pt_BR/
Translation: komga/desktop
2025-10-08 15:09:32 +08:00
Hosted Weblate
f75ad77e85 i18n(webui): translated using Weblate (Slovak)
Currently translated at 100.0% (840 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: fantastron27 <fantastron27@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/sk/
Translation: komga/webui
2025-10-08 15:08:24 +08:00
Hosted Weblate
f2913d1e83 i18n(webui): translated using Weblate (Croatian)
Currently translated at 100.0% (840 of 840 strings)

Co-authored-by: Milo Ivir <mail@milotype.de>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/hr/
Translation: komga/webui
2025-10-08 15:08:24 +08:00
Hosted Weblate
0b3307cd70 i18n(webui): translated using Weblate (Czech)
Currently translated at 100.0% (840 of 840 strings)

i18n(webui): translated using Weblate (Czech)

Currently translated at 99.8% (839 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Petr Šimek <petr.simek@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/cs/
Translation: komga/webui
2025-10-08 15:08:24 +08:00
Hosted Weblate
1213309f35 i18n(webui): translated using Weblate (Portuguese (Brazil))
Currently translated at 77.1% (648 of 840 strings)

i18n(webui): translated using Weblate (Portuguese (Brazil))

Currently translated at 77.1% (648 of 840 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: lucas philippe <lucas.philippe.nunes@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/komga/webui/pt_BR/
Translation: komga/webui
2025-10-08 15:08:24 +08:00
Gauthier Roebroeck
5a5f8d701e fix(api): empty content when x-api-key is sent alongside session
Closes: #2099
2025-10-08 14:47:10 +08:00
dependabot[bot]
bdca990e82 deps(ci): bump peter-evans/dockerhub-description from 4.0.2 to 5.0.0
Bumps [peter-evans/dockerhub-description](https://github.com/peter-evans/dockerhub-description) from 4.0.2 to 5.0.0.
- [Release notes](https://github.com/peter-evans/dockerhub-description/releases)
- [Commits](https://github.com/peter-evans/dockerhub-description/compare/v4.0.2...v5.0.0)

---
updated-dependencies:
- dependency-name: peter-evans/dockerhub-description
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 15:45:50 +08:00
dependabot[bot]
8081439009 deps(ci): bump gradle/actions from 4 to 5
Bumps [gradle/actions](https://github.com/gradle/actions) from 4 to 5.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/v4...v5)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 15:44:31 +08:00
dependabot[bot]
80c604e089 deps(ci): bump peter-evans/repository-dispatch from 3 to 4
Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 3 to 4.
- [Release notes](https://github.com/peter-evans/repository-dispatch/releases)
- [Commits](https://github.com/peter-evans/repository-dispatch/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/repository-dispatch
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 15:44:07 +08:00
Gauthier Roebroeck
f19d7aac1e feat: support local artwork in gif format
Closes: #1853
2025-10-03 15:07:59 +08:00
Gauthier Roebroeck
43c1018e3e perf(api): remove no-transform cache-control from response header
Closes: #2091
2025-10-03 12:00:48 +08:00
Gauthier Roebroeck
eb8bdfc94c fix(api): relax JSON deserializer 2025-10-03 11:51:50 +08:00
github-actions[bot]
e842a5287f build(webui): update Browserslist db 2025-10-02 10:22:35 +08:00
dependabot[bot]
e0b583ff1d deps(ci): bump hydraulic-software/conveyor from 19.0 to 20.0
Bumps [hydraulic-software/conveyor](https://github.com/hydraulic-software/conveyor) from 19.0 to 20.0.
- [Commits](https://github.com/hydraulic-software/conveyor/compare/v19.0...v20.0)

---
updated-dependencies:
- dependency-name: hydraulic-software/conveyor
  dependency-version: '20.0'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-23 08:59:10 +08:00
Gauthier Roebroeck
5e3ca4d571 fix(api): add id field in HistoricalEventDto 2025-09-16 14:23:16 +08:00
Gauthier Roebroeck
730b093a5f refactor: add more logs when epub extension is missing 2025-09-16 11:38:14 +08:00
Gauthier Roebroeck
2f9b4e75d2 refactor: add more logs to koreader sync controller 2025-09-16 11:38:00 +08:00
dependabot[bot]
d9657587c4 deps(webui): bump axios from 1.8.2 to 1.12.0 in /komga-webui
Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.8.2...v1.12.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 09:17:13 +08:00
Gauthier Roebroeck
69ba569b04 refactor: make dslRO transaction aware 2025-09-09 12:32:17 +08:00
71 changed files with 2707 additions and 443 deletions

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Configure git

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v3
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.REPO_ACCESS_TOKEN }}
repository: gotson/komga-website

View file

@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@master
- name: DockerHub Description
uses: peter-evans/dockerhub-description@v4.0.2
uses: peter-evans/dockerhub-description@v5.0.0
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}

View file

@ -43,14 +43,14 @@ jobs:
version_next: ${{ steps.versions.outputs.version_next }}
should_release: ${{ steps.versions.outputs.should_release }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Install svu
run: brew install caarlos0/tap/svu
run: brew install --cask caarlos0/tap/svu
- name: Compute next version for release
run: |
echo "VERSION_NEXT=`svu ${{ inputs.bump }}`" | tee -a $GITHUB_ENV
@ -72,7 +72,7 @@ jobs:
sudo rm -rf /usr/share/dotnet
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
@ -84,7 +84,7 @@ jobs:
if: needs.version.outputs.should_release #only redo if the version changed
run: sed -i -e "s/version=.*/version=${{ needs.version.outputs.version_next }}/" gradle.properties
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: 'npm'
@ -117,7 +117,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Build
run: ./gradlew :komga:prepareThymeLeaf :komga:bootJar :komga-tray:jar
@ -143,7 +143,7 @@ jobs:
JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: JReleaser Changelog output
if: always() && needs.version.outputs.should_release
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: jreleaser-changelog
path: |
@ -167,7 +167,7 @@ jobs:
echo $APPLE_PRIVATE_KEY | base64 --decode > ./secret/apple_private_key.p8
- name: Conveyor make copied-site
uses: hydraulic-software/conveyor/actions/build@v19.0
uses: hydraulic-software/conveyor/actions/build@v20.0
if: inputs.conveyor-copied-site
with:
command: --cache-limit=2.0 -f conveyor.ci.conf make copied-site -o ./output/site
@ -182,7 +182,7 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.B2_SECRET_ACCESS_KEY }}
- name: Upload Conveyor log
if: always() && inputs.conveyor-copied-site
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: conveyor-make-copied-site
path: ~/.cache/hydraulic/conveyor/logs/log.latest.txt
@ -194,7 +194,7 @@ jobs:
JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: JReleaser Release output
if: always() && inputs.github_release
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: jreleaser-release
path: |
@ -212,7 +212,7 @@ jobs:
JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: JReleaser Publish output
if: always() && inputs.docker_release
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: jreleaser-publish
path: |
@ -220,7 +220,7 @@ jobs:
build/jreleaser/output.properties
- name: Conveyor - publish to Microsoft Store
uses: hydraulic-software/conveyor/actions/build@v19.0
uses: hydraulic-software/conveyor/actions/build@v20.0
if: inputs.msstore_release
with:
command: --cache-limit=2.0 -f conveyor.msstore.ci.conf make ms-store-release -o ./output/msstore
@ -236,7 +236,7 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.B2_SECRET_ACCESS_KEY }}
- name: Upload Conveyor log
if: always() && inputs.msstore_release
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: conveyor-ms-store-release
path: ~/.cache/hydraulic/conveyor/logs/log.latest.txt
@ -246,7 +246,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v3
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.REPO_ACCESS_TOKEN }}
repository: gotson/komga-website

View file

@ -19,7 +19,7 @@ jobs:
fail-fast: false
name: Test server - ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Java 21
uses: actions/setup-java@v5
with:
@ -28,27 +28,27 @@ jobs:
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Build
run: ./gradlew build :komga-tray:jar
- name: Upload Unit Test Results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: test-results-${{ matrix.os }}
path: komga/build/test-results/
- name: Upload Unit Test Reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: test-reports-${{ matrix.os }}
path: komga/build/reports/tests/
- name: Publish Test Report
uses: mikepenz/action-junit-report@v5
uses: mikepenz/action-junit-report@v6
if: always()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
@ -56,7 +56,7 @@ jobs:
- name: Conveyor - compute JDK module list
if: github.event_name == 'push' && github.repository_owner == 'gotson' && contains(matrix.os, 'ubuntu')
uses: hydraulic-software/conveyor/actions/build@v19.0
uses: hydraulic-software/conveyor/actions/build@v20.0
with:
command: -f conveyor.detect.conf -Kapp.machines=mac.aarch64 make processed-jars
signing_key: ${{ secrets.CONVEYOR_SIGNING_KEY }}
@ -69,7 +69,7 @@ jobs:
- name: Upload JDK required modules
if: steps.conveyor_compare.outcome == 'failure'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: conveyor-required-jdk-modules
path: ./output/required-jdk-modules.txt
@ -78,8 +78,8 @@ jobs:
runs-on: ubuntu-latest
name: Test webui builds
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: 'npm'

View file

@ -1,3 +1,99 @@
# [1.23.6](https://github.com/gotson/komga/compare/1.23.5...1.23.6) (2025-11-28)
## 🐛 Fixes
**kobo**
- proxy 401 errors on initialization ([3739951](https://github.com/gotson/komga/commits/3739951))
- prevent double URL encoding when proxying ([ce3ad4c](https://github.com/gotson/komga/commits/ce3ad4c)), closes [#2130](https://github.com/gotson/komga/issues/2130)
- proxy Content-Type headers for kobo ([b925f3e](https://github.com/gotson/komga/commits/b925f3e)), closes [#2074](https://github.com/gotson/komga/issues/2074)
**unscoped**
- properly decode cover href when generating epub cover ([f8ca936](https://github.com/gotson/komga/commits/f8ca936)), closes [#2118](https://github.com/gotson/komga/issues/2118)
## 🔄️ Changes
**kobo**
- log error responses ([454c6c7](https://github.com/gotson/komga/commits/454c6c7))
## 🛠 Build
**docker**
- use old-releases apt repo ([ba7b826](https://github.com/gotson/komga/commits/ba7b826))
**webui**
- update Browserslist db ([727fe39](https://github.com/gotson/komga/commits/727fe39))
**unscoped**
- fix svu install ([9a56b30](https://github.com/gotson/komga/commits/9a56b30))
## 📝 Documentation
**api**
- fix mediatype ([af66144](https://github.com/gotson/komga/commits/af66144))
## 🌐 Translation
**komga-tray**
- translated using Weblate (Arabic) ([a5548a5](https://github.com/gotson/komga/commits/a5548a5))
- translated using Weblate (Russian) ([8f8d20a](https://github.com/gotson/komga/commits/8f8d20a))
- translated using Weblate (Galician) ([0f69a3a](https://github.com/gotson/komga/commits/0f69a3a))
**webui**
- translated using Weblate (Croatian) ([dde0169](https://github.com/gotson/komga/commits/dde0169))
- translated using Weblate (Russian) ([a2ed7d3](https://github.com/gotson/komga/commits/a2ed7d3))
- translated using Weblate (Portuguese (Brazil)) ([475f026](https://github.com/gotson/komga/commits/475f026))
- translated using Weblate (Thai) ([a03f1bd](https://github.com/gotson/komga/commits/a03f1bd))
## ⚙️ Dependencies
**ci**
- bump actions/checkout from 5 to 6 ([f138fe3](https://github.com/gotson/komga/commits/f138fe3))
- bump mikepenz/action-junit-report from 5 to 6 ([6b07fda](https://github.com/gotson/komga/commits/6b07fda))
- bump actions/upload-artifact from 4 to 5 ([fe40ede](https://github.com/gotson/komga/commits/fe40ede))
- bump actions/setup-node from 5 to 6 ([c23f2d3](https://github.com/gotson/komga/commits/c23f2d3))
**webui**
- bump node-forge from 1.3.1 to 1.3.2 in /komga-webui ([0f25453](https://github.com/gotson/komga/commits/0f25453))
- bump js-yaml from 3.14.1 to 3.14.2 in /komga-webui ([cd47fc7](https://github.com/gotson/komga/commits/cd47fc7))
# [1.23.5](https://github.com/gotson/komga/compare/1.23.4...1.23.5) (2025-10-08)
## 🚀 Features
- support local artwork in gif format ([f19d7aa](https://github.com/gotson/komga/commits/f19d7aa)), closes [#1853](https://github.com/gotson/komga/issues/1853)
## 🐛 Fixes
**api**
- empty content when x-api-key is sent alongside session ([5a5f8d7](https://github.com/gotson/komga/commits/5a5f8d7)), closes [#2099](https://github.com/gotson/komga/issues/2099)
- relax JSON deserializer ([eb8bdfc](https://github.com/gotson/komga/commits/eb8bdfc))
- add id field in HistoricalEventDto ([5e3ca4d](https://github.com/gotson/komga/commits/5e3ca4d))
## 🏎 Perf
**api**
- remove no-transform cache-control from response header ([43c1018](https://github.com/gotson/komga/commits/43c1018)), closes [#2091](https://github.com/gotson/komga/issues/2091)
## 🔄️ Changes
- add more logs when epub extension is missing ([730b093](https://github.com/gotson/komga/commits/730b093))
- add more logs to koreader sync controller ([2f9b4e7](https://github.com/gotson/komga/commits/2f9b4e7))
- make dslRO transaction aware ([69ba569](https://github.com/gotson/komga/commits/69ba569))
## 🛠 Build
**webui**
- update Browserslist db ([e842a52](https://github.com/gotson/komga/commits/e842a52))
## 🌐 Translation
**komga-tray**
- translated using Weblate (Portuguese (Brazil)) ([2259e4b](https://github.com/gotson/komga/commits/2259e4b))
**webui**
- translated using Weblate (Slovak) ([f75ad77](https://github.com/gotson/komga/commits/f75ad77))
- translated using Weblate (Croatian) ([f2913d1](https://github.com/gotson/komga/commits/f2913d1))
- translated using Weblate (Czech) ([0b3307c](https://github.com/gotson/komga/commits/0b3307c))
- translated using Weblate (Portuguese (Brazil)) ([1213309](https://github.com/gotson/komga/commits/1213309))
## ⚙️ Dependencies
**ci**
- bump peter-evans/dockerhub-description from 4.0.2 to 5.0.0 ([bdca990](https://github.com/gotson/komga/commits/bdca990))
- bump gradle/actions from 4 to 5 ([8081439](https://github.com/gotson/komga/commits/8081439))
- bump peter-evans/repository-dispatch from 3 to 4 ([80c604e](https://github.com/gotson/komga/commits/80c604e))
- bump hydraulic-software/conveyor from 19.0 to 20.0 ([e0b583f](https://github.com/gotson/komga/commits/e0b583f))
**webui**
- bump axios from 1.8.2 to 1.12.0 in /komga-webui ([d965758](https://github.com/gotson/komga/commits/d965758))
# [1.23.4](https://github.com/gotson/komga/compare/1.23.3...1.23.4) (2025-09-09)
## 🐛 Fixes
**kobo**

View file

@ -1,2 +1,2 @@
version=1.23.4
version=1.23.6
org.gradle.jvmargs=-Xmx2G

View file

@ -0,0 +1,9 @@
dialog_error.close=إغلاق
dialog_error.copy_clipboard=النسخ إلى الحافظة
dialog_error.title=Komga لم يقدر على البدء
error_message.port_in_use=الباب {} على قيد الاستعمال.\nربما Komga جارٍ بالفعل.\nتحقق أيقونة علبة النظام أو القائمة لأيقونة Komga.
error_message.unexpected=حصل خطأ غير متوقع.
menu.open_komga=فتح Komga
menu.quit=إغلاق Komga
menu.show_conf_dir=فتح موقع الإعدادات
menu.show_log=إظهار ملف السجل

View file

@ -0,0 +1,9 @@
dialog_error.close=Pechar
dialog_error.copy_clipboard=Copiar ao portapapeis
dialog_error.title=Komga fallou ao iniciar
error_message.port_in_use=O porto {} está en uso.\nÉ probable que Komga xa esté a executarse.\nBusca a icona de Komga na bandexa de sistema ou a barra de menú.
error_message.unexpected=Aconteceu un erro imprevisto.
menu.open_komga=Abrir Komga
menu.quit=Saír de Komga
menu.show_conf_dir=Abrir o cartafol de configuración
menu.show_log=Amosar o ficheiro de rexistro

View file

@ -0,0 +1,9 @@
dialog_error.close=Fechar
dialog_error.copy_clipboard=Copiar para área de transferência
dialog_error.title=Komga falhou ao inicializar
error_message.port_in_use=A porta {} já está em uso.\nProvavelmente o Komga já está rodando.\nVerifique o ícone na barra de tarefas ou a barra de menu.
error_message.unexpected=Ocorreu um erro inesperado.
menu.open_komga=Abrir Komga
menu.quit=Sair do Komga
menu.show_conf_dir=Abrir configuração de diretório
menu.show_log=Mostrar arquivo de logs

View file

@ -1,7 +1,7 @@
dialog_error.close=Закрыть
dialog_error.copy_clipboard=Скопировать в буфер обмена
dialog_error.title=Komga не смог запуститься
error_message.port_in_use=Порт {] уже используется.\nKomga возможно уже работает.\nПроверьте панель задач или меню на наличие иконки Komga.
dialog_error.title=Komga не удалось запустить
error_message.port_in_use=Порт {} уже используется.\nВероятно, Komga уже запущен.\nПроверьте область уведомлений или панель меню на наличие иконки Komga.
error_message.unexpected=Произошла непредвиденная ошибка.
menu.open_komga=Открыть Komga
menu.quit=Закрыть Komga

View file

@ -10,7 +10,7 @@
"dependencies": {
"@d-i-t-a/reader": "github:gotson/R2D2BC#fork",
"@saekitominaga/isbn-verify": "^2.0.1",
"axios": "^1.8.2",
"axios": "^1.12.0",
"chart.js": "^2.9.4",
"core-js": "^3.8.3",
"date-fns": "^2.30.0",
@ -5102,13 +5102,12 @@
"integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
},
"node_modules/axios": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"license": "MIT",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz",
"integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@ -5715,6 +5714,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
@ -5768,9 +5779,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001739",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
"integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
"version": "1.0.30001757",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
"integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==",
"funding": [
{
"type": "opencollective",
@ -7288,6 +7299,19 @@
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@ -7614,12 +7638,9 @@
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"engines": {
"node": ">= 0.4"
}
@ -7638,15 +7659,26 @@
"integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==",
"dev": true
},
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
"integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
"dev": true,
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dependencies": {
"get-intrinsic": "^1.1.3",
"has": "^1.0.3",
"has-tostringtag": "^1.0.0"
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
@ -9240,12 +9272,14 @@
"dev": true
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@ -9406,15 +9440,20 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@ -9432,6 +9471,18 @@
"node": ">=8.0.0"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@ -9549,11 +9600,11 @@
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "^1.1.3"
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -9657,6 +9708,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@ -9665,9 +9717,9 @@
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"engines": {
"node": ">= 0.4"
},
@ -9676,11 +9728,11 @@
}
},
"node_modules/has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dependencies": {
"has-symbols": "^1.0.2"
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@ -12974,9 +13026,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"dependencies": {
"argparse": "^1.0.7",
@ -13683,6 +13735,14 @@
"node": ">= 18"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@ -14101,9 +14161,9 @@
}
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
"integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==",
"dev": true,
"engines": {
"node": ">= 6.13.0"
@ -23587,12 +23647,12 @@
"integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
},
"axios": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz",
"integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==",
"requires": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@ -24043,6 +24103,15 @@
"set-function-length": "^1.2.1"
}
},
"call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"requires": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
}
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
@ -24084,9 +24153,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001739",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
"integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA=="
"version": "1.0.30001757",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
"integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ=="
},
"case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
@ -25214,6 +25283,16 @@
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true
},
"dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"requires": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
}
},
"duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@ -25489,12 +25568,9 @@
}
},
"es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"requires": {
"get-intrinsic": "^1.2.4"
}
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
},
"es-errors": {
"version": "1.3.0",
@ -25507,15 +25583,23 @@
"integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==",
"dev": true
},
"es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
"integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
"dev": true,
"es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"requires": {
"get-intrinsic": "^1.1.3",
"has": "^1.0.3",
"has-tostringtag": "^1.0.0"
"es-errors": "^1.3.0"
}
},
"es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"requires": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
}
},
"es-shim-unscopables": {
@ -26700,12 +26784,14 @@
}
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
}
},
@ -26818,15 +26904,20 @@
"dev": true
},
"get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"requires": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
}
},
"get-package-type": {
@ -26835,6 +26926,15 @@
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true
},
"get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"requires": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@ -26919,12 +27019,9 @@
}
},
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"requires": {
"get-intrinsic": "^1.1.3"
}
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
},
"graceful-fs": {
"version": "4.2.11",
@ -26998,19 +27095,20 @@
"has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dev": true
},
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
},
"has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"requires": {
"has-symbols": "^1.0.2"
"has-symbols": "^1.0.3"
}
},
"hash-sum": {
@ -29407,9 +29505,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@ -29975,6 +30073,11 @@
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.4.tgz",
"integrity": "sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw=="
},
"math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
},
"mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@ -30295,9 +30398,9 @@
}
},
"node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
"integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==",
"dev": true
},
"node-int64": {

View file

@ -11,7 +11,7 @@
"dependencies": {
"@d-i-t-a/reader": "github:gotson/R2D2BC#fork",
"@saekitominaga/isbn-verify": "^2.0.1",
"axios": "^1.8.2",
"axios": "^1.12.0",
"chart.js": "^2.9.4",
"core-js": "^3.8.3",
"date-fns": "^2.30.0",

View file

@ -19,7 +19,15 @@
},
"account_settings": {
"account_settings": "إعدادات الحساب",
"change_password": "تغيير كلمة السر"
"api_key": {
"created_date": "تاريخ الصنع: {date}",
"force_kobo_sync": "إجبار مزامنة Kobo",
"generate_api_key": "توليد مفتاح API",
"no_keys": "لم يخلق مفتاح API بعد"
},
"change_password": "تغيير كلمة السر",
"details": "تفاصيل",
"my_account": "حسابي"
},
"announcements": {
"mark_all_read": "اشر عليها بانها قرأت",
@ -27,6 +35,7 @@
"tab_title": "إعلانات"
},
"authentication_activity": {
"api_key": "مفتاح API",
"datetime": "التاريخ والوقت",
"email": "البريد الإلكتروني",
"error": "خطأ",
@ -85,7 +94,8 @@
"bookreader": {
"beginning_of_book": "أنت في بداية الكتاب.",
"changing_reading_direction": "تغيير اتجاه القراءة إلى",
"cycling_page_layout": "تخطيط دوري للصفحة",
"cycling_page_layout": "تغيير تخطيط الصفحة",
"cycling_page_margin": "تغيير هامش الصفحة",
"cycling_scale": "تغيير حجم التحرير",
"cycling_side_padding": "تغيير الحدود الجانبية",
"download_current_page": "تنزيل الصفحة الحالية",
@ -128,6 +138,7 @@
"general": "إعدادات عامة",
"gestures": "الإيماءات",
"page_layout": "تخطيط الصفحة",
"page_margin": "هامش الصفحة",
"paged": "خيارات القارئ المرقم",
"reading_mode": "وضع القراءة",
"scale_type": "نوع القياس",
@ -137,8 +148,9 @@
},
"shortcuts": {
"close": "إغلاق",
"cycle_page_layout": "دورة تخطيط الصفحة",
"cycle_scale": "مقياس الدورة",
"cycle_page_layout": "تغيير تخطيط الصفحة",
"cycle_page_margin": "تغيير هامش الصفحة",
"cycle_scale": "تغيير الحجم",
"cycle_side_padding": "تغيير الحدود الجانبية",
"first_page": "الصفحة الأولى",
"fullscreen": "إدخال/إنهاء ملء الشاشة",
@ -161,6 +173,8 @@
},
"browse_book": {
"comment": "تعليق",
"date_created": "صُنِع",
"date_modified": "آخر تعديل",
"download_file": "تحميل الملف",
"file": "ملف",
"format": "صيغة",
@ -170,6 +184,8 @@
"outdated_tooltip": "تم تغيير ملف هذا الكتاب ، يجب إعادة تحليل هذا الكتاب",
"read_book": "قراءة كتاب",
"read_incognito": "قراءة التصفح المتخفي",
"remove_from_collection": "إزالة الكتاب من المجموعة",
"remove_from_readlist": "إزالة الكتاب من قائمة القراءة",
"size": "حجم"
},
"browse_collection": {
@ -184,6 +200,7 @@
},
"browse_series": {
"earliest_year_from_release_dates": "هذه هي السنة الأولى من تواريخ الإصدار لجميع الكتب في السلسلة",
"remove_from_collection": "إزالة السلسلة من المجموعة",
"series_no_summary": "هذه السلسلة لا تحتوي على ملخص ، لذلك اخترنا واحدًا لك!",
"summary_from_book": "ملخص من الكتاب {number}:"
},
@ -194,6 +211,8 @@
"common": {
"age": "العمر",
"all_libraries": "كل المكتبات",
"all_of": "كل من",
"any_of": "أي من",
"book": "كتاب",
"books": "كتب",
"books_n": "لا يوجد كتاب | 1 كتاب | {count} كتب",
@ -203,15 +222,19 @@
"choose_image": "اختر صورة",
"close": "إغلاق",
"collections": "المجموعات",
"copied": "تم النسخ!",
"create": "صنع",
"delete": "حذف",
"discard": "تجاهل",
"dimension": "ع:{width}, ط:{height}",
"discard": "تخلص",
"disk_space": "مساحة القرص",
"dismiss": "رفض",
"download": "تحميل",
"drag_drop": "أمسك واسحب",
"duplicate": "مكرر",
"email": "البريد الإلكتروني",
"epub": "Epub",
"epub": "EPUB",
"error": "خطأ",
"filename": "اسم الملف",
"filter_no_matches": "الفلتر النشط ليس له تطابق",
"genre": "نوع",
@ -219,13 +242,17 @@
"go_to_library": "اذهب إلى المكتبة",
"go_to_readlist": "انتقل إلى قائمة القراءة",
"go_to_series": "الانتقال إلى السلسلة",
"i_understand": "فهمت",
"library": "مكتبة",
"locale_name": "العربية",
"locale_rtl": "true",
"lock_all": "قفل الكل",
"media": "الوسائط",
"more": "المزيد",
"n_selected": "{count} مختار",
"nothing_to_show": "لا شيء للعرض",
"oneshot": "طلقة واحدة",
"ok": "حسنا",
"oneshot": "قصة منفردة",
"outdated": "متقادمة",
"page": "صفحة",
"page_number": "رقم الصفحة",
@ -235,19 +262,23 @@
"password": "كلمة السر",
"pdf": "PDF",
"pending_tasks": "لا توجد مهام معلقة | مهمة واحدة معلقة | {count} مهام معلقة",
"pinned_libraries": "المكتبات المثبتة",
"publisher": "الناشر",
"read": "اقرأ",
"read_on": "قرأ في {date}",
"readlist": "قائمة القراءة",
"readlists": "قوائم القراءة",
"remember-me": "تذكرني",
"reorder": "تغيير الترتيب",
"required": "مطلوب",
"reset_filters": "إعادة تعيين فلتر",
"roles": "أدوار",
"save_changes": "حفظ التغييرات",
"series": "سلسلة",
"settings": "الإعدادات",
"sidecars": "Sidecars",
"tags": "التصنيف",
"ui": "واجهة المستخدم",
"unavailable": "غير متوفر",
"unlock_all": "فتح الكل",
"url": "عنوان URL",
@ -271,10 +302,11 @@
"comicrack_preambule_html": "يمكنك استيراد قوائم قراءة ComicRack الحالية بتنسيق <code>cbl</code>.<br>سيحاول Komga مطابقة السلسلة ورقم الكتاب المقدمين مع السلسلة والكتب في مكتباتك.",
"dialog_confirmation": {
"body": "{unmatched} / {total} الكتب غير متطابقة",
"body2": "{duplicates} / {total} كتاب مكرر",
"create": "الخلق على أي حال",
"title": "بعض الكتب لا تتطابق"
},
"field_file_label": "ComicRack Reading List (.cbl)",
"field_file_label": "قائمة قراءة ComicRack (من نوع cbl.)",
"field_files_label": "قوائم القراءة ComicRack (.cbl)",
"import_read_lists": "استيراد قوائم القراءة",
"imported_as": "مستورد كـ {name}",
@ -286,6 +318,14 @@
"tab_title": "استيراد البيانات"
},
"dialog": {
"add_api_key": {
"button_confirm": "توليد",
"context": "يمكن استعمال مفاتيح الAPI للتوثيق عبر بروتوكول Kobo Sync.",
"dialog_title": "توليد مفتاح API جديد",
"field_comment": "تعليق",
"field_comment_hint": "ما الغرض من مفتاح الAPI هذا؟",
"info_copy": "يرجى التأكد من نسخ مفتاح الAPI الآن. لا يستطاع النظر عليه مجددا!"
},
"add_to_collection": {
"button_create": "خلق",
"card_collection_subtitle": "لا توجد سلسلة | 1 سلسلة | {count} سلسلة",
@ -319,6 +359,12 @@
"filter": "مصنف حسب رقم الكتاب أو العنوان أو تاريخ الإصدار",
"title": "حدد كتاب"
},
"delete_apikey": {
"button_confirm": "حذف",
"confirm_delete": "فهمت, حذف مفتاح الAPI المسمى {name}",
"dialog_title": "حذف مفتاح الAPI",
"warning_html": "كل التطبيقات التي تستعمل مفتاح الAPI هذا ستصبح غير قادرة على الوصول إلى الAPI التابع لKomga. لا يمكن التراجع عن هذا الإجراء."
},
"delete_book": {
"button_confirm": "حذف",
"confirm_delete": "نعم، حذف كتاب \"{name}\" وملفاته",
@ -389,6 +435,8 @@
"field_summary": "ملخص",
"field_tags": "التصنيف",
"field_title": "العنوان",
"number_sort_decrement": "تقليل الجميع ب1",
"number_sort_increment": "زيادة الجميع ب1",
"tab_authors": "المؤلفون",
"tab_general": "إعدادات عامة",
"tab_links": "الروابط",
@ -415,6 +463,7 @@
"dialot_title_edit": "تحرير المكتبة",
"field_analysis_analyze_dimensions": "تحليل أبعاد الصفحات",
"field_analysis_hash_files": "حساب تجزئة للملفات",
"field_analysis_hash_koreader": "حسابة التلبيد لملفات KOReader",
"field_analysis_hash_pages": "حساب تجزئة الصفحات",
"field_convert_to_cbz": "تحويل تلقائيا إلى CBZ",
"field_import_barcode_isbn": "الباركود ردمك",
@ -422,15 +471,19 @@
"field_import_comicinfo_collections": "المجموعات",
"field_import_comicinfo_readlists": "قوائم القراءة",
"field_import_comicinfo_series": "سلسلة البيانات الوصفية",
"field_import_comicinfo_series_append_volume": "إضافة المجلد إلى عنوان المسلسل",
"field_import_epub_book": "البيانات الوصفية للكتاب",
"field_import_epub_series": "سلسلة البيانات الوصفية",
"field_import_local_artwork": "الأعمال الفنية المحلية",
"field_import_mylar_series": "سلسلة البيانات الوصفية",
"field_name": "الاسم",
"field_oneshotsdirectory": "‬موقع القصص المنفردة",
"field_repair_extensions": "إصلاح ملحقات الملفات غير الصحيحة تلقائيًا",
"field_root_folder": "المجلد الرئيسي",
"field_scan_interval": "تكرار البحث عن الملفات",
"field_scanner_empty_trash_after_scan": "تفريغ المهملات تلقائيا بعد كل فحص",
"field_scanner_force_directory_modified_time": "فرض وقت تعديل الدليل",
"field_scanner_scan_startup": "البحث عن الملفات عند بدء البرنامج",
"field_series_cover": "غلاف السلسلة",
"file_browser_dialog_button_confirm": "إختيار",
"file_browser_dialog_title": "المجلد الجذر للمكتبة",
@ -441,11 +494,14 @@
"label_import_epub": "استيراد البيانات الوصفية من ملفات EPUB",
"label_import_local": "استيراد أصول الوسائط المحلية",
"label_import_mylar": "استيراد البيانات الوصفية التي تم إنشاؤها بواسطة Mylar",
"label_scan_directory_exclusions": "استبعاد المواقع",
"label_scan_types": "البحث عن أنواع الملفات هذه",
"label_scanner": "الماسح الضوئي",
"label_series_cover": "غلاف السلسلة",
"tab_general": "إعدادات عامة",
"tab_metadata": "البيانات الوصفية",
"tab_options": "الخيارات",
"tooltip_oneshotsdirectory": "يترك فارغا للتعطيل",
"tooltip_scanner_force_modified_time": "تمكين إذا كانت المكتبة على Google Drive",
"tooltip_use_resources": "يمكن أن تستهلك الكثير من الموارد في مكتبات كبيرة أو أجهزة بطيئة"
},
@ -453,11 +509,16 @@
"button_cancel": "إلغاء",
"button_confirm": "حفظ التغييرات",
"dialog_title": "تحرير قائمة القراءة",
"field_manual_ordering": "ترتيب يدوي",
"field_name": "الاسم",
"field_summary": "ملخص",
"tab_general": "عام",
"tab_poster": "ملصق"
},
"edit_recommended": {
"button_confirm": "حفظ التعديلات",
"button_reset": "الإعادة إلى التعديلات الأصلية"
},
"edit_series": {
"button_cancel": "إلغاء",
"button_confirm": "حفظ التغييرات",
@ -483,6 +544,7 @@
"tab_poster": "ملصق",
"tab_sharing": "مشاركة",
"tab_tags": "التصنيف",
"tab_titles": "العناوين البديلة",
"tags_notice_multiple_edit": "أنت تقوم بتحرير العلامات لسلسلة متعددة. سيؤدي هذا إلى تجاوز العلامات الموجودة في كل سلسلة."
},
"edit_user": {
@ -525,6 +587,9 @@
},
"title": "اسم الوجهة"
},
"force_kobo_sync": {
"dialog_title": "إجبار مزامنة Kobo"
},
"password_change": {
"button_cancel": "إلغاء",
"button_confirm": "تغيير كلمة السر",
@ -541,6 +606,7 @@
},
"series_picker": {
"label_search_series": "سلسلة البحث",
"no_results": "لم يتم العثور على أي سلسلة",
"title": "اختيار سلسلة"
},
"server_stop": {
@ -579,21 +645,31 @@
}
},
"duplicate_pages": {
"action_auto_delete_remaining": "باق إلغاء {count} أوتوماتيكيا",
"action_delete_auto": "حذف تلقائي",
"action_delete_manual": "حذف يدوي",
"action_delete_matches": "حذف المطابقات",
"action_ignore": "تجاهل",
"action_ignore_remaining": "تجاهل الباقي ({count})",
"action_manual_delete_remaining": "إلغاء الباقي يدويا ({count})",
"delete_to_save": "حذف لحفظ {size}",
"deleted_count": "تم حذف {count} مرة",
"empty_title": "لم يتم العثور على صفحات مكررة",
"empty_title_known": "لا توجد نسخ مكررة معروف عنها",
"filter": {
"count": "عدد",
"date_added": "تاريخ الإضافة",
"date_modified": "تاريخ التعديل",
"delete_count": "عدد الحذف",
"delete_size": "المساحة المحفوظة",
"match_count": "عدد النتائج المتطابقة",
"size": "حجم",
"total_size": "الحجم الإجمالي"
},
"info": "حذف الصفحات المكررة سيغيّر ملفاتك. احتفظ بنسخة احتياطية من ملفاتك واستخدم الحذف اليدوي قبل استخدام الحذف التلقائي.",
"known": "المعروفة",
"matches_n": "لا يوجد تطابق | 1 تطابق | {count} التطابق",
"new": "الجديدة",
"saved_size": "حُفظ {size}",
"title": "صفحات مكررة",
"unknown_size": "حجم مجهول"
@ -609,6 +685,23 @@
"HARDLINK": "ملفات الروابط الثابتة/النسخ",
"MOVE": "نقل الملفات"
},
"epubreader": {
"appearances": {
"day": "النهار",
"night": "الليل",
"sepia": "سيبيا"
},
"column_count": {
"auto": "أوتوماتيكي",
"one": "واحد",
"two": "اثنان"
},
"reading_direction": {
"auto": "أوتوماتيكي",
"ltr": "من اليسار إلى اليمين",
"rtl": "من اليمين إلى اليسار"
}
},
"historical_event_type": {
"BookConverted": "تمّ تحويل الكتاب",
"BookFileDeleted": "تمّ مسح ملف الكتاب",
@ -616,6 +709,11 @@
"DuplicatePageDeleted": "تمّ حذف الصفحة المكررة",
"SeriesFolderDeleted": "تمّ حذف مجلد السلسلة"
},
"media_profile": {
"DIVINA": "DIVINA",
"EPUB": "EPUB",
"PDF": "PDF"
},
"media_status": {
"ERROR": "خطأ",
"OUTDATED": "قديمه",
@ -634,6 +732,14 @@
"VERTICAL": "عمودي",
"WEBTOON": "ويبتون"
},
"scan_interval": {
"DAILY": "يوميا",
"DISABLED": "معطل",
"EVERY_12H": "كل 12 ساعة",
"EVERY_6H": "كل 6 ساعات",
"HOURLY": "كل ساعة",
"WEEKLY": "كل أسبوع"
},
"series_cover": {
"FIRST": "أول",
"LAST": "أخير"
@ -645,6 +751,34 @@
"ONGOING": "مستمر"
}
},
"epubreader": {
"current_chapter": "الفصل الحالي",
"page_of": "الصفحة {page} من {count}",
"publisher_font": "الناشر",
"settings": {
"column_count": "عدد أعمدة",
"font_family": "الخط",
"layout": "تخطيط",
"layout_scroll": "تمرير",
"navigation_mode": "وضع التنقل",
"navigation_options": {
"both": "كلاهما",
"buttons": "أزرار",
"click": "نقر/ضغط"
},
"page_margins": "هوامش الصفحة"
},
"shortcuts": {
"cycle_pagination": "تغيير عدد الأعمدة",
"font_size_decrease": "تصغير الخط",
"font_size_increase": "تكبير الخط",
"menus": "القائمات",
"next": "أمام",
"previous": "خلف",
"settings": "الإعدادات",
"show_hide_toc": "إظهار/إخفاء الفهرس"
}
},
"error_codes": {
"ERR_1000": "تعذر الوصول إلى الملف أثناء التحليل",
"ERR_1001": "نوع الوسائط غير مدعوم",
@ -667,11 +801,13 @@
"filter": {
"age_rating": "التصنيف العمري",
"age_rating_none": "لا شيء",
"any": "أي",
"complete": "اكتمل",
"genre": "نوع",
"in_progress": "قيد التقدم",
"language": "اللغة",
"library": "المكتبة",
"oneshot": "قصة منفردة",
"publisher": "الناشر",
"read": "اقرأ",
"release_date": "تاريخ الاصدار",
@ -699,6 +835,8 @@
},
"library_navigation": {
"browse": "تصفّح",
"browse_books": "كتب",
"browse_series": "سلسلات",
"collections": "المجموعات",
"readlists": "قوائم القراءة",
"recommended": "موصى به"
@ -730,6 +868,7 @@
"empty_trash": "إفراغ سلة المهملات",
"mark_read": "تحديد كمقروء",
"mark_unread": "تحديد كغير مقروء",
"pin": "تثبيت",
"refresh_metadata": "تحديث البيانات الوصفية",
"scan_library_files": "فحص ملفات المكتبة",
"select_all": "تحديد الكل"
@ -745,6 +884,9 @@
"libraries": "المكتبات",
"logout": "تسجيل الخروج"
},
"no_libraries_pinned": {
"title": "لا مكتبات مثبتة"
},
"page_not_found": {
"go_back_to_home_page": "العودة إلى الصفحة الرئيسية",
"page_does_not_exist": "الصفحة التي تبحث عنها غير موجودة.",
@ -754,6 +896,12 @@
"less": "أقرأ أقل",
"more": "اقرأ أكثر"
},
"readlist_import": {
"row": {
"duplicate_book": "كتاب مكرر",
"error_choose_book": "اختيار كتاب"
}
},
"readlists_expansion_panel": {
"manage_readlist": "إدارة قائمة القراءة",
"title": "{name} قائمة القراءة"
@ -775,12 +923,21 @@
"button_empty_trash": "إفراغ سلة المهملات لكل المكتبات",
"button_scan_libraries": "مسح جميع المكتبات",
"button_shutdown": "إيقاف التشغيل",
"download_log": "تحميل ملف السجل",
"notification_tasks_cancelled": "لا يوجد مهام لإلغائها | تمّ إلغاء 1 مهمة | تمّ إلغاء {count} مهمة",
"section_title": "إدارة الخادم"
},
"tab_title": "خادم"
"tab_title": "خادم",
"updates": "تحديثات"
},
"server_settings": {
"dialog_regenerate_thumbnails": {
"btn_alternate": "نعم، جميع الكتب",
"btn_cancel": "لا",
"btn_confirm": "نعم، لكن فقط إن كانت اكبر"
},
"label_kobo_port": "باب مزامنة Kobo الخارجي",
"label_server_port": "باب الخادم",
"server_settings": "إعدادات الخادم"
},
"settings_user": {
@ -795,12 +952,15 @@
"sort": {
"books_count": "عدد الكتب",
"date_added": "تاريخ الإضافة",
"date_read": "تاريخ القراءة",
"date_updated": "تاريخ التحديث",
"file_name": "اسم الملف",
"file_size": "حجم الملف",
"folder_name": "اسم المجلد",
"name": "الاسم",
"number": "رقم",
"page_count": "عدد الصفحات",
"random": "عشوائي",
"release_date": "تاريخ الإصدار"
},
"theme": {
@ -818,16 +978,36 @@
"tooltip_too_big": "الملف كبير جدًا!",
"tooltip_user_uploaded": "تمّ الرفع بواسطة المستخدم"
},
"titles_more": {
"less": "عناوين أقل",
"more": "عناوين أكثر"
},
"ui_settings": {
"general": "عام",
"section_oauth2": "OAuth2",
"series_groups": {
"alpha": "ابجدي",
"japanese": "غوجون (ياباني)"
}
},
"updates": {
"available": "توجد تحديثات"
},
"user_roles": {
"ADMIN": "مدير",
"FILE_DOWNLOAD": "تحميل الملف",
"PAGE_STREAMING": "صفحات البث",
"KOBO_SYNC": "مزامنة Kobo",
"KOREADER_SYNC": "مزامنة KOReader",
"PAGE_STREAMING": "بث الصفحات",
"USER": "مستخدم"
},
"users": {
"api_keys": "مفاتيح الAPI",
"users": "المستخدمين"
},
"validation": {
"one_or_more": "يجب أن يكون 1 أو أكثر",
"tcp_port": "يجب أن يكون بين 1 و 65535",
"zero_or_more": "يجب أن يكون 0 أو أكثر"
},
"welcome": {

View file

@ -827,7 +827,12 @@
"ERR_1031": "V ComicRack CBL Book chybí série nebo číslo",
"ERR_1032": "Soubor EPUB má chybný typ média",
"ERR_1033": "Některé položky chybí",
"ERR_1034": "API key s touto poznámkou již existuje"
"ERR_1034": "API key s touto poznámkou již existuje",
"ERR_1035": "Chyba při získávání obsahu EPUB",
"ERR_1036": "Chyba při získávání záložek EPUB",
"ERR_1037": "CHyba při získávání seznamu stran EPUB",
"ERR_1038": "Chyba při získávání stran EPUB Divina",
"ERR_1039": "Chyba při získávání pozic EPUB"
},
"filter": {
"age_rating": "Věkové hodnocení",

View file

@ -19,7 +19,15 @@
},
"account_settings": {
"account_settings": "Configuración da conta",
"change_password": "Cambiar o contrasinal"
"api_key": {
"created_date": "Data de creación: {date}",
"force_kobo_sync": "Forzar a sincronización con Kobo",
"generate_api_key": "Xerar clave API",
"no_keys": "Aínda non se xerou clave API ningunha"
},
"change_password": "Cambiar o contrasinal",
"details": "Detalles",
"my_account": "A miña conta"
},
"announcements": {
"mark_all_read": "Marcar todo como lido",
@ -27,6 +35,7 @@
"tab_title": "Avisos"
},
"authentication_activity": {
"api_key": "Clave API",
"datetime": "Data e Hora",
"email": "Correo electrónico",
"error": "Erro",
@ -58,9 +67,159 @@
"button_scan": "Escanear",
"button_select_series": "Escolle a serie",
"field_import_path": "Importar dende un cartafol",
"info_part1": "Esta pantalla permitiralle importar arquivos que están fóra das súas bibliotecas existentes. Só pódese importar arquivos a series que xa existan, nese caso Komga moverá ou copiará os arquivos cara o directorio da serie escollida."
"info_part1": "Esta pantalla permitiralle importar arquivos que están fóra das súas bibliotecas existentes. Só pódese importar arquivos a series que xa existan, nese caso Komga moverá ou copiará os arquivos cara o directorio da serie escollida.",
"info_part2": "Se escolles un número para un libro e existe xa algún outro libro con ese número, poderás comparalos. Se decides importar o libro, Komga actualizará o existente co novo, substituindo o ficheiro existente co novo.",
"no_files_found": "Non se atoparon ficheiros",
"notification": {
"go_to_book": "Ir ao libro",
"import_failure": "Fallou a importación do libro: {file}",
"import_successful": "Libro importado con éxito: {book}",
"source_file": "Ficheiro orixe: {file}"
},
"row": {
"error_analyze_first": "É necesario analizar antes o libro",
"error_choose_series": "Escoller serie",
"error_only_import_no_errors": "Só se poden importar libros sen erros",
"warning_upgrade": "Actualizarase o libro existente"
},
"table": {
"destination_name": "Nome de destino",
"file_name": "Nome do ficheiro",
"number": "Número",
"series": "Serie"
},
"title": "Importar",
"try_another_directory": "Tenta atopar outro directorio"
},
"bookreader": {
"beginning_of_book": "Atópaste ao principio do libro.",
"changing_reading_direction": "Trocar o sentido de lectura a",
"download_current_page": "Baixar a páxina actual",
"end_of_book": "Chegaches ao final do libro.",
"from_series_metadata": "dende os metadatos da serie",
"move_next": "Volve pulsar \"Seguinte\" para pasar ao seguinte libro.",
"move_next_exit": "Volve pulser \"Seguinte\" para saír do lector.",
"move_previous": "Volve pulsar \"Anterior\" para pasar ao libro anterior.",
"notification_poster_set_book": "A portada do libro estableceuse á páxina actual.",
"notification_poster_set_readlist": "A portada da lista de lectura estableceuse á páxina actual.",
"notification_poster_set_series": "A portada de serie estableceuse á páxina actual.",
"paged_reader_layout": {
"double": "Dobre páxina",
"double_no_cover": "Dobre páxina (sen portada)",
"single": "Páxina simple"
},
"reader_settings": "Configuración do lector",
"scale_type": {
"continuous_original": "Orixinal",
"continuous_width": "Axuste á anchura",
"height": "Axuste á altura",
"original": "Orixinal",
"screen": "Pantalla"
},
"settings": {
"always_fullscreen": "Sempre a pantalla completa",
"animate_page_transitions": "Transicións de páxina animadas",
"background_color": "Cor de fondo",
"background_colors": {
"black": "Negro",
"gray": "Gris",
"white": "Branco"
},
"general": "Xeral",
"gestures": "Xestos",
"page_layout": "Disposición de páxina",
"page_margin": "Marxe de páxina",
"reading_mode": "Modo de lectura",
"side_padding_none": "Ningún"
},
"shortcuts": {
"close": "Pechar",
"first_page": "Primeira páxina",
"last_page": "Derradeira páxina",
"left_to_right": "De esquerda a dereita",
"menus": "Menús",
"next_page": "Seguinte páxina",
"previous_page": "Páxina anterior",
"right_to_left": "De dereita a esquerda"
},
"tooltip_incognito": "Non se gardará o progreso de lectura"
},
"browse_book": {
"comment": "COMENTARIO",
"date_created": "CREACIÓN",
"date_modified": "ÚLTIMA MODIFICACIÓN",
"download_file": "Baixar ficheiro",
"file": "FICHEIRO",
"format": "FORMATO",
"isbn": "ISBN",
"links": "LIGAZÓNS",
"outdated_tooltip": "Cambiouse o ficheiro deste libro; é necesario volver analizalo",
"read_book": "Ler libro",
"read_incognito": "Ler de incógnito",
"remove_from_collection": "Quitar libro da colección",
"remove_from_readlist": "Quitar libro da lista de lectura",
"size": "TAMAÑO"
},
"browse_collection": {
"edit_collection": "Editar colección",
"edit_elements": "Editar elementos",
"manual_ordering": "Ordenación manual"
},
"browse_readlist": {
"edit_elements": "Editar elementos",
"edit_readlist": "Editar lista de lectura",
"manual_ordering": "Ordenación manual"
},
"browse_series": {
"earliest_year_from_release_dates": "Este é o ano máis temperán entre as datas de lanzamento de tódolos libros da serie",
"remove_from_collection": "Quitar serie da colección",
"series_no_summary": "Esta serie non ten sumario, así que escollemos un por ti!",
"summary_from_book": "Sumario do libro {number}:"
},
"collections_expansion_panel": {
"manage_collection": "Xestionar colección",
"title": "{name} colección"
},
"common": {
"locale_name": "Galego"
"age": "Idade",
"all_libraries": "Tódalas bibliotecas",
"all_of": "Todo",
"any_of": "Calquera",
"book": "Libro",
"books": "Libros",
"books_n": "Ningún libro | 1 libro | {count} libros",
"books_total": "{count} / {total} libros",
"cancel": "Cancelar",
"choose_image": "Escoller unha imaxe",
"close": "Pechar",
"collections": "Coleccións",
"copied": "Copiado!",
"create": "Crear",
"delete": "Borrar",
"discard": "Descartar",
"disk_space": "Espazo en disco",
"download": "Baixar",
"duplicate": "Duplicar",
"epub": "EPUB",
"error": "Erro",
"filename": "Nome de ficheiro",
"genre": "Xénero",
"go_to_collection": "Ir á colección",
"go_to_library": "Ir á biblioteca",
"go_to_readlist": "Ir á lista de lectura",
"go_to_series": "Ir á serie",
"i_understand": "Entendido",
"library": "Biblioteca",
"locale_name": "Galego",
"more": "Máis",
"nothing_to_show": "Nada que amosar",
"page": "Páxina",
"page_number": "Número de páxina",
"pages": "páxinas",
"pages_left": "Non quedan páxinas | Queda 1 páxina | Quedan {count} páxinas",
"pages_n": "Ningunha páxina | 1 páxina | {count} páxinas",
"password": "Contrasinal",
"pdf": "PDF",
"pending_tasks": "Sen tarefas pendentes | 1 tarefa pendente | {count} tarefas pendentes"
}
}

View file

@ -46,7 +46,7 @@
},
"author_roles": {
"colorist": "koloristi",
"cover": "omot",
"cover": "naslovnica",
"editor": "urednici",
"inker": "bojitelj",
"letterer": "tipografi",
@ -109,7 +109,7 @@
"notification_poster_set_series": "Trenutačna stranica je sada postavljena kao poster serije.",
"paged_reader_layout": {
"double": "Dvije stranice",
"double_no_cover": "Dvije stranice (bez omota)",
"double_no_cover": "Dvije stranice (bez naslovnice)",
"single": "Jedna stranica"
},
"reader_settings": "Postavke čitača",
@ -138,7 +138,7 @@
"general": "Općenito",
"gestures": "Geste",
"page_layout": "Raspored stranica",
"page_margin": "Margine stranica",
"page_margin": "Margina stranice",
"paged": "Postavke prikaza stranica",
"reading_mode": "Modus čitanja",
"scale_type": "Vrsta skaliranja",
@ -484,7 +484,7 @@
"field_scanner_empty_trash_after_scan": "Automatski isprazni smeće nakon svakog pretraživanja",
"field_scanner_force_directory_modified_time": "Pretraži mape na osnovi vremena promjene",
"field_scanner_scan_startup": "Pretraži tijekom pokretanja",
"field_series_cover": "Omot serije",
"field_series_cover": "Naslovnica serije",
"file_browser_dialog_button_confirm": "Odaberi",
"file_browser_dialog_title": "Osnovna mapa biblioteke",
"label_analysis": "Analiza",
@ -497,7 +497,7 @@
"label_scan_directory_exclusions": "Isključivanja mapa",
"label_scan_types": "Traži ove vrste datoteka",
"label_scanner": "Tražilica",
"label_series_cover": "Omot serije",
"label_series_cover": "Naslovnica serije",
"tab_general": "Općenito",
"tab_metadata": "Metapodaci",
"tab_options": "Opcije",
@ -780,7 +780,7 @@
"buttons": "Gumbovi",
"click": "Pritisni / Dodirni"
},
"page_margins": "Margine stranica",
"page_margins": "Margine stranice",
"viewing_theme": "Tema prikaza"
},
"shortcuts": {
@ -827,7 +827,12 @@
"ERR_1031": "ComicRack CBL knjizi nedostaje serija ili broj",
"ERR_1032": "EPUB datoteka sadrži neispravnu vrstu medija",
"ERR_1033": "Neki unosi nedostaju",
"ERR_1034": "API ključ s tim komentarom već postoji"
"ERR_1034": "API ključ s tim komentarom već postoji",
"ERR_1035": "Greška prilikom dohvaćanja tablice sadržaja EPUB-a",
"ERR_1036": "Greška prilikom dohvaćanja straničnika EPUB-a",
"ERR_1037": "Greška prilikom dohvaćanja popisa stranica EPUB-a",
"ERR_1038": "Greška prilikom dohvaćanja divina stranica EPUB-a",
"ERR_1039": "Greška prilikom dohvaćanja pozicija EPUB-a"
},
"filter": {
"age_rating": "dobna kategorija",
@ -984,7 +989,7 @@
"btn_confirm": "Da, ali samo ako su veće",
"title": "Nanovo generiraj minijature"
},
"hint_kobo_port": "Postavi samo u slučaju problema sa sinkronizacijom omota i preuzimanja",
"hint_kobo_port": "Postavi samo u slučaju problema sa sinkronizacijom naslovnica i preuzimanja",
"label_delete_empty_collections": "Izbriši prazne zbirke nakon pretraživanja",
"label_delete_empty_readlists": "Izbriši prazne liste čitanja nakon pretraživanja",
"label_kepubify_path": "Staza do kepubify",

View file

@ -19,7 +19,15 @@
},
"account_settings": {
"account_settings": "Configurações de conta",
"change_password": "alterar senha"
"api_key": {
"created_date": "Data de criação: {date}",
"force_kobo_sync": "Forçar sincronização com Kobo",
"generate_api_key": "Gerar chave de API",
"no_keys": "Nenhuma Chave de API foi criada"
},
"change_password": "alterar senha",
"details": "Detalhes",
"my_account": "Minha conta"
},
"announcements": {
"mark_all_read": "Marque todos como lido",
@ -27,6 +35,7 @@
"tab_title": "Anúncios"
},
"authentication_activity": {
"api_key": "Chave de API",
"datetime": "Data e Hora",
"email": "E-mail",
"error": "Erro",
@ -86,6 +95,7 @@
"beginning_of_book": "Você está no inicio do livro.",
"changing_reading_direction": "Mudar Direção de Leitura para",
"cycling_page_layout": "Alterando Layout de Página",
"cycling_page_margin": "Alterando Margem de Página",
"cycling_scale": "Alterando Escala",
"cycling_side_padding": "Alterando Preenchimento Lateral",
"download_current_page": "Baixar a pagina atual",
@ -128,6 +138,7 @@
"general": "Geral",
"gestures": "Gestos",
"page_layout": "Layout de página",
"page_margin": "Margem de página",
"paged": "Opções do Reader paginado",
"reading_mode": "Modo de leitura",
"scale_type": "Tipo de escala",
@ -138,6 +149,7 @@
"shortcuts": {
"close": "Fechar",
"cycle_page_layout": "Alterar layout de página",
"cycle_page_margin": "Alterar margem de página",
"cycle_scale": "Alterar escala",
"cycle_side_padding": "Alterar preenchimento lateral",
"first_page": "Primeira página",
@ -161,6 +173,8 @@
},
"browse_book": {
"comment": "COMENTÁRIO",
"date_created": "CRIADO",
"date_modified": "MODIFICADO POR ÚLTIMO",
"download_file": "Baixar arquivo",
"file": "ARQUIVO",
"format": "FORMATO",
@ -170,6 +184,8 @@
"outdated_tooltip": "O arquivo para este livro foi alterado, este livro deve ser reanalisado",
"read_book": "Ler livro",
"read_incognito": "Ler incógnito",
"remove_from_collection": "Remover livro da coleção",
"remove_from_readlist": "Remover livro da lista de leitura",
"size": "TAMANHO"
},
"browse_collection": {
@ -184,6 +200,7 @@
},
"browse_series": {
"earliest_year_from_release_dates": "Este é o ano mais antigo dentre as datas de lançamento de todos os livros na série",
"remove_from_collection": "Remover série da coleção",
"series_no_summary": "Esta série não contém um resumo, então escolhemos um para você!",
"summary_from_book": "Resumo do livro {number}:"
},
@ -194,6 +211,8 @@
"common": {
"age": "Idade",
"all_libraries": "Todas as bibliotecas",
"all_of": "Todos",
"any_of": "Qualquer",
"book": "Livro",
"books": "Livros",
"books_n": "Nenhum livro | 1 livro | {count} livros",
@ -203,6 +222,7 @@
"choose_image": "Escolher uma imagem",
"close": "Fechar",
"collections": "Coleções",
"copied": "Copiado!",
"create": "Criar",
"delete": "Excluir",
"dimension": "l: {width}, a:{height}",
@ -211,8 +231,10 @@
"dismiss": "Ignorar",
"download": "Baixar",
"drag_drop": "Arrastar e soltar",
"duplicate": "Duplicado",
"email": "Email",
"epub": "Epub",
"error": "Erro",
"filename": "Nome do arquivo",
"filter_no_matches": "O filtro ativo não contém correspondências",
"genre": "Gênero",
@ -220,12 +242,17 @@
"go_to_library": "Ir para biblioteca",
"go_to_readlist": "Ir para lista de lidos",
"go_to_series": "Ir para séries",
"i_understand": "Eu compreendo",
"library": "Biblioteca",
"locale_name": "Português (Brasil)",
"locale_rtl": "false",
"lock_all": "Bloquear todos",
"media": "Mídia",
"more": "Mais",
"n_selected": "{count} selecionados",
"nothing_to_show": "Nada para exibir",
"ok": "OK",
"oneshot": "One-shot",
"outdated": "Desatualizado",
"page": "Página",
"page_number": "Número da página",
@ -235,19 +262,23 @@
"password": "Senha",
"pdf": "PDF",
"pending_tasks": "Nenhuma tarefa pendente | 1 tarefa pendente | {count} tarefas pendentes",
"pinned_libraries": "Bibliotecas Fixadas",
"publisher": "Editora",
"read": "Ler",
"read_on": "Lido em {date}",
"readlist": "Lista de Leitura",
"readlists": "Listas de Leitura",
"remember-me": "Me lembre",
"reorder": "Reordernar",
"required": "Obrigatório",
"reset_filters": "Redefinir filtros",
"roles": "Funções",
"save_changes": "Salvar mudanças",
"series": "Séries | Séries",
"settings": "Configurações",
"sidecars": "Carrinho",
"tags": "Tags",
"ui": "Interface de Usuário",
"unavailable": "Indisponível",
"unlock_all": "Desbloquear tudo",
"url": "URL",
@ -271,6 +302,7 @@
"comicrack_preambule_html": "Você pode importar Listas de Leitura do ComicRack existentes no formato <code>.cbl</code><br>Komga tentará combinar a série fornecida e o número do livro com as séries e livros em suas bibliotecas.",
"dialog_confirmation": {
"body": "{unmatched} / {total} livro(s) não foram marcados",
"body2": "{duplicates} / {total} livros são duplicados",
"create": "Criar de todo modo",
"title": "Alguns livros não estão combinados"
},
@ -286,6 +318,14 @@
"tab_title": "Importação de Dados"
},
"dialog": {
"add_api_key": {
"button_confirm": "Gerar",
"context": "Chaves API podem ser usadas para autenticar no protocolo Kobo Sync.",
"dialog_title": "Gerar chave de API",
"field_comment": "Comentar",
"field_comment_hint": "Como a chave API será usada?",
"info_copy": "Tenha a certeza de que a chave API foi salva. Você não poderá vê-la novamente!"
},
"add_to_collection": {
"button_create": "Criar",
"card_collection_subtitle": "Nenhuma série | 1 série | {count} série",
@ -319,6 +359,12 @@
"filter": "Filtrar por número, título ou data de lançamento",
"title": "Selecionar livro"
},
"delete_apikey": {
"button_confirm": "Excluir",
"confirm_delete": "Eu entendo, exclua a chave API \"{name}\"",
"dialog_title": "Deletar chave API",
"warning_html": "Quaisquer aplicativos ou scripts que utilizem esta chave API não poderão mais acessar a API Komga. Você não poderá desfazer esta ação."
},
"delete_book": {
"button_confirm": "Excluir",
"confirm_delete": "Sim, exclua o livro \"{name}\" e seus arquivos",
@ -468,6 +514,9 @@
"tab_general": "Geral",
"tab_poster": "Pôster"
},
"edit_recommended": {
"button_confirm": "Salvar mudanças"
},
"edit_series": {
"button_cancel": "Cancelar",
"button_confirm": "Salvar mudanças",
@ -536,6 +585,9 @@
},
"title": "Nome do Arquivo de Destino"
},
"force_kobo_sync": {
"dialog_title": "Forçar sincronização com Kobo"
},
"password_change": {
"button_cancel": "Cancelar",
"button_confirm": "Mudar senha",
@ -603,6 +655,9 @@
"HARDLINK": "Hardlink/Copiar arquivos",
"MOVE": "Mover Arquivos"
},
"media_profile": {
"PDF": "PDF"
},
"media_status": {
"ERROR": "Erro",
"OUTDATED": "Desatualizado",
@ -629,6 +684,14 @@
"ONGOING": "Em andamento"
}
},
"epubreader": {
"settings": {
"page_margins": "Margem de Páginas"
},
"shortcuts": {
"settings": "Configurações"
}
},
"error_codes": {
"ERR_1000": "O arquivo não pôde ser acessado durante a análise",
"ERR_1001": "O tipo de mídia não é compatível",
@ -660,6 +723,7 @@
"in_progress": "Em progresso",
"language": "idioma",
"library": "biblioteca",
"oneshot": "One-shot",
"publisher": "editora",
"read": "Lidos",
"release_date": "data de lançamento",
@ -673,7 +737,8 @@
},
"history": {
"header": {
"book": "Livro"
"book": "Livro",
"details": "Detalhes"
}
},
"home": {
@ -795,6 +860,9 @@
"tooltip_too_big": "Arquivo muito grande!",
"tooltip_user_uploaded": "Enviado pelo usuário"
},
"ui_settings": {
"general": "Geral"
},
"user_roles": {
"ADMIN": "Administrador",
"FILE_DOWNLOAD": "Baixar arquivos",
@ -802,6 +870,7 @@
"USER": "Usuário"
},
"users": {
"api_keys": "Chaves API",
"authentication_activity": "Atividade de Autenticação",
"users": "Usuários"
},

View file

@ -4,8 +4,8 @@
"pageText": "{0}-{1} из {2}"
},
"dataIterator": {
"loadingText": "Загрузка объектов...",
"noResultsText": "Подходящих записей не найдено"
"loadingText": "Загрузка элементов...",
"noResultsText": "Совпадений не найдено"
},
"dataTable": {
"itemsPerPageText": "Строк на странице:",
@ -15,30 +15,33 @@
"counter": "Файлов: {0}",
"counterSize": "Файлов: {0} (всего {1})"
},
"noDataText": "Отсутствуют данные"
"noDataText": "Данные отсутствуют"
},
"account_settings": {
"account_settings": "Настройки Аккаунта",
"account_settings": "Настройки аккаунта",
"api_key": {
"created_date": "Дата создания: {date}",
"generate_api_key": "Сгенерировать API ключ",
"force_kobo_sync": "Принудительная синхронизация с Kobo",
"generate_api_key": "Сгенерировать ключ API",
"no_keys": "Ни одного ключа API еще не создано"
},
"change_password": "изменить пароль"
"change_password": "изменить пароль",
"details": "Подробности",
"my_account": "Мой аккаунт"
},
"announcements": {
"mark_all_read": "Пометить всё как прочитанное",
"mark_read": "Пометить как прочитанное",
"mark_all_read": "Отметить всё как прочитанное",
"mark_read": "Отметить как прочитанное",
"tab_title": "Объявления"
},
"authentication_activity": {
"api_key": "API Ключ",
"api_key": "APIлюч",
"datetime": "Дата/Время",
"email": "Эл. почта",
"error": "Ошибка",
"ip": "IP-адрес",
"source": "Источник",
"success": "Статус",
"success": "Успех",
"user_agent": "User Agent"
},
"author_roles": {
@ -54,18 +57,18 @@
"book_card": {
"error": "Ошибка",
"no_release_date": "Дата релиза отсутствует",
"unknown": "Необходимо проанализировать",
"unknown": "Подлежит анализу",
"unread": "Не прочитано",
"unsupported": "Неподдерживаемый"
"unsupported": "Не поддерживается"
},
"book_import": {
"button_browse": "Обзор",
"button_import": "Импортировать",
"button_import": "Импорт",
"button_scan": "Сканировать",
"button_select_series": "Выберите Серию",
"button_select_series": "Выберите серию",
"field_import_path": "Импортировать из каталога",
"info_part1": "Этот раздел позволяет вам импортировать файлы, которые находятся за пределами ваших существующих библиотек. Вы можете импортировать файлы только в существующие Серии, в этом случае Komga переместит или скопирует файлы в каталог выбранной Серии.",
"info_part2": "Если вы выберете номер для книги и книга с таким номером уже существует, то вы сможете сравнить 2 книги. Если вы решите импортировать книгу, Komga обновит существующую книгу, эффективно заменив старый файл новым.",
"info_part1": "Этот раздел позволяет вам импортировать файлы, которые находятся за пределами ваших существующих библиотек. Вы можете импортировать файлы только в существующие серии; в этом случае Komga переместит или скопирует файлы в каталог выбранной серии.",
"info_part2": "Если вы укажете номер для книги, и книга с таким номером уже существует, вы сможете сравнить оба варианта. Если вы решите импортировать книгу, Komga обновит существующий вариант, заменив старый файл новым.",
"no_files_found": "Файлы не найдены",
"notification": {
"go_to_book": "Перейти к книге",
@ -74,9 +77,9 @@
"source_file": "Исходный файл: {file}"
},
"row": {
"error_analyze_first": "Книгу нужно сначала проанализировать",
"error_analyze_first": "Книга нуждается в предварительном анализе",
"error_choose_series": "Выберите серию",
"error_only_import_no_errors": "Можно импортировать только книги без ошибок",
"error_only_import_no_errors": "Можно импортировать только книги без наличия ошибок",
"warning_upgrade": "Существующая книга будет обновлена"
},
"table": {
@ -90,10 +93,11 @@
},
"bookreader": {
"beginning_of_book": "Вы находитесь в начале книги.",
"changing_reading_direction": "Изменение Направления Чтения на",
"cycling_page_layout": "Переключить Формат Страниц",
"cycling_scale": "Переключить Масштабирование",
"cycling_side_padding": "Переключить Боковой Отступ",
"changing_reading_direction": "Изменение направления чтения на",
"cycling_page_layout": "Переключение макета страницы",
"cycling_page_margin": "Переключение полей страницы",
"cycling_scale": "Масштабирование",
"cycling_side_padding": "Переключение боковых отступов",
"download_current_page": "Скачать текущую страницу",
"end_of_book": "Вы достигли конца книги.",
"from_series_metadata": "из метаданных серии",
@ -104,8 +108,8 @@
"notification_poster_set_readlist": "Текущая страница теперь используется в качестве постера списка чтения.",
"notification_poster_set_series": "Текущая страница теперь используется в качестве постера серии.",
"paged_reader_layout": {
"double": "Двойные страницы",
"double_no_cover": "Двойные страницы (без обложки)",
"double": "Две страницы",
"double_no_cover": "Две страницы (без обложки)",
"single": "Одна страница"
},
"reader_settings": "Настройки Ридера",
@ -114,16 +118,16 @@
"continuous_width": "По ширине",
"height": "По высоте",
"original": "Исходное",
"screen": "По экрану",
"screen": "По размеру экрана",
"width": "По ширине",
"width_shrink_only": "По ширине (с отступами)"
},
"set_current_page_as_book_poster": "Установить страницу в качестве постера для книги",
"set_current_page_as_readlist_poster": "Установить страницу в качестве постера для списка чтения",
"set_current_page_as_series_poster": "Установить страницу в качестве постера для серии",
"set_current_page_as_book_poster": "Установить страницу в качестве обложки для книги",
"set_current_page_as_readlist_poster": "Установить страницу в качестве обложки для списка чтения",
"set_current_page_as_series_poster": "Установить страницу в качестве обложки для серии",
"settings": {
"always_fullscreen": "Всегда в полный экран",
"animate_page_transitions": "Анимировать переходы страниц",
"always_fullscreen": "Всегда полноэкранный режим",
"animate_page_transitions": "Анимировать переходы между страницами",
"background_color": "Цвет фона",
"background_colors": {
"black": "Чёрный",
@ -134,54 +138,60 @@
"general": "Общее",
"gestures": "Жесты",
"page_layout": "Формат страницы",
"paged": "Настройки Отображения Страниц",
"page_margin": "Поля страницы",
"paged": "Настройки отображения страниц",
"reading_mode": "Режим чтения",
"scale_type": "Масштабирование",
"side_padding": "Боковой отступ",
"side_padding": "Боковые отступы",
"side_padding_none": "Нет",
"webtoon": "Параметры Режима Webtoon"
"webtoon": "Настройки режима цифрового комикса"
},
"shortcuts": {
"close": "Закрыть",
"cycle_page_layout": "Переключить формат страниц",
"cycle_page_layout": "Переключить макет страницы",
"cycle_page_margin": "Переключить поля страницы",
"cycle_scale": "Переключить масштаб",
"cycle_side_padding": "Переключить боковой отступ",
"cycle_side_padding": "Переключить боковые отступы",
"first_page": "Первая страница",
"fullscreen": "Войти/выйти из полноэкранного режима",
"last_page": "Последняя страница",
"left_to_right": "Слева Направо",
"left_to_right": "Слева направо",
"menus": "Меню",
"next_page": "Следующая страница",
"previous_page": "Предыдущая страница",
"reader_navigation": "Навигация",
"right_to_left": "Справа Налево",
"right_to_left": "Справа налево",
"settings": "Настройки",
"show_hide_help": "Показать/скрыть помощь",
"show_hide_settings": "Показать/скрыть меню настроек",
"show_hide_thumbnails": "Показать/скрыть просмотр эскизов",
"show_hide_thumbnails": "Показать/скрыть обозреватель миниатюр",
"show_hide_toolbars": "Показать/скрыть панели инструментов",
"vertical": "Вертикально",
"webtoon": "Webtoon"
"webtoon": "Цифровой комикс"
},
"tooltip_incognito": "Прогресс чтения не будет сохранен"
},
"browse_book": {
"comment": "КОММЕНТАРИЙ",
"date_created": "СОЗДАНО",
"date_modified": "ПОСЛЕДНЕЕ ИЗМЕНЕНИЕ",
"download_file": "Скачать файл",
"file": "ФАЙЛ",
"format": "ФОРМАТ",
"isbn": "ISBN",
"links": "ССЫЛКИ",
"navigation_within_readlist": "Навигация в пределах списка чтения: {name}",
"outdated_tooltip": "Файл этой книги изменен, книгу необходимо повторно проанализировать",
"navigation_within_readlist": "Навигация внутри списка чтения: {name}",
"outdated_tooltip": "Файл этой книги изменился, книгу необходимо повторно проанализировать",
"read_book": "Читать книгу",
"read_incognito": "Читать инкогнито",
"remove_from_collection": "Удалить книгу из коллекции",
"remove_from_readlist": "Удалить книгу из списка чтения",
"size": "РАЗМЕР"
},
"browse_collection": {
"edit_collection": "Редактировать коллекцию",
"edit_elements": "Редактировать элементы",
"manual_ordering": "ручной порядок"
"manual_ordering": "ручная сортировка"
},
"browse_readlist": {
"edit_elements": "Редактировать элементы",
@ -189,17 +199,19 @@
"manual_ordering": "ручная сортировка"
},
"browse_series": {
"earliest_year_from_release_dates": "Это самая ранняя дата выпуска из всех книг в этой серии",
"series_no_summary": "У серии нет описания, поэтому мы подобрали его для вас!",
"earliest_year_from_release_dates": "Это самый ранний год из дат выхода всех книг серии",
"remove_from_collection": "Удалить серию из коллекции",
"series_no_summary": "У этой серии нет описания, поэтому мы подобрали его для вас!",
"summary_from_book": "Краткое описание из книги {number}:"
},
"collections_expansion_panel": {
"manage_collection": "Управлять коллекцией",
"manage_collection": "Управление коллекцией",
"title": "Коллекция {name}"
},
"common": {
"age": "Возраст",
"all_libraries": "Все Библиотеки",
"all_libraries": "Все библиотеки",
"any_of": "Любой из",
"book": "Книга",
"books": "Книги",
"books_n": "Книг нет | 1 книга | {count} книг",
@ -212,14 +224,16 @@
"copied": "Скопировано!",
"create": "Создать",
"delete": "Удалить",
"dimension": "шир.: {width} выс.: {height}",
"dimension": "шир.: {width}, выс.: {height}",
"discard": "Отмена",
"disk_space": "Дисковое пространство",
"dismiss": "Отклонить",
"download": "Скачать",
"drag_drop": "перетащить",
"duplicate": "Дублировать",
"email": "Эл. почта",
"epub": "Epub",
"error": "Ошибка",
"filename": "Имя файла",
"filter_no_matches": "Нет совпадений по заданному фильтру",
"genre": "Жанр",
@ -227,12 +241,16 @@
"go_to_library": "Вернуться к библиотеке",
"go_to_readlist": "Перейти к списку чтения",
"go_to_series": "Перейти к серии",
"i_understand": "Я понимаю",
"library": "Библиотека",
"locale_name": "Русский",
"locale_rtl": "false",
"lock_all": "Заблокировать все",
"media": "Медиа",
"more": "Ещё",
"n_selected": "{count} выбрано",
"nothing_to_show": "Нет данных для отображения",
"ok": "OK",
"outdated": "Устарело",
"page": "Страница",
"page_number": "Номер страницы",
@ -242,17 +260,20 @@
"password": "Пароль",
"pdf": "PDF",
"pending_tasks": "Нет незавершенных задач | 1 незавершенная задача | {count} незавершенных задач",
"pinned_libraries": "Закреплённые библиотеки",
"publisher": "Издатель",
"read": "Читать",
"read_on": "Читать {date}",
"read_on": "Прочитано {date}",
"readlist": "Список чтения",
"readlists": "Списки чтения",
"remember-me": "Запомнить",
"required": "Необходимо",
"remember-me": "Запомнить меня",
"reorder": "Изменить порядок",
"required": "Обязательно",
"reset_filters": "Сбросить фильтры",
"roles": "Роли",
"save_changes": "Сохранить изменения",
"series": "Серии",
"settings": "Настройки",
"tags": "Теги",
"unavailable": "Недоступно",
"unlock_all": "Разблокировать все",

View file

@ -67,7 +67,7 @@
"button_scan": "Skenovať",
"button_select_series": "Vybrať sériu",
"field_import_path": "Importovať z priečinka",
"info_part1": "Na tejto obrazovke môžete importovať súbory, ktoré nie sú súčasťou vašich existujúcich knižníc. Súbory môžete importovať iba do existujúcich sérií, pričom Komga presunie alebo skopíruje súbory do adresára zvolenej série.",
"info_part1": "Na tejto obrazovke môžete importovať súbory, ktoré nie sú súčasťou vašich existujúcich knižníc. Súbory môžete importovať iba do existujúcich sérií, pričom Komga presunie alebo skopíruje súbory do priečinka zvolenej série.",
"info_part2": "Ak vyberiete číslo pre knihu a kniha s týmto číslom už existuje, budete môcť porovnať obe knihy. Ak sa rozhodnete knihu importovať, Komga aktualizuje existujúcu knihu novou verziou, čím efektívne nahradí starý súbor novým.",
"no_files_found": "Nenašli sa žiadne súbory",
"notification": {
@ -89,12 +89,12 @@
"series": "Séria"
},
"title": "Importovať",
"try_another_directory": "Skúste vyhľadať v inom adresári"
"try_another_directory": "Skúste vyhľadať v inom priečinku"
},
"bookreader": {
"beginning_of_book": "Ste na začiatku knihy.",
"changing_reading_direction": "Zmena smeru čítania na",
"cycling_page_layout": "Zmeniť vzhľad strany",
"cycling_page_layout": "Zmeniť rozloženie strany",
"cycling_page_margin": "Zmeniť okraj strany",
"cycling_scale": "Zmeniť mierku",
"cycling_side_padding": "Zmeniť odsadenie strany",
@ -105,8 +105,8 @@
"move_next_exit": "Kliknite alebo stlačte znovu tlačidlo „Ďalej“, aby ste opustili čítačku.",
"move_previous": "Kliknite alebo stlačte znovu tlačidlo „Predchádzajúca“, aby ste prešli na predchádzajúcu knihu.",
"notification_poster_set_book": "Súčasná strana bola nastavená ako plagát knihy.",
"notification_poster_set_readlist": "Súčasná strana bola nastavená ako plagát zoznamu čítania.",
"notification_poster_set_series": "Súčasná strana bola nastavená ako plagát série.",
"notification_poster_set_readlist": "Aktuálna strana bola nastavená ako plagát zoznamu čítania.",
"notification_poster_set_series": "Aktuálna strana bola nastavená ako plagát série.",
"paged_reader_layout": {
"double": "Dvojstránka",
"double_no_cover": "Dvojstránka (bez obálky)",
@ -252,6 +252,7 @@
"n_selected": "{count} vybrané",
"nothing_to_show": "Nič na zobrazenie",
"ok": "OK",
"oneshot": "Samostatný diel",
"outdated": "Zastarané",
"page": "Strana",
"page_number": "Číslo strany",
@ -267,7 +268,7 @@
"read_on": "Čítané {date}",
"readlist": "Zoznam čítania",
"readlists": "Zoznamy čítania",
"remember-me": "Zapamätaj si ma",
"remember-me": "Zapamätať si ma",
"reorder": "Zmeniť poradie",
"required": "Povinné",
"reset_filters": "Resetovať filtre",
@ -275,6 +276,7 @@
"save_changes": "Uložiť zmeny",
"series": "Séria | Série",
"settings": "Nastavenia",
"sidecars": "Sprievodne súbory",
"tags": "Štítky",
"ui": "Používateľské rozhranie",
"unavailable": "Nedostupné",
@ -296,6 +298,7 @@
"book_number": "Číslo knihy: {name}",
"book_series": "Séria: {name}",
"button_import": "Importovať",
"button_match": "Porovnať",
"comicrack_preambule_html": "Existujúce zoznamy čítania ComicRack môžete importovať vo formáte <code>.cbl</code>.<br>Komga sa pokúsi priradiť zadané čísla sérií a kníh k sériám a knihám vo vašich knižniciach.",
"dialog_confirmation": {
"body": "{unmatched} / {total} kníh je neporovnaných",
@ -318,7 +321,7 @@
"add_api_key": {
"button_confirm": "Generovať",
"context": "Kľúče API možno používať na overenie prostredníctvom protokolu Kobo Sync.",
"dialog_title": "Generovať nový kľúč API",
"dialog_title": "Generovať nový API kľúč",
"field_comment": "Poznámka",
"field_comment_hint": "Na čo slúži tento API kľúč?",
"info_copy": "Nezabudnite si teraz skopírovať svoj API kľúč. Už ho nebudete môcť znovu zobraziť!"
@ -392,13 +395,16 @@
"confirm_delete_multiple": "Áno, odstrániť {count} zoznamy čítania",
"dialog_title": "Odstrániť zoznam čítania",
"dialog_title_multiple": "Odstrániť zoznamy čítania",
"warning_html": "Zoznam čítania <b>{name}</b> bude odstránený z tohto servera. Vaše mediálne súbory nebudú ovplyvnené. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?"
"warning_html": "Zoznam čítania <b>{name}</b> bude odstránený z tohto servera. Vaše mediálne súbory nebudú ovplyvnené. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?",
"warning_multiple_html": "{count} zoznamy čítania budú z tohto servera odstránené. Vaše mediálne súbory nebudú ovplyvnené. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?"
},
"delete_series": {
"button_confirm": "Odstrániť",
"confirm_delete": "Áno, odstrániť sériu \"{name}\" a jej súbory",
"confirm_delete_multiple": "Áno, odstrániť {count} série a ich súbory",
"dialog_title": "Odstrániť sériu"
"dialog_title": "Odstrániť sériu",
"warning_html": "Séria <b>{name}</b> bude spolu s uloženými mediálnymi súbormi odstránená z tohto servera. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?",
"warning_multiple_html": "{count} sérií bude z tohto servera odstránených spolu s uloženými mediálnymi súbormi. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?"
},
"delete_user": {
"button_confirm": "Odstrániť",
@ -406,11 +412,239 @@
"dialog_title": "Odstrániť používateľa",
"warning_html": "Používateľ <b>{name}</b> bude odstránený z tohto servera. Túto akciu <b>nie je možné</b> vrátiť späť. Pokračovať?"
},
"edit_books": {
"add_author_role_error_duplicate": "Už existuje",
"authors_notice_multiple_edit": "Upravujete autorov viacerých kníh. Týmto sa prepíšu existujúci autori každej knihy.",
"button_cancel": "Zrušiť",
"button_confirm": "Uložiť zmeny",
"copy_from": "Kopírovať z {field}",
"dialog_title_multiple": "Upraviť {count} knihu | Upraviť {count} knihy",
"dialog_title_single": "Upraviť {book}",
"field_alternate_title": "Alternatívny názov",
"field_isbn": "ISBN",
"field_isbn_error": "Musí byť platné ISBN 13",
"field_link_label": "Značka",
"field_link_url": "URL",
"field_link_url_error_protocol": "Musí byť http alebo https",
"field_link_url_error_url": "Musí byť platná adresa URL",
"field_number": "Číslo",
"field_number_sort": "Poradové číslo",
"field_number_sort_hint": "Môžete použiť desatinné čísla",
"field_release_date": "Dátum vydania",
"field_release_date_error": "Musí byť platný dátum vo formáte RRRR-MM-DD",
"field_summary": "Zhrnutie",
"field_tags": "Štítky",
"field_title": "Titul",
"number_sort_decrement": "Znížiť všetky o 1",
"number_sort_increment": "Zvýšiť všetko o 1",
"tab_authors": "Autori",
"tab_general": "Všeobecné",
"tab_links": "Odkazy",
"tab_poster": "Plagát",
"tab_tags": "Štítky",
"tags_notice_multiple_edit": "Upravujete štítky pre viacero kníh. Týmto sa prepíšu existujúce štítky každej knihy."
},
"edit_collection": {
"button_cancel": "Zrušiť",
"button_confirm": "Uložiť zmeny",
"dialog_title": "Upraviť zbierku",
"field_manual_ordering": "Manuálne zoradenie",
"label_ordering": "V predvolenom nastavení budú série v zbierke zoradené podľa názvu. Môžete aktivovať ručné zoradenie a definovať si vlastné poradie.",
"tab_general": "Všeobecné",
"tab_poster": "Plagát"
},
"edit_library": {
"button_browse": "Prechádzať",
"button_cancel": "Zrušiť",
"button_confirm_add": "Pridať",
"button_confirm_edit": "Upraviť",
"button_next": "Ďalší",
"dialog_title_add": "Pridať knižnicu",
"dialot_title_edit": "Upraviť knižnicu",
"field_analysis_analyze_dimensions": "Analyzovať rozmery strán",
"field_analysis_hash_files": "Vypočítať hash pre súbory",
"field_analysis_hash_koreader": "Vypočítať hash súborov pre KOReader",
"field_analysis_hash_pages": "Vypočítať hash pre strany",
"field_convert_to_cbz": "Automaticky konvertovať na CBZ",
"field_import_barcode_isbn": "ISBN čiarový kód",
"field_import_comicinfo_book": "Metadáta knihy",
"field_import_comicinfo_collections": "Zbierky",
"field_import_comicinfo_readlists": "Zoznamy čítania",
"field_import_comicinfo_series": "Metadáta série",
"field_import_comicinfo_series_append_volume": "Pridať zväzok k názvu série",
"field_import_epub_book": "Metadáta knihy",
"field_import_epub_series": "Metadáta série",
"field_import_local_artwork": "Lokálne ilustrácie",
"field_import_mylar_series": "Metadáta série",
"field_name": "Názov",
"field_oneshotsdirectory": "Priečinok samostatných dielov",
"field_repair_extensions": "Automaticky opraviť nesprávne prípony súborov",
"field_root_folder": "Koreňový priečinok",
"field_scan_interval": "Interval skenovania",
"field_scanner_empty_trash_after_scan": "Po každom skenovaní automaticky vyprázdniť kôš",
"field_scanner_force_directory_modified_time": "Vynútiť čas úpravy priečinka",
"field_scanner_scan_startup": "Skenovať pri spustení",
"field_series_cover": "Obálka série",
"file_browser_dialog_button_confirm": "Vybrať",
"file_browser_dialog_title": "Koreňový priečinok knižnice",
"label_analysis": "Analýza",
"label_file_management": "Správa súborov",
"label_import_barcode_isbn": "Importovať ISBN z čiarového kódu",
"label_import_comicinfo": "Importovať metadáta pre CBR/CBZ obsahujúce súbor ComicInfo.xml",
"label_import_epub": "Importovať metadáta zo súborov EPUB",
"label_import_local": "Importovať lokálne mediálne súbory",
"label_import_mylar": "Importovať metadáta generované pomocou Mylar",
"label_scan_directory_exclusions": "Vyňaté priečinky",
"label_scan_types": "Skenovať tieto typy súborov",
"label_scanner": "Skener",
"label_series_cover": "Obálka série",
"tab_general": "Všeobecné",
"tab_metadata": "Metadáta",
"tab_options": "Možnosti",
"tooltip_oneshotsdirectory": "Nechajte prázdne, pre deaktiváciu",
"tooltip_scanner_force_modified_time": "Aktivujte, ak je knižnica na disku Google Drive",
"tooltip_use_resources": "Môže spotrebovať veľa zdrojov na veľkých knižniciach alebo pomalom hardvéri"
},
"edit_readlist": {
"button_cancel": "Zrušiť",
"button_confirm": "Uložiť zmeny",
"dialog_title": "Upraviť zoznam čítania",
"field_manual_ordering": "Manuálne zoradenie",
"field_name": "Názov",
"field_summary": "Zhrnutie",
"label_ordering": "Knihy v zozname čítania sú pri východzom nastavení zoradené ručne. Ručné zoradenie môžete vypnúť a knihy zoradiť podľa dátumu vydania.",
"tab_general": "Všeobecné",
"tab_poster": "Plagát"
},
"edit_recommended": {
"button_confirm": "Uložiť zmeny",
"button_reset": "Obnoviť predvolené nastavenia",
"dialog_title": "Upraviť odporúčané zobrazenie"
},
"edit_series": {
"button_cancel": "Zrušiť",
"button_confirm": "Uložiť zmeny",
"dialog_title_multiple": "Upraviť {count} sériu | Upraviť {count} série",
"dialog_title_single": "Upraviť {series}",
"field_age_rating": "Vekové hodnotenie",
"field_age_rating_error": "Vekové hodnotenie musí byť 0 alebo viac",
"field_genres": "Žánre",
"field_labels": "Značky",
"field_language": "Jazyk",
"field_language_hint": "Jazyková značka IETF BCP 47",
"field_publisher": "Vydavateľ",
"field_reading_direction": "Smer čítania",
"field_sort_title": "Názov pre radenie",
"field_status": "Stav",
"field_summary": "Zhrnutie",
"field_tags": "Štítky",
"field_title": "Titul",
"field_total_book_count": "Celkový počet kníh",
"field_total_book_count_error": "Celkový počet kníh musí byť 1 alebo viac",
"mixed": "ZMIEŠANÉ",
"tab_general": "Všeobecné",
"tab_poster": "Plagát",
"tab_sharing": "Zdieľanie",
"tab_tags": "Štítky",
"tab_titles": "Alternatívne názvy",
"tags_notice_multiple_edit": "Upravujete štítky pre viacero sérií. Týmto sa prepíšu existujúce štítky každej série."
},
"edit_user": {
"button_cancel": "Zrušiť",
"button_confirm": "Uložiť zmeny",
"dialog_title": "Upraviť používateľa",
"label_roles_for": "Roly pre {name}"
},
"edit_user_restrictions": {
"age_restriction": {
"allow_under": "Povoliť len do",
"exclude_over": "Vylúčiť nad",
"none": "Bez obmedzení"
},
"edit_restrictions_for": "Upraviť obmedzenia pre {name}",
"label_age_restriction": "Vekové obmedzenie",
"label_allow_only_labels": "Povoliť iba tieto označenia",
"label_exclude_labels": "Vylúčiť tieto označenia",
"tab_content_restrictions": "Obmedzenia obsahu",
"tab_shared_libraries": "Zdieľané knižnice"
},
"empty_trash": {
"body": "V štandardnom nastavení mediálny server neodstraňuje informácie o médiách ihneď. To je užitočné v prípade, ak je disk dočasne odpojený. Keď vyprázdnite kôš knižnice, všetky informácie o chýbajúcich médiách sa odstránia."
"body": "V štandardnom nastavení mediálny server neodstraňuje informácie o médiách ihneď. To je užitočné v prípade, ak je disk dočasne odpojený. Keď vyprázdnite kôš knižnice, všetky informácie o chýbajúcich médiách sa odstránia.",
"button_confirm": "Vyprázdniť",
"title": "Vyprázdniť kôš pre knižnicu"
},
"file_browser": {
"button_cancel": "Zrušiť",
"button_confirm_default": "Vybrať",
"dialog_title_default": "Prehliadač súborov",
"parent_directory": "Nadriadený"
},
"filename_chooser": {
"button_choose": "Vybrať",
"field_destination_filename": "Názov cieľového súboru",
"label_source_filename": "Názov zdrojového súboru",
"table": {
"existing_file": "Existujúci súbor",
"order": "Poradie"
},
"title": "Názov cieľového súboru"
},
"force_kobo_sync": {
"dialog_title": "Vynútiť synchronizáciu Kobo",
"warning_html": "Týmto sa vymaže celá história synchronizácie pre tento kľúč API. Váš Kobo synchronizuje všetko pri najbližšej synchronizácii."
},
"password_change": {
"button_cancel": "Zrušiť",
"button_confirm": "Zmeniť heslo",
"dialog_title": "Zmeniť heslo",
"field_new_password": "Nové heslo",
"field_new_password_error": "Je nutné zadať nové heslo.",
"field_repeat_password": "Zopakujte nové heslo",
"field_repeat_password_error": "Heslá sa musia zhodovať."
},
"refresh_library_metadata": {
"body": "Obnoví metadáta všetkých mediálnych súborov v knižnici. V závislosti od veľkosti knižnice to môže trvať dlhší čas.",
"button_confirm": "Obnoviť",
"title": "Obnoviť metadáta pre knižnicu"
},
"series_picker": {
"label_search_series": "Vyhľadať sériu",
"no_results": "Nenašla sa žiadna séria",
"title": "Vybrať sériu"
},
"server_stop": {
"button_confirm": "Stop",
"confirmation_message": "Naozaj chcete zastaviť Komga?",
"dialog_title": "Vypnúť server"
},
"shortcut_help": {
"label_description": "Popis",
"label_key": "Kľúč"
},
"transient_book_details": {
"label_candidate": "Kandidát",
"label_existing": "Existujúci",
"label_format": "Formát",
"label_name": "Názov",
"label_pages": "Strany",
"label_size": "Veľkosť",
"pages_table": {
"filename": "Názov súboru",
"height": "Výška",
"index": "Index",
"media_type": "Typ média",
"size": "Veľkosť",
"width": "Šírka"
},
"title": "Podrobnosti o knihe",
"title_comparison": "Porovnanie kníh"
},
"transient_book_viewer": {
"label_candidate": "Kandidát",
"label_existing": "Existujúci",
"page_of_pages": "{page} / {pages}",
"title": "Preskúmať knihu",
"title_comparison": "Porovnanie kníh"
}
},
"duplicate_pages": {
@ -418,30 +652,441 @@
"action_delete_auto": "Automatické odstránenie",
"action_delete_manual": "Manuálne odstránenie",
"action_delete_matches": "Odstrániť zhody",
"action_ignore": "Ignorovať",
"action_ignore_remaining": "Ignorovať zostávajúcich ({count})",
"action_manual_delete_remaining": "Manuálne odstránenie zostávajúcich ({count})",
"confirm_auto_delete_remaining": "Všetky zostávajúce hashe strán na tejto stránke ({count}) budú označené na automatické vymazanie.",
"confirm_manual_delete_remaining": "Všetky zostávajúce hashe strán na tejto stránke ({count}) budú označené na manuálne vymazanie.",
"delete_to_save": "Odstrániť, aby sa ušetrilo {size}",
"deleted_count": "Odstránené {count} krát"
"deleted_count": "Odstránené {count} krát",
"empty_title": "Nenašli sa žiadne duplicitné strany",
"empty_title_known": "Žiadne známe duplicity",
"filter": {
"count": "Počet",
"date_added": "Dátum pridania",
"date_modified": "Dátum úpravy",
"delete_count": "Počet vymazaní",
"delete_size": "Ušetrené miesto",
"match_count": "Počet zhôd",
"size": "Veľkosť",
"total_size": "Celková veľkosť"
},
"info": "Odstránenie duplicitných stránok spôsobí úpravu vašich súborov. Pred použitím automatického odstránenia si zálohujte súbory a použite ručné odstránenie.",
"known": "Známe",
"matches_n": "Žiadne zhody | 1 zhoda | {count} zhody",
"new": "Nové",
"saved_size": "Ušetrené {size}",
"title": "Duplicitné strany",
"unknown_size": "Neznáma veľkosť"
},
"duplicates": {
"file_hash": "Hash súboru",
"size": "Veľkosť",
"title": "Duplicitné súbory",
"url": "URL"
},
"enums": {
"copy_mode": {
"HARDLINK": "Hardlink/Kopírovanie súborov",
"MOVE": "Presunúť súbory"
},
"epubreader": {
"appearances": {
"day": "Deň",
"night": "Noc",
"sepia": "Sépia"
},
"column_count": {
"auto": "Auto",
"one": "Jeden",
"two": "Dva"
},
"reading_direction": {
"auto": "Automatický",
"ltr": "Zľava doprava",
"rtl": "Sprava doľava"
}
},
"historical_event_type": {
"BookConverted": "Kniha konvertovaná",
"BookFileDeleted": "Súbor knihy bol odstránený",
"BookImported": "Kniha importovaná",
"DuplicatePageDeleted": "Duplicitná strana bola odstránená",
"SeriesFolderDeleted": "Priečinok série bol odstránený"
},
"media_profile": {
"DIVINA": "DIVINA",
"EPUB": "EPUB",
"PDF": "PDF"
},
"media_status": {
"ERROR": "Chyba",
"OUTDATED": "Zastarané",
"READY": "Pripravený",
"UNKNOWN": "Neznámy",
"UNSUPPORTED": "Nepodporované"
},
"page_hash_action": {
"DELETE_AUTO": "Automatické odstránenie",
"DELETE_MANUAL": "Manuálne odstránenie"
"DELETE_MANUAL": "Manuálne odstránenie",
"IGNORE": "Ignorovať"
},
"reading_direction": {
"LEFT_TO_RIGHT": "Zľava doprava",
"RIGHT_TO_LEFT": "Sprava doľava",
"VERTICAL": "Vertikálny",
"WEBTOON": "Webtoon"
},
"scan_interval": {
"DAILY": "Denne",
"DISABLED": "Vypnuté",
"EVERY_12H": "Každých 12 hodín",
"EVERY_6H": "Každých 6 hodín",
"HOURLY": "Každú hodinu",
"WEEKLY": "Týždenne"
},
"series_cover": {
"FIRST": "Prvá",
"FIRST_UNREAD_OR_FIRST": "Najskôr neprečítané, inak prvé",
"FIRST_UNREAD_OR_LAST": "Najskôr neprečítané, inak posledné",
"LAST": "Posledný"
},
"series_status": {
"ABANDONED": "Opustená",
"ENDED": "Ukončená",
"HIATUS": "Pozastavená",
"ONGOING": "Prebiehajúca"
},
"thumbnail_size": {
"DEFAULT": "Predvolené (300px)",
"LARGE": "Veľký (900px)",
"MEDIUM": "Stredný (600px)",
"XLARGE": "Extra veľký (1200px)"
}
},
"epubreader": {
"current_chapter": "Aktuálna kapitola",
"page_of": "Strana {page} z {count}",
"publisher_font": "Vydavateľ",
"settings": {
"column_count": "Počet stĺpcov",
"font_family": "Písmo",
"layout": "Rozloženie",
"layout_paginated": "Stránkované",
"layout_scroll": "Posuvné",
"navigation_mode": "Režim navigácie",
"navigation_options": {
"both": "Oba",
"buttons": "Tlačidlá",
"click": "Kliknutie / Klepnutie"
},
"page_margins": "Okraje strany",
"viewing_theme": "Motív zobrazenia"
},
"shortcuts": {
"cycle_pagination": "Zmena počtu stĺpcov",
"cycle_viewing_theme": "Zmena motívu zobrazenia",
"font_size_decrease": "Zmenšiť veľkosť písma",
"font_size_increase": "Zväčšiť veľkosť písma",
"menus": "Ponuky",
"next": "Vpred",
"previous": "Späť",
"reader_navigation": "Navigácia v čítačke",
"scroll": "Zmeniť rozloženie na posúvanie",
"settings": "Nastavenia",
"show_hide_toc": "Zobraziť/skryť obsah"
}
},
"error_codes": {
"ERR_1000": "Počas analýzy nebolo možné získať prístup k súboru",
"ERR_1001": "Typ média nie je podporovaný",
"ERR_1002": "Šifrované archívy RAR nie sú podporované",
"ERR_1003": "Solid RAR archívy nie sú podporované",
"ERR_1004": "Viaczväzkové archívy RAR nie sú podporované",
"ERR_1005": "Neznáma chyba pri analýze knihy",
"ERR_1006": "Kniha neobsahuje žiadnu stranu",
"ERR_1007": "Niektoré záznamy nebolo možné analyzovať",
"ERR_1008": "Neznáma chyba pri získavaní záznamov o knihe",
"ERR_1009": "Zoznam čítania s týmto názvom už existuje",
"ERR_1015": "Chyba pri deserializácii ComicRack CBL",
"ERR_1016": "Priečinok nie je dostupný alebo nie je priečinkom",
"ERR_1017": "Nie je možné skenovať priečinok, ktorý je súčasťou existujúcej knižnice",
"ERR_1018": "Súbor nenájdený",
"ERR_1019": "Nie je možné importovať súbor, ktorý je súčasťou existujúcej knižnice",
"ERR_1020": "Kniha na upgrade nepatrí do poskytnutej série",
"ERR_1021": "Cieľový súbor už existuje",
"ERR_1022": "Novo importovaná kniha sa nedala naskenovať",
"ERR_1023": "Kniha sa už nachádza v ReadingList",
"ERR_1024": "Chyba prihlásenia OAuth2: chýba atribút e-mail",
"ERR_1025": "Chyba prihlásenia OAuth2: neexistuje žiadny lokálny používateľ s touto e-mailovou adresou",
"ERR_1026": "Chyba prihlásenia OpenID Connect: e-mail nebol overený",
"ERR_1027": "Chyba prihlásenia OpenID Connect: chýba atribút email_verified",
"ERR_1028": "Chyba prihlásenia OpenID Connect: chýba atribút email",
"ERR_1029": "ComicRack CBL neobsahuje žiadny element Book",
"ERR_1030": "ComicRack CBL neobsahuje žiaden element Name",
"ERR_1031": "ComicRack CBL Book chýba séria alebo číslo",
"ERR_1032": "Súbor EPUB má nesprávny typ média",
"ERR_1033": "Niektoré položky chýbajú",
"ERR_1034": "Kľúč API s týmto komentárom už existuje",
"ERR_1035": "Chyba pri získavaní EPUB TOC",
"ERR_1036": "Chyba pri získavaní EPUB Landmarks",
"ERR_1037": "Chyba pri získavaní zoznamu stránok EPUB",
"ERR_1038": "Chyba pri načítaní stránok EPUB divina",
"ERR_1039": "Chyba pri získavaní EPUB pozícií"
},
"filter": {
"age_rating": "vekové hodnotenie",
"age_rating_none": "Bez obmedzení",
"any": "Akýkoľvek",
"complete": "Kompletná",
"genre": "žáner",
"in_progress": "Rozčítaná",
"language": "jazyk",
"library": "knižnica",
"media_profile": "Profil média",
"media_status": "Stav médií",
"oneshot": "Samostatný diel",
"publisher": "vydavateľ",
"read": "Prečítaná",
"release_date": "dátum vydania",
"sharing_label": "Značka zdieľania",
"status": "stav",
"tag": "štítok",
"unread": "Neprečítaná"
},
"filter_drawer": {
"filter": "filter",
"sort": "zoradiť"
},
"history": {
"header": {
"book": "Kniha",
"date": "Dátum",
"details": "Podrobnosti",
"series": "Séria",
"type": "Typ"
},
"title": "História"
},
"home": {
"theme": "Vzhľad",
"translation": "Preklad"
},
"library_navigation": {
"browse": "Prechádzať",
"browse_books": "Knihy",
"browse_series": "Série",
"collections": "Zbierky",
"readlists": "Zoznamy čítania",
"recommended": "Odporúčané"
},
"login": {
"create_user_account": "Vytvoriť používateľský účet",
"login": "Prihlásiť sa",
"unclaimed_html": "Tento server Komga ešte nie je aktívny, aby ste mali k nemu prístup, musíte si vytvoriť používateľský účet. <br><br>Zvoľte si <strong>e-mailovú adresu</strong> a <strong>heslo</strong> a kliknite na <strong>Vytvoriť používateľský účet</strong>."
},
"media_analysis": {
"comment": "Komentár",
"media_analysis": "Analýza médií",
"media_type": "Typ média",
"name": "Názov",
"size": "Veľkosť",
"status": "Stav",
"url": "URL"
},
"menu": {
"delete": "Odstrániť"
"add_to_collection": "Pridať do zbierky",
"add_to_readlist": "Pridať do zoznamu čítania",
"analyze": "Analyzovať",
"bulk_edit_metadata": "Hromadná úprava metadát",
"delete": "Odstrániť",
"deselect_all": "Zrušiť označenie všetkých",
"download_readlist": "Stiahnuť zoznam čítania",
"download_series": "Stiahnuť sériu",
"edit": "Upraviť",
"edit_metadata": "Upraviť metadáta",
"empty_trash": "Vyprázdniť kôš",
"mark_read": "Označiť ako prečítané",
"mark_unread": "Označiť ako neprečítané",
"pin": "Pripnúť",
"refresh_metadata": "Obnoviť metadáta",
"scan_library_files": "Skenovať súbory knižnice",
"scan_library_files_deep": "Skenovať súbory knižnice (hĺbkovo)",
"select_all": "Vybrať všetko",
"unpin": "Odopnúť"
},
"metrics": {
"library_books": "Počet kníh na knižnicu",
"library_disk_space": "Miesto na disku knižnice",
"library_series": "Počet sérií na knižnicu",
"library_sidecars": "Počet sprievodných súborov na knižnicu",
"tasks_executed": "Vykonané úlohy",
"tasks_total_time": "Celkový čas úloh",
"title": "Metriky"
},
"missing_posters": {
"title": "Chýbajúce plagáty"
},
"navigation": {
"home": "Domov",
"libraries": "Knižnice",
"logout": "Odhlásiť sa"
},
"no_libraries_pinned": {
"subtitle": "Knižnicu môžete pripnúť z menu s tromi bodkami",
"title": "Žiadne pripnuté knižnice"
},
"page_not_found": {
"go_back_to_home_page": "Vrátiť sa na hlavnú stránku",
"page_does_not_exist": "Stránka, ktorú hľadáte, neexistuje.",
"page_not_found": "Stránka nenájdená"
},
"read_more": {
"less": "Menej",
"more": "Viac"
},
"readlist_import": {
"row": {
"duplicate_book": "Duplicitná kniha",
"error_choose_book": "Vybrať knihu"
}
},
"readlists_expansion_panel": {
"manage_readlist": "Spravovať zoznam čítania",
"title": "{name} zoznam čítania"
},
"search": {
"no_results": "Vyhľadávanie nevrátilo žiadne výsledky",
"search": "Vyhľadávanie",
"search_for_something_else": "Skúste vyhľadať niečo iné",
"search_results_for": "Výsledky vyhľadávania pre \"{name}\""
},
"searchbox": {
"in_library": "v {library}",
"no_results": "Žiadne výsledky",
"search_all": "Vyhľadávať všetko…"
},
"server": {
"server_management": {
"button_cancel_all_tasks": "Zrušiť všetky úlohy",
"button_empty_trash": "Vyprázdniť kôš pre všetky knižnice",
"button_scan_libraries": "Skenovať všetky knižnice",
"button_scan_libraries_deep": "Skenovať všetky knižnice (hĺbkovo)",
"button_shutdown": "Vypnúť",
"download_log": "Stiahnuť log súbor",
"notification_tasks_cancelled": "Žiadne úlohy na zrušenie | Jedna úloha zrušená | {count} úlohy zrušené",
"section_title": "Správa servera"
},
"tab_title": "Server",
"updates": "Aktualizácie"
},
"server_settings": {
"config_precedence": "Má prednosť pred konfiguračným súborom",
"dialog_regenerate_thumbnails": {
"body": "Veľkosť náhľadov sa zmenila. Chcete znovu vygenerovať náhľady kníh?",
"btn_alternate": "Áno, všetky knihy",
"btn_cancel": "Nie",
"btn_confirm": "Áno, ale len ak je väčší",
"title": "Regenerovať náhľady"
},
"hint_kobo_port": "Nastavte len v prípade problémov so synchronizáciou obálok a sťahovaním",
"label_delete_empty_collections": "Po skenovaní odstrániť prázdne zbierky",
"label_delete_empty_readlists": "Po skenovaní odstrániť prázdne zoznamy čítania"
"label_delete_empty_readlists": "Po skenovaní odstrániť prázdne zoznamy čítania",
"label_kepubify_path": "Cesta ku kepubify",
"label_kobo_port": "Externý port Kobo Sync",
"label_kobo_proxy": "Presmerovať požiadavky Kobo Sync na Kobo Store",
"label_rememberme_duration": "Doba trvania \"Zapamätať si ma\" (v dňoch)",
"label_server_context_path": "Základná URL adresa",
"label_server_port": "Port servera",
"label_task_pool_size": "Vlákna pre úlohy",
"label_thumbnail_size": "Veľkosť náhľadov",
"requires_restart": "Vyžaduje reštart, aby sa zmeny prejavili",
"server_settings": "Nastavenia servera"
},
"settings_user": {
"change_password": "Zmeniť heslo",
"edit_restrictions": "Upravovať obmedzenia",
"edit_user": "Upraviť používateľa",
"latest_activity": "Posledná aktivita: {date}",
"no_recent_activity": "Žiadna nedávna aktivita",
"role_administrator": "Administrátor",
"role_user": "Používateľ"
},
"sort": {
"books_count": "Počet kníh",
"date_added": "Dátum pridania",
"date_read": "Dátum prečítania",
"date_updated": "Dátum aktualizácie",
"file_name": "Názov súboru",
"file_size": "Veľkosť súboru",
"folder_name": "Názov priečinka",
"name": "Názov",
"number": "Číslo",
"page_count": "Počet strán",
"random": "Náhodné",
"release_date": "Dátum vydania",
"series": "Séria"
},
"theme": {
"dark": "Tmavý",
"light": "Svetlý",
"system": "Systém"
},
"thumbnail_card": {
"tooltip_delete": "Odstrániť",
"tooltip_to_be_deleted": "Na odstránenie"
"tooltip_generated": "Vygenerované ilustrácie",
"tooltip_mark_as_selected": "Označiť ako vybrané",
"tooltip_selected": "Vybrané",
"tooltip_sidecar": "Miestna ilustrácia",
"tooltip_to_be_deleted": "Na odstránenie",
"tooltip_to_be_uploaded": "Na nahranie",
"tooltip_too_big": "Súbor je príliš veľký!",
"tooltip_user_uploaded": "Nahrané používateľom"
},
"titles_more": {
"less": "Menej titulov",
"more": "Viac titulov"
},
"ui_settings": {
"general": "Všeobecné",
"label_oauth2_auto_login": "Automatické prihlásenie cez OAuth2",
"label_oauth2_hide_login": "Skryť prihlasovacie polia, ak je povolené OAuth2",
"label_poster_blur_unread": "Rozmazať plagát pre neprečítané knihy a série",
"label_poster_stretch": "Prispôsobiť plagát karte",
"label_series_groups": "Zoskupovanie sérií",
"section_oauth2": "OAuth2",
"series_groups": {
"alpha": "Abecedne",
"japanese": "Gojūon (japonský)"
},
"tooltip_oauth2_auto_login": "Vyžaduje jedného poskytovateľa OAuth2 a povolenú funkciu 'skryť prihlasovacie polia'"
},
"updates": {
"available": "Aktualizácie sú k dispozícii",
"latest_installed": "Najnovšia verzia Komga je už nainštalovaná"
},
"user_roles": {
"ADMIN": "Administrátor",
"FILE_DOWNLOAD": "Stiahnutie súboru",
"KOBO_SYNC": "Kobo Sync",
"KOREADER_SYNC": "Synchronizácia KOReader",
"PAGE_STREAMING": "Streamovanie strán",
"USER": "Používateľ"
},
"users": {
"api_keys": "API kľúče",
"authentication_activity": "Autentifikačná aktivita",
"users": "Používatelia"
},
"validation": {
"context_path": "Musí začínať znakom '/', nesmie končiť znakmi '/-_', a môže obsahovať iba znaky '/-_a-z0-9'",
"one_or_more": "Musí byť 1 alebo viac",
"tcp_port": "Musí byť medzi 1 a 65535",
"zero_or_more": "Musí byť 0 alebo viac"
},
"welcome": {
"add_library": "Pridať knižnicu",
"no_libraries_yet": "Zatiaľ neboli pridané žiadne knižnice!",
"welcome_message": "Vitajte v Komga"
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,8 @@ FROM ubuntu:24.10 as build-amd64
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:23-jre $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN apt -y update && \
RUN sed -i -re 's/([a-z]{2}\.)?archive.ubuntu.com|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list.d/ubuntu.sources && \
apt -y update && \
apt -y install ca-certificates locales libjxl-dev libheif-dev libwebp-dev libarchive-dev wget curl && \
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \
locale-gen en_US.UTF-8 && \
@ -23,7 +24,8 @@ FROM ubuntu:24.10 as build-arm64
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:23-jre $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN apt -y update && \
RUN sed -i -re 's/([a-z]{2}\.)?ports.ubuntu.com\/ubuntu-ports/old-releases.ubuntu.com\/ubuntu/g' /etc/apt/sources.list.d/ubuntu.sources && \
apt -y update && \
apt -y install ca-certificates locales libjxl-dev libheif-dev libwebp-dev libarchive-dev wget curl && \
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \
locale-gen en_US.UTF-8 && \

View file

@ -7,7 +7,7 @@
"url": "https://github.com/gotson/komga/blob/master/LICENSE"
},
"title": "Komga API",
"version": "1.23.4"
"version": "1.23.6"
},
"externalDocs": {
"description": "Komga documentation",
@ -4919,7 +4919,7 @@
"operationId": "matchComicRackList",
"requestBody": {
"content": {
"application/json": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
@ -10313,6 +10313,9 @@
"bookId": {
"type": "string"
},
"id": {
"type": "string"
},
"properties": {
"type": "object",
"additionalProperties": {
@ -10331,6 +10334,7 @@
}
},
"required": [
"id",
"properties",
"timestamp",
"type"

View file

@ -418,6 +418,7 @@ class BookLifecycle(
val extension =
mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub
?: throw IllegalArgumentException("Epub extension not found")
.also { logger.error { "Epub extension not found for book ${book.id}. Book should be re-analyzed." } }
extension.positions[page - 1]
} else {
null
@ -496,6 +497,7 @@ class BookLifecycle(
val extension =
mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub
?: throw IllegalArgumentException("Epub extension not found")
.also { logger.error { "Epub extension not found for book ${book.id}. Book should be re-analyzed." } }
// match progression with positions
val matchingPositions = extension.positions.filter { it.href == href }
val matchedPosition =

View file

@ -13,6 +13,7 @@ import org.gotson.komga.infrastructure.image.MosaicGenerator
import org.gotson.komga.infrastructure.metadata.comicrack.ReadListProvider
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.support.TransactionTemplate
private val logger = KotlinLogging.logger {}
@ -31,6 +32,7 @@ class ReadListLifecycle(
@Throws(
DuplicateNameException::class,
)
@Transactional
fun addReadList(readList: ReadList): ReadList {
logger.info { "Adding new read list: $readList" }
@ -44,6 +46,7 @@ class ReadListLifecycle(
return readListRepository.findByIdOrNull(readList.id)!!
}
@Transactional
fun updateReadList(toUpdate: ReadList) {
logger.info { "Update read list: $toUpdate" }
val existing =
@ -71,6 +74,7 @@ class ReadListLifecycle(
* Add book to read list by name.
* Read list will be created if it doesn't exist.
*/
@Transactional
fun addBookToReadList(
readListName: String,
book: Book,

View file

@ -11,6 +11,7 @@ import org.gotson.komga.domain.persistence.ThumbnailSeriesCollectionRepository
import org.gotson.komga.infrastructure.image.MosaicGenerator
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.support.TransactionTemplate
private val logger = KotlinLogging.logger {}
@ -27,6 +28,7 @@ class SeriesCollectionLifecycle(
@Throws(
DuplicateNameException::class,
)
@Transactional
fun addCollection(collection: SeriesCollection): SeriesCollection {
logger.info { "Adding new collection: $collection" }
@ -40,6 +42,7 @@ class SeriesCollectionLifecycle(
return collectionRepository.findByIdOrNull(collection.id)!!
}
@Transactional
fun updateCollection(toUpdate: SeriesCollection) {
logger.info { "Update collection: $toUpdate" }
@ -67,6 +70,7 @@ class SeriesCollectionLifecycle(
* Add series to collection by name.
* Collection will be created if it doesn't exist.
*/
@Transactional
fun addSeriesToCollection(
collectionName: String,
series: Series,

View file

@ -20,6 +20,8 @@ class Hasher {
return computeHash(path.inputStream())
}
fun computeHash(string: String): String = computeHash(string.byteInputStream())
fun computeHash(stream: InputStream): String {
val hash = Algorithm.XXH3_128.Seeded(SEED.toLong()).createDigest()

View file

@ -0,0 +1,21 @@
package org.gotson.komga.infrastructure.jooq
import org.jooq.DSLContext
import org.springframework.transaction.support.TransactionSynchronizationManager
abstract class SplitDslDaoBase {
val dslRW: DSLContext
private val _dslRO: DSLContext
constructor(dslRW: DSLContext, dslRO: DSLContext) {
this.dslRW = dslRW
this._dslRO = dslRO
}
val dslRO: DSLContext
get() =
if (TransactionSynchronizationManager.isActualTransactionActive() && !TransactionSynchronizationManager.isCurrentTransactionReadOnly())
dslRW
else
_dslRO
}

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.AuthenticationActivity
import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.persistence.AuthenticationActivityRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.toOrderBy
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.AuthenticationActivityRecord
@ -21,9 +22,10 @@ import java.time.LocalDateTime
@Component
class AuthenticationActivityDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : AuthenticationActivityRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
AuthenticationActivityRepository {
private val aa = Tables.AUTHENTICATION_ACTIVITY
private val sorts =

View file

@ -1,6 +1,7 @@
package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.ContentRestrictions
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.toCondition
import org.gotson.komga.jooq.main.Tables
import org.jooq.DSLContext
@ -19,8 +20,9 @@ import java.time.LocalDateTime
@Component
class BookCommonDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO) {
private val b = Tables.BOOK
private val m = Tables.MEDIA
private val d = Tables.BOOK_METADATA

View file

@ -6,6 +6,7 @@ import org.gotson.komga.domain.model.SearchContext
import org.gotson.komga.domain.persistence.BookRepository
import org.gotson.komga.infrastructure.jooq.BookSearchHelper
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.rlbAlias
import org.gotson.komga.infrastructure.jooq.toOrderBy
@ -30,10 +31,11 @@ import java.time.ZoneId
@Component
class BookDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : BookRepository {
) : SplitDslDaoBase(dslRW, dslRO),
BookRepository {
private val b = Tables.BOOK
private val m = Tables.MEDIA
private val d = Tables.BOOK_METADATA

View file

@ -7,6 +7,7 @@ import org.gotson.komga.domain.model.SearchContext
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
import org.gotson.komga.infrastructure.jooq.BookSearchHelper
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.noCase
@ -51,11 +52,13 @@ import java.net.URL
@Component
class BookDtoDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val luceneHelper: LuceneHelper,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
private val bookCommonDao: BookCommonDao,
) : BookDtoRepository {
) : SplitDslDaoBase(dslRW, dslRO),
BookDtoRepository {
private val b = Tables.BOOK
private val m = Tables.MEDIA
private val d = Tables.BOOK_METADATA

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.BookMetadataAggregation
import org.gotson.komga.domain.persistence.BookMetadataAggregationRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.BookMetadataAggregationAuthorRecord
@ -18,10 +19,11 @@ import java.time.ZoneId
@Component
class BookMetadataAggregationDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : BookMetadataAggregationRepository {
) : SplitDslDaoBase(dslRW, dslRO),
BookMetadataAggregationRepository {
private val d = Tables.BOOK_METADATA_AGGREGATION
private val a = Tables.BOOK_METADATA_AGGREGATION_AUTHOR
private val t = Tables.BOOK_METADATA_AGGREGATION_TAG

View file

@ -4,6 +4,7 @@ import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.model.BookMetadata
import org.gotson.komga.domain.model.WebLink
import org.gotson.komga.domain.persistence.BookMetadataRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.BookMetadataAuthorRecord
@ -20,10 +21,11 @@ import java.time.ZoneId
@Component
class BookMetadataDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : BookMetadataRepository {
) : SplitDslDaoBase(dslRW, dslRO),
BookMetadataRepository {
private val d = Tables.BOOK_METADATA
private val a = Tables.BOOK_METADATA_AUTHOR
private val bt = Tables.BOOK_METADATA_TAG

View file

@ -1,5 +1,6 @@
package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.interfaces.api.rest.dto.ClientSettingDto
import org.gotson.komga.jooq.main.Tables
import org.jooq.DSLContext
@ -8,9 +9,9 @@ import org.springframework.stereotype.Component
@Component
class ClientSettingsDtoDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO) {
private val g = Tables.CLIENT_SETTINGS_GLOBAL
private val u = Tables.CLIENT_SETTINGS_USER

View file

@ -1,5 +1,6 @@
package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.toOrderBy
import org.gotson.komga.interfaces.api.persistence.HistoricalEventDtoRepository
import org.gotson.komga.interfaces.api.rest.dto.HistoricalEventDto
@ -15,8 +16,10 @@ import org.springframework.stereotype.Component
@Component
class HistoricalEventDtoDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : HistoricalEventDtoRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
HistoricalEventDtoRepository {
private val e = Tables.HISTORICAL_EVENT
private val ep = Tables.HISTORICAL_EVENT_PROPERTIES
@ -41,6 +44,7 @@ class HistoricalEventDtoDao(
.map { er ->
val epr = dslRO.selectFrom(ep).where(ep.ID.eq(er.id)).fetch()
HistoricalEventDto(
id = er.id,
type = er.type,
timestamp = er.timestamp,
bookId = er.bookId,

View file

@ -2,6 +2,7 @@ package org.gotson.komga.infrastructure.jooq.main
import com.fasterxml.jackson.databind.ObjectMapper
import org.gotson.komga.domain.model.MediaExtensionEpub
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.deserializeMediaExtension
import org.gotson.komga.interfaces.api.kobo.dto.ContributorDto
import org.gotson.komga.interfaces.api.kobo.dto.KoboBookMetadataDto
@ -16,9 +17,11 @@ import java.time.ZoneId
@Component
class KoboDtoDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val mapper: ObjectMapper,
) : KoboDtoRepository {
) : SplitDslDaoBase(dslRW, dslRO),
KoboDtoRepository {
private val b = Tables.BOOK
private val m = Tables.MEDIA
private val d = Tables.BOOK_METADATA

View file

@ -7,6 +7,7 @@ import org.gotson.komga.domain.model.ContentRestrictions
import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.model.UserRoles
import org.gotson.komga.domain.persistence.KomgaUserRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.UserApiKeyRecord
import org.gotson.komga.language.toCurrentTimeZone
@ -21,9 +22,10 @@ import java.time.ZoneId
@Component
class KomgaUserDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : KomgaUserRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
KomgaUserRepository {
private val u = Tables.USER
private val ur = Tables.USER_ROLE
private val ul = Tables.USER_LIBRARY_SHARING

View file

@ -2,6 +2,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Library
import org.gotson.komga.domain.persistence.LibraryRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.LibraryRecord
import org.gotson.komga.language.toCurrentTimeZone
@ -17,9 +18,10 @@ import java.time.ZoneId
@Component
class LibraryDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : LibraryRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
LibraryRepository {
private val l = Tables.LIBRARY
private val ul = Tables.USER_LIBRARY_SHARING
private val le = Tables.LIBRARY_EXCLUSIONS

View file

@ -8,6 +8,7 @@ import org.gotson.komga.domain.model.MediaExtension
import org.gotson.komga.domain.model.MediaFile
import org.gotson.komga.domain.model.ProxyExtension
import org.gotson.komga.domain.persistence.MediaRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.deserializeMediaExtension
import org.gotson.komga.infrastructure.jooq.serializeJsonGz
@ -27,11 +28,12 @@ import java.time.ZoneId
@Component
class MediaDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
private val mapper: ObjectMapper,
) : MediaRepository {
) : SplitDslDaoBase(dslRW, dslRO),
MediaRepository {
private val m = Tables.MEDIA
private val p = Tables.MEDIA_PAGE
private val f = Tables.MEDIA_FILE

View file

@ -5,6 +5,7 @@ import org.gotson.komga.domain.model.PageHashKnown
import org.gotson.komga.domain.model.PageHashMatch
import org.gotson.komga.domain.model.PageHashUnknown
import org.gotson.komga.domain.persistence.PageHashRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.toOrderBy
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.PageHashRecord
@ -25,9 +26,10 @@ import java.time.ZoneId
@Component
class PageHashDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : PageHashRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
PageHashRepository {
private val p = Tables.MEDIA_PAGE
private val b = Tables.BOOK
private val ph = Tables.PAGE_HASH

View file

@ -4,6 +4,7 @@ import org.gotson.komga.domain.model.ContentRestrictions
import org.gotson.komga.domain.model.ReadList
import org.gotson.komga.domain.persistence.ReadListRepository
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
import org.gotson.komga.infrastructure.jooq.sortByValues
@ -32,11 +33,12 @@ import java.util.SortedMap
@Component
class ReadListDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val luceneHelper: LuceneHelper,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : ReadListRepository {
) : SplitDslDaoBase(dslRW, dslRO),
ReadListRepository {
private val rl = Tables.READLIST
private val rlb = Tables.READLIST_BOOK
private val b = Tables.BOOK

View file

@ -5,6 +5,7 @@ import org.gotson.komga.domain.model.ReadListRequestBookMatchBook
import org.gotson.komga.domain.model.ReadListRequestBookMatchSeries
import org.gotson.komga.domain.model.ReadListRequestBookMatches
import org.gotson.komga.domain.persistence.ReadListRequestRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.noCase
import org.gotson.komga.jooq.main.Tables
import org.jooq.DSLContext
@ -18,8 +19,10 @@ import java.time.LocalDate
@Component
class ReadListRequestDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : ReadListRequestRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
ReadListRequestRepository {
private val sd = Tables.SERIES_METADATA
private val b = Tables.BOOK
private val bd = Tables.BOOK_METADATA

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import org.gotson.komga.domain.model.R2Locator
import org.gotson.komga.domain.model.ReadProgress
import org.gotson.komga.domain.persistence.ReadProgressRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.deserializeJsonGz
@ -24,11 +25,12 @@ import java.time.ZoneId
@Component
class ReadProgressDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
private val mapper: ObjectMapper,
) : ReadProgressRepository {
) : SplitDslDaoBase(dslRW, dslRO),
ReadProgressRepository {
private val r = Tables.READ_PROGRESS
private val rs = Tables.READ_PROGRESS_SERIES
private val b = Tables.BOOK

View file

@ -1,5 +1,6 @@
package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.interfaces.api.persistence.ReadProgressDtoRepository
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressDto
import org.gotson.komga.interfaces.api.rest.dto.TachiyomiReadProgressV2Dto
@ -16,8 +17,10 @@ import java.math.BigDecimal
@Component
class ReadProgressDtoDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : ReadProgressDtoRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
ReadProgressDtoRepository {
private val rlb = Tables.READLIST_BOOK
private val b = Tables.BOOK
private val d = Tables.BOOK_METADATA

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Author
import org.gotson.komga.domain.persistence.ReferentialRepository
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.udfStripAccents
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.BookMetadataAggregationAuthorRecord
@ -22,8 +23,10 @@ import java.time.LocalDate
@Component
class ReferentialDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : ReferentialRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
ReferentialRepository {
private val a = Tables.BOOK_METADATA_AUTHOR
private val sd = Tables.SERIES_METADATA
private val bma = Tables.BOOK_METADATA_AGGREGATION

View file

@ -4,6 +4,7 @@ import org.gotson.komga.domain.model.ContentRestrictions
import org.gotson.komga.domain.model.SeriesCollection
import org.gotson.komga.domain.persistence.SeriesCollectionRepository
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
import org.gotson.komga.infrastructure.jooq.sortByValues
@ -31,11 +32,12 @@ import java.time.ZoneId
@Component
class SeriesCollectionDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val luceneHelper: LuceneHelper,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : SeriesCollectionRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SeriesCollectionRepository {
private val c = Tables.COLLECTION
private val cs = Tables.COLLECTION_SERIES
private val s = Tables.SERIES

View file

@ -6,6 +6,7 @@ import org.gotson.komga.domain.model.Series
import org.gotson.komga.domain.persistence.SeriesRepository
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SeriesSearchHelper
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.csAlias
import org.gotson.komga.jooq.main.Tables
@ -28,10 +29,11 @@ import java.time.ZoneId
@Component
class SeriesDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : SeriesRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SeriesRepository {
private val s = Tables.SERIES
private val d = Tables.SERIES_METADATA
private val rs = Tables.READ_PROGRESS_SERIES

View file

@ -6,6 +6,7 @@ import org.gotson.komga.domain.model.SeriesSearch
import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SeriesSearchHelper
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.infrastructure.jooq.csAlias
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
@ -52,10 +53,12 @@ const val BOOKS_READ_COUNT = "booksReadCount"
@Component
class SeriesDtoDao(
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val luceneHelper: LuceneHelper,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : SeriesDtoRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SeriesDtoRepository {
private val s = Tables.SERIES
private val d = Tables.SERIES_METADATA
private val rs = Tables.READ_PROGRESS_SERIES

View file

@ -4,6 +4,7 @@ import org.gotson.komga.domain.model.AlternateTitle
import org.gotson.komga.domain.model.SeriesMetadata
import org.gotson.komga.domain.model.WebLink
import org.gotson.komga.domain.persistence.SeriesMetadataRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.SeriesMetadataRecord
@ -19,10 +20,11 @@ import java.time.ZoneId
@Component
class SeriesMetadataDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : SeriesMetadataRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SeriesMetadataRepository {
private val d = Tables.SERIES_METADATA
private val g = Tables.SERIES_METADATA_GENRE
private val st = Tables.SERIES_METADATA_TAG

View file

@ -1,5 +1,6 @@
package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.jooq.DSLContext
import org.springframework.beans.factory.annotation.Qualifier
@ -7,9 +8,9 @@ import org.springframework.stereotype.Component
@Component
class ServerSettingsDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO) {
private val s = Tables.SERVER_SETTINGS
fun <T> getSettingByKey(

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Sidecar
import org.gotson.komga.domain.model.SidecarStored
import org.gotson.komga.domain.persistence.SidecarRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.SidecarRecord
@ -16,10 +17,11 @@ import java.net.URL
@Component
class SidecarDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : SidecarRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SidecarRepository {
private val sc = Tables.SIDECAR
override fun findAll(): Collection<SidecarStored> = dslRO.selectFrom(sc).fetch().map { it.toDomain() }

View file

@ -8,6 +8,7 @@ import org.gotson.komga.domain.model.SyncPoint.ReadList.Companion.ON_DECK_ID
import org.gotson.komga.domain.persistence.SyncPointRepository
import org.gotson.komga.infrastructure.jooq.BookSearchHelper
import org.gotson.komga.infrastructure.jooq.RequiredJoin
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.language.toZonedDateTime
import org.jooq.DSLContext
@ -28,10 +29,11 @@ import java.time.ZoneId
@Component
class SyncPointDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
private val bookCommonDao: BookCommonDao,
) : SyncPointRepository {
) : SplitDslDaoBase(dslRW, dslRO),
SyncPointRepository {
private val b = Tables.BOOK
private val m = Tables.MEDIA
private val d = Tables.BOOK_METADATA
@ -128,7 +130,7 @@ class SyncPointDao(
.where(condition),
).execute()
return dslRW.findByIdOrNull(syncPointId)!!
return findByIdOrNull(syncPointId)!!
}
@Transactional
@ -172,10 +174,9 @@ class SyncPointDao(
}
}
override fun findByIdOrNull(syncPointId: String): SyncPoint? = dslRO.findByIdOrNull(syncPointId)
private fun DSLContext.findByIdOrNull(syncPointId: String): SyncPoint? =
selectFrom(sp)
override fun findByIdOrNull(syncPointId: String): SyncPoint? =
dslRO
.selectFrom(sp)
.where(sp.ID.eq(syncPointId))
.fetchInto(sp)
.map {

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Dimension
import org.gotson.komga.domain.model.ThumbnailBook
import org.gotson.komga.domain.persistence.ThumbnailBookRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.ThumbnailBookRecord
@ -15,10 +16,11 @@ import java.net.URL
@Component
class ThumbnailBookDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : ThumbnailBookRepository {
) : SplitDslDaoBase(dslRW, dslRO),
ThumbnailBookRepository {
private val tb = Tables.THUMBNAIL_BOOK
override fun findAllByBookId(bookId: String): Collection<ThumbnailBook> =

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Dimension
import org.gotson.komga.domain.model.ThumbnailReadList
import org.gotson.komga.domain.persistence.ThumbnailReadListRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.ThumbnailReadlistRecord
import org.jooq.DSLContext
@ -12,9 +13,10 @@ import org.springframework.transaction.annotation.Transactional
@Component
class ThumbnailReadListDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : ThumbnailReadListRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
ThumbnailReadListRepository {
private val tr = Tables.THUMBNAIL_READLIST
override fun findAllByReadListId(readListId: String): Collection<ThumbnailReadList> =

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Dimension
import org.gotson.komga.domain.model.ThumbnailSeriesCollection
import org.gotson.komga.domain.persistence.ThumbnailSeriesCollectionRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.ThumbnailCollectionRecord
import org.jooq.DSLContext
@ -12,9 +13,10 @@ import org.springframework.transaction.annotation.Transactional
@Component
class ThumbnailSeriesCollectionDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
) : ThumbnailSeriesCollectionRepository {
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
) : SplitDslDaoBase(dslRW, dslRO),
ThumbnailSeriesCollectionRepository {
private val tc = Tables.THUMBNAIL_COLLECTION
override fun findByIdOrNull(thumbnailId: String): ThumbnailSeriesCollection? =

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.jooq.main
import org.gotson.komga.domain.model.Dimension
import org.gotson.komga.domain.model.ThumbnailSeries
import org.gotson.komga.domain.persistence.ThumbnailSeriesRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.infrastructure.jooq.TempTable.Companion.withTempTable
import org.gotson.komga.jooq.main.Tables
import org.gotson.komga.jooq.main.tables.records.ThumbnailSeriesRecord
@ -15,10 +16,11 @@ import java.net.URL
@Component
class ThumbnailSeriesDao(
private val dslRW: DSLContext,
@Qualifier("dslContextRO") private val dslRO: DSLContext,
dslRW: DSLContext,
@Qualifier("dslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int,
) : ThumbnailSeriesRepository {
) : SplitDslDaoBase(dslRW, dslRO),
ThumbnailSeriesRepository {
private val ts = Tables.THUMBNAIL_SERIES
override fun findByIdOrNull(thumbnailId: String): ThumbnailSeries? =

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import io.github.oshai.kotlinlogging.KotlinLogging
import org.gotson.komga.application.tasks.Task
import org.gotson.komga.application.tasks.TasksRepository
import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase
import org.gotson.komga.jooq.tasks.Tables
import org.jooq.DSLContext
import org.jooq.Query
@ -22,11 +23,12 @@ private val logger = KotlinLogging.logger {}
@Component
@DependsOn("flywaySecondaryMigrationInitializer")
class TasksDao(
@Qualifier("tasksDslContextRW") private val dslRW: DSLContext,
@Qualifier("tasksDslContextRO") private val dslRO: DSLContext,
@Qualifier("tasksDslContextRW") dslRW: DSLContext,
@Qualifier("tasksDslContextRO") dslRO: DSLContext,
@param:Value("#{@komgaProperties.tasksDb.batchChunkSize}") private val batchSize: Int,
private val objectMapper: ObjectMapper,
) : TasksRepository {
) : SplitDslDaoBase(dslRW, dslRO),
TasksRepository {
private val t = Tables.TASK
private val tasksAvailableCondition =

View file

@ -18,6 +18,7 @@ import org.springframework.util.LinkedMultiValueMap
import org.springframework.web.client.RestClient
import org.springframework.web.client.toEntity
import org.springframework.web.server.ResponseStatusException
import org.springframework.web.util.DefaultUriBuilderFactory
import kotlin.time.Duration.Companion.minutes
import kotlin.time.toJavaDuration
@ -29,11 +30,15 @@ class KoboProxy(
private val komgaSyncTokenGenerator: KomgaSyncTokenGenerator,
private val komgaSettingsProvider: KomgaSettingsProvider,
) {
private val koboApiClient =
private val koboApiClient: RestClient =
RestClient
.builder()
.baseUrl("https://storeapi.kobo.com")
.requestFactory(
.uriBuilderFactory(
DefaultUriBuilderFactory("https://storeapi.kobo.com")
.apply {
this.encodingMode = DefaultUriBuilderFactory.EncodingMode.NONE
},
).requestFactory(
ClientHttpRequestFactoryBuilder.reactor().build(
ClientHttpRequestFactorySettings
.defaults()
@ -42,7 +47,7 @@ class KoboProxy(
),
).build()
private val pathRegex = """\/kobo\/[-\w]*(.*)""".toRegex()
private val pathRegex = """/kobo/[-\w]*(.*)""".toRegex()
private val headersOutInclude =
setOf(
@ -50,6 +55,7 @@ class KoboProxy(
HttpHeaders.USER_AGENT,
HttpHeaders.ACCEPT,
HttpHeaders.ACCEPT_LANGUAGE,
HttpHeaders.CONTENT_TYPE,
)
private val headersOutExclude =
@ -110,6 +116,7 @@ class KoboProxy(
}.apply { if (body != null) body(body) }
.retrieve()
.onStatus(HttpStatusCode::isError) { _, response ->
logger.debug { "Kobo response: ${response.statusCode}: ${response.body.bufferedReader().use { it.readText() }}" }
throw ResponseStatusException(response.statusCode, response.statusText)
}.toEntity<JsonNode>()

View file

@ -67,7 +67,7 @@ class EpubExtractor(
?: // try id="cover-image"
manifest.values.firstOrNull { it.id == "cover-image" }
if (coverManifestItem != null) {
val href = coverManifestItem.href
val href = URLDecoder.decode(coverManifestItem.href, Charsets.UTF_8)
val mediaType = coverManifestItem.mediaType
val coverPath = normalizeHref(opfDir, href)
zip.getEntryBytes(coverPath)?.let { coverBytes ->

View file

@ -28,7 +28,7 @@ class LocalArtworkProvider(
private val imageAnalyzer: ImageAnalyzer,
) : SidecarSeriesConsumer,
SidecarBookConsumer {
val supportedExtensions = listOf("png", "jpeg", "jpg", "tbn", "webp")
val supportedExtensions = listOf("png", "jpeg", "jpg", "tbn", "webp", "gif")
val supportedSeriesFiles = listOf("cover", "default", "folder", "poster", "series")
fun getBookThumbnails(book: Book): List<ThumbnailBook> {

View file

@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.security
import jakarta.servlet.Filter
import org.gotson.komga.domain.model.UserRoles
import org.gotson.komga.infrastructure.configuration.KomgaSettingsProvider
import org.gotson.komga.infrastructure.hash.Hasher
import org.gotson.komga.infrastructure.security.apikey.ApiKeyAuthenticationFilter
import org.gotson.komga.infrastructure.security.apikey.ApiKeyAuthenticationProvider
import org.gotson.komga.infrastructure.security.apikey.HeaderApiKeyAuthenticationConverter
@ -34,6 +35,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
@Configuration
@ -51,6 +53,7 @@ class SecurityConfiguration(
private val opdsAuthenticationEntryPoint: OpdsAuthenticationEntryPoint,
private val authenticationEventPublisher: AuthenticationEventPublisher,
private val tokenEncoder: TokenEncoder,
private val hasher: Hasher,
clientRegistrationRepository: InMemoryClientRegistrationRepository?,
) {
private val oauth2Enabled = clientRegistrationRepository != null
@ -158,7 +161,7 @@ class SecurityConfiguration(
)
}
http.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter::class.java)
http.addFilterAfter(restAuthenticationFilter(), BasicAuthenticationFilter::class.java)
return http.build()
}
@ -239,19 +242,19 @@ class SecurityConfiguration(
fun koboAuthenticationFilter(): Filter =
ApiKeyAuthenticationFilter(
apiKeyAuthenticationProvider(),
UriRegexApiKeyAuthenticationConverter(Regex("""/kobo/([\w-]+)"""), tokenEncoder, userAgentWebAuthenticationDetailsSource),
UriRegexApiKeyAuthenticationConverter(Regex("""/kobo/([\w-]+)"""), hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
)
fun kosyncAuthenticationFilter(): Filter =
ApiKeyAuthenticationFilter(
apiKeyAuthenticationProvider(),
HeaderApiKeyAuthenticationConverter("X-Auth-User", tokenEncoder, userAgentWebAuthenticationDetailsSource),
HeaderApiKeyAuthenticationConverter("X-Auth-User", hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
)
fun restAuthenticationFilter(): Filter =
ApiKeyAuthenticationFilter(
apiKeyAuthenticationProvider(),
HeaderApiKeyAuthenticationConverter("X-API-Key", tokenEncoder, userAgentWebAuthenticationDetailsSource),
HeaderApiKeyAuthenticationConverter("X-API-Key", hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
)
fun apiKeyAuthenticationProvider(): AuthenticationManager =

View file

@ -55,6 +55,8 @@ class ApiKeyAuthenticationFilter(
} catch (ex: AuthenticationException) {
unsuccessfulAuthentication(request, response, ex)
}
filterChain.doFilter(request, response)
}
private fun unsuccessfulAuthentication(
@ -78,7 +80,6 @@ class ApiKeyAuthenticationFilter(
}
securityContextHolderStrategy.context = context
securityContextRepository.saveContext(context, request, response)
filterChain.doFilter(request, response)
}
private fun authenticationIsRequired(username: String): Boolean {

View file

@ -1,6 +1,7 @@
package org.gotson.komga.infrastructure.security.apikey
import jakarta.servlet.http.HttpServletRequest
import org.gotson.komga.infrastructure.hash.Hasher
import org.gotson.komga.infrastructure.security.TokenEncoder
import org.springframework.security.authentication.AuthenticationDetailsSource
import org.springframework.security.core.Authentication
@ -11,11 +12,13 @@ import org.springframework.security.web.authentication.AuthenticationConverter
* and convert it to an [ApiKeyAuthenticationToken]
*
* @property headerName the header name from which to retrieve the API key
* @property hasher the hasher to use to encode the API key as username in the [Authentication] object
* @property tokenEncoder the encoder to use to encode the API key in the [Authentication] object
* @property authenticationDetailsSource the [AuthenticationDetailsSource] to enrich the [Authentication] details
*/
class HeaderApiKeyAuthenticationConverter(
private val headerName: String,
private val hasher: Hasher,
private val tokenEncoder: TokenEncoder,
private val authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>,
) : AuthenticationConverter {
@ -23,7 +26,8 @@ class HeaderApiKeyAuthenticationConverter(
request
.getHeader(headerName)
?.let {
val (maskedToken, hashedToken) = it.take(6) + "*".repeat(6) to tokenEncoder.encode(it)
val maskedToken = hasher.computeHash(it)
val hashedToken = tokenEncoder.encode(it)
ApiKeyAuthenticationToken
.unauthenticated(maskedToken, hashedToken)
.apply { details = authenticationDetailsSource.buildDetails(request) }

View file

@ -1,6 +1,7 @@
package org.gotson.komga.infrastructure.security.apikey
import jakarta.servlet.http.HttpServletRequest
import org.gotson.komga.infrastructure.hash.Hasher
import org.gotson.komga.infrastructure.security.TokenEncoder
import org.springframework.security.authentication.AuthenticationDetailsSource
import org.springframework.security.core.Authentication
@ -11,11 +12,13 @@ import org.springframework.security.web.authentication.AuthenticationConverter
* request URI, and convert it to an [ApiKeyAuthenticationToken]
*
* @property tokenRegex the regex used to extract the API key
* @property tokenEncoder the encoder to use to encode the API key in the [Authentication] object
* @property hasher the hasher to use to encode the API key as username in the [Authentication] object
* @property tokenEncoder the encoder to use to encode the API key as credentials in the [Authentication] object
* @property authenticationDetailsSource the [AuthenticationDetailsSource] to enrich the [Authentication] details
*/
class UriRegexApiKeyAuthenticationConverter(
private val tokenRegex: Regex,
private val hasher: Hasher,
private val tokenEncoder: TokenEncoder,
private val authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>,
) : AuthenticationConverter {
@ -24,7 +27,8 @@ class UriRegexApiKeyAuthenticationConverter(
?.let {
tokenRegex.find(it)?.groupValues?.lastOrNull()
}?.let {
val (maskedToken, hashedToken) = it.take(6) + "*".repeat(6) to tokenEncoder.encode(it)
val maskedToken = hasher.computeHash(it)
val hashedToken = tokenEncoder.encode(it)
ApiKeyAuthenticationToken
.unauthenticated(maskedToken, hashedToken)
.apply { details = authenticationDetailsSource.buildDetails(request) }

View file

@ -21,7 +21,6 @@ fun ResponseEntity.BodyBuilder.setCachePrivate() = this.cacheControl(cachePrivat
val cachePrivate =
CacheControl
.maxAge(0, TimeUnit.SECONDS)
.noTransform()
.cachePrivate()
.mustRevalidate()

View file

@ -194,6 +194,7 @@ class KoboController(
try {
koboProxy.proxyCurrentRequest().body?.get("Resources")
} catch (e: Exception) {
if (e is ResponseStatusException && e.statusCode == HttpStatus.UNAUTHORIZED) throw e
logger.warn { "Failed to get response from Kobo /v1/initialization, fallback to noproxy" }
null
} ?: koboProxy.nativeKoboResources
@ -233,7 +234,7 @@ class KoboController(
): Any {
try {
return koboProxy.proxyCurrentRequest(body)
} catch (e: Exception) {
} catch (_: Exception) {
logger.warn { "Failed to get response from Kobo /v1/auth/device, fallback to noproxy" }
}
@ -395,7 +396,7 @@ class KoboController(
addAll(
// changed books are also passed as changed reading state because Kobo does not process ChangedEntitlement even if it contains a ReadingState
(booksChanged.content + changedReadingState.content).mapNotNull { book ->
readProgress[book.bookId]?.let { it ->
readProgress[book.bookId]?.let {
ChangedReadingStateDto(
WrappedReadingStateDto(
it.toDto(),
@ -570,7 +571,10 @@ class KoboController(
locator =
if (koboUpdate.statusInfo.status == StatusDto.FINISHED) {
// If the book is finished, Kobo sends the first resource instead of the last, so we can't trust what Kobo sent
val epubExtension = mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub ?: throw IllegalArgumentException("Epub extension not found")
val epubExtension =
mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub
?: throw IllegalArgumentException("Epub extension not found")
.also { logger.error { "Epub extension not found for book ${book.id}. Book should be re-analyzed." } }
epubExtension.positions.last()
} else {
R2Locator(

View file

@ -1,5 +1,6 @@
package org.gotson.komga.interfaces.api.kosync
import io.github.oshai.kotlinlogging.KotlinLogging
import org.gotson.komga.domain.model.MediaExtensionEpub
import org.gotson.komga.domain.model.MediaProfile
import org.gotson.komga.domain.model.R2Device
@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
import java.time.ZonedDateTime
private val logger = KotlinLogging.logger {}
@RestController
@RequestMapping("/koreader", produces = ["application/vnd.koreader.v1+json"])
class KoreaderSyncController(
@ -48,8 +51,14 @@ class KoreaderSyncController(
@PathVariable bookHash: String,
): DocumentProgressDto {
val books = bookRepository.findAllByHashKoreader(bookHash)
if (books.isEmpty()) throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
if (books.size > 1) throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
if (books.isEmpty()) {
logger.debug { "No book found with KOReader hash: $bookHash" }
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
}
if (books.size > 1) {
logger.debug { "No unique book found with KOReader hash: $bookHash. Found ${books.size} books with the same hash." }
throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
}
val book = books.first()
val media = mediaRepository.findById(book.id)
@ -69,7 +78,10 @@ class KoreaderSyncController(
when (media.profile) {
MediaProfile.DIVINA, MediaProfile.PDF -> readProgress.page.toString()
MediaProfile.EPUB -> {
val extension = mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Epub extension not found")
val extension =
mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Epub extension not found")
.also { logger.error { "Epub extension not found for book ${book.id}. Book should be re-analyzed." } }
// convert the href to its index for KOReader
val resourceIndex =
@ -81,6 +93,7 @@ class KoreaderSyncController(
// return a progress string that points to the beginning of the resource
"/body/DocFragment[${resourceIndex + 1}].0"
}
null -> throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book has no media profile")
}
@ -99,8 +112,14 @@ class KoreaderSyncController(
@RequestBody koreaderProgress: DocumentProgressDto,
) {
val books = bookRepository.findAllByHashKoreader(koreaderProgress.document)
if (books.isEmpty()) throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
if (books.size > 1) throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
if (books.isEmpty()) {
logger.debug { "No book found with KOReader hash: ${koreaderProgress.document}" }
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")
}
if (books.size > 1) {
logger.debug { "No unique book found with KOReader hash: ${koreaderProgress.document}. Found ${books.size} books with the same hash." }
throw ResponseStatusException(HttpStatus.CONFLICT, "More than 1 book found with the same hash")
}
val book = books.first()
val media = mediaRepository.findById(book.id)
@ -139,8 +158,12 @@ class KoreaderSyncController(
?.value
?.toIntOrNull()
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not get Epub resource index from progress: ${koreaderProgress.progress}")
.also { logger.error { "Could not get Epub resource index from progress: ${koreaderProgress.progress}" } }
val extension = mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Epub extension not found")
val extension =
mediaRepository.findExtensionByIdOrNull(book.id) as? MediaExtensionEpub
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Epub extension not found")
.also { logger.error { "Epub extension not found for book ${book.id}. Book should be re-analyzed." } }
// get the href from the index provided by KOReader
val href =

View file

@ -268,7 +268,7 @@ class ReadListController(
}
@Operation(summary = "Match ComicRack list", tags = [OpenApiConfiguration.TagNames.COMICRACK])
@PostMapping("match/comicrack")
@PostMapping("match/comicrack", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
@PreAuthorize("hasRole('ADMIN')")
fun matchComicRackList(
@RequestParam("file") file: MultipartFile,

View file

@ -3,6 +3,7 @@ package org.gotson.komga.interfaces.api.rest.dto
import java.time.LocalDateTime
data class HistoricalEventDto(
val id: String,
val type: String,
val timestamp: LocalDateTime,
val bookId: String?,

View file

@ -46,6 +46,9 @@ spring:
jackson:
deserialization:
FAIL_ON_NULL_FOR_PRIMITIVES: true
mapper:
accept-case-insensitive-properties: true
accept-case-insensitive-values: true
config:
import:
- "optional:file:\${komga.config-dir}/application.yml"