The device catalog flattened every visible library into one list, so
duplicate copies of the same book each got an entry and search had no
library context. The root feed is now a standard OPDS 1.1 navigation feed
that mirrors the web UI's library model:
- Root /catalog: an "All Books" entry first (the previous flat cross-
library behavior, also still served at ?all=1 for clients that want
the single flat list), followed by one folder entry per visible
library with live book counts from GetVisibleLibraryMediaCounts.
- New GET /library/:libraryId/catalog: acquisition feed scoped to one
visible library (403 when the device owner cannot see it), paginated,
with an up-link back to the root.
- Scoped search: each library feed's rel="search" OpenSearch template
pins &library_id=<id>, so opening search from inside a library folder
searches only that library — clients substitute only {searchTerms},
so no client-side changes are required. Root search stays global.
- Global search entries now carry the owning library as a category (and
as a fallback summary when the book has no description), so duplicate
copies are distinguishable in unscoped result lists.
The acquisition entry builder is extracted into addAcquisitionEntries and
shared by the all-books and per-library feeds. bookhoard.koplugin needs
no changes: it only registers the root URL, and KOReader's stock OPDS
client renders navigation feeds natively.
Tests: TestOPDSLibraryFolders covers the nav-feed shape, flat ?all=1
mode, scoped catalog isolation, scoped/global search behavior, and 403s
for libraries outside the device's visibility. Library names avoid the
word "test" on purpose — setupDeviceTest re-runs setupTestServer's
setup-time cleanup, which deletes every library whose name contains it.
29 lines
1.2 KiB
Go
29 lines
1.2 KiB
Go
package router
|
|
|
|
// Register OPDS routes with device authentication
|
|
//
|
|
// Authentication Methods:
|
|
// - Kobo devices: URL path parameter (e.g., /opds/devices/kobo-clara/catalog?token=dev_abc...)
|
|
// (Token stored in device for use in stock firmware sync)
|
|
//
|
|
// - KOReader devices: Bearer token in Authorization header (e.g., Authorization: Bearer dev_xyz...)
|
|
// (Token configured in device settings, passed to plugins)
|
|
//
|
|
// Middleware supports both methods (see device_auth.go)
|
|
// Returns 401 Unauthorized if device token is missing, invalid, or device sync is disabled
|
|
|
|
func registerOPDSRoutes(cfg *Config) {
|
|
e := cfg.Echo
|
|
|
|
// Require device authentication for all OPDS endpoints
|
|
opds := e.Group("/opds/devices")
|
|
opds.Use(cfg.DeviceAuthMiddleware.Authenticate)
|
|
opds.GET("/:deviceId/catalog", cfg.OPDSHandler.GetDeviceCatalog)
|
|
opds.GET("/:deviceId/library/:libraryId/catalog", cfg.OPDSHandler.GetLibraryCatalog)
|
|
opds.GET("/:deviceId/search", cfg.OPDSHandler.SearchDeviceCatalog)
|
|
opds.GET("/:deviceId/nav", cfg.OPDSHandler.GetDeviceNavigation)
|
|
opds.GET("/:deviceId/download/:bookId", cfg.OPDSHandler.DownloadBook)
|
|
opds.GET("/:deviceId/cover/:bookId", cfg.OPDSHandler.GetCoverImage)
|
|
opds.GET("/:deviceId/formats/:bookId", cfg.OPDSHandler.ListFormats)
|
|
}
|