Modes
| Mode | When to use | How JAVP uses it |
|---|---|---|
| v1 — bulk dump | Small and medium libraries (rough guide: under ~5–10k titles) | One GET, then all items are imported into the on-device catalog |
| v2 — query API | Large libraries | Search, browse and page remotely; only what the user opens gets cached |
Both are implemented. Prefer v2 when a full dump would be huge. In-app search
filters locally synced items and, for v2 sources, also calls /search.
v1 — Bulk catalog
Request
GET {catalogUrl}
Accept: application/json
Response — object form (preferred)
{
"name": "My Home Library",
"version": 1,
"items": [
{
"id": "movie-42",
"title": "Big Buck Bunny",
"playUrl": "https://cdn.example.com/bbb.mp4",
"kind": "vod",
"thumbnailUrl": "https://cdn.example.com/bbb.jpg",
"group": "Open Movies",
"subtitle": "2008 · Blender Foundation",
"durationMs": 596000,
"audioLanguages": ["en"],
"subtitleLanguages": ["en", "fr"],
"subtitles": [
{
"url": "https://cdn.example.com/bbb.en.vtt",
"language": "en",
"label": "English",
"default": true
}
],
"segments": [
{ "type": "intro", "startMs": 0, "endMs": 90000 }
]
}
]
}
Response — bare array (also accepted)
[
{
"title": "Live News",
"url": "https://cdn.example.com/news.m3u8",
"kind": "live"
}
]
Aliases for the items list: items, entries or media.
Item fields
| Field | Required | Notes |
|---|---|---|
title | yes | Display name |
playUrl | yes* | Stream URL, local path or magnet:?…. *Not required for kind: "series" shells, or rows that only carry playVariants |
url / streamUrl | * | Aliases for playUrl |
id | no | Stable id — strongly recommended. Auto-generated when omitted, which breaks clean re-sync |
kind | no | vod (default), live, series, network, local, catchup |
thumbnailUrl | no | Poster or logo. Aliases: poster, logo |
group | no | Category / shelf. Alias: category |
subtitle | no | Secondary display line such as year or genre — not a caption track |
durationMs | no | Duration in milliseconds |
channelId / streamId / epgChannelId | no | IPTV-style ids |
catchupDays | no | Archive window for live and catch-up |
tmdbId | no | TMDB movie or TV id — enables skip-intro and enrichment |
anilistId | no | AniList media id (integer) — anime bridges and enrichment |
imdbId | no | IMDb id (tt…) for IntroDB / TheIntroDB |
tvdbId | no | TheTVDB id |
torrentFile / fileHint | no | Preferred file name, or a substring of it, inside a multi-file magnet |
poster / posterUrl | no | Portrait poster — preferred for VOD shelves |
backdrop / backdropUrl | no | Backdrop / hero art |
plot / description | no | Synopsis for the detail screen |
genres | no | Array of strings, or a comma-separated string |
rating | no | Numeric rating, e.g. 7.8 |
year | no | Release year |
releaseDate | no | ISO date string when known |
trailerUrl | no | Direct trailer URL, or a YouTube watch URL |
trailerKey / youtubeTrailer | no | YouTube video id |
cast | no | ["Name"], or [{ "name", "character", "profileUrl", "order" }] |
season / seasonNumber | no | Episode season |
episode / episodeNumber | no | Episode number |
seriesId / parentId | no | Links an episode row to a series shell id |
seasons | no | Nested season/episode tree on a series shell — see below |
audioLanguages | no | ["ja","en"] or "ja,en". Aliases: audio, audioLangs |
subtitleLanguages | no | Known subtitle languages. Aliases: subLanguages, subtitleLangs, subs |
subtitles | no | External subtitle files. Alias: externalSubtitles |
audioTracks | no | External audio files. Aliases: externalAudio, audioFiles |
httpHeaders / headers | no | Map of headers used when opening the stream |
segments | no | Skip windows: intro, credits and so on |
playVariants / variants | no | Quality or edition ladder, expanded into sibling rows |
contentRating / certification | no | e.g. PG-13, TV-MA |
studio / network | no | Studio or network label |
originalTitle | no | Original-language title |
tags | no | Freeform tags, array or CSV |
resolution | no | e.g. 1080p, 4K |
videoCodec / audioCodec | no | e.g. hevc, aac |
hdr | no | e.g. HDR10, DV |
updatedAt | no | ISO timestamp for the row |
Rows are skipped when they have no title, or no playable URL while
not being a series shell or a variant parent.
Series
The episode picker reads only one of these two things:
- Nested
seasons[].episodes[]on the series shell — preferred, or - Flat catalog rows carrying
seriesIdplusseasonNumber/episodeNumber
| Rule | Detail |
|---|---|
Shell playUrl | Optional. Never treated as "the only playable thing" |
Empty seasons | Empty episode UI, even when the shell has a magnet or playVariants |
Episode playUrl | Optional on stubs; fill it later via /items/{episodeId}, or include it on /items/{id} |
Shell playVariants | Show-level editions only — not the episode list |
Option A — nested seasons on a shell
Preferred for bring-your-own and anime bridges.
{
"id": "anilist-154587",
"title": "Frieren: Beyond Journey's End",
"kind": "series",
"anilistId": 154587,
"posterUrl": "https://cdn.example.com/show.jpg",
"plot": "…",
"seasons": [
{
"seasonNumber": 1,
"name": "Season 1",
"episodes": [
{
"id": "anilist-154587-s1e1",
"episodeNumber": 1,
"title": "The Journey's End",
"playUrl": "magnet:?xt=urn:btih:…"
}
]
}
]
}
Option B — flat episode rows
[
{ "id": "show-1", "title": "Example Show", "kind": "series", "posterUrl": "…" },
{
"id": "show-1-s1e1",
"title": "Pilot",
"playUrl": "https://cdn.example.com/s1e1.mp4",
"seriesId": "show-1",
"seasonNumber": 1,
"episodeNumber": 1
}
]
Batch and multi-file magnets
Setting the same batch magnet as playUrl on every
episode is valid. The torrent engine picks a file by, in order:
torrentFile/fileHinton the episode, else- Matching
episodeNumber— andseasonNumberwhen present — against file names such asS01E02,- 02 (orE02, else - The largest streamable file, as a legacy fallback
Play variants
"playVariants": [
{ "id": "hd", "label": "1080p", "playUrl": "https://…/1080.mp4", "resolution": "1080p" },
{ "id": "uhd", "label": "4K", "playUrl": "https://…/2160.mp4", "resolution": "4K", "hdr": "HDR10" }
]
| Where | Behaviour |
|---|---|
| VOD / movie row | Expanded into sibling rows, shown as Versions chips on the detail screen |
| Series shell | Show-level extras only; ignored by the episode picker |
| Nested or flat episode row | Per-episode quality ladder, expanded the same way as VOD |
External subtitles
"subtitles": [
{
"url": "https://cdn.example.com/en.vtt",
"language": "en",
"label": "English",
"default": true,
"format": "vtt"
},
{
"url": "https://cdn.example.com/en.forced.srt",
"language": "en",
"forced": true
},
{
"url": "https://cdn.example.com/en.sdh.vtt",
"language": "en",
"sdh": true
}
]
| Field | Required | Notes |
|---|---|---|
url | yes | Aliases: uri, src |
language | no | Alias: lang |
label | no | Aliases: title, name |
default | no | Auto-selected on play. Alias: isDefault |
forced | no | Alias: isForced |
hearingImpaired / sdh / cc | no | SDH / captions flag |
format | no | srt, vtt, ass |
External audio
"audioTracks": [
{ "url": "https://cdn.example.com/ja.mka", "language": "ja", "label": "Japanese", "default": true },
{ "url": "https://cdn.example.com/en.mka", "language": "en", "label": "English" }
]
HTTP headers
"httpHeaders": {
"User-Agent": "JAVP",
"Referer": "https://cdn.example.com/"
}
Passed to the player when it opens playUrl.
Skip segments
"segments": [
{ "type": "intro", "startMs": 90000, "endMs": 150000 },
{ "type": "credits", "startMs": 5400000 }
]
Types: intro / opening, recap,
credits / outro, preview.
start and end in seconds are also
accepted when the *Ms form is omitted.
Playback URLs
- HTTP(S) progressive, HLS or DASH — played directly
magnet:?…— resolved by the bring-your-own torrent engine into a localhost HTTP stream- File paths — treated as local media where applicable
v2 — Query API
Point Sources → JSON at the catalog root. JAVP detects
version: 2, or a non-empty capabilities array, and stops
expecting a full item dump.
GET /catalog — describe the catalog
Your source URL can be this document, or any URL that returns:
{
"name": "Huge Library",
"version": 2,
"capabilities": ["search", "browse", "groups"],
"itemCount": 128400
}
If that same response also includes items, they are imported as a warm cache.
GET /search?q=&page=&limit=
GET /search?q=bunny&page=1&limit=50
{
"query": "bunny",
"page": 1,
"limit": 50,
"total": 3,
"items": [ { "id": "…", "title": "…", "playUrl": "…", "kind": "vod" } ]
}
Wired to the in-app Search screen for v2 sources.
GET /browse?group=&page=&limit=
Lists a shelf or category without a text query.
GET /items/{id}
Fetches one title, including cast, trailer and nested seasons when available. Series shells may return either:
- Full
seasons[].episodes[]withplayUrls, or - Season or episode stubs without
playUrl, for progressive detail
Optional: GET /items/{id}/episodes?season=
A lazy episode list, so you do not have to resolve every magnet on first open.
GET /items/anilist-154587/episodes?season=1
{
"season": 1,
"episodes": [
{
"id": "anilist-154587-s1e1",
"episodeNumber": 1,
"title": "The Journey's End",
"playUrl": "magnet:?xt=urn:btih:…"
}
]
}
A top-level seasons array or a bare episode array are also accepted.
When episode stubs omit playUrl, JAVP calls
GET /items/{episodeId} on play.
Optional: GET /groups
{
"groups": [
{ "id": "movies", "name": "Movies", "count": 4200 },
{ "id": "kids", "name": "Kids", "count": 310 }
]
}
Client behaviour
- Source is added, JAVP detects
version: 2orcapabilities - No bulk
itemsdump is required - Search calls
/searchand caches the hits locally - Continue-watching and detail screens use cached rows, plus
/items/{id}when needed - Progressive series detail — first paint comes from
/items/{seriesId}and stubs are fine;playUrlandplayVariantsresolve on Play or Versions via/items/{episodeId} - Simulcasts and new episodes — shell episode lists are soft-revalidated on open after roughly 30 minutes, and on pull-to-refresh. New episode ids merge in, and already-resolved magnets are kept when the bridge still returns stubs for those ids
URL joining. When the source URL ends in /catalog or a
.json file, query paths are resolved from the parent
directory — so https://host/nyaa/catalog becomes
https://host/nyaa/search.
Tips for bridge authors
- Emit stable ids so a re-sync updates rows instead of duplicating them.
- Keep v1 payloads to a few MB where you can; past that, switch to v2.
- Use HTTPS. Cleartext HTTP still works on Android for IPTV-style setups, but it is discouraged.
- For series, emit nested
seasonsor flatseriesIdrows. A shell magnet on its own will not populate the episode UI. - Magnets in
playUrlare for legal bring-your-own torrents only. Batch magnets shared across episodes are supported, with per-episode file selection. - Put cast, trailer and seasons in the item — or in v2
/items/{id}— so the detail screen works without TMDB. - Prefer
anilistIdfor anime bridges;tags: ["mal:…"]stays optional. - For large series, use
/items/{id}/episodes?season=, or stub the episode ids and fill them in later.
One-click add (https://javp.app/add + javp://)
A website can deep-link into JAVP so a visitor adds a catalog, an M3U playlist or an Xtream login in one tap. The app always shows a confirmation dialog first.
Prefer HTTPS App Links for public “Add to JAVP” buttons. With the
app installed, Android opens JAVP directly. Without it, the browser loads
/add — install from the download page, then
open the same link again. Nothing is added automatically after
install (a Play Store listing URL cannot do that either without extra deferred-link
plumbing).
Keep javp://add?… for QR codes, TV pairing paste, and non-HTTPS contexts.
Custom JSON catalog
https://javp.app/add?type=custom&url=https%3A%2F%2Fexample.com%2Fcatalog.json&name=My%20Library
javp://add?type=custom&url=https%3A%2F%2Fexample.com%2Fcatalog.json&name=My%20Library
| Query | Required | Notes |
|---|---|---|
type | yes | custom, or the aliases json / catalog |
url | yes | HTTP(S) catalog URL. catalog and playlist also accepted |
name | no | Display name in Sources |
As an HTML link:
<a href="https://javp.app/add?type=custom&url=https%3A%2F%2Fexample.com%2Fcatalog.json&name=My%20Library">
Add to JAVP
</a>
M3U playlist
https://javp.app/add?type=m3u&url=https%3A%2F%2Fexample.com%2Flist.m3u&name=My%20IPTV&epg=https%3A%2F%2Fexample.com%2Fepg.xml
javp://add?type=m3u&url=https%3A%2F%2Fexample.com%2Flist.m3u&name=My%20IPTV&epg=https%3A%2F%2Fexample.com%2Fepg.xml
| Query | Required | Notes |
|---|---|---|
type | yes | m3u, or the aliases m3u8 / playlist |
url | yes | Playlist URL |
name | no | Display name |
epg | no | Optional EPG XML URL. epgUrl also accepted |
Xtream Codes
https://javp.app/add?type=xtream&url=http%3A%2F%2Fexample.com%3A8080&username=user&password=pass&name=My%20IPTV&alt=http%3A%2F%2Falt.example.com
javp://add?type=xtream&url=http%3A%2F%2Fexample.com%3A8080&username=user&password=pass&name=My%20IPTV&alt=http%3A%2F%2Falt.example.com
| Query | Required | Notes |
|---|---|---|
type | yes | xtream, or the aliases xc / xtream-codes |
url | yes | Server DNS. server, host and dns also accepted |
username | yes | Also user / login |
password | yes | Also pass / pwd |
name | no | Display name |
alt | no | Optional Samsung/LG DNS. alternate, altDns and dns2 also accepted |
The confirmation dialog shows the server and username only — the password is never displayed. Prefer HTTPS where the portal supports it, since credentials in a URL can end up in browser history and share sheets.
If the same type and URL — or the same Xtream server and username — is already present, JAVP offers a re-sync instead of duplicating the source. Jellyfin, Emby and Plex are not supported through deep links.
Phone deep link vs Android TV QR
HTTPS and javp:// share the same query string:
| Surface | How |
|---|---|
| Phone or tablet with JAVP | Tap https://javp.app/add?… (or javp:// / a QR), then confirm — the source syncs on that device |
| Android TV | The TV shows a LAN QR; on your phone browser, paste the same javp://add link or fill the form, and it posts to the TV |
So you can publish one "Add to JAVP" HTTPS link for phones, and tell TV users to open
Sources → Add with phone and paste the javp:// form of
the same query. There is no second parameter set to maintain.
Where it shows up in the app
| Surface | Behaviour |
|---|---|
| Sources → JSON | Add a catalog URL and sync, whether it is a v1 dump or a v2 descriptor |
https://javp.app/add?… / javp://add?… | Confirm, then add and sync a custom, M3U or Xtream source |
| TV pairing form | Paste javp://add, or fill the fields manually |
| Home and Search | Local filtering, plus remote /search for v2 |
| Title detail | Cast, trailer, audio and subtitle tracks, versions, technical tags |
| Player | HTTP headers, external audio and subtitles, skip segments |
| Library → magnet | A separate bring-your-own torrent entry, not a remote catalog |
An XML flavour using the same field names may come later. For now JSON is the supported format.
Building a bridge and something here does not match what the app does? Email [email protected].