Commit Graph

1904 Commits

Author SHA1 Message Date
Weblate (bot)
11b2a6b616 Weblate translations (#1605)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/de/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/es/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/vi/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/zh_Hans/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: Christian Heinrich <ch.heinrich16@gmail.com>
Co-authored-by: Constantin Piber <cp.piber@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Weblate <allendandstart@gmail.com>
Co-authored-by: zeedif <carlos_antonio-rl@hotmail.com>
2025-09-01 17:02:38 -04:00
Constantin Piber
04ad0033d7 GraphQL directly uses getUserFromToken and expects a valid user for (#1616)
other authentication methods, so re-introduce that check
2025-08-25 06:02:12 -04:00
Constantin Piber
46e2ef125a OPDS: Allow fallback to Basic Auth (#1613)
* Move API authorization to UserType

We already verify the JWT there, so do the same with cookies. This makes
the next steps easier

* OPDS: Allow basic auth as fallback

* Send 404 for any unmatched API request

Redirecting to the UI is weird and can cause problems with the
SIMPLE_LOGIN check (which ignores API requests)

* Webview: Present Login page in SIMPLE_LOGIN mode

For BASIC_AUTH, the dialog is always presented. With UI_LOGIN, we have a
custom login dialog.
Before, SIMPLE_LOGIN would just say "Unauthorized", as with all API
endpoints. With the last commits, SIMPLE_LOGIN is checked by the
endpoints, which Webview did not, so the page would load, but then the
Websocket would error out, despite showing the login dialog.

* Lint
2025-08-24 12:36:11 -04:00
schroda
9a33e3808a Feature/graphql settings add jwt settings (#1612)
* Add jwt settings to grapqhl SettingsType

* Sort proto BackupServerSettings by ProtNumber
2025-08-24 12:35:59 -04:00
Zeedif
8ae451ece5 refactor(opds): align feed generation with RFC5005 and OpenSearch specs (#1611)
* refactor(opds): align feed generation with RFC5005 and OpenSearch specs

This commit refactors the OPDS feed generation to strictly adhere to official specifications for search and pagination.

Previously, OpenSearch response elements (totalResults, itemsPerPage, startIndex) were incorrectly included in all acquisition feeds. According to the OPDS 1.2 and OpenSearch 1.1 specifications, these elements should only be present in feeds that are a direct response to a search query. This change restricts their inclusion to search result feeds only, ensuring spec compliance.

Additionally, pagination link relations were not fully implemented as per RFC 5005. This commit enhances all paginated feeds to include `first` and `last` links, in addition to the existing `prev` and `next` links. This provides a complete and standard-compliant navigation experience for OPDS clients.

- `FeedBuilderInternal` now accepts an `isSearchFeed` flag to conditionally add OpenSearch elements.
- All feed generation methods in `OpdsFeedBuilder` and `OpdsV1Controller` now correctly identify search contexts.
- RFC 5005 pagination links (`first`, `last`, `prev`, `next`) are now generated for all paginated feeds.
- Added necessary link relation constants to `OpdsConstants`.

* feat(opds): improve pagination navigation and code organization
2025-08-24 12:35:47 -04:00
Zeedif
a5d64be197 fix(kosync): use correct partial hash for binary checksum (#1610)
When generating a hash on-the-fly for the binary checksum method, the code was performing a full MD5 hash of the entire file stream. This was incorrect as the KOReader binary method expects a specific partial hash (reading small chunks at different offsets).

This change ensures that when an in-memory CBZ is created, it is first written to a temporary file. Then, the correct `KoreaderHelper.hashContents()` function is used on that file to generate the partial hash, matching KOReader's logic. The temporary file is deleted immediately after.
2025-08-24 12:35:38 -04:00
Constantin Piber
4482b325d7 [#1575] Disable Alt translation (#1608) 2025-08-21 19:18:33 -04:00
Zeedif
f46745d70c feat(kosync): Implement On-the-Fly Deterministic Hashing for KOReader Sync (#1606)
* fix(archive): unify CBZ generation to produce deterministic archives

Previously, CBZ files generated on-the-fly (`FolderProvider`) had a different hash than those created directly (`ArchiveProvider`), even with identical content. This inconsistency was caused by using two different ZIP libraries (`java.util.zip` vs. `org.apache.commons.compress`) and not normalizing file metadata.

This inconsistent hashing breaks binary-based synchronization with external services like KOReader Sync Server, as the same chapter could be identified as a different file on each generation.

This change ensures CBZ generation is fully deterministic by:

- Unifying both providers to use `org.apache.commons.compress`.
- Setting a fixed epoch timestamp (`time = 0L`) for all ZIP entries.
- Explicitly setting the compression method and level to `DEFLATED` with default compression.

This guarantees that a CBZ file for a given chapter will always have the same hash, regardless of how it's generated, resolving synchronization issues.

* feat(kosync): lazily generate and cache CBZ hashes for sync

Previously, KOReader progress sync in binary mode was limited to chapters explicitly downloaded as CBZ files. Chapters stored as folders lacked a hash, preventing them from being synced.

With the recent move to deterministic CBZ generation, it's now possible to create a consistent hash for any downloaded chapter on-the-fly.

This commit enhances the `getOrGenerateChapterHash` function to act as a central point for hash management. If a hash is requested for a downloaded chapter that doesn't have one cached in the database:

1.  It generates the CBZ archive in-memory from the downloaded folder or existing CBZ using `ChapterDownloadHelper.getAsArchiveStream()`.
2.  It calculates the deterministic hash of the generated archive content.
3.  It saves this hash to the `koreader_hash` column in the `Chapter` table for future use.

The cached hash is cleared when the chapter download is deleted, ensuring hashes are only tracked for available content.

This change transparently extends Koreader Sync compatibility to all downloaded chapters, regardless of their storage format, without requiring users to pre-convert their library to CBZ.

* fix: rename getAsArchiveStream to getArchiveStreamWithSize
2025-08-21 19:18:27 -04:00
renovate[bot]
b213de19cc Update actions/setup-java action to v5 (#1607)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 19:18:08 -04:00
renovate[bot]
75355f0784 Update dependency com.auth0:java-jwt to v4.5.0 (#1603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 19:18:00 -04:00
Constantin Piber
8547159eec Basic JWT implementation (#1524)
* Basic JWT implementation

* Move JWT to UI_LOGIN mode and bring back SIMPLE_LOGIN as before

* Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt

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

* Refresh: Update only access token

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

* Implement JWT Audience

* Store JWT key

Generates the key on startup if not set

* Handle invalid Base64

* Make JWT expiry configurable

* Missing value parse

* Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt

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

* Simplify Duration parsing

* JWT Protect Mutations

* JWT Protect Queries and Subscriptions

* JWT Protect v1 WebSockets

* WebSockets allow sending token via protocol header

* Also respect the `suwayomi-server-token` cookie

* JWT reduce default token expiry

* JWT Support cookie on WebSocket as well

* Lint

* Authenticate graphql subscription via connection_init payload

* WebView: Prefer explicit token over cookie

This hack was implemented because WebView sent `"null"` if no token was
supplied, just don't send a bad token, then we can do this properly

* WebView: Implement basic login dialog if no token supplied

---------

Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
Co-authored-by: schroda <50052685+schroda@users.noreply.github.com>
2025-08-20 18:04:48 -04:00
Weblate (bot)
d90bfb6e3e Weblate translations (#1599)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/es/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/vi/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: zeedif <carlos_antonio-rl@hotmail.com>
2025-08-20 18:03:46 -04:00
Zeedif
82ad2fbe80 feat(opds): Enhance KOSync Conflict Handling and Reliability (#1602)
* feat(opds): Enhance KOSync conflict handling and reliability

This commit introduces several improvements to the KOSync integration within the OPDS feed, focusing on fixing bugs, improving network stability, and enhancing user feedback during synchronization conflicts.

- fix(sync): Corrects a KOSync JSON deserialization issue by mapping the `updated_at` field from the server response to the `timestamp` property in the client's data model. This resolves a critical bug where remote progress was being ignored.
- fix(sync): Adds a `Connection: close` header to all KOSync API requests. This prevents `unexpected end of stream` errors by ensuring a fresh connection is used, improving network reliability.
- feat(opds): Resolve sync conflicts by generating separate OPDS entries for local and remote progress. This aligns with the OPDS-PSE specification's implicit design of one stream link per entry. Instead of incorrectly adding multiple links to a single entry, the feed now presents two distinct, clearly labeled entries, allowing users to choose their desired reading position from compatible clients.
- chore(sync): Adds detailed debug logging for KOSync `GET` and `PUT` requests, including request URLs, sent data, and received responses. This improves traceability and makes debugging future issues significantly easier.

* change synced icon

* unnecessary comments removed
2025-08-20 18:03:33 -04:00
Zeedif
a414860626 fix(api): optimize HEAD requests for chapter downloads (#1601)
Previously, handling a HEAD request on the chapter download endpoint was inefficient as it triggered the full CBZ file generation process in-memory just to retrieve metadata like Content-Length and Content-Disposition. This caused unnecessary latency especially for OPDS clients.

This commit introduces a separate, lightweight path for HEAD requests.

- A new `getCbzMetadataForDownload` method is added to `ChapterDownloadHelper` to calculate the filename and file size without generating an archive stream.
- The `ChaptersFilesProvider` interface is updated with a `getArchiveSize()` method, implemented by both `ArchiveProvider` and `FolderProvider`, to retrieve the total size.
- The `MangaController` now differentiates between GET and HEAD methods, invoking the appropriate helper to ensure HEAD requests are served instantly with only the required metadata.
2025-08-20 18:03:26 -04:00
renovate[bot]
392f66e938 Update plugin shadowjar to v8.3.9 (#1597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 18:03:08 -04:00
renovate[bot]
a40021a823 Update actions/checkout action to v5 (#1582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 15:01:50 -04:00
renovate[bot]
c86f7c5356 Update dependency com.android.tools.build:apksig to v8.12.1 (#1595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 15:01:37 -04:00
Constantin Piber
3075888d26 [#1575] Support copy & paste (#1593)
* [#1575] Support paste

* WebView: Implement copy

* Localize copy dialog, lint

* Implement a custom context menu for copy/paste

* Remove click event which causes double events

* WebView: Fix input events broken by moved preventDefault

We want to fall back to the `input` event for Android bug and paste, but
we want to prevent the event for the others, so change the order
2025-08-19 15:01:31 -04:00
Mitchell Syer
7a0d3a1efe Expose the Source baseUrl (#1585)
* Expose the source baseUrl

* Lint
2025-08-19 15:01:14 -04:00
renovate[bot]
7b22397a82 Update kotlin monorepo to v2.2.10 (#1589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 15:01:07 -04:00
Constantin Piber
b2cfb5a1e9 AndroidCompat: Use NotoSans as default font (#1572)
* Initial Noto fonts

* Use Noto for other default fonts

* Typeface: Prefer main font

Eagerly switch back to main font as soon as it can display again;
otherwise we might never switch back (or later than necessary); we
should always prefer the main font

* fix: Font metrics with fallback font on TextLine
2025-08-19 15:00:59 -04:00
renovate[bot]
283e38c30a Update actions/download-artifact action to v5 (#1573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 15:00:43 -04:00
Constantin Piber
02717b317c [#1550] Allow page icons outside authentication (#1577) 2025-08-19 15:00:35 -04:00
Weblate (bot)
7fa1250a67 Weblate translations (#1580)
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/de/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/es/
Translate-URL: https://hosted.weblate.org/projects/suwayomi/suwayomi-server/vi/
Translation: Suwayomi/Suwayomi-Server

Co-authored-by: Constantin Piber <cp.piber@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: zeedif <carlos_antonio-rl@hotmail.com>
2025-08-19 15:00:28 -04:00
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
Mitchell Syer
16e9c0b19a Migrate to GradleUp Shadow (#1561) 2025-08-19 15:00:09 -04:00
renovate[bot]
8661438f69 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.13 (#1568)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 14:59:59 -04:00
Mitchell Syer
9049b4a090 [skip ci] Weblate fixes (#1579) 2025-08-09 15:20:00 -04:00
Constantin Piber
a2622fe3e1 [#1563] Return default if no matching locale found (#1576) 2025-08-07 21:35:19 -04:00
Constantin Piber
664d5fe637 Canvas: Implement drawColor for author notes (#1562) 2025-08-01 01:35:49 -04:00
renovate[bot]
97f9180063 Update xmlserialization to v0.91.2 (#1549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 19:55:52 -04:00
renovate[bot]
eed8012521 Update dependency io.github.oshai:kotlin-logging-jvm to v7.0.10 (#1559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 19:55:41 -04:00
renovate[bot]
ab1e3e4302 Update dependency com.android.tools.build:apksig to v8.12.0 (#1557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 19:55:35 -04:00
renovate[bot]
c12b6f39d8 Update dependency org.apache.commons:commons-compress to v1.28.0 (#1555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 19:55:29 -04:00
renovate[bot]
b280c03afa Update dependency com.squareup.okio:okio to v3.16.0 (#1554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 19:55:23 -04:00
schroda
1d9991e562 Feature/Improve chapter download with valid existing download handling (#1553)
* Fix early exit on download for existing download for FolderProvider

The current check only worked for the "ArchiveProvider". The "FolderProvider" never moved the existing download to the cache folder.

In case the existing download is considered to be reusable, there is no need to proceed with the download logic.

* Fix "ArchiveProvider#extractExistingDownload"

The "ChaptersFilesProvider#extractExistingDownload" expects the download to be extracted into the final download folder.
However, the "ArchiveProvider" extracted the download into the chapter download cache folder.

* Add chapter download function call requirements
2025-07-31 19:55:09 -04:00
Zeedif
87aae46a1f Overhaul OPDS feeds for discovery, filtering, and enhanced UX (#1543)
* fix: correct chapter facets URL to include /chapters endpoint

Update addChapterSortAndFilterFacets to use the correct URL path
from `/manga/{id}` to `/manga/{id}/chapters` for proper routing.

* feat(opds): restructure feeds and add exploration capabilities

This commit completely refactors the OPDS v1.2 implementation to align it more closely with the WebUI experience, separating "Library" browsing from "Explore" functionality.

Key changes include:

- The root feed is now a navigation feed directing to distinct "Library" and "Explore" sections.
- A new "History" feed has been added to the root to show recently read chapters.
- The "Explore" section now allows browsing all available sources, not just those with manga in the library.
- Feeds for exploring a source now support faceting by "Popular" and "Latest", mirroring the WebUI.
- The "Library" section retains all previous browsing methods (by category, genre, status, etc.).
- Facet link generation has been corrected to use the proper base URL, fixing broken navigation in chapter lists.
- The `OpdsFeedBuilder.kt` file has been refactorized into smaller, more manageable helper files (`OpdsEntryBuilder.kt`, `OpdsFeedHelper.kt`) to resolve a `java.lang.OutOfMemoryError: GC overhead limit exceeded` error during compilation.
- All OPDS-related strings (`strings.xml`) have been updated to reflect the new structure and improve clarity.

This new structure provides a much more intuitive and powerful browsing experience for OPDS clients, enabling content discovery in addition to library management.

* feat(opds)!: implement advanced filtering and sorting for library feeds

This commit significantly enhances the OPDS library feeds by introducing advanced sorting and filtering capabilities, mirroring the features available in the WebUI. It also standardizes the terminology from "manga" to "series" across all user-facing OPDS feeds for better clarity and consistency.

Key Features & Changes:

- **Library Facets:** All library feeds (All Series, By Source, By Category, By Genre, etc.) now include OPDS facets for:
  - **Sorting:** By title (A-Z, Z-A), last read, latest chapter, date added, and total unread chapters.
  - **Filtering:** By content status including unread, downloaded, ongoing, and completed.

- **Terminology Update:** The term "manga" has been replaced with "series" in all user-facing OPDS titles, descriptions, and endpoints to align with the frontend terminology.

- **Code Refactoring:**
  - `MangaRepository` has been updated with the correct Exposed SQL syntax (`Case`/`sum` for conditional counts, `having` clause for filtering on aggregates) to support the new facets.
  - `OpdsEntryBuilder` now includes a new function `addLibraryMangaSortAndFilterFacets` to generate the facet links.
  - `OpdsV1Controller` and `OpdsFeedBuilder` have been updated to handle the new `sort` and `filter` parameters and to call the new facet generation logic.

BREAKING CHANGE: The API endpoints for manga have been renamed to use 'series'. Any client implementation will need to update its routes.
For example, `/api/opds/v1.2/manga/{id}/chapters` is now `/api/opds/v1.2/series/{id}/chapters`.

* feat(opds): add item counts (thr:count) to navigation and facet links

This change enhances the OPDS feeds by including the number of items for various navigation links and filter facets, adhering to the OPDS 1.2 specification.

The `thr:count` attribute provides a hint to clients about the number of entries in a linked feed, significantly improving the user experience by showing counts upfront.

- Navigation Feeds (Categories, Sources, Genres, Statuses, Languages) now display the total number of manga for each entry in their respective links.
- Acquisition Feeds for the library and chapters now include counts for their filter facets (e.g., Unread, Downloaded, Completed).

This required updating DTOs to carry count data, modifying repository queries to calculate these counts efficiently, and adjusting the feed builders to include the `thr:count` attribute in the generated XML.

* refactor(opds)!: simplify root feed by removing library sub-level

The OPDS feed navigation was previously nested, requiring users to first select "Library" and then navigate to a subsection like "All Series" or "Categories". This extra step is cumbersome for OPDS clients and complicates the user experience.

This change elevates all library-related navigation entries directly to the root feed, flattening the hierarchy and making content more accessible.

As part of this refactoring:
- The `getLibraryFeed` builder and its corresponding controller/API endpoints have been removed.
- Unused string resources for the "Library" entry have been deleted.

BREAKING CHANGE: The `/api/opds/v1.2/library` endpoint has been removed. Clients should now discover library sections directly from the root feed at `/api/opds/v1.2`.

* feat(opds): enhance feeds with comprehensive manga and chapter details

This commit significantly enriches the OPDS feeds to provide a more detailed and compliant user experience.

- Refactored `OpdsMangaAcqEntry` and `OpdsChapterMetadataAcqEntry` to include additional fields such as status, source information, author, description, and web URLs.
- The OPDS entry builder (`OpdsEntryBuilder`) now populates entries with this richer metadata, including summaries, content descriptions, authors, and categories, aligning more closely with the OPDS Catalog specification.
- Added OPDS constants for 'popular' and 'new' sort relations to align with the specification.
- Included "alternate" links for both manga and chapters, allowing clients to open the item on its source website ("View on web").
- Updated internationalization strings and constants to support the new features and metadata.

* fix(opds): fetch chapters for non-library manga in feed

Previously, when accessing the OPDS chapter feed for a manga discovered via the "Explore" feature (and thus not yet in the library), the feed would be empty. This was because the feed generation logic only queried the local database, which had no chapter entries for these manga.

This commit resolves the issue by modifying `getSeriesChaptersFeed` to be a suspend function. It now implements a fallback mechanism:
- It first attempts to load chapters from the local database.
- If no chapters are found, it triggers an online fetch from the source to populate the database.
- It then re-queries the local data to build the complete chapter feed.

This ensures that chapter lists are correctly displayed for all manga, whether they are in the library or being explored for the first time.

Additionally, this commit includes a minor correction to the URN identifier for the root feed to better align with its path.

* feat(opds): provide direct stream and acquisition links when page count is known

Previously, the OPDS chapter feed always provided a single link to a separate metadata feed for each chapter. This was done to defer the costly operation of fetching the page count for undownloaded chapters, ensuring the main chapter list loaded quickly.

This commit introduces a more efficient, conditional approach. If a chapter's page count is already known (e.g., because it's downloaded or has been previously fetched), the chapter feed entry now includes direct links for:

-   OPDS-PSE page streaming (`pse:stream`).
-   CBZ file acquisition (`acquisition/open-access`).
-   Chapter cover image (`image`).

If the page count is not known, the entry falls back to the previous behavior, linking to the metadata feed to perform the page count lookup on-demand.

This significantly improves the user experience for OPDS clients by reducing the number of requests needed to start reading or downloading chapters that are already available on the server, making navigation faster and more fluid.

* fix(opds): resolve suspend calls and add missing lastReadAt for OPDS feeds

The OPDS feed generation was failing to compile due to two main issues:
1. The `OpdsChapterListAcqEntry` DTO was missing the `lastReadAt` property, which is required for the OPDS-PSE `lastReadDate` attribute.
2. Several functions in `OpdsFeedBuilder` were attempting to call the `suspend` function `createChapterListEntry` from a non-coroutine context.

This commit resolves these issues by:
- Adding the `lastReadAt` field to `OpdsChapterListAcqEntry` and populating it correctly from the database in the `ChapterRepository`.
- Refactoring `getHistoryFeed`, `getLibraryUpdatesFeed`, and `getSeriesChaptersFeed` in `OpdsFeedBuilder` to be `suspend` functions.
- Wrapping the entry creation logic in `withContext(Dispatchers.IO)` to provide the necessary coroutine scope for the suspend call and to perform the mapping on a background thread.

* refactor(opds): standardize library feed generation and enhance facets

This commit refactors the OPDS v1.2 feed generation logic to improve code structure, correctness, and feature capability.

The primary changes include:
- A new private `getLibraryFeed` helper function in `OpdsV1Controller` has been introduced to centralize and DRY up the logic for creating library-specific acquisition feeds.
- A new `OpdsMangaFilter` DTO now encapsulates all filtering, sorting, and pagination parameters, simplifying the controller handlers and making them more maintainable.
- URL generation for category, genre, status, and language feeds has been corrected. Links now correctly point to root-level paths (e.g., `/opds/v1.2/genre/{name}`) instead of being incorrectly nested under `/library/`.
- The OPDS facet system is enhanced with more specific facet groups and "All" links for a better user experience when clearing filters.

Associated changes:
- i18n strings in `strings.xml` have been reorganized with comments and new strings have been added to support the enhanced facet groups.
- The route for the publication status feed has been renamed from `/status/{id}` to `/statuses` for consistency.
- KDoc comments have been added and improved throughout the affected files for better code documentation.

* fix(opds): revert direct acquisition links in chapter feeds to improve performance

This reverts commit 33cdc0d534292760a3225cee18e274df542f0778.

The previous change introduced direct stream and download links in chapter list feeds when the page count was known. While convenient, this caused a significant performance degradation on feeds with many chapters, as it required checking for the existence of a CBZ file for every single entry.

This commit restores the original behavior where chapter list entries always link to a dedicated metadata feed. This approach defers expensive I/O operations until a user explicitly requests a single chapter's details, ensuring that chapter list feeds load quickly and efficiently. Direct acquisition and streaming links are now exclusively generated within the metadata feed.
2025-07-31 19:53:40 -04:00
Syer10
7e211ee9a1 Release v2.1.1867 v2.1.1867 2025-07-31 13:56:03 -04:00
Mitchell Syer
8e8883ba37 Update electron (#1556) 2025-07-31 12:01:33 -04:00
schroda
02c4398e48 Fix handling of too long page image urls migration (#1552)
* Delete duplicated chapter page rows by index and chapter

In case duplicated rows based on the condition for the updated unique constraint existed, the new constraint could not be added and caused the migration to fail

* Drop UC_PAGE only if it exists
2025-07-29 18:00:10 -04:00
schroda
ad7a8dd7dc Fix/page download conversion reduce logs (#1545)
* Cleanup chapter page conversion

* Reduce chapter page conversion logging
2025-07-25 19:42:06 -04:00
schroda
e3338211d6 Handle too long page image urls (#1544)
Attempted fix of 3ff29aa38a might not work, because there is no guarantee that the extension supports retrieving a specific page.
2025-07-25 19:42:00 -04:00
Mitchell Syer
7cab4b9229 Simplify secondary config parse (#1540)
* Add backslash escaping

* Use parseMap instead
2025-07-21 22:20:58 -04:00
Mitchell Syer
ac5f1a0d93 Add enabled preference setting (#1539)
* Add enabled preference setting

* Don't change preference if its not enabled
2025-07-21 15:13:17 -04:00
Mitchell Syer
798b9d0c98 Fix cookies when domain is null (#1538) 2025-07-21 15:13:04 -04:00
Chiru-Dey
3ff29aa38a snowmtl extension error fix: dynamic retrieval (#1531)
* dynamic retrieval

* ktlint errors fixed

* reinstated comments
2025-07-21 15:12:57 -04:00
renovate[bot]
f8c2b9ffb0 Update dependency adoptium/temurin21-binaries to jdk-21.0.8+9 (#1529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 15:12:49 -04:00
Syer10
888bb8897a Update AppImageTool download location
Not very impressed by the random name change that breaks existing scripts, no warning given.
2025-07-20 17:26:15 -04:00
schroda
192136e66c Change "download conversion compression level" type to Double (#1535)
https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/writing-schemas/scalars/#primitive-types
2025-07-20 17:00:00 -04:00
schroda
5057a57f7f Properly bind track privately (#1534)
In case the track was read from the TrackSearchTable the private status was never applied
2025-07-20 16:59:48 -04:00