{"_id":"@aliran/react-native","_rev":"6-351b74503a4772a895cabea132089a19","name":"@aliran/react-native","dist-tags":{"latest":"0.1.5"},"versions":{"0.1.0":{"name":"@aliran/react-native","version":"0.1.0","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"license":"MIT","_id":"@aliran/react-native@0.1.0","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"dist":{"shasum":"d6f6541a89ec3da61fc4ba89e24d8bcf2f1eeb13","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.0.tgz","fileCount":8,"integrity":"sha512-zHyMRSWwNbuMG7/0gPL6hk5PDO/ZLEFVcM5MuylXWXDlYxJtpouKzdxilnA3niGvw4KJJ806KYuCypKWC7MLEQ==","signatures":[{"sig":"MEUCIFwba4s1JsP+FZbhNishY+0JhS02n7v+z1eReMkwwWgNAiEAi2iIcRzFmDCTVuY2ClW6in8INqARsxTXIZcW6sDL4pI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48134},"main":"src/index.ts","types":"src/index.ts","comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"140e299e79f56c6f011a14f56cdc4cd0ccfc5d04","_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"repository":{"url":"git+https://github.com/AbueloSimpson/aliran.git","type":"git","directory":"sdk/react-native"},"_npmVersion":"11.13.0","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-video":"^6","react-native-bare-kit":">=0.13.3"},"_npmOperationalInternal":{"tmp":"tmp/react-native_0.1.0_1784688091060_0.6779043319467957","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aliran/react-native","version":"0.1.1","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"license":"MIT","_id":"@aliran/react-native@0.1.1","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"dist":{"shasum":"eee67371b7bd52d63b39b8a4883471d2959d35ea","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.1.tgz","fileCount":8,"integrity":"sha512-X1IpfaSXviSa2fWBXTLj2V77+Tmi+Ol7iGaGqjqtHsVHThTAkrglvH5s3V9ev/3knQOACqC6wW52GqVxlydeBQ==","signatures":[{"sig":"MEQCICPiCnJv4aY4sp4MgpZLaIceppLZU0pfPiVuZFkBZ7pvAiAm8LeF0v7bJugWWhjuIC3VeZhQP1dctw5QZJrP9qTy5g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48718},"main":"src/index.ts","types":"src/index.ts","comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"3c15b9b3929a044e97082e43e62b148c7c05e3e9","_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"repository":{"url":"git+https://github.com/AbueloSimpson/aliran.git","type":"git","directory":"sdk/react-native"},"_npmVersion":"11.13.0","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-video":"^6","react-native-bare-kit":">=0.13.3"},"_npmOperationalInternal":{"tmp":"tmp/react-native_0.1.1_1784695444217_0.2376313952775122","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aliran/react-native","version":"0.1.2","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"license":"MIT","_id":"@aliran/react-native@0.1.2","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"dist":{"shasum":"4fcb40c59cd72e05f18c314f1061f941ff97f564","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.2.tgz","fileCount":8,"integrity":"sha512-2aBzdm5h4DyHw9Oi3KuSi9R5ueSVBH+C+nRU2HGEU/WwwKPS9CuGmp7GHbOU26sne3Uid3N2mnrQKeO9vNxt9A==","signatures":[{"sig":"MEUCIAYjDyXPpTD8/bSnYIE9NUM8V9y8sURIQzIqXaJGHfx/AiEAyPEIUXpbl9cRpT6voUAMMjICEA6XUsTJde3ieD9nZT0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58661},"main":"src/index.ts","types":"src/index.ts","comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"64ca86553f7bfe677867c78db1f12df00d7ee3d5","_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"repository":{"url":"git+https://github.com/AbueloSimpson/aliran.git","type":"git","directory":"sdk/react-native"},"_npmVersion":"11.13.0","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-video":"^6","react-native-bare-kit":">=0.13.3"},"_npmOperationalInternal":{"tmp":"tmp/react-native_0.1.2_1784778791526_0.1579079404818835","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aliran/react-native","version":"0.1.3","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"license":"MIT","_id":"@aliran/react-native@0.1.3","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"dist":{"shasum":"ba0c29ab397b223750bcbf35fcb344a5ec9ae0db","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.3.tgz","fileCount":8,"integrity":"sha512-4g9mGWazZf9L9Njc2M7jaI6NjTZceQSMUDn54yDwPgk0iIiji8pZiikQgrtdIjqMw+NwxV2DtkcaE2V5HNhWtw==","signatures":[{"sig":"MEUCIQDqYgo4HVsNGT+xksdCgAkPu1QQTQGeYZbAn7AEUIHQBgIgc4bgfTcRhALthm0EVDIfnjuaDhxIWj/UDTsIh5eeoXk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":59342},"main":"src/index.ts","types":"src/index.ts","comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"8e954fd360df3b9675b344f6861b6e69d7da29ba","_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"repository":{"url":"git+https://github.com/AbueloSimpson/aliran.git","type":"git","directory":"sdk/react-native"},"_npmVersion":"11.13.0","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-video":"^6","react-native-bare-kit":">=0.13.3"},"_npmOperationalInternal":{"tmp":"tmp/react-native_0.1.3_1784782685100_0.2153153209131351","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@aliran/react-native","version":"0.1.4","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"license":"MIT","_id":"@aliran/react-native@0.1.4","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"dist":{"shasum":"c337ef9f0b6bbab3a3cfce9acf2126b05385ed3a","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.4.tgz","fileCount":9,"integrity":"sha512-pZWbe+zuzWSuU3xxOjCRHhDO3xL7GSXSjTM8V7Q5ntc2pOQXbScRhwcGHTmIy+y7ZwxNwNOl073cpsVBJEdXTQ==","signatures":[{"sig":"MEUCIGtXF5uewBmysk2oyq1oW+KxfrbVxonyvCMLdrlCwFx5AiEAhonTNW3ss2uMZwfCDlZyCSNaVke1KR5e42n5QFOB/Ao=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63662},"main":"src/index.ts","types":"src/index.ts","comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"dc9b0c27fb8a25269513299c4ef3281a0fdd23b4","_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"repository":{"url":"git+https://github.com/AbueloSimpson/aliran.git","type":"git","directory":"sdk/react-native"},"_npmVersion":"11.13.0","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-video":"^6","react-native-bare-kit":">=0.13.3"},"_npmOperationalInternal":{"tmp":"tmp/react-native_0.1.4_1784785449679_0.9454238705321822","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@aliran/react-native","version":"0.1.5","description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","license":"MIT","main":"src/index.ts","types":"src/index.ts","repository":{"type":"git","url":"git+https://github.com/AbueloSimpson/aliran.git","directory":"sdk/react-native"},"homepage":"https://abuelosimpson.github.io/aliran/sdk/","bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"publishConfig":{"access":"public"},"peerDependencies":{"b4a":"^1.6.6","react":">=18","react-native":"*","react-native-bare-kit":">=0.13.3","react-native-video":"^6"},"comment":"Ships TypeScript source — Metro consumes it directly (RN CLI's default sourceExts include ts/tsx). No native code of its own: react-native-bare-kit provides the worklet runtime and react-native-video the player. The host app supplies the worklet bundle (bare-pack output; see client/backend). The react-native peer stays '*' on purpose: TV apps install it as an npm alias of react-native-tvos, whose prerelease versions fail any strict semver range (ERESOLVE) — with those, install with --legacy-peer-deps.","gitHead":"faa056315a0f4425eb7952991512c17511f3224f","_id":"@aliran/react-native@0.1.5","_nodeVersion":"24.17.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-5WHEGZ3T4G/oflcVU5ts9XWISmAITIVev+NBPq1kKuCQoktd6hlNF+whQ12SPPL+T43bU7aJpOWN1is58ucs5w==","shasum":"199614d6eb7769c61751d4868e0fcbd110a3a925","tarball":"https://registry.npmjs.org/@aliran/react-native/-/react-native-0.1.5.tgz","fileCount":10,"unpackedSize":78924,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDHlgjyzH3fw3Uc7kNsEOJFk60oNcPFRBKkRcDO0MAYnwIgRKXv4OSmfC5D+nZkuvJj3eM+i3CV/R76bv9nnnqlz4Q="}]},"_npmUser":{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"},"directories":{},"maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native_0.1.5_1785575212918_0.9401213296587256"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-22T02:41:30.886Z","modified":"2026-08-01T09:06:53.262Z","0.1.0":"2026-07-22T02:41:31.248Z","0.1.1":"2026-07-22T04:44:04.363Z","0.1.2":"2026-07-23T03:53:11.669Z","0.1.3":"2026-07-23T04:58:05.242Z","0.1.4":"2026-07-23T05:44:09.826Z","0.1.5":"2026-08-01T09:06:53.071Z"},"bugs":{"url":"https://github.com/AbueloSimpson/aliran/issues"},"license":"MIT","homepage":"https://abuelosimpson.github.io/aliran/sdk/","keywords":["react-native","android-tv","p2p","streaming","ott","hls","holepunch","aliran"],"repository":{"type":"git","url":"git+https://github.com/AbueloSimpson/aliran.git","directory":"sdk/react-native"},"description":"Aliran React Native binding — Bare worklet host + drop-in <AliranVideo> on react-native-video","maintainers":[{"name":"abuelosimpson","email":"abuelo.simpson@nym.hush.com"}],"readme":"# @aliran/react-native\r\n\r\nDrop-in React Native binding for the Aliran P2P player: hosts the engine in a Bare\r\nworklet (`react-native-bare-kit`) and renders live HLS with `react-native-video` —\r\nP2P channels and **redirect channels** (catalog entries that play an operator's\r\nCDN/HLS URL directly) both flow through the same `<AliranVideo>` surface.\r\n\r\n```tsx\r\nimport { AliranBackend, AliranVideo } from '@aliran/react-native'\r\nimport bundleBase64 from './backend/app.bundle.js' // your bare-pack'd engine bundle\r\n\r\nconst backend = new AliranBackend()\r\nbackend.start(bundleBase64, {\r\n  panelPubKey: SERVICE.panelPubKey,\r\n  prewarm: 12,       // warm the first N channels' feeds at login (fast first zap)\r\n  zapPrefetch: false // keep CH+/CH- neighbors' newest segment warm while playing —\r\n                     // OFF by default: costs standing bandwidth (see sdk/README.md)\r\n  // swarm: { maxPeers: 256 } — seed-node hosts only; viewers keep the default (64)\r\n  // uploadPolicy: 'client-only' — never announce on feed topics: ~zero\r\n  //                viewer-to-viewer upload (default 'reseed' serves blocks back)\r\n})\r\n\r\n// \"Smooth zapping\" is a runtime, user-facing choice — wire it to a Settings switch\r\n// and feed the network profile so the engine can suspend it on metered connections:\r\nbackend.setZapPrefetch(true)             // ON mid-play (echoed as {type:'zap-prefetch'})\r\nbackend.setNetworkProfile(expensive)     // from NetInfo state.details.isConnectionExpensive\r\n// The engine also auto-suspends while the ACTIVE stream stalls or the pipe shows no\r\n// headroom, and resumes by itself — listen for {type:'zap-prefetch', state, reason}.\r\n// after backend.login(user, pass) resolves entitlements ('streams' message):\r\n<AliranVideo\r\n  backend={backend}\r\n  streamId=\"news\"\r\n  onPeers={(n) => setPeers(n)}\r\n  onTune={(e) => setTuning(e.phase === 'playing' ? null : e)} // drive your tuning UI from this\r\n  onError={setError}\r\n/>\r\n```\r\n\r\n- **`AliranBackend`** — boots the worklet from a\r\n  [bare-pack](https://abuelosimpson.github.io/aliran/client-build/) bundle (base64 or bytes) and speaks the engine's IPC protocol: `login()`, `play()`,\r\n  `playRaw()`, `reconnect()` (tear down the active feed's swarm connections and dial\r\n  fresh — the wedged-transport escalation), `onMessage()`, with `streams` / `port` /\r\n  `url` / `source` cached for screens that mount after the one-shot replies.\r\n  **Runtime service descriptor** (keyless generic apps): omit `panelPubKey` from\r\n  `start()` to boot the worklet without connecting, read the persisted prefs, then\r\n  `connect(panelPubKey)` — with the engine already on a *different* panel this swaps\r\n  it wholesale (teardown + fresh engine), so wait for the new `{type:'ready'}` before\r\n  logging in. `saveService()` / `clearService()` persist the viewer-entered service\r\n  beside the saved credentials (mirrored on `backend.service`).\r\n- **`<AliranVideo>`** — chrome-free video surface: plays the ACTIVE source URL (a\r\n  localhost P2P URL, or a redirect channel's remote URL passed through verbatim),\r\n  auto-retries while the P2P live edge replicates, and remounts on `feed-changed`\r\n  (the broadcaster rotated the watched channel's feed — reload to flush the stale\r\n  playlist). (`onFallback`/`onSourceChanged` exist for the engine's internal hybrid\r\n  test mode — production never configures it.) It also\r\n  self-heals a **frozen live edge**: live HLS windows are short, so a network blip\r\n  longer than the window slides it past the playhead with *no* error event — once the\r\n  playhead sits still for `stallTimeoutMs` (default 12 s) while playing, the component\r\n  remounts onto a fresh playlist load at the live edge and fires `onStall`. If a\r\n  resync mount then fails to play within another window, the ladder escalates to\r\n  `backend.reconnect()` — the network flap left the engine's peer connection\r\n  transport-alive but replication-dead, and only a fresh dial (not a remount)\r\n  recovers that. Drive your **tuning indicator from `onTune`**, not from raw player\r\n  events: ONE localhost URL serves every P2P channel, so after a zap the *previous*\r\n  channel keeps playing (and emitting `onProgress`/`onBuffer`) under the same URL\r\n  until the engine flips the served feed. `onTune` reports each switch as a tune\r\n  (monotonic `id`): `start` (arm/reset the indicator), `retune` / `reconnect` (the\r\n  engine is self-healing — say \"reconnecting\", don't freeze a fake percentage),\r\n  `playing` (the FIRST real playback of *this* tune — dismiss; edge-proof against\r\n  mid-tune remounts and the old channel's events). The friendly tune-timeout arrives\r\n  via `onError` and ends the tune. Zap-tuned start buffer:\r\n  ExoPlayer begins playback at ~1 s buffered (vs its ~2.5 s default) — override via\r\n  the `bufferConfig` prop (merged over the defaults) if your feeds need more headroom.\r\n  Overlays (badges, peer counts, spinners) belong to\r\n  the host app via the callbacks — see `client/src/screens/LiveScreen.tsx` for a\r\n  complete example (the Aliran app dogfoods this package).\r\n\r\nRequirements: peers `react-native-bare-kit` (min SDK 29) and `react-native-video`;\r\nAndroid release builds need cleartext-to-loopback permitted for the local media\r\nserver (see the [client build guide](https://abuelosimpson.github.io/aliran/client-build/)).\r\n\r\n**Older Android (below 10 / API 29):** the engine's native runtime cannot load\r\nthere — that floor is a libc symbol dependency, not a pin. The SDK itself still\r\nworks below it, and **one APK can cover Android 7 → current**: apply the\r\nbare-kit lazy-load patch (ships in the reference app,\r\n`client/patches/react-native-bare-kit+0.13.3.patch` — turns the link-time\r\n`libbare-kit.so` dependency into an API-29-gated `dlopen`), set `minSdk 24`,\r\nand gate on `AliranBackend.isSupported()`: `true` on Android 10+ (full P2P),\r\n`false` below — where the backend stays **silently inactive** (`start()` and\r\nevery method are safe no-ops; no message ever fires). Below Android 10 no P2P\r\ndata is reachable at all, so in that branch your app offers its own method —\r\nand the SDK ships the screen for it, **`<EngineNotice>`**:\r\n\r\n```tsx\r\nimport React, { useState } from 'react'\r\nimport { AliranBackend, EngineNotice } from '@aliran/react-native'\r\nimport Video from 'react-native-video'\r\n\r\nexport default function App () {\r\n  const [fallback, setFallback] = useState(false)\r\n\r\n  if (!AliranBackend.isSupported()) {\r\n    // Engine can't run here (Android 7-9). Offer your own delivery instead —\r\n    // e.g. plain HLS from your CDN via ExoPlayer; the SDK never provides content.\r\n    if (fallback) {\r\n      return <Video source={{ uri: 'https://cdn.example.com/live/main.m3u8' }} style={{ flex: 1 }} />\r\n    }\r\n    return (\r\n      <EngineNotice\r\n        title=\"Acme TV\"\r\n        colors={{ background: '#0B1220', accent: '#0EA5E9' }}   // your brand\r\n        actionLabel=\"Watch over the internet\"                    // omit both to\r\n        onAction={() => setFallback(true)}                       // hide the button\r\n      />\r\n    )\r\n  }\r\n\r\n  return <YourNormalP2PApp />  // backend.start(...) etc — full engine (Android 10+)\r\n}\r\n```\r\n\r\nThe action button is D-pad focusable for TV. This exact pattern — notice →\r\nbutton → plain-HLS fallback — is verified on a Fire OS 7 stick (Android 9).\r\nRecipe + details in the\r\n[SDK guide](https://abuelosimpson.github.io/aliran/sdk-guide/).\r\nShips TypeScript source (Metro consumes it\r\ndirectly); if the package lives outside your app root (monorepo / `file:` dep), add\r\nits path to Metro `watchFolders` and map its peers in `tsconfig` paths — see\r\n`client/metro.config.js` + `client/tsconfig.json`.\r\n","readmeFilename":"README.md"}