Repository navigation
Dashboard HTTP API
The web dashboard is a static page talking to a small JSON API
on the same port. The API can also be used directly — from a script, a
home-automation system or curl — for example to read the current HDR format
or to switch VS10 modes.
The API is an internal interface of the dashboard. It is stable in practice but not versioned; check this page after updates.
Base URL: http://<box-ip>:<port> (port 8099 by default). The server
listens on IPv6 and IPv4, e.g. http://[fd00::20]:8099.
Send the access token in the X-TinyPPI-Token header. Only the header
counts: a token query parameter is ignored since 2.15.1, because an
address ends up in histories and logs.
curl -H 'X-TinyPPI-Token: ABCD2345' http://192.168.1.20:8099/api/stateA browser cannot send a header with an EventSource or an <img>, so the
page trades the token for a read session cookie first (see
POST /api/session). The cookie counts as
the token for the read routes only. Scripts simply send the header on every
request.
| Route type | Token required |
|---|---|
GET /api/hello |
never |
Other GET routes ("readers") |
only with Require the token for reading too on, from a client beyond the home network (see Dashboard Security), or under an untrusted Host name (see Dashboard Security); the read session cookie counts as the token |
POST /api/session |
always (the header), but not Allow VS10 switching |
All other POST routes ("writers") |
always (the header — the cookie does not count), and Allow VS10 switching from the dashboard must be on |
Errors are JSON: {"error": "<message>"}.
| Status | Body | Meaning |
|---|---|---|
| 400 |
bad body length, bad JSON, unknown mode, command failed, playback failed, update failed, watched required, no title named
|
The request was not understood or the action failed. |
| 401 | token required |
Missing or wrong token (in the header). |
| 403 | control disabled |
A write with Allow VS10 switching off. |
| 403 | library disabled |
The film/series library is switched off (or control is off). |
| 404 |
no such route, no such artwork, no artwork, no such series, no such title
|
Unknown path, or nothing to return. |
| 429 |
too many wrong tokens (+ retry_ms, Retry-After) |
The address (beyond the home network: its IPv6 /64) is locked out for guessing. |
| 503 |
too many streams / shutting down (+ retry_ms) |
All six stream slots are taken, or Kodi is shutting down. |
| 503 | library unavailable |
The video database could not be read. |
Start-up information for the page. No token, no player state.
{
"name": "TinyPPI",
"version": "2.15.1",
"auth_read": false,
"control": true,
"library": true,
"series": true,
"interval_ms": 200,
"strings": { "...": "localized UI strings" }
}| Field | Meaning |
|---|---|
auth_read |
Whether this request would need the token to read (setting, client beyond the home network, or untrusted host). |
control |
Allow VS10 switching from the dashboard. |
library / series
|
Whether the film / series shelves are offered (each needs its setting and control), and with them a film's / an episode's or show's details (/api/details). The dashboard and the TinyPPI app hide a shelf that is off, and the overview's key to the details of a title on it. |
interval_ms |
How often a watched snapshot is rebuilt (200 ms). |
The current snapshot, once. Use it for polling when you don't want a stream.
While something plays the snapshot contains, among others:
| Key | Content |
|---|---|
seq |
Snapshot sequence number. |
playing, paused
|
Player state. |
title, filename
|
Title; the file name only when Show file name is on. |
hdr_type |
Source format: "" (SDR), hdr10, hdr10plus, hlg, dolbyvision. |
effective |
The type the overlay layout follows (differs during VS10 conversion). |
output_type |
The format actually being output. |
time, duration, finish
|
Position, duration, and the clock time it ends at (box's regional format). |
metrics |
Cache, frame rate and other figures for the metric tiles. |
groups |
The overlay's rows as cards: [{id, title, rows: [{id, label, value, detail}]}]. Card ids: video, processing, audio, system, hdr, dv, metadata. |
metadata |
The Dolby Vision metadata list (only for Dolby Vision, with Show the Dolby Vision metadata view on). |
vs10 |
{"options": [{"mode", "label"}], "output": "…"} — the switchable modes for this source and the current output. |
art |
Tags of the playing title's artwork (used in /api/art URLs). |
media |
year, genre (its first two), show, season, episode; and for the overview: tagline, plot (markup stripped, line breaks kept), ratings ([{source, rating, votes}], each out of ten, the known sources first), mpaa (the age rating as written), age and age_level (its mark, e.g. fsk12, and level, e.g. 12; empty where it has no mark), userrating (1–10, 0 for none), top250 (0 for none), genres (every one), directors (every one), writers (every one), cast (its first 20) with cast_more and roles (who each played, "" where the library names nobody; read off VideoPlayer.CastAndRole in Kodi's language), studios (the first), and item — which library title it is, `{"kind": "movie" |
controls |
What the remote can offer: audio tracks, subtitles, chapters, the audio and subtitle offsets (audio_delay, subtitle_delay, seconds; null where Kodi reports none) … |
session |
{seq, switches, warnings} — when seq changes, re-read /api/history. |
library |
The library version; when it changes, re-read the shelves. |
control |
Same as in /api/hello. |
streams_full |
Whether a stream request would currently be refused. |
With nothing playing: playing: false, empty groups/metadata, plus last
— the summary of the title that ended within the last ten minutes
(title, position, ago, switches, warnings, peak, events).
Example — read the source format with jq:
curl -s http://192.168.1.20:8099/api/state | jq -r '.hdr_type, .output_type'A Server-Sent Events stream of snapshots. One stream slot of six is held while connected.
| Event | Data |
|---|---|
state |
The first frame: a complete snapshot (as /api/state). |
delta |
Every following frame: only what changed (see below). |
bye |
{"retry_ms": 20000} — the server is stopping; wait before reconnecting. |
: ping (comment)
|
Heartbeat every 15 s on an idle stream. |
A delta frame has the shape:
{
"seq": 1234,
"set": { "time": "01:12:45", "metrics": { "...": "..." } },
"del": ["some_key"],
"groups": { "rows": [["<row id>", "<value>", "<detail>"]] },
"metadata": { "rows": [[17, "<value or cells>"]] }
}-
set— top-level keys whose value changed, sent whole. -
del— top-level keys that disappeared. -
groups/metadata— either{"rows": [...]}with only the changed rows (while the card/row layout is unchanged), or the complete new list when the layout changed.
Frames come at most five times a second, and only when something moved.
curl -N -H 'X-TinyPPI-Token: ABCD2345' http://192.168.1.20:8099/api/streamThe chart and event history of the current (or just finished) title:
{
"now": 4380.2, "step": 1.0,
"t": [0.0, 1.0, "..."], "max": [812.3, "..."], "avg": [96.1, "..."],
"events": [{"t": 1203.4, "pos": "00:20:03", "kind": "audio", "...": "..."}],
"seq": 7, "switches": 3
}t is seconds since playback started; max / avg are L1 peak and average in
nits (null outside Dolby Vision). Event kinds: vs10, mode, audio,
subtitle, fps, temperature, cpu.
The film library: {"movies": [...], "count": N, "tag": "…"}. Each film:
id, title, poster (tag), and when known year, duration (seconds),
watched, resume, rating, added, genres (up to four), and — once
Kodi has read the file — video (dv, hdr10plus, hdr10, hlg, sdr),
for Dolby Vision its dv_profile as Kodi's scan found it (5, 7 FEL,
7 MEL, 8.1, 8.4, …),
resolution (2160p, 1080p, 720p, sd) and audio, the first audio
track's codec family (truehd, eac3, ac3, dtshd_ma, dtshd_hra, dts,
flac, pcm, aac, opus, mp3, vorbis). Kodi's stream details carry no
Atmos or DTS:X flag, so the family is all a filter can go by. The response has
an ETag; send it back as If-None-Match to get a cheap 304 Not Modified.
The series library: {"shows": [...], "count": N, "tag": "…"}. Each show
carries its genres as a film does.
The episodes of one series: {"tvshowid", "title", "episodes": [...], "count", "tag"}.
404 no such series means the library changed; reload /api/series.
Films and episodes stopped part-way, newest first (up to 30), and with the
series shelf on, the next up row: the next unwatched episode (specials
left out) of up to 15 shows being watched, the show watched last first —
An episode in items carries its show's tvshowid, which is how the page
offers the way to that show —
{"items": [{"kind": "movie" | "episode", ...}], "next": [{"kind": "episode", "id", "title", "tvshowid", "show", "poster", "thumb", "season", "episode", "duration", "rating"}], "count", "tag"}. An episode already in items is not
repeated in next.
One title's details, read when it is opened: kind, id, title, the
ratings the snapshot's overview would show for the same title — the
library's, MDBList's over them and WhatsOn's Rotten Tomatoes ratings and
certificates (a film's answer is waited on for up to four seconds; one that
takes longer is there on the next read) — and where there are any mpaa with
its age mark and age_level (an episode's are its show's), userrating,
top250 and awards; and where the library has them plot, tagline,
premiered, year, duration, genres, directors, writers, studios,
countries, the cast (each entry has name, role and an optional
thumbnail; the whole cast in billing order, cast_more always false) and the file's video,
dv_profile, resolution and audio, and the tags of its pictures for
/api/art — poster and fanart for a film or a series, thumb for an
episode. Every title has watched (a series once every episode is) and
resume (seconds, 0 for none), which the page's keys are drawn from. An
episode adds show, tvshowid, season and episode; a series its number
of seasons. Each kind needs its shelf's setting and Allow VS10 switching
from the dashboard (403 library disabled); an unknown title is
404 no such title.
A cast member's optional thumbnail is an opaque portrait tag (16 hex
digits), not a source URL. It is present only when Kodi supplies a non-empty thumbnail path.
Request /api/art?kind=thumb&portrait=<thumbnail>&v=<thumbnail> to load it.
Clients should keep an initial or another placeholder if the field is absent
or the image request fails; older servers omit the field.
With a TMDb key set (TMDb API key), the details also carry what TMDb's
credits of the title say: the crew — Kodi's directors and writers
({name, role: "director"|"writer"}), then everyone else TMDb's credits name,
department by department, once each with all their jobs as role
("Producer, Screenplay", in Kodi's language where the add-on knows the
job) — a portrait for a member of the cast or crew Kodi has none for, and a member's
tmdb — their TMDb person id, for a link to
https://www.themoviedb.org/person/<tmdb> — where the credits name them
unambiguously. A film adds, where TMDb knows them, its budget and revenue
(whole US dollars) and cinema, the day it first ran in cinemas
(YYYY-MM-DD) in the country Kodi's language points to, else its earliest
cinema run anywhere, with cinema_country the country's code ("DE"); else
its primary release date, without a country. Older servers, or a title TMDb does not know, omit them.
Artwork: kind is poster, fanart or thumb. Without an id or portrait,
the playing title's artwork. With a v tag the answer is cacheable for a week
(immutable); the tag must be plain hex (as the snapshot and the lists give
it), anything else is ignored and the answer is not cached. Images are served
from Kodi's texture cache where possible.
For a cast portrait, use kind=thumb&portrait=<tag> with the optional v tag
set to the same value. The portrait tag comes from /api/details. The server
resolves only paths Kodi supplied while details were read; callers cannot
provide a file path or source URL. At most 4096 portrait paths are retained in
memory. An unknown or evicted tag, or an unreadable image, returns 404; reopen
the details to register its portraits again. Portraits use the same read
authorization and cache policy as other artwork.
Trades the token for the read session cookie the page's stream and pictures
use. Needs the token in the header and a JSON body ({}); works with
Allow VS10 switching off.
curl -i -X POST -H 'X-TinyPPI-Token: ABCD2345' -H 'Content-Type: application/json' \
-d '{}' http://192.168.1.20:8099/api/sessionThe answer is {"ok": true} with
Set-Cookie: tinyppi_session=…; Path=/api/; HttpOnly; SameSite=Strict. The
cookie is valid for the read routes only, ends with the browser session, and
ends for everyone when the dashboard's server restarts (a new token
included).
All take a JSON body (max 4 KB), need the token in the header and Allow VS10 switching.
curl -X POST -H 'X-TinyPPI-Token: ABCD2345' -H 'Content-Type: application/json' \
-d '{"mode": "hdr10"}' http://192.168.1.20:8099/api/modeAccepted modes are exactly the dashboard's buttons: original_sdr, hdr10,
dv, original_hdr, sdr8, original_dv. The call returns at once
({"ok": true, "mode": "hdr10"}); the switch runs in the background, and a
newer request replaces one still waiting. For sdr10 and original_hlg use
RunScript(script.tinyppi,run_mode,…) (see
Launching and Keymaps).
Body: {"action": "<action>", "value": <value>}.
| Action | Value | Effect |
|---|---|---|
playpause |
— | Toggle pause. |
stop |
— | Stop playback. |
seek |
seconds, −3600 … 3600 | Relative seek. |
seek_percent |
0 … 100 | Absolute seek (relative to the EPG event on live TV). |
volume_up / volume_down / mute
|
— | Sent as remote actions (CEC-friendly). |
volume |
0 … 100 | Absolute Kodi volume (kept for older clients). |
chapter_previous / chapter_next
|
— | Chapter skip; fails without at least two chapters. |
audio |
stream index 0 … 64 | Select an audio track. |
subtitle |
index 0 … 64, or −1 | Select and enable a subtitle; −1 turns subtitles off. |
subtitles |
true / false
|
Show or hide the selected subtitle. |
audio_delay |
seconds, −10 … 10 | Set the audio offset (rounded to Kodi's 25 ms steps). |
subtitle_delay |
seconds, −60 … 60 | Set the subtitle offset, in Kodi's 100 ms steps; each step shows Kodi's own slider on the TV for a moment (there is no JSON-RPC method for it). One request moves at most 60 s; requests run one after another. |
curl -X POST -H 'X-TinyPPI-Token: ABCD2345' -H 'Content-Type: application/json' \
-d '{"action": "seek", "value": 30}' http://192.168.1.20:8099/api/command{"movieid": 123} or {"episodeid": 456}; add "resume": false to start from
the beginning (anything else resumes).
{"movieid": 123, "watched": true} — or tvshowid (every episode) or
episodeid. watched must be a boolean.
{"movieid": 123} or {"episodeid": 456}.
/, /index.html, /metadata, /metadata.html, /manifest.webmanifest,
/icon.png, /fanart.png, and the page's own css/, js/, icons/ and
ratings/ (the rating marks) files. They are served gzip-compressed (when the browser accepts it) with an
ETag, so a revisit is answered with 304.
User guide
- Installation
- Getting Started
- Launching and Keymaps
- The TinyPPI Overlay
- Dolby Vision Metadata View
- VS10 Output Modes
- Codec Logos
- Channel Layout Graphic
- IMAX Detection
- Colors and Themes
Web dashboard
Reference
Developers
Project