Commit Graph

88 Commits

Author SHA1 Message Date
Zeedif
590e43c827 feat(sync/koreader): Add KOReader reading progress synchronization (#1560)
* feat(sync/koreader): implement reading progress synchronization

This commit introduces a comprehensive integration with KOReader Sync Server to enable two-way synchronization of reading progress.

The core logic is encapsulated in a new `KoreaderSyncService`, which handles authentication, registration, and progress pushing/pulling based on user-defined strategies (LATEST, KOSYNC, SUWAYOMI).

Key changes include:

- A new GraphQL API is added to manage the KOReader Sync connection:
  - `connectKoSyncAccount` mutation provides a simplified flow that attempts to log in and, if the user doesn't exist, automatically registers them.
  - `logoutKoSyncAccount` mutation to clear credentials.
  - `koSyncStatus` query to check the current connection status.

- Reading progress is now synchronized at key points:
  - The `fetchChapterPages` mutation pulls the latest progress from the sync server before loading the reader. It respects the configured sync strategy and updates the local database if necessary.
  - The `updateChapters` and other progress-updating methods now push changes to the sync server automatically.
- OPDS chapter entries also pull the latest progress, ensuring clients receive up-to-date reading status.

- Supporting backend changes have been made:
  - The `Chapter` table is extended with a `koreader_hash` column to uniquely identify documents. A database migration is included.
  - New configuration options are added to `server.conf` to manage the feature (enable, server URL, credentials, strategy, etc.).

* perf(opds): defer KOReader sync to improve chapter feed performance

Removes the KOReader Sync progress-pulling logic from the `createChapterListEntry` function.

The previous implementation triggered a network request to the sync server for every single chapter when generating a list, leading to severe performance issues and slow load times on feeds with many entries.

This change reverts to the more performant approach of always linking to the chapter's metadata feed. The expensive sync operation will be handled within the metadata entry generation instead, ensuring it's only triggered on-demand for a single chapter. This restores the responsiveness of browsing chapter feeds.

* refactor(koreader): Use enums for sync settings and correct OPDS logic

Refactor Koreader Sync settings to use enums instead of raw strings for `checksumMethod` and `strategy`. This improves type safety, prevents typos, and makes the configuration handling more robust.

The changes include:
- Introducing `KoreaderSyncChecksumMethod` and `KoreaderSyncStrategy` enums.
- Updating `ServerConfig`, GraphQL types, and backup models to use these new enums.
- Refactoring `KoreaderSyncService` to work with the enum types.

Additionally, this commit fixes an issue in `OpdsEntryBuilder` where the logic for determining which sync progress to use (local vs. remote) was duplicated. The builder now correctly delegates this decision to `KoreaderSyncService.pullProgress`, which already contains the necessary strategy logic. This centralizes the logic and ensures consistent behavior.

* refactor(koreader): Improve config handling and remove redundant update

This commit combines several refactoring and cleanup tasks:

- **Koreader Sync:** The sync service is updated to use the modern `serverConfig` provider instead of the legacy `GlobalConfigManager`. This aligns it with the current configuration management approach in the project.

- **Download Provider:** A redundant `pageCount` database update is removed from `ChaptersFilesProvider`. This operation was unnecessary because the `getChapterDownloadReady` function, which is called earlier in the download process, already verifies and corrects the page count. This change eliminates a superfluous database write and fixes a related import issue.

* feat(sync/koreader)!: enhance sync strategy and add progress tolerance

This commit overhauls the KOReader synchronization feature to provide more granular control and robustness. The simple on/off toggle has been replaced with a more flexible strategy-based system.

Key changes include:
- Replaced `koreaderSyncEnabled` with a more powerful `koreaderSyncStrategy` enum.
- Introduced new sync strategies: `PROMPT`, `SILENT`, `SEND`, `RECEIVE`, and `DISABLE`, allowing for fine-grained control over the sync direction and conflict resolution.
- Added a `koreaderSyncPercentageTolerance` setting. This prevents unnecessary sync updates for minor progress differences between Suwayomi and KOReader.
- Refactored the `KoreaderSyncService` to implement the new strategies and use the configurable tolerance.
- Updated GraphQL schemas, mutations, and server configuration to remove the old setting and incorporate the new ones.
- Adjusted the backup and restore process to correctly handle the new configuration parameters.
- Modified API endpoints and internal logic to check and apply remote progress based on the selected strategy.

BREAKING CHANGE: The `koreaderSyncEnabled` setting is removed and replaced by a more granular `koreaderSyncStrategy`. The enum values for the strategy have been completely changed, making previous configurations incompatible.

* fix: remove unused imports

* feat(opds, sync): enhance Koreader sync and OPDS conflict handling

This commit introduces significant improvements to the Koreader synchronization feature, focusing on providing a better user experience for handling progress conflicts in both OPDS and GraphQL clients.

Key changes include:

- **OPDS Conflict Resolution:** When a reading progress conflict is detected, the OPDS feed for a chapter now provides two distinct "Read Online" links: one to continue from the local progress and another to continue from the synced progress from the remote device (e.g., "Continue Reading Online (Synced from KOReader)"). This empowers users to choose which progress to follow.

- **GraphQL Sync Conflict Information:** The `fetchChapterPages` GraphQL mutation now includes a `syncConflict` field in its payload. This field provides the remote device name and page number, allowing GraphQL clients to implement a user-facing prompt to resolve sync conflicts.

- **Improved Sync Strategy Handling:**
  - The `connectKoSyncAccount` mutation no longer unconditionally sets the sync strategy to `PROMPT`. It now respects the user's existing setting, preventing accidental configuration changes upon re-login.
  - The default `koreaderSyncStrategy` in the configuration is changed to `DISABLED`, providing a safer and more intuitive default for new users.

- **Refinements & Fixes:**
  - The fallback for the remote device name is now centralized within the KoreaderSyncService, defaulting to "KOReader" if not provided.
  - Renamed `KoreaderSyncStrategy.DISABLE` to `DISABLED` for consistency.
  - Updated i18n strings for OPDS links to be more descriptive and user-friendly.

* refactor(kosync): rename stream page link titles for consistency

* refactor(kosync): return SettingsType in auth mutation payloads

The `connectKoSyncAccount` and `logoutKoSyncAccount` mutations modify server settings (username and userkey) but did not previously return the updated configuration. This forced client applications to manually refetch settings to avoid a stale cache.

This change modifies the payloads for both mutations to include the full `SettingsType`.

By returning the updated settings directly, GraphQL clients like Apollo Client can automatically update their cache, simplifying client-side state management and ensuring the UI always reflects the current server configuration.

Additionally, `clientMutationId` has been added to `KoSyncConnectPayload` for consistency with GraphQL practices, aligning it with the logout mutation.

Refs: #1560

* refactor(kosync): replace KoSyncConnectPayload with ConnectResult in connect method

* fix(kosync): add koreaderSyncPercentageTolerance default setting
2025-08-19 15:00:19 -04:00
Constantin Piber
df0078b725 [#1496] Image conversion (#1505)
* [#1496] First conversion attempt

* [#1496] Configurable conversion

* Fix: allow nested configs (map)

* [#1496] Support explicit `none` conversion

* Use MimeUtils for provided download

* [1496] Support image conversion on load for downloaded images

* Lint

* [#1496] Support conversion on fresh download as well

Previous commit was only for already downloaded images, now also for
fresh and cached

* [#1496] Refactor: Move where conversion for download happens

* Rewrite config handling, improve custom types

* Lint

* Add format to pages mutation

* Lint

* Standardize url encode

* Lint

* Config: Allow additional conversion parameters

* Implement conversion quality parameter

* Lint

* Implement a conversion util to allow fallback readers

* Add downloadConversions to api and backup, fix updateValue issues

* Lint

* Minor cleanup

* Update libs.versions.toml

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2025-07-14 17:51:18 -04:00
Constantin Piber
68a131dbeb [#1349] Basic Cookie Authentication (#1498)
* [#1349] Stub basic cookie authentication

* [#1349] Basic login page

Also adjusts WebView header color and shadow to match WebUI. WebUI uses
a background-image gradient to change the perceived color, which was not
noticed originally.

* [#1349] Handle login post

* [#1349] Redirect to previous URL

* [#1349] Return a basic 401 for api endpoints

Instead of redirecting to a visual login page, API should just indicate
the bad state

* Use more appropriate 303 redirect

* Update server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Update server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>

* Lint

* Transition to AuthMode enum with migration path

* Make basicAuthEnabled auto property, Lint

* ConfigManager: Make sure to re-parse the config after migration

* basicAuth{Username,Password} -> auth{Username,Password}

* Lint

* Update server settings backup model

* Update comment

* Minor cleanup

* Improve backup legacy settings fix

* Lint

* Simplify config value migration

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-07-06 12:08:29 -04:00
Mitchell Syer
abea85d831 Update Tracking Backend (#1457)
* Update Tracking Library

* Update Bangumi

* Update Anilist

* Update MangaUpdates

* Update MAL

* Add private to bind track

* Use null

* Remove old nullable

* Remove custom implementation of supportsTrackDeletion

* Add private to updateTrack

* Some descriptions

* Another description
2025-06-22 10:38:22 -04:00
schroda
4086a73727 Feature/backup suwayomi data (#1430)
* Export meta data

* Import meta data

* Add missing "opdsUseBinaryFileSize" setting to gql

* Export server settings

* Import server settings

* Streamline server config enum handling

* Use "restore amount" in backup import progress
2025-06-15 17:14:13 -04:00
Shirish
0405a535c7 Feat: Adds OPDS Chapter Filtering/Ordering (#1392)
* Adds server level configs for OPDS

* PR comments

* Refactor server-reference.conf (itemsPerPage range)

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Change opdsChapterSortOrder type to Enum(SortOrder)

* Fix serialization of SortOrderEnum & Add `opdsShowOnlyDownloadedChapters` config
2025-05-22 19:57:55 -04:00
schroda
96b50f52ec Ensure webui "channel" is always of corresponding enum (#1334)
The "channel" was just the string from the config file, which will never equal the enum unless via case-insensitive comparison
2025-04-06 15:10:07 -04:00
schroda
439e0c8284 Emit only updater job changes instead of full status (#1302)
The update subscription emitted the full update status, which, depending on how big the status was, took forever because the graphql subscription does not support data loader batching, causing it to run into the n+1 problem
2025-03-22 19:34:43 -04:00
schroda
633ea97848 Feature/optimize backup import (#1270)
* Optimize restoring manga chapters

* Streamline restoring manga data

* Optimize restoring manga trackers

* Simplify passing manga category restore data

* Properly prevent mangas from getting added to default category

76595233fc never actually worked...

* Extract logic to add manga to categories from gql mutation

* Optimize restoring manga categories

* Optimize restoring categories
2025-02-16 13:00:26 -05:00
schroda
36cb899b91 Prevent chapter lastReadPage coerceIn error (#1272)
coerceIn throws an error in case the max value is less than the min value ("Cannot coerce value to an empty range: maximum <max> is less than minimum <min>")

Regression from c8bd39b4bf
2025-02-14 23:05:04 -05:00
schroda
c8bd39b4bf Prevent negative lastPageRead values (#1267)
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2025-02-14 19:06:54 -05:00
schroda
1d1535dc55 Send dequeue download mutation response (#1218)
Response was never sent due to incorrect updates filter condition
2025-01-01 21:26:01 -05:00
schroda
1d541a30ae Feature/update to exposed v0.57.0 (#1150)
* Update to exposed-migrations v3.5.0

* Update to kotlin-logging v7.0.0

* Update to exposed v0.46.0

* Update to exposed v0.47.0

* Update to exposed v0.55.0

* Update to exposed v0.56.0

* Update to exposed v0.57.0
2024-12-07 23:49:11 -05:00
schroda
6951b4b20d Remove "grapqhl log level" setting (#1155)
internal logging was removed with graphql-java v22.0
2024-11-17 21:11:26 -05:00
renovate[bot]
9cd8cb3d54 Update dependency io.javalin:javalin to v6 (#1152)
* Update dependency io.javalin:javalin to v6

* Simple compile fixes

* Simple compile fixes pass 2

* Add results to futures

* Setup jetty server and api routes

* Setup Cors

* Setup basic auth

* Documentation stubs

* Replace chapter mutex cache

* Fix compile

* Disable Jetty Logging

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-11-17 15:00:53 -05:00
Mitchell Syer
0670f298cd Switch from Kodein to Koin (#1112)
* Switch from Kodein to Koin

* Ktlint
2024-11-14 18:08:19 -05:00
schroda
aa1e98544b Fix/invalid server settings gql mutation request (#1092)
* Validate setting values on mutation

* Handle invalid negative setting values

* Ensure at least one source is downloading at all times

* Prevent possible IllegalArgumentException

The "serverConfig.maxSourcesInParallel" value could have changed after the if-condition
2024-11-14 18:08:07 -05:00
schroda
168b76cb0c Feature/graphql download queue subscription send only updates (#1011)
* Emit only download changes instead of full status

The download subscription emitted the full download status, which, depending on how big the queue was, took forever because the graphql subscription does not support data loader batching, causing it to run into the n+1 problem

* Rename "DownloadManager#status" to "DownloadManager#updates"

* Add initial queue to download subscription type

Adds the current queue at the time of sending the initial message.
This field is null for all following messages after the initial one

* Optionally limit and omit download updates

To prevent the n+1 dataloader issue, the max number of updates included in the download subscription can be limited.
This way, the problem will be circumvented and instead, the latest download status should be (re-)fetched via the download status query, which does not run into this problem.

* Formatting
2024-11-14 18:07:14 -05:00
Syer10
6c1fbfa63b [skip ci] Formatting 2024-09-03 21:37:18 -04:00
schroda
414972d545 Feature/update log file rotation (#1023)
* Keep up to 31 log files

On average one log file per day gets created, thus, increasing to 31 files will store log files for one month

* Decrease total log files size to 100mb

* Make log appender settings configurable
2024-08-31 18:55:26 -04:00
schroda
5b08b81239 Initialize manga on add to library (#1016)
In case a manga gets added to the library which has not been initialized yet, it should be tried to initialize it.
Since it's not an error to have uninitialized manga in the library, this can be done in the background via the updater and the client receives the updated data via the update subscription.
2024-08-31 18:54:30 -04:00
AeonLucid
9e006166a8 Add setting to use the flaresolverr response (#990) 2024-07-28 15:57:40 -04:00
schroda
aee9f1032c Exit early in case of empty chapter id list (#980)
In case the list was empty, the batch update failed with a NoSuchElement exception
2024-06-27 20:34:12 -04:00
Mitchell Syer
306eb0e3c7 Update manga info when browsing if not in library (#958)
* Update manga info when browsing if not in library

* Cleanup
2024-06-06 21:17:17 -04:00
schroda
ff23f58a4f Support partial mutation responses (#954)
In case e.g. a mutation was made which looked like this

myMutation {
  mutationA { ... }
  mutationB { ... }
  mutationC { ... }
}

and mutation A and B succeeded while mutation C failed, the response only included the error of C and the successful mutation data response of A and B was missing
2024-06-02 20:33:17 -04:00
schroda
cf1ede9cf7 Update lastPageRead on chapter update (#939)
Broken with 729385588a3d8e06ec8be38865a12c47e88f6bcb...
2024-04-28 10:35:33 -04:00
schroda
729385588a Prevent greater last page read than page count (#938)
In case multiple chapters are getting updated, the last page read might be higher than the available pages of a chapter
2024-04-28 00:34:40 -04:00
schroda
48e19f7914 Feature/auto download of new chapters improve handling of unhandable reuploads (#921)
* Update test/server-reference file

* Properly handle re-uploaded chapters in auto download of new chapters

In case of unhandable re-uploaded chapters (different chapter numbers) they potentially would have prevented auto downloads due being considered as unread.

Additionally, they would not have been considered to get downloaded due to not having a higher chapter number than the previous latest existing chapter before the chapter list fetch.

* Add option to ignore re-uploads for auto downloads

* Extract check for manga category download inclusion

* Extract logic to get new chapter ids to download

* Simplify manga category download inclusion check

In case the DEFAULT category does not exist, someone messed with the database and it is basically corrupted
2024-04-06 22:53:36 -04:00
schroda
89dd570b30 Add mutation to fetch the latest track data from the tracker (#920)
* Add mutation to fetch the latest track data from the tracker

* Update Track.kt

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-03-31 13:25:58 -04:00
schroda
16474d4328 Feature/tracking gql add option to delete remote binding on tracker (#919)
* Extract unbinding track into function

* Introduce new unbind mutation

* Add option to delete track binding on track service

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
2024-03-31 13:22:13 -04:00
schroda
9db612bf03 Move trigger for track progress update to client (#918)
Triggering the progress update on server side does not work because the client needs to get the mutation result, otherwise, the clients cache will get outdated
2024-03-31 13:20:37 -04:00
schroda
dbfea5d02b Update inLibraryAt timestamp when adding manga to library (#911) 2024-03-31 13:20:08 -04:00
schroda
6d539d3404 Fix/update subscription clear data loader cache (#908)
* Set updater running flag to false only at the end of the update

For clearing the data loader cache properly, the update status subscription requires the update to be running.

For the last completed manga update the flag was immediately set to false which prevented the dataloader cache from getting cleared, returning outdated data for the last updated manga

* Correctly clear the "MangaForIdsDataLoader" cache

The cache keys for this dataloader are lists of manga ids.
Thus, it is not possible to clear only the cached data of the provided manga id and instead each cache entry that includes the manga id has to be cleared

* Ensure that manga dataloader caches gets cleared during global update

The "StateFlow" drops value updates in case the collector is too slow, which was the case for the "UpdateSubscription".

This caused the dataloader cache to not get properly cleared because the running state of the update was already set to false.
2024-03-31 13:19:49 -04:00
Aria Moradi
fc53d69f82 Add auth and version support to socks proxy (#883)
* Add auth support to socsk proxy

* better logging

* fix lint issue

* implement fixes and version

* add to test reference too
2024-02-19 11:06:39 -05:00
Mitchell Syer
af0dde5ae8 Add Source Meta (#875) 2024-02-17 11:24:01 -05:00
schroda
6fbd2f1079 Feature/remove download ahead logic (#867)
* Remove download ahead logic

Unnecessary on server side, should just be done by the client

* Rename "autoDownloadAheadLimit" to "autoDownloadNewChaptersLimit"

* Deprecate the old field

* Update Stable WebUI

* Update Stable WebUI

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-02-17 11:23:13 -05:00
schroda
9edbc7f1d7 Feature/support different webui flavors (#863)
* Run functions for specific webui flavor

* Set default flavor of WebUIFlavor enum

* Consider flavor of served webUI when checking for update

In case the flavor was changed and the served webui files are still for the previous flavor, the update check could incorrectly detect no update

* Skip validation during initial setup

In case initial setup is triggered because of an invalid local webUI, doing a validation again is unnecessary

* Handle changed flavor on startup
2024-02-17 11:23:01 -05:00
Chance Zibolski
dc124fb15c Make flaresolverr session options configurable (#854)
Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
2024-01-24 21:11:53 -05:00
Mitchell Syer
63e1082b97 Minor fixes for FlareSolverr (#851)
* Minor fixes for FlareSolverr

* Weird crash but ok
2024-01-24 17:49:51 -05:00
Mitchell Syer
4bec027f11 Change Track.bind to use trackerId + remoteId (#842) 2024-01-22 21:35:56 -05:00
Mitchell Syer
b9053e3057 Fix graphql tracking (#840) 2024-01-21 20:04:24 -05:00
Mitchell Syer
46e1e4c043 Table for Track Searches (#833)
* Table for Track Searches

* Lint
2024-01-20 23:12:18 -05:00
Chance Zibolski
57d5bc6480 Add support for configuring which categories are downloaded automatically (#832)
* Rename IncludeInUpdate class to IncludeOrExclude

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

* Add support for configuring which categories are downloaded automatically

If a manga has no configured categories, behavior remains the same and
the automatic download functionality will download new chapters without
consulting the category includeInDownload configuration.

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

---------

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
2024-01-20 19:41:47 -05:00
Tachimanga
5a178ada74 add trackers support (#720)
* add trackers support

* Cleanup Tracker Code

* Add GraphQL support for Tracking

* Fix lint and deprecation errors

* remove password from logs

* Fixes after merge

* Disable tracking for now

* More disabled

---------

Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2024-01-07 15:07:41 -05:00
Mitchell Syer
230427e758 Support Custom Repos (#803)
* Support custom repos

* Fix migration

* Make extension after update optional
2024-01-05 19:14:09 -05:00
schroda
fb545947ec Feature/gql improve webui update status (#783)
* Remove "updateAvailable" from webui update info

Doesn't add anything

* Extract status creation into function

* Optionally emit status immediately

Otherwise, some emissions can get lost due to the 1s sample period

* Rename "STOPPED" state to "IDLE"

* Reset webui update status

Currently, the update status never gets reset.
Thus, it will be "FINISHED" or "ERROR" until the next update gets triggered.
Due to this, the client won't know that the update result was already handled and will handle it again the next time it gets the update status.

To prevent this, the client has to be able to tell the server that it has handled the update result and that it can be resetted
2023-12-08 19:17:25 -05:00
Mitchell Syer
a2d3fa6e1d Use new Tachiyomi backup filename format (#787)
* Use new Tachiyomi backup filename format

* Lint

* Get Backup Filename in more places

* Delete BackupFull
2023-12-08 19:16:25 -05:00
schroda
94b670eb81 Fix/gql about webui query same response type as webui update info (#781)
* Use a new type for the webui about info query

Using the same type for this and the webui update queries/mutations caused apollo to save it as the same data in the cache, overwriting the "about info"

* Use a new type for the webui about check query

To prevent similar issues (cc3bf5f34a8afebadd306d037db1a10088ef9334) with the "update check" and the "update progress" payloads

* Throw update check error when calling it via the query

Otherwise, the error is never raised to the frontend

* Set "ERROR" state in case the update check failed on WebUI update trigger
2023-11-26 17:17:28 -05:00
schroda
d21b2018cb Add mutation to clear the cached images (#775) 2023-11-25 11:53:29 -05:00
schroda
7ed8f43859 Fix/backup import failure not resetting status (#746)
* Reset backup status to idle in case of an exception

* Rename "performRestore" function

* Set backup status to failure on exception

Makes it possible to detect if the restore failed or not after the first status was received

* Set backup status to success on completion

Since the status is not provided over a subscription, but over a query that should be pulled, it is not really easily detectable if a restore finished or not, since both states will be indicated by "idle"

* Correctly wait for first new status when triggering backup import

The status is only "Idle" in case no backup import has ever run.
Once the first backup process finished it is either "Failure" or "Success"

* Rename "ProtoBackupImport::restore" function

* Add id to restore process

Makes it possible to differentiate between backup restore processes.
2023-10-31 21:21:11 -04:00