MCPcopy Index your code
hub / github.com/dubyte/dir2opds

github.com/dubyte/dir2opds @v1.9.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.9.0 ↗ · + Follow
113 symbols 298 edges 16 files 9 documented · 8%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

dir2opds

Go Reference License: GPL v3 Releases

Serve an OPDS 1.1–compliant book catalog from a directory. No database, no Calibre—just point dir2opds at a folder and use any OPDS client to browse and download your books.


Table of contents


What is OPDS?

OPDS (Open Publication Distribution System) is a standard for cataloging and distributing digital publications. OPDS clients (ebook readers, apps) can discover, browse, and download books from an OPDS server. dir2opds turns a plain directory tree into such a server.

Features

  • OPDS 1.1 compliant — Works with standard ebook readers and OPDS clients
  • No database — Reads directly from your filesystem; no Calibre or extra setup
  • Flexible layout — Organize by folders; optional metadata from EPUB/PDF
  • Search — Optional filename search (OpenSearch)
  • Covers — Optional cover.jpg / folder.jpg as catalog covers, or extract covers from EPUB files
  • Web-friendly — Optional HTML interface for browsing your collection via a web browser
  • Pagination — Configurable page size for large catalogs
  • Caching — ETag/Last-Modified for conditional requests, gzip compression
  • Health endpoint/health endpoint for monitoring and load balancers
  • Structured Logging — Uses log/slog for JSON (default) or text logging
  • Multiple formats — EPUB, PDF, MOBI, AZW3, and more via configurable MIME types
  • Lightweight — Single binary; suitable for headless servers and containers

Quick start

Using Docker (replace v1.9.0 with the latest release if desired):

docker run -d -p 8080:8080 -v ./books:/books --name dir2opds ghcr.io/dubyte/dir2opds:v1.9.0

Then open

Then open `http://localhost:8080` in an OPDS client or browser.

**Using Go:**

```bash
go install github.com/dubyte/dir2opds@latest
dir2opds -dir /path/to/books -port 8080

Tip: For best client compatibility, use folders that contain either only subfolders (navigation) or only book files (acquisition), not mixed.


Installation

Go install

go install github.com/dubyte/dir2opds@latest

For other installation methods (Docker, Podman, pre-built binaries, etc.), see INSTALLATION.md.


Usage

Default: serve ./books on http://0.0.0.0:8080.

dir2opds -dir /path/to/books -port 8080

Options

Flag Description
-calibre Hide files stored by Calibre
-debug Log requests
-dir Directory with books (default: ./books)
-enable-cache Enable ETag/Last-Modified headers for conditional requests (bandwidth optimization)
-enable-html Enable web-friendly HTML view for browsers
-extract-metadata Extract title/author from EPUB and PDF, and covers from EPUB
-gzip Enable gzip compression for responses (reduces bandwidth)
-hide-dot-files Hide files whose names start with a dot
-host Listen address (default: 0.0.0.0)
-log-format Log format: json (default), text
-mime-map Custom MIME types, e.g. .mobi:application/x-mobipocket-ebook,.azw3:application/vnd.amazon.ebook
-no-cache Add response headers to disable client caching
-no-pagination Disable pagination and show all entries in a single feed
-page-size Number of entries per page (default: 50, max: 200)
-port Listen port (default: 8080)
-search Enable basic filename search
-search Enable basic filename search
-show-covers Use cover.jpg or folder.jpg as catalog covers
-sort Sort entries: name, date, or size (default: name)
-url The base URL used for absolute links in the feed (e.g., https://opds.example.com)

Recommended Configuration

For the best experience, use these flags:

dir2opds -dir /path/to/books -extract-metadata -enable-cache -gzip -enable-html

This enables: - Metadata extraction — Shows book titles and authors instead of filenames, plus cover thumbnails - Caching — Reduces bandwidth with ETag/Last-Modified headers - Gzip compression — Further reduces bandwidth for large catalogs - Web-friendly UI — Provides a modern HTML interface when browsing via a web browser

For public servers, also set the base URL:

dir2opds -dir /path/to/books -extract-metadata -enable-cache -gzip -url https://opds.example.com

Caching

dir2opds provides two caching-related options with different use cases:

Default (no flags)

Clients use their default caching behavior. No special headers are sent.

-no-cache — Disable Caching

Forces clients to always fetch fresh data from the server. Useful for: - Frequently changing libraries (adding/removing books often) - Ensuring clients always see the latest catalog

dir2opds -dir /books -no-cache

This adds the following headers to every response:

Cache-Control: no-cache, no-store, must-revalidate
Expires: 0

-enable-cache — Enable Conditional Requests

Enables bandwidth optimization through HTTP conditional requests. Useful for: - Large static libraries that rarely change - Reducing bandwidth when clients re-fetch the same catalog - Mobile clients on metered connections

dir2opds -dir /books -enable-cache

This adds the following headers to responses:

ETag: "<hash>"
Last-Modified: <timestamp>

Clients can then send conditional requests:

If-None-Match: "<hash>"
If-Modified-Since: <timestamp>

If the catalog hasn't changed, the server responds with 304 Not Modified (no body), saving bandwidth.

Combining Flags

Using both -no-cache and -enable-cache is not recommended. -no-cache prevents clients from caching anything, so the 304 optimization from -enable-cache would never be used.


Pagination

For large libraries, dir2opds paginates catalog feeds to improve performance and reduce bandwidth.

How It Works

  • Feeds are split into pages with a configurable number of entries per page
  • Each page includes navigation links (first, previous, next, last)
  • Clients can request specific pages via the ?page=N query parameter

Configuration

# Default: 50 entries per page
dir2opds -dir /books

# Custom page size: 100 entries per page
dir2opds -dir /books -page-size 100

# Maximum page size: 200 entries
dir2opds -dir /books -page-size 200

# Disable pagination: show all entries
dir2opds -dir /books -no-pagination

Disabling Pagination

For small libraries or when using clients that work better with complete feeds, you can disable pagination entirely:

dir2opds -dir /books -no-pagination

When -no-pagination is set: - All entries are included in a single feed - No pagination navigation links are generated - The ?page=N query parameter is ignored - Recommended for libraries with fewer than a few hundred books

OPDS Feed Links

When pagination is active, feeds include navigation links:

<feed>
  <link rel="first" href="https://github.com/dubyte/dir2opds/raw/v1.9.0/?page=1" type="..."/>
  <link rel="previous" href="https://github.com/dubyte/dir2opds/raw/v1.9.0/?page=1" type="..."/>
  <link rel="next" href="https://github.com/dubyte/dir2opds/raw/v1.9.0/?page=3" type="..."/>
  <link rel="last" href="https://github.com/dubyte/dir2opds/raw/v1.9.0/?page=10" type="..."/>

</feed>

Client Usage

Clients can navigate pages directly:

GET /                    # Page 1 (default)
GET /?page=2             # Page 2
GET /mybook?page=1       # Page 1 of /mybook

Compatible clients

These OPDS clients have been tested with dir2opds:

Client Platform Notes
Moon+ Reader Android Tested
Cantook iPhone Tested
KYBook 3 iOS Enable Settings → Apps → KyBook 3 → Local Network. Older app may not show the prompt; enable manually.

Documentation


Contributing

Contributions are welcome. Please read CONTRIBUTING.md for license agreements, development setup, and pull request process.


License

This project is licensed under the GNU General Public License v3.0. See LICENSE for the full text.


Acknowledgments

  • @clach04 — Testing and reporting missing content type for comics.
  • @masked-owl — Reporting the HTTP path traversal security issue.
  • @mufeedali — Update to push image to ghcr.io.
  • @kulak — Add podman support.
  • @thenktor - init files and Makefile improvements.
  • @rockavoldy — For the docker command example.

Extension points exported contracts — how you extend this code

IsDirer (Interface)
(no doc)
internal/service/service.go

Core symbols most depended-on inside this repo

Handler
called by 13
internal/service/service.go
Build
called by 13
opds/text_builder.go
Rel
called by 12
opds/link_builder.go
Scan
called by 11
internal/service/service.go
Type
called by 11
opds/text_builder.go
AddLink
called by 11
opds/feed_builder.go
Href
called by 11
opds/link_builder.go
joinURL
called by 10
internal/service/service.go

Shape

Function 52
Method 46
Struct 9
TypeAlias 5
Interface 1

Languages

Go100%

Modules by API surface

internal/service/service.go33 symbols
internal/service/internal_test.go13 symbols
opds/entry_builder.go10 symbols
opds/feed_builder.go9 symbols
opds/link_builder.go8 symbols
opds/author_builder.go6 symbols
internal/service/gzip.go6 symbols
main.go5 symbols
internal/service/service_test.go5 symbols
internal/service/html.go5 symbols
opds/text_builder.go4 symbols
main_test.go4 symbols

For agents

$ claude mcp add dir2opds \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page