Extend Pageturner
Pageturner is built on a fully extensible plugin architecture. Add your own metadata sources and download providers through simple JSON configurations — no code required.
Metadata Providers
Fetch book info — covers, descriptions, ratings, genres — and populate your home screen with discovery sections.
Download Sources
Search for audiobook files from any API — direct downloads, streaming links, or debrid-powered sources. Your configs, your sources.
Getting Started
Adding a new source to Pageturner takes just a few steps. Here's the quick version:
Write a JSON config
Define how Pageturner should call an API — the URL, response format, and field mappings. See the schemas below.
Import it into the app
Go to Settings → Metadata Providers or Settings → Download Sources and import via URL, paste JSON, or load a file.
Test your config
Use the built-in test button to verify your source works. Pageturner will show you latency, result counts, and any errors.
Metadata Providers
Metadata providers tell Pageturner how to fetch book information from external APIs. They power two things: search (finding book metadata by title/author) and discovery (homepage sections like trending, new releases, etc.).
Pageturner ships with a built-in provider, but you can add your own to pull metadata from any API that returns JSON.
Schema Overview
A metadata provider config is a JSON object with these top-level fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✓ | Unique identifier (lowercase, hyphens ok) |
name | string | ✓ | Display name shown in the app |
version | string | ✓ | Semantic version (e.g. "1.0.0") |
description | string | Brief description | |
icon | string | URL to an icon image | |
capabilities | object | ✓ | What this provider supports |
auth | object | Authentication config | |
search | object | * | Search endpoint config |
discover | object | * | Discovery sections config |
rateLimit | object | Rate limiting config |
* Required if the corresponding capability is enabled.
Capabilities
The capabilities object declares what your provider supports:
{ "capabilities": { "search": true, "discover": true } }
search— Provider can search for books by query. Requires asearchconfig.discover— Provider can serve discovery sections for the home screen. Requires adiscoverconfig.
Search Configuration
The search object defines how Pageturner calls the API and parses results:
"search": { "request": { "method": "GET", "url": "https://api.example.com/search?q={QUERY}", "headers": { "Accept": "application/json" }, "timeout": 15000 }, "response": { "type": "json", "resultsPath": "data.books", "mapping": { "title": "name", "author": "writer", "cover": "coverUrl" } } }
Response Field Mapping
Map fields from the API response to Pageturner's standard format. Use dot notation for nested fields (e.g. "data.author.name").
| Mapping Field | Description |
|---|---|
title ✓ | Book title |
subtitle | Subtitle |
author | Primary author name |
authors | Array of author names |
narrator | Primary narrator name |
narrators | Array of narrator names |
cover | Cover image URL |
description | Book description/synopsis |
isbn | ISBN |
asin | ASIN (Amazon/Audible ID) |
publisher | Publisher name |
publishedYear | Year published |
duration | Duration value |
durationUnit | "seconds" or "minutes" (default: seconds) |
language | Language |
genres | Array of genre names |
seriesName | Series name |
seriesPosition | Position in series |
rating | Average rating (0–5) |
ratingsCount | Number of ratings |
Discovery Configuration
Discovery sections appear on the home screen. Each section has its own request and optional response config:
"discover": { "sections": [ { "id": "trending", "title": "Trending", "icon": "flame", "request": { "method": "GET", "url": "https://api.example.com/trending" }, "response": { "type": "json", "resultsPath": "results", "mapping": { "title": "title", "author": "author", "cover": "cover" } } }, { "id": "new-releases", "title": "New Releases", "icon": "sparkles", "request": { ... } } ] }
If a discovery section omits its own response config, it inherits the mapping from the search config.
Template Variables
Use these placeholders in your request URLs and bodies — Pageturner substitutes them at runtime:
| Variable | Description | Example |
|---|---|---|
{QUERY} | User's search query | Project Hail Mary |
{TITLE} | Book title | Project Hail Mary |
{AUTHOR} | Author name | Andy Weir |
{ISBN} | ISBN if available | 978-0593135204 |
{ASIN} | Audible ASIN | B08G9PRS1K |
{USER_API_KEY} | User-provided API key | (from secure storage) |
{PAGE} | Current page number | 1 |
Authentication
If the API requires authentication, add an auth object:
"auth": { "type": "apiKey", // "none" | "apiKey" | "bearer" | "basic" "headerName": "X-API-Key", // header to send the key in "userProvided": true // user enters credentials in-app }
When userProvided is true, the app prompts the user to enter their API key (stored in secure device storage).
Complete Example
Here's a complete metadata provider config for a fictional audiobook API:
{ "id": "my-audiobook-api", "name": "My Audiobook API", "version": "1.0.0", "description": "Custom metadata from my audiobook database", "capabilities": { "search": true, "discover": true }, "auth": { "type": "apiKey", "headerName": "Authorization", "userProvided": true }, "search": { "request": { "method": "GET", "url": "https://api.example.com/audiobooks/search?q={QUERY}", "timeout": 15000 }, "response": { "type": "json", "resultsPath": "data.books", "mapping": { "title": "title", "author": "author.name", "narrator": "narrator.name", "cover": "images.cover", "description": "synopsis", "genres": "categories", "duration": "lengthMinutes", "durationUnit": "minutes", "rating": "averageRating", "seriesName": "series.name", "seriesPosition": "series.position" } } }, "discover": { "sections": [ { "id": "trending", "title": "Trending", "icon": "flame", "request": { "method": "GET", "url": "https://api.example.com/audiobooks/trending" } }, { "id": "new-releases", "title": "New Releases", "icon": "sparkles", "request": { "method": "GET", "url": "https://api.example.com/audiobooks/new" } } ] }, "rateLimit": { "requestsPerMinute": 60, "retryAfterMs": 1000 } }
Importing a Provider
Three ways to add a metadata provider to Pageturner:
- URL import — Host your JSON config at a public URL. In the app, go to Settings → Metadata Providers → Add → From URL.
- JSON paste — Copy the JSON and paste it directly in the app's import dialog.
- File import — Save a
.jsonfile to your device and import from Files.
Download Sources
Download sources (also called addons) tell Pageturner how to search for audiobook files from external APIs. When you browse a book and tap download, Pageturner queries all your enabled sources and shows the results.
Schema Overview
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✓ | Unique identifier (lowercase, hyphens ok) |
name | string | ✓ | Display name |
version | string | ✓ | Semantic version |
description | string | Brief description | |
icon | string | Icon URL | |
type | string | ✓ | "torrent", "directDownload", or "stream" |
legal | object | Legal status info | |
request | object | ✓ | HTTP request config |
auth | object | Authentication config | |
response | object | ✓ | Response parsing config |
matching | object | Result filtering config | |
rateLimit | object | Rate limiting config | |
pagination | object | Multi-page results config |
Source Types
"torrent"— Returns magnet links or hashes. Pageturner automatically routes these through your configured debrid service, which converts them into direct streams. Pageturner is not a torrent client — it relies on debrid services to resolve these into streamable links."directDownload"— Returns direct download URLs (HTTP links to audio files)."stream"— Returns streaming URLs that can be played directly.
Legal Info
"legal": { "type": "public-domain", // "public-domain" | "user-content" | "third-party" "disclaimer": "All content is in the public domain." }
Request Configuration
"request": { "method": "GET", // "GET" or "POST" "url": "https://api.example.com/search?q={TITLE}+{AUTHOR}", "headers": { "Content-Type": "application/json" }, "body": { // for POST requests "query": "{TITLE} {AUTHOR}", "limit": 50 }, "timeout": 30000 // ms, default 30000, max 120000 }
Response Parsing
Tell Pageturner how to extract results from the JSON response:
"response": { "type": "json", "resultsPath": "hits", // dot notation path to results array "mapping": { "title": "name", // required "url": "downloadUrl", // direct download link "magnetUrl": "magnet", // magnet link (routed via debrid) "infoHash": "hash", // hash (routed via debrid) "size": "fileSize", // file size in bytes "seeders": "seeders", // availability indicator "leechers": "leechers", // availability indicator "quality": "quality", // e.g. "320kbps" "format": "format", // e.g. "M4B", "MP3" "source": "uploader", // source/uploader name "date": "addedDate" // upload date } }
Result Matching
Pageturner can automatically filter results to find the best matches for the book you're looking for:
"matching": { "field": "title", // field to match against "threshold": 0.7, // 0.0 – 1.0, higher = stricter "algorithm": "fuzzy" // "fuzzy" | "contains" | "exact" }
"fuzzy"— Fuzzy string matching (recommended). Handles typos, word order differences."contains"— Result title must contain the search term."exact"— Exact match only.
Template Variables
| Variable | Description | Example |
|---|---|---|
{TITLE} | Audiobook title | Project Hail Mary |
{AUTHOR} | Author name | Andy Weir |
{NARRATOR} | Narrator name | Ray Porter |
{ASIN} | Audible ASIN | B08G9PRS1K |
{YEAR} | Release year | 2021 |
{SERIES} | Series name | The Bobiverse |
{SERIES_POSITION} | Book number in series | 1 |
{ISBN} | ISBN if available | 978-0593135204 |
{USER_API_KEY} | User-provided API key | (from secure storage) |
{PAGE} | Current page number | 1 |
Pagination
For APIs that return paginated results:
"pagination": { "type": "page", // "page" | "offset" | "cursor" "paramName": "page", // query parameter name "startAt": 1, // first page value (0 or 1) "maxPages": 3, // max pages to fetch (1–50) "hasMorePath": "meta.hasMore" // JSON path to check for more }
Built-in Sources
Pageturner ships with two built-in download sources for public domain content:
- LibriVox — Free public domain audiobooks read by volunteers. Streams directly.
- Internet Archive — Free audiobooks and audio from the Internet Archive. Streams directly.
These are always available and can be enabled/disabled in Settings → Download Sources.
Example Configs
Stream Source (Public API)
{ "id": "librivox", "name": "LibriVox", "version": "1.0.0", "description": "Free public domain audiobooks read by volunteers", "type": "stream", "legal": { "type": "public-domain", "disclaimer": "All LibriVox recordings are in the public domain." }, "request": { "method": "GET", "url": "https://librivox.org/api/feed/audiobooks?title={TITLE}&format=json", "timeout": 30000 }, "response": { "type": "json", "resultsPath": "books", "mapping": { "title": "title", "url": "url_zip_file", "source": "reader" } }, "matching": { "field": "title", "threshold": 0.6, "algorithm": "fuzzy" } }
Debrid Source (Authenticated API)
{ "id": "custom-indexer", "name": "Custom Indexer", "version": "1.0.0", "description": "My private indexer (uses debrid for streaming)", "type": "torrent", "legal": { "type": "third-party", "disclaimer": "User is responsible for content accessed through this source." }, "request": { "method": "POST", "url": "https://api.example.com/search", "headers": { "Content-Type": "application/json" }, "body": { "query": "{TITLE} {AUTHOR}", "limit": 50 }, "timeout": 30000 }, "auth": { "type": "apiKey", "headerName": "X-API-Key", "userProvided": true }, "response": { "type": "json", "resultsPath": "hits", "mapping": { "title": "name", "magnetUrl": "magnet", "infoHash": "hash", "size": "size", "seeders": "seeders", "leechers": "leechers", "date": "added" } }, "matching": { "field": "title", "threshold": 0.7, "algorithm": "fuzzy" }, "rateLimit": { "requestsPerMinute": 30, "retryAfterMs": 2000 }, "pagination": { "type": "page", "paramName": "page", "startAt": 1, "maxPages": 3 } }
Importing a Source
Same three methods as metadata providers:
- URL import — Host your JSON at a public URL. Settings → Download Sources → Add → From URL.
- JSON paste — Paste the JSON directly in the import dialog.
- File import — Load a
.jsonfile from your device.
Debrid Services
Debrid services act as a middleman — they take magnet links from your download sources and convert them into direct streaming URLs. Pageturner is not a torrent client and never downloads torrents directly. Instead, it passes magnet links to your debrid provider, which handles everything and returns a streamable link.
Supported Providers
- Real-Debrid — real-debrid.com
- AllDebrid — alldebrid.com
Setup
Get your API key
Sign up for a debrid service and generate an API key from their website.
Add it in Pageturner
Go to Settings → Debrid, select your provider, and paste your API key.
That's it
Magnet results from your download sources will now show a stream option. Pageturner sends them to your debrid provider and gets back a direct streaming link.