opds

package module
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 6 Imported by: 0

README

opds

A Go library for building, implementing, and embedding OPDS (Open Publication Distribution System) catalog services — the standard ebook readers and library systems use to browse, search, and acquire publications.

You model your catalog once, with version-neutral types, and the library serializes it to either supported wire format, chosen by content negotiation. The same types come back the other way: opdsclient fetches and decodes catalogs, so one traversal works against either format.

Version Format Status Clients
OPDS 1.2 Atom (XML) stable, universal Calibre, KOReader, Thorium, FBReader, Foliate, Aldiko, Moon+, …
OPDS 2.0 JSON (Readium Web Publication Manifest) current draft Thorium, Readium toolkits, Palace/SimplyE, newer apps

No runtime dependencies — the library itself uses only the Go standard library. (A single test-only dependency, santhosh-tekuri/jsonschema, powers the JSON conformance tests; it never appears in your builds.)

Install

go get github.com/ophymx/opds

Packages

Package Purpose
opds Version-neutral domain model, constants, and fluent builders.
opds/opds1 Encodes and decodes OPDS 1.2 (Atom XML).
opds/opds2 Encodes and decodes OPDS 2.0 (JSON).
opds/opensearch OpenSearch description documents and template expansion (1.x search).
opds/opdshttp Embeddable http.Handler: routing, content negotiation, pagination, search, Basic authentication, progression sync.
opds/opdsclient HTTP client: fetches and decodes either version, authenticates, syncs progression.
opds/progstore Durable file-backed ProgressionStore (plus OPDS-PSE last-read storage).

Quick start

Implement the opds.Source interface (and optionally opds.Searcher), hand it to opdshttp, and you have a catalog that speaks both OPDS versions.

package main

import (
	"context"
	"net/http"

	"github.com/ophymx/opds"
	"github.com/ophymx/opds/opdshttp"
)

type catalog struct{}

func (catalog) Root(_ context.Context, _ opds.FeedRequest) (*opds.Feed, error) {
	return opds.NewFeed("urn:cat:root", "My Library").
		AddNav("All Books", "/opds/feed/all", opds.MediaTypeAcquisition, opds.RelSortNew), nil
}

func (catalog) Feed(_ context.Context, req opds.FeedRequest) (*opds.Feed, error) {
	if req.ID != "all" {
		return nil, opds.ErrNotFound
	}
	book := opds.NewPublication("urn:isbn:9780134190440", "The Go Programming Language").
		By("Alan Donovan").By("Brian Kernighan").
		In("en").ISBN("9780134190440").About("Computers").
		Cover("/covers/gopl.jpg", "image/jpeg").
		OpenAccess("/download/gopl.epub", "application/epub+zip")
	return opds.NewFeed("urn:cat:all", "All Books").Add(*book), nil
}

func (catalog) Publication(_ context.Context, id string) (*opds.Publication, error) {
	return nil, opds.ErrNotFound // serve standalone entry documents if you like
}

func main() {
	h := opdshttp.New(catalog{}, opdshttp.WithPrefix("/opds"))
	http.Handle("/opds/", h)
	http.ListenAndServe(":8080", nil)
}
curl localhost:8080/opds/                                    # OPDS 1.2 (default)
curl -H 'Accept: application/opds+json' localhost:8080/opds/ # OPDS 2.0
curl 'localhost:8080/opds/?version=2'                        # OPDS 2.0 via query

A complete runnable catalog with multiple feeds, subjects, and search lives in examples/bookstore: go run ./examples/bookstore.

The domain model

Both OPDS versions express the same concepts, so you build them once:

  • Feed — a navigation feed (entries link to sub-feeds via AddNav) or an acquisition feed (entries are publications via Add).
  • Publication — bibliographic metadata, cover images, and acquisition links.
  • Acquisition — how to obtain a publication: open-access, buy, borrow, sample, or subscribe, with prices, indirect acquisition (e.g. LCP → EPUB), and library-lending availability / holds / copies.
  • Facet / Group — filtered/sorted views and labelled sections.
  • Pagination via Feed.Page(total, perPage, startIndex) for the counters and Feed.Paged(baseHref, page, hasNext) for the prev/next links (build baseHref with opdshttp.FeedPagePath / opdshttp.SearchPagePath).

Fluent builders keep construction terse:

p := opds.NewPublication("urn:isbn:9781503280786", "Moby-Dick").
	By("Herman Melville").In("en").ISBN("9781503280786").
	PublishedAt(pubDate).About("Fiction").
	Summarize("The voyage of the Pequod.").
	Cover("/covers/moby.jpg", "image/jpeg").
	Buy("/buy/moby", "application/epub+zip", "USD", 4.99)

// Library lending with availability, a hold queue, and copy counts:
pos := 5
p.Acquire(opds.Acquisition{
	Rel:          opds.AcquireBorrow,
	Href:         "/borrow/moby",
	Type:         "application/epub+zip",
	Availability: &opds.Availability{State: opds.StateUnavailable, Until: dueDate},
	Holds:        &opds.Holds{Total: 12, Position: &pos},
	Copies:       &opds.Copies{Total: 3, Available: 0},
})

If your Source also implements opds.Searcher, the handler automatically:

  • serves search results at {prefix}/search?q=...,
  • serves an OpenSearch description at {prefix}/opensearch.xml (for 1.x clients),
  • advertises a search link in every feed (OpenSearch for 1.2, a templated link for 2.0).
func (catalog) Search(_ context.Context, req opds.SearchRequest) (*opds.Feed, error) {
	results := db.Query(req.Terms) // also: req.Author, req.Title, req.Page
	f := opds.NewFeed("urn:cat:search", "Results")
	for _, b := range results {
		f.Add(toPublication(b))
	}
	return f, nil
}

func (catalog) SearchDescription() opds.SearchDescription {
	return opds.SearchDescription{ShortName: "My Library", Description: "Search the catalog"}
}

Page streaming (OPDS-PSE)

For comics and manga, the OPDS Page Streaming Extension lets clients such as KOReader fetch one page image at a time instead of downloading a whole CBZ. Advertise a stream on a publication with Stream (and optionally LastRead for server-side resume, PSE 1.2):

comic := opds.NewPublication("urn:comic:vol1", "Vol. 1").
	OpenAccess("/dl/vol1.cbz", "application/vnd.comicbook+zip").
	Stream(h.PageStreamURL("vol1"), "image/jpeg", 35). // 35 pages
	LastRead(10, lastReadAt)                           // resume at page 10

If your Source also implements opds.PageSource, the handler serves the page images behind the template that PageStreamURL / PageStreamPath builds ({prefix}/page/{id}?page={pageNumber}&width={maxWidth}):

func (catalog) Page(_ context.Context, req opds.PageRequest) (*opds.PageImage, error) {
	img, err := openPage(req.ID, req.Number) // req.Number is zero-based
	if err != nil {
		return nil, opds.ErrNotFound
	}
	return &opds.PageImage{Type: "image/jpeg", Content: img}, nil
}

req.MaxWidth carries the client's desired maximum width; implementations may ignore it and serve full-size images. PSE is an OPDS 1.x extension with no 2.0 mapping: the opds2 encoder omits it and 2.0 clients fall back to the acquisition links.

Authentication and progression sync

WithAuth puts the catalog behind HTTP Basic authentication. You supply the credential check (an Authenticator — a password file, a database, an upstream service); the library answers unauthenticated requests with both things real clients need:

  • a WWW-Authenticate: Basic challenge, which is all that header-only clients such as KOReader and Foliate require, and
  • an OPDS Authentication Document as the 401 body (and at {prefix}/auth), which clients such as Thorium and Cantook render as a native login dialog.
h := opdshttp.New(catalog{},
	opdshttp.WithPrefix("/opds"),
	opdshttp.WithAuth(myAuth{}, opdshttp.AuthDocument{
		Title:       "My Library", // also used as the Basic realm
		Description: "Sign in with your library account.",
	}),
	opdshttp.WithProgression(store), // requires WithAuth
)

The authenticated identity reaches your Source through the request context: user, ok := opdshttp.User(ctx). For small deployments, opdshttp.StaticUsers(map[string]string{...}) is a ready-made Authenticator with constant-time comparison; anything hashed-at-rest (bcrypt, htpasswd) or rate-limited is a small wrapper you write, keeping those dependencies out of the library.

WithProgression adds per-user reading-position sync per the OPDS Progression 1.0 draft: every publication is advertised with a progression link, and the handler serves GET/PUT at {prefix}/progression/{id} through the ProgressionStore interface you supply, keyed by user and Publication.ID (NewMemProgressionStore covers tests and examples; progstore.New(dir) is a durable file-backed store that also persists OPDS-PSE last-read pages, so one store carries all per-user reading state). The same endpoint and store also answer the pre-spec Cantook rel (http://www.cantook.com/api/progression, the Readium-locator shape) that Komga and Stump serve and the Cantook/Aldiko client family consumes.

In 2.0 feeds the injected links carry the draft's authenticate hint (properties.authenticate, pointing at the Authentication Document) so a client can present credentials without first spending a 401. A modified timestamp implausibly far ahead of the server is refused (see WithProgressionSkew), so a reader whose clock is years fast cannot store a position that no honest later update could beat. Errors follow the draft's registry: 400 for an invalid payload, 409 when the stored progression is more recent, and 403 when your store returns opdshttp.ErrProgressionIncorrectUser or opdshttp.ErrProgressionLocked — each with an RFC 7807 problem body, except the 401 challenge, which carries the Authentication Document.

Try it live: go run ./examples/bookstore --auth (user demo, password demo).

Consuming a catalog

opdsclient is the other side of opdshttp. It works in the same version-neutral types, so one traversal runs against an OPDS 1.2 Atom catalog and an OPDS 2.0 JSON one alike: the client negotiates the version, decodes whichever it is handed, and resolves every href in the result to an absolute URL so you can follow links without tracking base URLs yourself.

c, err := opdsclient.New("https://example.com/opds/",
    opdsclient.WithBasicAuth("jane", "secret"),
    opdsclient.WithUserAgent("tankobon/1.0"),
    opdsclient.WithDevice(opds.Device{ID: "urn:uuid:…", Name: "Kobo Elipsa"}))

root, err := c.Root(ctx)
for _, nav := range root.Navigation {
    feed, err := c.Feed(ctx, nav.Href)   // relative or absolute, either works
    for _, p := range feed.Publications {
        res, err := c.Open(ctx, p.Acquisitions[0].Href)  // download
        defer res.Body.Close()
    }
}

// Paging, across both versions:
for f, err := c.Root(ctx); f != nil && err == nil; f, err = c.Next(ctx, f) { … }

// Search, whichever way the catalog advertises it:
results, err := c.Search(ctx, root, "kafka")

Authentication. Credentials are sent preemptively — but only to the catalog's own host, so following a cover link to a CDN or an acquisition to a partner site does not leak them. A 401 comes back as an *opdsclient.Error carrying the catalog's Authentication Document, and DiscoverAuth asks for it up front, so an application can prompt with the catalog's own labels ("Library card", "PIN") before it has any credentials to try:

doc, err := c.DiscoverAuth(ctx)   // nil, nil when the catalog is open
if doc != nil && doc.SupportsBasic() {
    user, pass := prompt(doc.Title, doc.LoginLabel, doc.PasswordLabel)
}

Progression sync. ProgressionLink finds a publication's endpoint — preferring the draft relation, falling back to the pre-spec Cantook one that Komga and Stump serve — and the client speaks whichever document shape the link implies:

link, ok := opdsclient.ProgressionLink(&pub)
pos, err := c.Progression(ctx, link)   // nil, nil when nothing is stored yet

err = c.SetProgression(ctx, link, &opds.Progression{
    Progression: 0.42,
    Title:       "Chapter 4",
    References:  []string{"/chapter4.html#p12"},
})   // device and timestamp filled in from the client's configuration
if errors.Is(err, opdsclient.ErrProgressionStale) {
    // the server has a newer position; take it rather than retrying
}

Every failing request returns an *opdsclient.Error — carrying the status, the RFC 7807 problem body, and the Authentication Document when there was one — that unwraps to a sentinel: ErrUnauthorized, opds.ErrNotFound, ErrProgressionStale, ErrProgressionLocked, and the rest.

Page streaming. For comic and manga catalogs, PageURL expands an OPDS-PSE template and Page fetches one page:

res, err := c.Page(ctx, pub.PageStream, 0, 1200)   // page 0, max width 1200px

Deployment notes

  • Serve authenticated catalogs over HTTPS. Basic authentication sends credentials in cleartext, and reader clients will happily do so over plain HTTP.
  • Set WithBaseURL unless a trusted proxy fronts the handler. Absolute URLs (the OpenSearch template, the Authentication Document id) are otherwise derived from the request's Host, X-Forwarded-Proto, and X-Forwarded-Host headers, which are client-controlled: fine behind a reverse proxy that overwrites them (the usual multi-user deployment), but a directly exposed handler should pin its canonical base with opdshttp.WithBaseURL("https://books.example.com").
  • Rate limiting and brute-force lockout are the Authenticator implementation's responsibility; the library only defines the boundary.

Content negotiation

The handler picks the version per request, in priority order:

  1. ?version=1 / ?version=2 (or ?f=atom / ?f=json) query parameter.
  2. The Accept header (application/opds+json → 2.0; atom+xml → 1.2).
  3. The configured default (WithDefaultVersion, defaulting to 1.2 for maximum client compatibility).

URL layout

opdshttp.Handler serves this layout relative to its mount prefix. Use the FeedPath, PublicationPath, and SearchPath helpers (or the handler's FeedURL etc. methods) to build matching hrefs in your Source:

{prefix}/                  root feed
{prefix}/feed/{id}         a feed by id
{prefix}/publication/{id}  a single publication document
{prefix}/search            search results
{prefix}/opensearch.xml    OpenSearch description
{prefix}/page/{id}         a page image (OPDS-PSE, if the Source is a PageSource)
{prefix}/auth              the Authentication Document (with WithAuth)
{prefix}/progression/{id}  per-user reading progression, GET/PUT (with WithProgression)

Using the codecs directly

You don't have to use opdshttp or opdsclient. The codecs turn a *opds.Feed into bytes and back, so they drop into any framework, static generation pipeline, or crawler:

xmlBytes, _ := opds1.Marshal(feed)  // OPDS 1.2 Atom
jsonBytes, _ := opds2.Marshal(feed) // OPDS 2.0 JSON

feed, _ := opds1.Unmarshal(xmlBytes)
feed, _ := opds2.Unmarshal(jsonBytes)

Decoding is the inverse of encoding, and the round trip is tested both ways. A handful of members belong to only one version and do not survive the other (PageStream is 1.x-only, Series and SortAs are 2.0-only, and so on); each decoder's doc comment lists exactly what it drops.

Conformance

The encoders are tested against the official OPDS schemas, not just hand-written expectations:

  • OPDS 2.0 output is validated against the official JSON Schemas from opds-community/specs and the Readium Web Publication Manifest schemas they reference. These tests are pure Go (using a vendored copy of the schemas) and run under go test ./....
  • OPDS 1.2 output is validated against the official RELAX NG schema (opds.rnc) with Jing — the reference validator behind the official OPDS validator. These tests need a JRE and skip automatically when Java/Jing are absent, so go test ./... still passes everywhere.
  • Authentication Documents and Progression Documents emitted by opdshttp are validated against the official schemas from drafts.opds.io (vendored, with the pinned draft revision recorded in opdshttp/testdata/schema/SOURCES.md), and their exact serializations are locked with golden files.

To run the 1.2 RELAX NG tests, which fetch Jing into tools/ on first run:

scripts/conformance.sh

Vendored schema provenance (and the one documented upstream-typo fix in opds.rnc) is recorded in the testdata/schema/*/SOURCES.md files.

Status / scope

  • OPDS 1.2 and 2.0 feeds, navigation and acquisition — validated against the official schemas (see Conformance).
  • Acquisition model including prices, nested indirect acquisition, and library lending (availability/holds/copies).
  • Facets, groups, pagination, OpenSearch.
  • HTTP Basic authentication via Authentication for OPDS 1.0 (opdshttp.WithAuth): 401 responses carry both a WWW-Authenticate: Basic challenge (for clients like KOReader and Foliate) and an Authentication Document body (for clients like Thorium and Cantook), feeds advertise the document link, and the authenticated identity reaches your Source via opdshttp.User.
  • A client (opdsclient) for the same protocols: version-negotiated fetching and decoding of both formats, absolute-href resolution, Basic authentication with Authentication Document discovery, OpenSearch and 2.0-template search, OPDS-PSE page fetching, and progression sync over both the draft endpoint and the Cantook alias — with typed errors for every condition the specs define. It is tested by driving the library's own handler over HTTP in both versions.
  • Per-user reading-progression sync via the OPDS Progression 1.0 draft (opdshttp.WithProgression, backed by a caller-supplied ProgressionStore): publications are advertised with a progression link (carrying the draft's authenticate hint in 2.0), and the handler serves GET/PUT with the draft's validation, staleness (409), refusal (403) and problem-details semantics. Requires WithAuth — progression is per-user by definition. The same endpoint and store also serve the pre-spec Cantook alias (http://www.cantook.com/api/progression, the Readium-locator shape that Komga and Stump serve and Cantook/Aldiko consume), translated with the deployed servers' status semantics.
  • Page streaming for comics/manga via OPDS-PSE 1.2 (pse:count, pse:lastRead, pse:lastReadDate) — 1.x feeds only, like the extension itself. The stream link's templated href ({pageNumber}) necessarily goes beyond Atom's strict URI datatype; the conformance tests document that this is the extension's only deviation.

A note on library lending in 1.2: opds:availability/holds/copies are standard in OPDS 2.0 but are not part of the official OPDS 1.2 RELAX NG schema — they are a de-facto 1.x extension (Library Simplified/Palace). The library emits them in both versions because real library clients rely on them; just be aware that a 1.2 feed using them intentionally goes beyond the core 1.2 schema.

Deliberately out of scope: user management (the Authenticator interface is the boundary), auth flows beyond Basic, KOReader kosync, and annotation sync. Contributions welcome.

References

License

MIT © 2026 Jeffrey T. Peckham

Documentation

Overview

Package opds provides a version-neutral domain model and helpers for building OPDS (Open Publication Distribution System) catalog services.

OPDS is a syndication format for electronic publications. Two wire formats are in wide use:

  • OPDS 1.2, an Atom (XML) based format. Universally supported by readers such as Calibre, KOReader, Thorium, FBReader and many others.
  • OPDS 2.0, a JSON format built on the Readium Web Publication Manifest.

Both express the same conceptual model, so this library models a catalog once, with version-neutral types, and lets pluggable codecs serialize to and from either format:

  • Package opds the domain model, constants and builders (this package).
  • Package opds/opds1 encodes and decodes OPDS 1.2 (Atom XML).
  • Package opds/opds2 encodes and decodes OPDS 2.0 (JSON).
  • Package opds/opensearch OpenSearch description documents (1.x search).
  • Package opds/opdshttp an embeddable http.Handler serving a catalog.
  • Package opds/opdsclient an HTTP client consuming one.
  • Package opds/progstore a durable store for reading progression.

To expose a catalog you implement the Source interface (and, optionally, Searcher) and hand it to opds/opdshttp, or drive the encoders directly. To consume one, point opds/opdsclient at its root: it negotiates the version, decodes whichever it is served, and hands back the same neutral types, so a caller writes one traversal for both formats.

Index

Constants

View Source
const (
	// MediaTypeNavigation is the media type of an OPDS 1.x navigation feed.
	MediaTypeNavigation = "application/atom+xml;profile=opds-catalog;kind=navigation"
	// MediaTypeAcquisition is the media type of an OPDS 1.x acquisition feed.
	MediaTypeAcquisition = "application/atom+xml;profile=opds-catalog;kind=acquisition"
	// MediaTypeEntry is the media type of a standalone OPDS 1.x entry document.
	MediaTypeEntry = "application/atom+xml;type=entry;profile=opds-catalog"

	// MediaTypeFeed is the media type of an OPDS 2.0 feed.
	MediaTypeFeed = "application/opds+json"
	// MediaTypePublication is the media type of an OPDS 2.0 publication.
	MediaTypePublication = "application/opds-publication+json"

	// MediaTypeOpenSearch is the media type of an OpenSearch description document.
	MediaTypeOpenSearch = "application/opensearchdescription+xml"

	// MediaTypeAuthDocument is the media type of an OPDS Authentication Document
	// (see https://drafts.opds.io/authentication-for-opds-1.0.html).
	MediaTypeAuthDocument = "application/opds-authentication+json"

	// MediaTypeProgression is the media type of an OPDS Progression Document
	// (see Progression).
	MediaTypeProgression = "application/opds-progression+json"

	// MediaTypeProgressionReadium is the media type of the pre-spec,
	// Readium-locator-shaped progression document served under
	// RelProgressionCantook.
	MediaTypeProgressionReadium = "application/vnd.readium.progression+json"
)

Media types used by OPDS catalogs.

View Source
const (
	NSAtom       = "http://www.w3.org/2005/Atom"
	NSOPDS       = "http://opds-spec.org/2010/catalog"
	NSDCTerms    = "http://purl.org/dc/terms/"
	NSOpenSearch = "http://a9.com/-/spec/opensearch/1.1/"
	NSThreading  = "http://purl.org/syndication/thread/1.0"
	// NSPSE is the OPDS Page Streaming Extension namespace (see PageStream).
	NSPSE = "http://vaemendis.net/opds-pse/ns"
)

XML namespace URIs used by OPDS 1.x (Atom) feeds.

View Source
const (
	RelSelf       = "self"
	RelStart      = "start"
	RelUp         = "up"
	RelNext       = "next"
	RelPrevious   = "previous"
	RelFirst      = "first"
	RelLast       = "last"
	RelSearch     = "search"
	RelAlternate  = "alternate"
	RelRelated    = "related"
	RelSubsection = "subsection"
	RelCollection = "collection"

	RelImage     = "http://opds-spec.org/image"
	RelThumbnail = "http://opds-spec.org/image/thumbnail"

	// RelPageStream is the OPDS-PSE page streaming relation (see PageStream).
	RelPageStream = "http://vaemendis.net/opds-pse/stream"

	// RelAuthDocument advertises the catalog's OPDS Authentication Document
	// (see opdshttp.WithAuth).
	RelAuthDocument = "http://opds-spec.org/auth/document"

	// RelProgression advertises a publication's progression endpoint
	// (see Progression).
	RelProgression = "http://opds-spec.org/progression"

	// RelProgressionCantook is the pre-spec progression relation that predates
	// the OPDS Progression draft: Komga and Stump serve it and the
	// Cantook/Aldiko client family consumes it. The document shape is a
	// Readium Locator (MediaTypeProgressionReadium), not a Progression
	// Document; opdshttp aliases it onto the same store.
	RelProgressionCantook = "http://www.cantook.com/api/progression"

	RelFacet         = "http://opds-spec.org/facet"
	RelGroup         = "http://opds-spec.org/group"
	RelSortNew       = "http://opds-spec.org/sort/new"
	RelSortPopular   = "http://opds-spec.org/sort/popular"
	RelFeatured      = "http://opds-spec.org/featured"
	RelRecommended   = "http://opds-spec.org/recommended"
	RelShelf         = "http://opds-spec.org/shelf"
	RelSubscriptions = "http://opds-spec.org/subscriptions"
	RelCrawlable     = "http://opds-spec.org/crawlable"
)

Standard (RFC 5988 / Atom) and OPDS-specific link relations.

The structural relations (RelSelf, RelStart, ...) are bare tokens shared by both OPDS versions. The relations carrying the "http://opds-spec.org/" prefix are OPDS-specific and identical across versions 1.2 and 2.0.

View Source
const (
	// StateAvailable means the publication can be borrowed immediately.
	StateAvailable = "available"
	// StateUnavailable means all copies are loaned out.
	StateUnavailable = "unavailable"
	// StateReserved means the user holds a reservation in the queue.
	StateReserved = "reserved"
	// StateReady means a hold is ready to be borrowed by the user.
	StateReady = "ready"
)

Availability states for library lending (used by AcquireBorrow links).

View Source
const (
	// AuthFlowBasic is the HTTP Basic Authentication flow.
	AuthFlowBasic = "http://opds-spec.org/auth/basic"
)

Authentication flow type URIs used in an OPDS Authentication Document (see https://drafts.opds.io/authentication-for-opds-1.0.html).

Variables

View Source
var ErrNotFound = errors.New("opds: not found")

ErrNotFound is returned by a Source when a requested feed or publication does not exist. The HTTP layer maps it to 404 Not Found.

Functions

func PageHref added in v0.1.1

func PageHref(href string, page int) string

PageHref returns href with its "page" query parameter set to page, preserving any other query parameters. For page 1 (or lower) the parameter is removed instead, so the first page and the unpaged href are the same URL. An unparseable href is returned unchanged.

Types

type Acquisition

type Acquisition struct {
	// Rel is the acquisition relation. Defaults to AcquireGeneric when empty.
	Rel AcquisitionRel
	// Href is the acquisition URL. Required.
	Href string
	// Type is the media type acquired. For indirect acquisition this is the
	// media type of the intermediate resource (e.g. an HTML purchase page).
	Type string
	// Title is an optional label.
	Title string

	// Prices lists the cost(s) of acquisition. Required for AcquireBuy.
	Prices []Price
	// Indirect describes formats obtained after following the link
	// (e.g. an LCP license that yields an EPUB).
	Indirect []IndirectAcquisition
	// Availability describes lending availability (AcquireBorrow).
	Availability *Availability
	// Holds describes the reservation queue (library lending).
	Holds *Holds
	// Copies describes copy counts (library lending).
	Copies *Copies
}

Acquisition describes one way to obtain a publication.

type AcquisitionRel

type AcquisitionRel string

AcquisitionRel identifies how a publication may be acquired. The values are the full relation URIs used (identically) in OPDS 1.2 and 2.0.

const (
	// AcquireGeneric is a generic acquisition relation; the acquisition method
	// is unspecified.
	AcquireGeneric AcquisitionRel = "http://opds-spec.org/acquisition"
	// AcquireOpenAccess is freely accessible without payment or authentication.
	AcquireOpenAccess AcquisitionRel = "http://opds-spec.org/acquisition/open-access"
	// AcquireBuy must be purchased; carries at least one Price.
	AcquireBuy AcquisitionRel = "http://opds-spec.org/acquisition/buy"
	// AcquireBorrow is borrowed for a limited period (library lending).
	AcquireBorrow AcquisitionRel = "http://opds-spec.org/acquisition/borrow"
	// AcquireSample provides a sample or preview of the publication.
	AcquireSample AcquisitionRel = "http://opds-spec.org/acquisition/sample"
	// AcquireSubscribe is acquired through a subscription.
	AcquireSubscribe AcquisitionRel = "http://opds-spec.org/acquisition/subscribe"
)

type AuthDocument added in v0.5.0

type AuthDocument struct {
	// ID is the document's canonical URL.
	ID string
	// Title names the catalog access is being requested for. Required; a
	// server also uses it as the Basic realm in its WWW-Authenticate challenge.
	Title string
	// Description optionally tells the user how to authenticate
	// (e.g. "Enter your library card number and PIN.").
	Description string
	// LoginLabel and PasswordLabel are alternate labels for the credential
	// fields (e.g. "Library card" and "PIN"), a shorthand for the common case
	// of a document declaring nothing but the Basic flow: a server with no
	// explicit Authentication synthesizes that flow from them, and a parsed
	// document copies them back out of it. Empty means the client shows its
	// own defaults.
	LoginLabel    string
	PasswordLabel string
	// Authentication lists the declared flows. A server may leave it empty,
	// which declares HTTP Basic (AuthFlowBasic); a parsed document always has
	// at least one entry, since the format requires it.
	Authentication []AuthFlow
	// Links are associated resources: rel "logo" (an image type), "help" (a
	// page or mailto: URL), and "register".
	Links []Link
}

AuthDocument describes how a client authenticates with a catalog, per Authentication for OPDS 1.0 (https://drafts.opds.io/authentication-for-opds-1.0.html). It is the body a server returns with a 401 and serves at a stable URL, and the document a client parses to learn which credentials to ask its user for.

Like Progression, this is version-neutral wire vocabulary: the model lives here, opdshttp serves it, and opdsclient consumes it.

func (*AuthDocument) Flow added in v0.5.0

func (d *AuthDocument) Flow(typ string) (AuthFlow, bool)

Flow returns the declared flow of the given type. A document with no explicit flows is treated as declaring AuthFlowBasic, matching how a server renders it.

func (*AuthDocument) SupportsBasic added in v0.5.0

func (d *AuthDocument) SupportsBasic() bool

SupportsBasic reports whether the document offers the HTTP Basic flow, the one flow this library can satisfy without application help.

type AuthFlow added in v0.5.0

type AuthFlow struct {
	// Type is the flow's URI, e.g. AuthFlowBasic. Required.
	Type string
	// LoginLabel and PasswordLabel are alternate labels for the flow's
	// credential fields.
	LoginLabel    string
	PasswordLabel string
	// Links are resources specific to this flow (an authenticate endpoint for
	// a token-based flow, for instance). The library passes them through.
	Links []Link
}

AuthFlow is one authentication method a catalog offers.

type AuthenticateHint added in v0.5.0

type AuthenticateHint struct {
	// Href is the Authentication Document's URL. Required.
	Href string
	// Type is its media type; it defaults to MediaTypeAuthDocument.
	Type string
}

AuthenticateHint points at an OPDS Authentication Document (https://drafts.opds.io/authentication-for-opds-1.0.html) describing how to authenticate for a Link whose target requires it.

type Author

type Author struct {
	// Name is the display name. Required.
	Name string
	// URI optionally links to the author (a feed of their works, a homepage).
	URI string
	// SortAs is an optional collation key (2.0).
	SortAs string
}

Author identifies a person or organization responsible for a feed or publication.

type Availability

type Availability struct {
	// State is one of StateAvailable, StateUnavailable, StateReserved, StateReady.
	State string
	// Since is when the current state began (e.g. loan start). Optional.
	Since time.Time
	// Until is when the current state ends (e.g. loan or hold expiry). Optional.
	Until time.Time
}

Availability describes whether a borrowable publication can currently be obtained.

type Copies

type Copies struct {
	// Total is the number of copies owned.
	Total int
	// Available is the number of copies currently available to borrow.
	Available int
}

Copies describes copy counts for a borrowable publication.

type Device added in v0.3.0

type Device struct {
	// ID is a URI identifying the device (e.g. a urn:uuid:). Required.
	ID string
	// Name is the user-facing device name. Required.
	Name string
}

Device identifies the device a Progression was recorded on.

type Facet

type Facet struct {
	// Group names the facet group this facet belongs to (e.g. "Language").
	Group string
	// Title is the facet label (e.g. "French"). Required.
	Title string
	// Href is the URL of the filtered/sorted feed. Required.
	Href string
	// Type is the media type of the target feed.
	Type string
	// Count is an optional hint at the number of items behind the facet.
	Count int
	// Active marks the facet as the one currently applied.
	Active bool
}

Facet is an alternate filtered or sorted view of a feed, grouped with other facets under a common Group label.

type Feed

type Feed struct {
	// ID is a stable, unique identifier for the feed (e.g. a URN or URL).
	// Maps to atom:id in 1.2; used as metadata.identifier in 2.0.
	ID string
	// Title is the human-readable feed title. Required by both versions.
	Title string
	// Subtitle is an optional secondary title (atom:subtitle).
	Subtitle string
	// Updated is the last time the feed changed. Defaults to time.Now when zero.
	Updated time.Time
	// Icon is an optional URL to a feed icon (atom:icon).
	Icon string
	// Authors describe who is responsible for the feed.
	Authors []Author
	// Links are feed-level links (self, start, up, next, search, ...).
	Links []Link

	// Navigation holds the entries of a navigation feed.
	Navigation []NavEntry
	// Publications holds the entries of an acquisition feed.
	Publications []Publication
	// Groups partition an acquisition feed into labelled sections (2.0 groups;
	// emitted via opds:group links in 1.2).
	Groups []Group
	// Facets describe alternate filtered/sorted views of the same feed.
	Facets []Facet

	// Pagination. Zero values are treated as "unset" and omitted.
	TotalResults int // total number of items across all pages
	ItemsPerPage int // number of items per page
	StartIndex   int // 1-based index of the first item on this page (1.x)
	CurrentPage  int // 1-based page number (2.0)
}

Feed is a version-neutral OPDS feed (an Atom feed in 1.2, a collection in 2.0). A feed is either a navigation feed (Navigation populated) or an acquisition feed (Publications populated); both may be present but most clients expect one or the other.

func NewFeed

func NewFeed(id, title string) *Feed

NewFeed returns a feed with the given id and title and Updated set to now.

func (*Feed) Add

func (f *Feed) Add(pubs ...Publication) *Feed

Add appends one or more publications to the feed.

func (*Feed) AddFacet

func (f *Feed) AddFacet(group, title, href, mediaType string, count int, active bool) *Feed

AddFacet appends a facet to the feed.

func (*Feed) AddGroup

func (f *Feed) AddGroup(g Group) *Feed

AddGroup appends a group to the feed.

func (*Feed) AddNav

func (f *Feed) AddNav(title, href, mediaType, rel string) *Feed

AddNav appends a navigation entry. rel may be empty (defaults to subsection).

func (*Feed) AddNavEntry

func (f *Feed) AddNavEntry(e NavEntry) *Feed

AddNavEntry appends a fully specified navigation entry.

func (*Feed) At

func (f *Feed) At(t time.Time) *Feed

At sets the feed's Updated timestamp.

func (*Feed) By

func (f *Feed) By(name string) *Feed

By adds an author to the feed.

func (*Feed) IsAcquisition

func (f *Feed) IsAcquisition() bool

IsAcquisition reports whether the feed is an acquisition feed (contains publications) as opposed to a navigation feed. It is used to select the correct OPDS 1.x media type.

func (f *Feed) Link(rel, href, mediaType string) *Feed

Link adds a feed-level link.

func (*Feed) Next

func (f *Feed) Next(href, mediaType string) *Feed

Next adds a next-page link.

func (*Feed) Page

func (f *Feed) Page(total, perPage, start int) *Feed

Page sets pagination links and counters. self/next/prev hrefs that are empty are skipped. total and perPage are recorded as counters; start is the 1-based index of the first item on this page.

func (*Feed) Paged added in v0.1.1

func (f *Feed) Paged(baseHref string, page int, hasNext bool) *Feed

Paged records the current page and adds previous/next pagination links derived from baseHref, the feed's unpaged href (a query string is allowed and preserved, e.g. a search href carrying its terms). A previous link is added when page > 1 and a next link when hasNext; hrefs are built with PageHref, so page 1 is baseHref itself. If ItemsPerPage is already set (see Page), StartIndex is derived when unset. The pagination links carry no media type; feeds served through opdshttp get it filled with the feed's own type.

func (*Feed) Prev

func (f *Feed) Prev(href, mediaType string) *Feed

Prev adds a previous-page link.

func (f *Feed) SearchLink(href, mediaType string, templated bool) *Feed

SearchLink adds a search link. For 1.x mediaType should be MediaTypeOpenSearch; for 2.0 use MediaTypeFeed with a templated href.

func (*Feed) Self

func (f *Feed) Self(href, mediaType string) *Feed

Self adds a self link. Feeds served through opdshttp should usually omit it: the handler injects a self link derived from the request URL, which — unlike a href built from the feed id alone — carries the page parameter of a paged request.

func (*Feed) Start

func (f *Feed) Start(href string) *Feed

Start adds a start (catalog root) link.

func (*Feed) SubtitledBy

func (f *Feed) SubtitledBy(s string) *Feed

SubtitledBy sets the feed subtitle.

func (*Feed) Up

func (f *Feed) Up(href, mediaType string) *Feed

Up adds an up (parent) link.

type FeedRequest

type FeedRequest struct {
	// ID identifies the requested feed (empty for the root). For the HTTP layer
	// this is the path segment after the feed prefix.
	ID string
	// Page is the requested 1-based page number (1 if unspecified).
	Page int
	// Version is the OPDS version the response will be encoded in, as
	// negotiated by the caller. Implementations may use it to tailor hrefs.
	Version Version
	// BaseURL is the absolute base URL of the catalog (scheme://host), if known.
	BaseURL string
	// Query holds the raw query parameters of the request (facet selections,
	// sort orders, and so on).
	Query url.Values
}

FeedRequest carries the parameters of a request for a feed.

type Group

type Group struct {
	// Title is the section label.
	Title string
	// Href is an optional link to the full feed for this group.
	Href string
	// Type is the media type of the group's full feed.
	Type string
	// Rel is an optional relation for the group's link.
	Rel string
	// Navigation holds the group's navigation entries.
	Navigation []NavEntry
	// Publications holds the group's publications.
	Publications []Publication
}

Group is a labelled section of a feed (2.0 groups). A group typically links to a fuller feed via Href and shows a preview of its contents.

type Holds

type Holds struct {
	// Total is the number of holds placed.
	Total int
	// Position is the requesting user's position in the queue, if known.
	Position *int
}

Holds describes a reservation queue for a borrowable publication.

type Image

type Image struct {
	// Href is the image URL. Required.
	Href string
	// Type is the image media type (e.g. "image/jpeg").
	Type string
	// Width and Height are optional pixel dimensions (2.0).
	Width, Height int
	// Thumbnail marks this as a reduced-size image. In 1.2 it selects the
	// image/thumbnail relation; in 2.0 all images share the images collection.
	Thumbnail bool
}

Image is a cover image or thumbnail.

type IndirectAcquisition

type IndirectAcquisition struct {
	// Type is the media type that will ultimately be acquired.
	Type string
	// Child holds further levels of indirection.
	Child []IndirectAcquisition
}

IndirectAcquisition declares a media type obtainable after following an acquisition link. Entries may nest to express multiple levels of indirection.

type Link struct {
	// Rel is the link relation (see the Rel* constants).
	Rel string
	// Href is the target URL or, when Templated is true, a URI template.
	Href string
	// Type is the media type of the target.
	Type string
	// Title is an optional human-readable label.
	Title string
	// Templated indicates Href is an RFC 6570 URI template (2.0 only).
	Templated bool
	// Authenticate optionally hints that the target requires authentication,
	// pointing at the Authentication Document a client should use. It saves
	// the client an unauthenticated round-trip, and is emitted as the link's
	// properties.authenticate in 2.0 only — OPDS 1.x Atom links have no
	// properties, so 1.x feeds advertise the target without the hint.
	Authenticate *AuthenticateHint
}

Link is a generic hypermedia link.

type NavEntry struct {
	// ID is a stable identifier. Synthesized from Href for 1.2 if empty.
	ID string
	// Title is the entry label. Required.
	Title string
	// Updated is the entry's last-modified time. Defaults to the feed's when zero.
	Updated time.Time
	// Content is an optional description of the target.
	Content string
	// Href is the URL of the target feed or resource. Required.
	Href string
	// Type is the media type of the target (e.g. MediaTypeAcquisition).
	Type string
	// Rel is an optional relation for the target link (e.g. RelSortNew,
	// RelSubsection, RelFeatured). Defaults to RelSubsection when empty.
	Rel string
	// Images are optional thumbnails/tiles for the entry.
	Images []Image
}

NavEntry is an entry in a navigation feed: a link to another feed or resource, with a title and optional description.

type PageImage added in v0.2.0

type PageImage struct {
	// Type is the image media type (e.g. "image/jpeg").
	Type string
	// Content is the image data. The HTTP layer closes it after serving when
	// it implements io.Closer.
	Content io.Reader
}

PageImage is a single page image returned by a PageSource.

type PageRequest added in v0.2.0

type PageRequest struct {
	// ID identifies the publication, as placed in the stream href by the Source.
	ID string
	// Number is the zero-based page number (the expanded {pageNumber} token).
	Number int
	// MaxWidth is the client's maximum desired image width in pixels (the
	// expanded {maxWidth} token), or 0 if unspecified. Implementations may
	// ignore it and serve the full-size image.
	MaxWidth int
	// Query holds the raw query parameters of the request.
	Query url.Values
}

PageRequest carries the parameters of a request for a single page image.

type PageSource added in v0.2.0

type PageSource interface {
	// Page returns one page image of a publication. It should return
	// ErrNotFound when the publication or page does not exist.
	Page(ctx context.Context, req PageRequest) (*PageImage, error)
}

PageSource is an optional interface a Source may also implement to serve the single-page images behind OPDS-PSE stream links (see PageStream). When present, the HTTP layer routes page-image requests to it.

type PageStream added in v0.2.0

type PageStream struct {
	// Href is the URL template for fetching a single page. It must contain the
	// token {pageNumber} (pages are numbered 0 to PageCount-1) and may contain
	// {maxWidth}, which clients replace with their maximum desired image width.
	Href string
	// Type is the media type of the page images: image/jpeg, image/png or
	// image/gif.
	Type string
	// PageCount is the total number of pages. Required: clients such as
	// KOReader render only the first page when the count is missing.
	PageCount int
	// LastRead is the 1-based number of the last page read, for server-side
	// resume (PSE 1.2). Zero means unknown and is omitted. A server tracking
	// per-user positions can populate LastRead/LastReadDate from the same
	// store that backs OPDS Progression (see Progression and
	// opdshttp.ProgressionStore) — both key on (user, Publication.ID), with
	// the user available via opdshttp.User.
	LastRead int
	// LastReadDate is when LastRead was recorded (PSE 1.2). Optional.
	LastReadDate time.Time
}

PageStream describes page-by-page image streaming of a publication per the OPDS Page Streaming Extension (OPDS-PSE), used by comic/manga clients such as KOReader to fetch one page at a time instead of downloading the whole publication. See https://anansi-project.github.io/docs/opds-pse/intro.

type Price

type Price struct {
	// Currency is an ISO 4217 currency code (e.g. "USD").
	Currency string
	// Value is the amount.
	Value float64
}

Price is a monetary amount in a specific currency.

type Progression added in v0.3.0

type Progression struct {
	// Progression is the total progression through the publication, as a
	// fraction in [0, 1]. Required.
	Progression float64
	// Modified is when the progression was recorded. Required; the server
	// rejects updates older than the stored progression.
	Modified time.Time
	// Device identifies where the progression was recorded. Required.
	Device Device
	// Title optionally contextualizes the position for display (e.g. the
	// current chapter heading).
	Title string
	// References optionally refine the position as media-fragment URIs. The
	// library passes them through opaquely and never interprets them.
	References []string
}

Progression is a user's last-known reading position in a publication, per the OPDS Progression 1.0 draft (https://drafts.opds.io/opds-progression-1.0.html, as retrieved 2026-08-22). Progression is peer wire vocabulary like PageStream: the model lives here, and the opdshttp handler serves it per-user behind authentication when a ProgressionStore is configured.

type Publication

type Publication struct {
	// ID is a stable, unique identifier (atom:id; metadata.identifier in 2.0).
	ID string
	// Title is the publication title. Required.
	Title string
	// SortAs is an optional collation key for the title (2.0 metadata.sortAs).
	SortAs string
	// Updated is when the entry last changed (atom:updated; metadata.modified).
	Updated time.Time
	// Published is the publication date (dcterms:issued; metadata.published).
	Published time.Time
	// Languages are ISO 639 language codes.
	Languages []string
	// Identifiers are external identifiers such as ISBN URNs (dcterms:identifier).
	Identifiers []string
	// Publisher is the publishing entity.
	Publisher string
	// Authors are the publication's authors.
	Authors []Author
	// Contributors are other contributors (editors, translators, ...).
	Contributors []Author
	// Subjects are categories/genres.
	Subjects []Subject
	// Summary is a short plain-text description (atom:summary).
	Summary string
	// Description is a longer description, may contain HTML (atom:content).
	Description string
	// Rights is a copyright/licensing statement (atom:rights).
	Rights string
	// Series places the publication within a series. OPDS 2.0 only: it renders
	// as belongsTo.series in JSON but is omitted from the 1.2 Atom rendering,
	// which has no standard representation for series membership.
	Series *Series

	// Images are cover images. By convention the first is the primary cover.
	Images []Image
	// Acquisitions are the ways the publication can be acquired. An acquisition
	// feed entry should have at least one.
	Acquisitions []Acquisition
	// PageStream advertises page-by-page image streaming (OPDS-PSE). OPDS 1.x
	// only: the extension has no 2.0 mapping, so the opds2 encoder omits it and
	// clients fall back to the acquisition links.
	PageStream *PageStream
	// Links are additional links (self, alternate to the full entry, related, ...).
	Links []Link
}

Publication is a single catalog entry describing a publication and how to acquire it.

func NewPublication

func NewPublication(id, title string) *Publication

NewPublication returns a publication with the given id and title and Updated set to now.

func (*Publication) About

func (p *Publication) About(name string) *Publication

About adds a subject/category.

func (*Publication) Acquire

func (p *Publication) Acquire(a Acquisition) *Publication

Acquire appends a fully specified acquisition.

func (*Publication) Author

func (p *Publication) Author(a Author) *Publication

Author adds a fully specified author.

func (*Publication) Borrow

func (p *Publication) Borrow(href, mediaType, state string) *Publication

Borrow adds a borrow acquisition with the given availability state.

func (*Publication) Buy

func (p *Publication) Buy(href, mediaType, currency string, value float64) *Publication

Buy adds a buy acquisition with a single price.

func (*Publication) By

func (p *Publication) By(name string) *Publication

By adds an author.

func (*Publication) Categorize

func (p *Publication) Categorize(name, code, scheme string) *Publication

Categorize adds a subject with a controlled-vocabulary code and scheme.

func (*Publication) Cover

func (p *Publication) Cover(href, mediaType string) *Publication

Cover adds a primary cover image.

func (*Publication) Describe

func (p *Publication) Describe(s string) *Publication

Describe sets the long description.

func (*Publication) From

func (p *Publication) From(publisher string) *Publication

From sets the publisher.

func (*Publication) ISBN

func (p *Publication) ISBN(isbn string) *Publication

ISBN adds an ISBN identifier as a URN.

func (*Publication) Identifier

func (p *Publication) Identifier(id string) *Publication

Identifier adds a raw identifier.

func (*Publication) In

func (p *Publication) In(langs ...string) *Publication

In sets one or more languages.

func (*Publication) LastRead added in v0.2.0

func (p *Publication) LastRead(page int, at time.Time) *Publication

LastRead records the 1-based last page read and when, for server-side resume (PSE 1.2; see PageStream). Pass a zero time if the date is unknown. The publication must also be given a stream link via Stream (in either order).

func (p *Publication) Link(rel, href, mediaType string) *Publication

Link adds an arbitrary link to the publication.

func (*Publication) OpenAccess

func (p *Publication) OpenAccess(href, mediaType string) *Publication

OpenAccess adds an open-access (free download) acquisition.

func (*Publication) PartOf

func (p *Publication) PartOf(series string, position float64) *Publication

PartOf places the publication in a series at the given position. Series membership only appears in OPDS 2.0 output; see Publication.Series.

func (*Publication) PublishedAt

func (p *Publication) PublishedAt(t time.Time) *Publication

PublishedAt sets the publication date.

func (*Publication) Sample

func (p *Publication) Sample(href, mediaType string) *Publication

Sample adds a sample/preview acquisition.

func (*Publication) Stream added in v0.2.0

func (p *Publication) Stream(hrefTemplate, mediaType string, pageCount int) *Publication

Stream advertises OPDS-PSE page streaming (1.x only; see PageStream). hrefTemplate must contain {pageNumber} and may contain {maxWidth}; opdshttp.PageStreamPath builds a template matching that package's routing. A LastRead recorded earlier is kept.

func (*Publication) Summarize

func (p *Publication) Summarize(s string) *Publication

Summarize sets the short summary.

func (*Publication) Thumbnail

func (p *Publication) Thumbnail(href, mediaType string) *Publication

Thumbnail adds a thumbnail image.

func (*Publication) UpdatedAt

func (p *Publication) UpdatedAt(t time.Time) *Publication

UpdatedAt sets the entry's last-modified time.

type SearchDescription

type SearchDescription struct {
	// ShortName is a brief name for the search engine (OpenSearch ShortName).
	ShortName string
	// Description is a human-readable description of the search.
	Description string
	// Template is the search URL template using RFC 6570 / OpenSearch syntax,
	// e.g. "/search?q={searchTerms}". If it contains no parameters the library
	// appends "?q={searchTerms}". Extra params such as {author} and {title}
	// are advertised when present.
	Template string
}

SearchDescription describes a catalog's search interface. It drives the OpenSearch description document (1.x) and the templated search link (2.0).

type SearchRequest

type SearchRequest struct {
	// Terms is the free-text query (the OpenSearch {searchTerms}).
	Terms string
	// Author optionally narrows the search by author.
	Author string
	// Title optionally narrows the search by title.
	Title string
	// Page is the requested 1-based page number (1 if unspecified).
	Page int
	// Version is the negotiated OPDS version of the response.
	Version Version
	// BaseURL is the absolute base URL of the catalog, if known.
	BaseURL string
	// Query holds the raw query parameters of the request.
	Query url.Values
}

SearchRequest carries the parameters of a search.

type Searcher

type Searcher interface {
	// Search returns a feed of results for the given request.
	Search(ctx context.Context, req SearchRequest) (*Feed, error)

	// SearchDescription returns metadata describing the search interface,
	// used to generate the OpenSearch document and the 2.0 search link.
	SearchDescription() SearchDescription
}

Searcher is an optional interface a Source may also implement to support search. When present, the HTTP layer advertises a search link and routes search requests to it.

type Series

type Series struct {
	// Name is the series title.
	Name string
	// Position is the publication's position in the series (0 if unknown).
	Position float64
}

Series places a publication within a sequence. It only appears in OPDS 2.0 output (belongsTo.series); the 1.2 Atom rendering drops it, so a catalog wanting series information visible to 1.x clients must fold it into another field (e.g. the Title or Summary).

type Source

type Source interface {
	// Root returns the catalog's root feed (usually a navigation feed).
	Root(ctx context.Context, req FeedRequest) (*Feed, error)

	// Feed returns the feed identified by req.ID. It should return ErrNotFound
	// if no such feed exists.
	Feed(ctx context.Context, req FeedRequest) (*Feed, error)

	// Publication returns the full entry for a single publication. It should
	// return ErrNotFound if no such publication exists. A Source that never
	// serves standalone publication documents may return ErrNotFound always.
	Publication(ctx context.Context, id string) (*Publication, error)
}

Source is the backend a catalog implements. It returns version-neutral Feed and Publication values; the library handles serialization to OPDS 1.2 or 2.0, content negotiation, and HTTP wiring.

Implementations are responsible for the URLs (hrefs) they place in feeds: the library does not rewrite them. Use Feed.Paged with a base href built from the opdshttp path helpers (FeedPath, FeedPagePath, SearchPagePath) to emit pagination links, or construct hrefs however suits the backend.

type Subject

type Subject struct {
	// Name is the human-readable label.
	Name string
	// Code is an optional controlled-vocabulary term (e.g. a BISAC code).
	Code string
	// Scheme optionally identifies the vocabulary the Code belongs to.
	Scheme string
}

Subject is a category, genre or keyword.

type Version

type Version int

Version identifies an OPDS wire format version.

const (
	// Version1 is OPDS 1.2 (Atom XML).
	Version1 Version = iota
	// Version2 is OPDS 2.0 (JSON).
	Version2
)

func (Version) String

func (v Version) String() string

Directories

Path Synopsis
examples
bookstore command
Command bookstore is a runnable example OPDS catalog backed by an in-memory list of books.
Command bookstore is a runnable example OPDS catalog backed by an in-memory list of books.
internal
wire
Package wire holds the JSON encodings shared by the server (opdshttp) and the client (opdsclient): the OPDS Authentication Document and the two progression document shapes.
Package wire holds the JSON encodings shared by the server (opdshttp) and the client (opdsclient): the OPDS Authentication Document and the two progression document shapes.
Package opds1 encodes the version-neutral opds model to OPDS 1.2 feeds and entry documents, serialized as Atom (XML) with the OPDS extension namespaces.
Package opds1 encodes the version-neutral opds model to OPDS 1.2 feeds and entry documents, serialized as Atom (XML) with the OPDS extension namespaces.
Package opds2 encodes the version-neutral opds model to OPDS 2.0, the JSON format built on the Readium Web Publication Manifest.
Package opds2 encodes the version-neutral opds model to OPDS 2.0, the JSON format built on the Readium Web Publication Manifest.
Package opdsclient consumes OPDS catalogs: it fetches and decodes feeds in either wire version, authenticates with them, and reads and writes per-user reading progression.
Package opdsclient consumes OPDS catalogs: it fetches and decodes feeds in either wire version, authenticates with them, and reads and writes per-user reading progression.
Package opdshttp provides an embeddable http.Handler that exposes an opds.Source as an OPDS catalog, handling routing, content negotiation between OPDS 1.2 and 2.0, pagination, search, and optional HTTP Basic authentication (see WithAuth).
Package opdshttp provides an embeddable http.Handler that exposes an opds.Source as an OPDS catalog, handling routing, content negotiation between OPDS 1.2 and 2.0, pagination, search, and optional HTTP Basic authentication (see WithAuth).
Package opensearch generates OpenSearch description documents, the mechanism OPDS 1.x catalogs use to advertise their search interface.
Package opensearch generates OpenSearch description documents, the mechanism OPDS 1.x catalogs use to advertise their search interface.
Package progstore provides a durable, file-backed reading-state store: an implementation of the opdshttp.ProgressionStore interface with companion storage for OPDS-PSE last-read pages, so one store persists everything a per-user catalog tracks about a publication (see opds.Progression and opds.PageStream).
Package progstore provides a durable, file-backed reading-state store: an implementation of the opdshttp.ProgressionStore interface with companion storage for OPDS-PSE last-read pages, so one store persists everything a per-user catalog tracks about a publication (see opds.Progression and opds.PageStream).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL