Estimate downloads
const url = 'https://example.com/api/v1/downloads/estimate';const options = { method: 'POST', headers: {cookie: 'kivo_session=<kivo_session>', 'Content-Type': 'application/json'}, body: '{"items":[{"workId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","versionId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","audioTrackId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","subtitleTrackId":"example"}],"capabilities":{"file":{"containers":["example"],"trackSelection":true},"video":[{"codec":"example","delivery":["file"],"containers":["example"],"tags":["example"],"profiles":["example"],"maxLevel":1,"maxBitDepth":1,"maxWidth":1,"maxHeight":1,"maxFrameRate":1,"maxBitrateBps":1,"maxRefFrames":1,"interlaced":true,"anamorphic":true,"hdr":["hdr10"],"dolbyVision":[{"profile":1,"compat":[1],"el":true}],"doviFallback":true}],"audio":[{"codec":"example","delivery":["file"],"containers":["example"],"maxChannels":1,"maxSampleRate":1,"maxBitDepth":1,"profiles":["example"]}],"subtitles":[{"codec":"example","delivery":["file"],"containers":["example"]}]},"settings":{"localQuality":{"mode":"auto","maxBitrateBps":1,"bandwidthBps":1},"remoteQuality":{"mode":"auto","maxBitrateBps":1,"bandwidthBps":1},"directPlay":true,"burnSubtitles":true}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/v1/downloads/estimate \ --header 'Content-Type: application/json' \ --cookie kivo_session=<kivo_session> \ --data '{ "items": [ { "workId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "versionId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "audioTrackId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "subtitleTrackId": "example" } ], "capabilities": { "file": { "containers": [ "example" ], "trackSelection": true }, "video": [ { "codec": "example", "delivery": [ "file" ], "containers": [ "example" ], "tags": [ "example" ], "profiles": [ "example" ], "maxLevel": 1, "maxBitDepth": 1, "maxWidth": 1, "maxHeight": 1, "maxFrameRate": 1, "maxBitrateBps": 1, "maxRefFrames": 1, "interlaced": true, "anamorphic": true, "hdr": [ "hdr10" ], "dolbyVision": [ { "profile": 1, "compat": [ 1 ], "el": true } ], "doviFallback": true } ], "audio": [ { "codec": "example", "delivery": [ "file" ], "containers": [ "example" ], "maxChannels": 1, "maxSampleRate": 1, "maxBitDepth": 1, "profiles": [ "example" ] } ], "subtitles": [ { "codec": "example", "delivery": [ "file" ], "containers": [ "example" ] } ] }, "settings": { "localQuality": { "mode": "auto", "maxBitrateBps": 1, "bandwidthBps": 1 }, "remoteQuality": { "mode": "auto", "maxBitrateBps": 1, "bandwidthBps": 1 }, "directPlay": true, "burnSubtitles": true } }'For each work, as POST /api/v1/downloads would decide it on a
app with these capabilities: the version, its files’ size, and
each quality’s delivery and size, before anything is asked for. A
video lists original, high and standard; a book only
original. An option whose asIs is true sends the original as it
is, and its size is exact; any other is converted, and its size is
an estimate from the quality’s cap, which a grainy title can
exceed. A work the user can’t see, or with
no file present, is playable: false with no reason, alike; one
the app can’t play, or a user without downloads, has its
reason. Answers in the order asked.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
A work to estimate, with the choices its download would be asked with.
object
A UUIDv7.
A UUIDv7.
A UUIDv7.
A subtitle track’s id, or off.
As for a download (DownloadCreate).
object
Files it plays whole, the original or a download. Absent, it plays none.
object
Containers it plays, such as mkv, mp4, webm, ts, m2ts, mov, m4a, mp3, flac, ogg.
It plays any audio track inside a file, not only the default one, so choosing another audio track keeps the file. Default false.
One kind of video the client decodes.
object
Such as h264, hevc, av1, vp9, mpeg2video, vc1.
Where it decodes it, file or stream.
For file, the containers it decodes this in; other containers don’t match the entry.
For file, the codec tags it takes, such as hvc1, hev1, dvh1, dvhe.
Profiles as ffprobe prints them, such as High, Main 10, Profile 2.
The highest level, as the codec’s specification writes it, such as 4.1 or 5.1.
The decoder’s limit in bits per second. A quality the user picks goes in settings instead.
Whether it plays interlaced video. Default true.
Whether it plays video with non-square pixels. Default true.
The HDR formats it shows, of hdr10, hdr10plus and hlg; SDR always plays. An HDR10+ video matches hdr10.
The Dolby Vision profiles it shows.
A Dolby Vision profile a client shows. A video matches when its
profile is listed, with its base layer’s compatibility ID among
compat when that’s given.
object
The profile, 5, 7, 8 or 10.
The base layer compatibility IDs it shows, such as 1, 2, 4.
Whether it plays a video with an enhancement layer. Absent means it does.
Play a Dolby Vision video whose profile isn’t listed by its base
layer, when that layer matches hdr: profile 8.1 and 7 are
HDR10, 8.4 is HLG, 8.2 is SDR, and 10 is what its compatibility
ID says. Default false.
One kind of audio the client plays.
object
Such as aac, ac3, eac3, truehd, dts, flac, opus, mp3, alac.
Where it plays it, file or stream.
For file, the containers it plays this in.
The most channels that reach the output: decoded on the device, no more than its speakers or its own setting; passed through, as many as the receiver takes. Audio with more channels is transcoded to AAC with as many as the client’s AAC entry allows, at most 6; to stereo it’s mixed keeping dialogue clear.
Profiles as ffprobe prints them, such as LC, HE-AAC, Dolby TrueHD + Dolby Atmos.
A subtitle codec a client shows, and how. The server converts a
text subtitle to a text codec listed for a delivery. In the stream
that’s always WebVTT, the only text HLS carries. As an external
file it’s WebVTT too, where webvtt is listed, converted from
SubRip, WebVTT, mov_text or ASS as plain text, with styling beyond
italics, bold and underline dropped.
An ASS track stays ASS for a client that lists ass, and an
embedded one is burned in for a client that doesn’t. Image
subtitles are never converted; a client that can’t show one has it
burned in.
object
Such as webvtt, subrip, ass, mov_text, hdmv_pgs_subtitle, dvd_subtitle.
file read from the played file, stream a WebVTT rendition of
the HLS stream, external its own file. In picture-in-picture
or on AirPlay a client leaves out external.
For file, the containers it reads this from; other containers don’t match the entry.
The client’s playback settings, kept on the client.
object
The client’s quality on the server’s local networks.
object
A quality pick: auto chosen from limits and measured bandwidth,
original with no cap, limit capped at maxBitrateBps.
For limit, from the fixed ladder.
For auto, the client’s last measured download rate from this server; the play stays below about 80% of it.
The client’s quality anywhere else, used instead of localQuality when the server counts the client as remote.
object
A quality pick: auto chosen from limits and measured bandwidth,
original with no cap, limit capped at maxBitrateBps.
For limit, from the fixed ladder.
For auto, the client’s last measured download rate from this server; the play stays below about 80% of it.
Play files directly. Default true.
Always burn in subtitles. Default false.
Responses
Section titled “Responses”Each work’s estimate.
object
One per work asked about, in order.
object
A UUIDv7.
The version the download would be, when playable.
Why it can’t be downloaded, when there’s one to give.
Each quality, as the download menu lists them.
object
A download’s quality. original is the file as it is, converted
only where the app can’t play it. high, for any screen, is
1080p with the video capped at 8 Mbps (about 3.6 GB an hour at
the cap, 1.1 GB on average), keeping 5.1 audio. standard, for a
phone, is 1080p capped at 3 Mbps (about 1.4 GB an hour at the cap,
0.65 GB on average), with stereo AAC at 128 kbps. A conversion
aims at the quality’s picture, so easy titles come out smaller,
and a grainy title can come out over the cap.
Exact when asIs, else an estimate from the quality’s cap, which a grainy title can exceed.
The original goes as it is.
What a download fetches: the original file, or a conversion, an
MP4 per part made for the download.
Example
{ "items": [ { "reason": "noTracks", "options": [ { "quality": "original", "delivery": "file" } ] } ]}The request is malformed or a field is invalid (code invalidRequest).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}No valid session or API key (code unauthenticated), or a failed sign-in (code invalidCredentials).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}default
Section titled “default”A server-side failure: 500 (code internal, or transcodeFailed
for a play’s media), or 503
for something unavailable (codes providerUnavailable and
transcodeBusy, for now; notConfigured, until an admin configures
it).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}