* 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
* 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.
* 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
* [#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>
* Remove immediate download notification for latest gql subscription
There is a problem where too many immediate updates can cause the client to lag out (e.g., in case it has to update the queue in the cache based on the updates).
This happens in case e.g., a source is broken and all its downloads error out basically immediately.
With each errored out download, a new one starts, which causes an immediate notification to the clients.
* Determine downloader status from active state of downloader jobs
In case the downloader is active but all downloads are erroring out immediately, no download will have the DOWNLOADING status.
This then would result in the downloader status to constantly be STOPPED.
* Prevent multiple update for the same downloads
It was possible that multiple updates got added for the same download.
This caused issues with the graphql apollo client, because it wasn't able to correctly update the client cache.
* Set download error state only after reaching max retries
In case the max retries haven't been reached yet, the download will be retried and thus setting and emitting the error state will cause weird looking ui updates.
* [#1497] WebView: Localstorage
* WebView: Transition to our own header/postData system
This is also what is recommended by most other posts, I haven't seen the
context used anywhere, and `KCEFResourceRequestHandler` seems to just
bypass a lot of CEF
* Lint
* [#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>
* WebView: Add initial controller
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
* WebView: Prepare page
* WebView: Basic HTML setup
* WebView: Improve navigation
* WebView: Refactor message class deserialization
* WebView: Refactor event message serialization
* WebView: Handle click events
* WebView: Fix events after refactor
* WebView: Fix normalizing of URLs
* WebView: HTML remove navigation buttons
* WebView: Handle more events
* WebView: Handle document change in events
* WebView: Refactor to send mutation events
* WebView: More mouse events
* WebView: Include bubbles, cancelable in event
Those seem to be important
* WebView: Attempt to support nested iframe
* WebView: Handle long titles
* WebView: Avoid setting invalid url
* WebView: Send mousemove
* WebView: Start switch to canvas-based render
* WebView: Send on every render
* WebView: Dynamic size
* WebView: Keyboard events
* WebView: Handle mouse events in CEF
This is important because JS can't click into iFrames, meaning the
previous solution doesn't work for captchas
* WebView: Cleanup
* WebView: Cleanup 2
* WebView: Document title
* WebView: Also send title on address change
* WebView: Load and flush cookies from store
* WebView: remove outdated TODOs
* Offline WebView: Load cookies from store
* Cleanup
* Add KcefCookieManager, need to figure out how to inject it
* ktLintFormat
* Fix a few cookie bugs
* Fix Webview on Windows
* Minor cleanup
* WebView: Remove /tmp image write, lint
* Remove custom cookie manager
* Multiple cookie fixes
* Minor fix
* Minor cleanup and add support for MacOS meta key
* Get enter working
* WebView HTML: Make responsive for mobile pages
* WebView: Translate touch events to mouse scroll
* WebView: Overlay an actual input to allow typing on mobile
Browsers will only show the keyboard if an input is focused. This also
removes the `tabstop` hack.
* WebView: Protect against occasional NullPointerException
* WebView: Use float for clientX/Y
* WebView: Fix ChromeAndroid being a pain
* Simplify enter fix
* NetworkHelper: Fix cache
* Improve CookieStore url matching, fix another cookie conversion issue
* Move distinctBy
* WebView: Mouse direction toggle
* Remove accidentally copied comment
---------
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
* 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
It's possible that the cover changed, but the url is still the same.
In that case the cover never gets updated unless the downloaded/cached file gets deleted
* Fix setting initial global update delay
In case no update has run yet, and the "last automated update" defaulted to 0, the calculation always resulted in a multiple of the interval.
This resulted for e.g., interval 24, to always be scheduled at 00:00.
For e.g., interval 6, it was always one of the following times: 00:00, 06:00, 12:00, 18:00; depending on the current system time.
* Delete the existing "last automated update time"
So that the update will be triggered based on the time the server got started again after the update.
* Extract migrations into separate functions
* Cleanup migration execution logic
* Implement Android's Looper
Looper handles thread messaging. This is used by extensions when they
want to enqueue actions e.g. for sleeping while WebView does someting
* Stub WebView
* Continue stubbing ViewGroup for WebView
* Implement WebView via Playwright
* Lint
* Implement request interception
Supports Yidan
* Support WebChromeClient
For Bokugen
* Fix onPageStarted
* Make Playwright configurable
* Subscribe to config changes
* Fix exposing of functions
* Support data urls
* Looper: Fix infinite sleep
* Looper: Avoid killing the loop on exception
Just log it and continue
* Pump playwright's message queue periodically
https://playwright.dev/java/docs/multithreading#pagewaitfortimeout-vs-threadsleep
* Update server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
* Stub a KCef WebViewProvider
* Initial Kcef Webview implementation
Still buggy, on the second call it just seems to fall over
* Format, restructure to create browser on load
This is much more consistent, before we would sometimes see errors from
about:blank, which block the actual page
* Implement some small useful properties
* Move inline objects to class
* Handle requests in Kcef
* Move Playwright implementation
* Document Playwright settings, fix deprecated warnings
* Inject default user agent from NetworkHelper
* Move playwright to libs.versions.toml
* Lint
* Fix missing imports after lint
* Update server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
* Fix default user agent set/get
Use System.getProperty instead of SystemProperties.get
* Configurable WebView provider implementation
* Simplify Playwright settings init
* Minor cleanup and improvements
* Remove playwright WebView impl
* Document WebView for Linux
---------
Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com>
* feat(opds): implement full internationalization and refactor feed generation
This commit introduces a comprehensive internationalization (i18n) framework
and significantly refactors the OPDS v1.2 implementation for improved
robustness, spec compliance, and localization.
Key changes:
Internationalization (`i18n`):
- Introduces `LocalizationService` to manage translations:
- Loads localized strings from JSON files (e.g., `en.json`, `es.json`)
stored in a new `i18n` data directory.
- Default `en.json` and `es.json` files are bundled and copied from
resources on first run if not present.
- Supports template resolution with `$t()` cross-references, locale
fallbacks (to "en" by default), and argument interpolation ({{placeholder}}).
- `ServerSetup` now initializes the `i18n` directory and `LocalizationService`.
OPDS Refactor & Enhancements:
- Replaces the previous `Opds.kt` and `OpdsDataClass.kt` with a new
`OpdsFeedBuilder.kt` and a set of more granular, spec-aligned XML
models (e.g., `OpdsFeedXml`, `OpdsEntryXml`, `OpdsLinkXml`).
- Integrates `LocalizationService` throughout all OPDS feeds:
- All user-facing text (feed titles, entry titles, summaries,
link titles, facet labels for sorting/filtering) is now localized.
- Adds a `lang` query parameter to all OPDS endpoints to allow
clients to request a specific UI language.
- Uses the `Accept-Language` header as a fallback for language detection.
- The OpenSearch description (`/search` endpoint) is now localized and
its template URL includes the determined language.
- Centralizes OPDS constants (namespaces, link relations, media types)
in `OpdsConstants.kt`.
- Adds utility classes `OpdsDateUtil.kt`, `OpdsStringUtil.kt`, and
`OpdsXmlUtil.kt` for common OPDS tasks.
- `MangaDataClass` now includes `sourceLang` to provide the content
language of the manga in OPDS entries (`<dc:language>`).
- Updates OpenAPI documentation for OPDS endpoints with more detail
and includes the new `lang` parameter.
Configuration:
- Adds `useBinaryFileSizes` server configuration option. File sizes in
OPDS feeds now respect this setting (e.g., MiB vs MB), utilized via
`OpdsStringUtil.formatFileSizeForOpds`.
This major refactor addresses the request for internationalization
originally mentioned in PR #1257 ("it would be great if messages were
adapted based on the user's language settings"). It builds upon the
foundational OPDS work in #1257 and subsequent enhancements in #1262,
#1263, #1278, and #1392, providing a more stable and extensible
OPDS implementation. Features like localized facet titles from #1392
are now fully integrated with the i18n system.
This resolves long-standing requests for better OPDS support (e.g., issue #769)
by making feeds more user-friendly, accessible, and standards-compliant,
also improving the robustness of features requested in #1390 (resolved by #1392)
and addressing underlying data needs for issues like #1265 (related to #1277, #1278).
* fix(opds): revert MIME type to application/xml for browser compatibility
* fix(opds): use chapter index for metadata feed and correct link relation
- Change `getChapterMetadataFeed` to use `chapterIndexFromPath` (sourceOrder)
instead of `chapterIdFromPath` for fetching chapter data, ensuring
consistency with how chapters are identified in manga feeds.
- Add error handling for cases where manga or chapter by index is not found.
- Correct OPDS link relation for chapter detail/fetch link in non-metadata
chapter entries from `alternate` to `subsection` as per OPDS spec
for navigation to more specific content or views.
* Use Moko-Resources
* Format
* Forgot the Languages.json
* refactor(opds)!: restructure OPDS feeds and introduce data repositories
This commit significantly refactors the OPDS v1.2 implementation by introducing dedicated repository classes for data fetching and by restructuring the feed generation logic for clarity and maintainability. The `chapterId` path parameter for chapter metadata feeds has been changed to `chapterIndex` (sourceOrder) to align with how chapters are identified in manga feeds.
BREAKING CHANGE: The OPDS endpoint for chapter metadata has changed from `/api/opds/v1.2/manga/{mangaId}/chapter/{chapterId}/fetch` to `/api/opds/v1.2/manga/{mangaId}/chapter/{chapterIndex}/fetch`. Clients will need to update to use the chapter's source order (index) instead of its database ID.
Key changes:
- Introduced `MangaRepository`, `ChapterRepository`, and `NavigationRepository` to encapsulate database queries and data transformation logic for OPDS feeds.
- Moved data fetching logic from `OpdsFeedBuilder` to these new repositories.
- `OpdsFeedBuilder` now primarily focuses on constructing the XML feed structure using DTOs provided by the repositories.
- Renamed `OpdsMangaAcqEntry.thumbnailUrl` to `rawThumbnailUrl` for clarity.
- Added various DTOs (e.g., `OpdsRootNavEntry`, `OpdsMangaDetails`, `OpdsChapterListAcqEntry`) to define clear data contracts between repositories and the feed builder.
- Simplified `OpdsV1Controller` by reorganizing feed endpoints into logical groups (Main Navigation, Filtered Acquisition, Item-Specific).
- Updated `OpdsAPI` to reflect the path parameter change for chapter metadata (`chapterIndex` instead of `chapterId`).
- Added `slugify()` utility to `OpdsStringUtil` for creating URL-friendly genre IDs.
- Standardized localization keys for root feed entry descriptions to use `*.entryContent` instead of `*.description`.
- Added `server.generated.BuildConfig` (likely from build process).
* style(opds): apply ktlint fixes
* Delete server/bin
* refactor(i18n): remove custom LocalizationService initialization
* refactor(i18n): remove unused imports from ServerSetup
* refactor(model): remove sourceLang from MangaDataClass
* refactor(opds): rename OPDS binary file size config property
- Rename `useBinaryFileSizes` to `opdsUseBinaryFileSizes` in code and config
- Update related condition check in formatFileSizeForOpds
BREAKING CHANGE: Existing server configurations using `server.useBinaryFileSizes` need to migrate to `server.opdsUseBinaryFileSizes`
* refactor(opds): improve OPDS endpoint structure and documentation
- Restructure endpoint paths for better resource hierarchy
- Add descriptive comments for each feed type and purpose
- Rename `/fetch` endpoint to `/metadata` for clarity
- Standardize feed naming conventions in route definitions
BREAKING CHANGE: Existing OPDS client integrations using old endpoint paths (`/manga/{mangaId}` and `/chapter/{chapterIndex}/fetch`) require updates to new paths (`/manga/{mangaId}/chapters` and `/chapter/{chapterIndex}/metadata`)
* fix(opds): Apply review suggestions for localization and comments
* Fix
* fix(opds): Update chapter links to include 'chapters' and 'metadata' in URLs
---------
Co-authored-by: Syer10 <syer10@users.noreply.github.com>