Change history
Every change to this page, newest first. Expand an entry to see what changed.
- 2 Oct 2026 · +55 −19 lines
Show changes
- // Wandura playback in an Android app (Kotlin 1.9+, kotlinx.coroutines, kotlinx.serialization). Reviewed by hand: - // the docs CI has no Kotlin compiler and only checks that brackets balance. - // Guide: https://docs.wandura.ai/developers/player-sdk/android - // - // The app never talks to Wandura and never holds an API key. It talks to YOUR backend, which applies your paywall - // and asks Wandura for tickets. Only `PlayerSdk` touches the Player SDK: fill it in with the calls named in the - // Player SDK guide (partner.wandura.ai → Developers → Player SDK). - package com.example.wandura + # Player SDK on Android - import kotlinx.coroutines.CoroutineScope - import kotlinx.coroutines.launch - import kotlinx.serialization.SerialName - import kotlinx.serialization.Serializable - import java.time.Instant + > **TL;DR** — Add `com.bytedanceapi:ttsdk-player_standard:1.52.100.5` with Gradle (API level 16+, Java 8, `armeabi-v7a`/`arm64-v8a`), put the licence file Wandura sends you in your assets, start the SDK, and read the two device values after the licence check passes. Your app sends them to **your** backend, gets a ticket back, and gives the ticket to the SDK. On an SDK error, ask for **one** fresh ticket and resume. - // #region sdk + ## Prerequisites {#prerequisites} + + | | | + |---|---| + | Licence | The `.lic` file and App ID from Wandura, bound to your **final** package name. See [Player SDK](index.md#licence). | + | Toolchain | Gradle, Android API level 16 or later, Java 8, ABIs `armeabi-v7a` or `arm64-v8a`. Permissions `INTERNET`, `WAKE_LOCK`, `ACCESS_NETWORK_STATE`, `ACCESS_WIFI_STATE`. | + | Devices | A real Android phone for encrypted playback. | + | Your backend | A ticket endpoint: [Playback flow and paywall](../guides/playback-flow.md). | + + The exact SDK calls (start-up, licence check, device values, playback, subtitles) are in the Player SDK guide on partner.wandura.ai → **Developers** → **Player SDK**. Wandura has verified this flow end to end on iOS; on Android it follows the vendor guide, so share your first test results in your channel. + + ## The flow in your app {#flow} + + 1. **Start the SDK** at launch with your App ID and licence file, and check the licence before anything else. + 2. **Read the device values** (device ID → `union_info`, device public key → `drm_kek`). Never send `null`, `"(null)"` or an empty string: throw instead. + 3. **Ask your backend** to play the episode. Your backend applies your paywall and asks Wandura; your app never holds an API key. + 4. **Play**: give `vid` and `play_auth_token` to the SDK; add the subtitles. + 5. **Preload** the next episodes with the prefetched tickets your backend returns. + 6. **On any SDK error** (an expired ticket included): request **one** fresh ticket and resume at the same position. Do not branch on SDK error codes, and do not loop. + 7. **On resume**: if the current ticket has less than a minute left, replace it before playing on. + + ## Code {#code} + + The file below is the app side of that flow, in Kotlin. Only `PlayerSdk` touches the SDK: fill it in with the calls from the portal guide. Your backend's response shape matches the [Node and Python backend examples](../guides/playback-flow.md#code). + + ### The SDK behind one interface {#code-sdk} + + ```kotlin data class DeviceValues(val unionInfo: String, val drmKek: String) ⋯ var onError: ((Throwable) -> Unit)? } - // #endregion sdk + ``` - // #region contract + ### Your backend's contract {#code-backend} + + ```kotlin @Serializable data class Subtitle(val lang: String, val url: String, val format: String) ⋯ suspend fun play(filmId: String, episodeNo: Int, device: DeviceValues): PlayResponse } - // #endregion contract + ``` - // #region controller + ### Start, prefetch, expiry, errors {#code-controller} + + ```kotlin sealed interface ScreenState { data object Loading : ScreenState ⋯ ) } - // #endregion controller + ``` + + ## Checklist {#checklist} + + - [ ] The licence check passes before device values are read. + - [ ] Device values are never `null`, `"(null)"` or empty when sent. + - [ ] The app holds no `wnd_live_` key (search the built APK/AAB). + - [ ] `play_auth_token` is kept in memory only: not in `SharedPreferences`, logs or crash reports. + - [ ] An SDK error leads to one fresh ticket and a resume, never a loop. + - [ ] Resuming after more than 29 minutes replaces the ticket first. + - [ ] An episode without subtitles plays without an error. + + ## Related {#related} + + - [Player SDK overview and licence](index.md) + - [Smooth playback under load](../guides/smooth-playback.md#in-the-app) + - [Subtitles](../guides/subtitles.md)
- 2 Oct 2026 · Page created