Chorus2 for Samsung Tizen TV
May 21, 2026 · View on GitHub
Wraps the Chorus2 Kodi web interface in a
Samsung Tizen .wgt and swaps the HTML5 <video> element for Tizen
AVPlay, so video playback gets hardware decoding (HEVC, AC3, etc.) on the
TV's native pipeline instead of being limited by the WebView.
The app talks JSON-RPC to a Kodi instance on your LAN and gives you a native-feeling TV client for browsing and controlling your library.
This is not a port of Kodi. It's a remote/streaming client that packages Chorus2 as a TV app. See What works below for the capability ceiling.
Screenshots
Install
-
Download the latest
.wgtfrom the Releases page. Each push to thetizenbranch produces a signed prerelease build. -
Enable Developer Mode on your Samsung TV (Samsung's instructions) and put your dev-machine's IP into the TV's "Host PC IP" field.
-
Sideload the
.wgt. Either:- via Tizen Studio's Device Manager → Permit to install → Drag-drop the file, or
- from the command line:
sdb connect <tv-ip> sdb install Chorus2-Tizen.wgt
-
Launch the app on your TV. On first run you'll see a setup screen.
-
Enter your Kodi server details — host/IP, port (default 8080), username, password — and press Connect. The app pings Kodi with the entered credentials and only proceeds if it gets a valid
pongresponse, so bad config is caught up-front rather than stalling the UI.
What works
- Browse your Kodi library — movies, TV shows, music, artists, albums
- Play direct files from the library (music + video) — video goes through Tizen AVPlay for hardware-accelerated decoding
- Remote-control a running Kodi instance (Chorus2's "Kodi" mode)
- Settings, search, now-playing
- TV remote:
- Arrow keys + OK navigate (manual spatial navigation — Tizen 5 doesn't ship one)
- Play / Pause / Track-prev / Track-next map to Chorus2's player controls
- Back exits the current view; from root, exits the app
- Authentication via HTTP Basic — credentials are stored in
localStorageand injected into XHR/fetch/WebSocket and the AVPlay streaming URL automatically - Cross-origin images — a Service Worker intercepts
<img>and CSSurl()loads to Kodi and adds the auth header, since those don't go through the XHR/fetch patches
What doesn't work
These are structural limits of Kodi's architecture or the JSON-RPC API — they aren't fixable from a remote client. Use a different solution if you need any of them:
- Kodi addons (
plugin://URIs) — resolved server-side inside Kodi's Python interpreter; no playable stream URL is ever exposed via JSON-RPC - InputStream Adaptive (DASH/HLS+DRM addons) — same reason
- PVR / Live TV backends
- DRM-protected content
- DLNA / casting to other devices
If you need addons or PVR, look at running Jellyfin + the jellyfin-tizen client instead.
Requirements
- Samsung TV with Tizen 4.0 or later (roughly 2018 and newer)
- A Kodi v17+ server reachable on the same network as the TV
- Kodi's web interface enabled, with:
- Allow control of Kodi via HTTP → on
- Allow remote control from applications on other systems → on
- HTTP Basic credentials configured (username + password)
Build from source
The whole build runs through tizen/build.sh. We do not rebuild
Chorus2 itself from CoffeeScript source — we patch the prebuilt dist/
that upstream ships, then run tizen build-web + tizen package.
git clone https://github.com/PatrickSt1991/chorus2-tizen.git
cd chorus2-tizen
# Smoke-test the prepare pipeline without installing Tizen Studio:
bash tizen/build.sh --dry-run
# Full build (needs Tizen Studio CLI on PATH or in $TIZEN_BIN):
TIZEN_BIN=~/tizen-studio/tools/ide/bin/tizen \
TIZEN_PROFILE=Chorus2 \
bash tizen/build.sh
# → release/Chorus2-Tizen.wgt
build.sh flags:
| Flag | What it does |
|---|---|
| (no flag) | Full pipeline: prepare + tizen build-web + tizen package |
--dry-run | Just the prepare steps (copy + sed-inject + icon resize). No Tizen CLI needed. |
--no-package | Prepare + tizen build-web, but skip the interactive tizen package. CI use. |
CI
.github/workflows/build-tizen.yml installs Tizen Studio 5.5 on
ubuntu-latest, creates a self-signed Chorus2 cert + security profile,
runs build.sh --no-package, drives tizen package through an
expect-script (the CLI prompts for cert passwords interactively), then
uploads the signed .wgt as both a workflow artifact and a GitHub
release. Every push to tizen produces a fresh build.
Debug
If something misbehaves on the TV, the app can stream every
console.log, error, click and XHR response to a dev-machine terminal
over WebSocket. The catch: there's no DevTools window on the TV, so this
is how we look behind the curtain.
1. Run the listener on a machine the TV can reach.
The script is at tools/debug-server.py in
this repo (and attached to every Release). Stdlib-only — no
pip install. From any shell:
python3 tools/debug-server.py # default port 9999
python3 tools/debug-server.py 9099 # custom port
python3 tools/debug-server.py 9999 -q # no colour, plain text
On Windows + WSL, prefer running it from PowerShell rather than WSL — PowerShell binds to the host IP the TV can see directly; WSL2 has its own internal network and needs port-forwarding. If Windows Firewall prompts you, allow the connection on Private networks.
2. Point the TV at it.
On the app's first-launch setup screen (or after a Reset), fill in the
Debug host field as <your-pc-ip>:9999 — e.g.
192.168.1.20:9999. Press Connect.
Leave the field blank to disable. The app makes one WebSocket connection on launch; if the listener isn't running there's no harm done (the app retries silently and otherwise behaves normally).
3. Watch the terminal. You'll see colour-coded events:
14:35:46 click DIV.mdi play "Play" @(852,144)
14:35:46 net.xhr POST .../Playlist.Insert -> 200
14:35:47 localplay.intercept {"kind":"playlist", "pid":1, "pos":1}
14:35:47 localplay.resolved {"file":"…jellyfin2samsung.mp4"}
14:35:47 localplay.navigate videoPlayer.html?src=…
14:35:48 [avplay] prepareAsync success READY
14:35:48 [avplay] play() called, state PLAYING
If you hit a bug, the exact event sequence makes it possible to reproduce locally and fix without a lot of guess-and-check.
How it works
The strategy is "patch upstream's prebuilt dist/" rather than
rebuilding Chorus2 from source:
chorus2-tizen/
├── dist/ # upstream Chorus2 — DO NOT EDIT
├── src/ # upstream Chorus2 source — DO NOT EDIT
├── tizen/
│ ├── wrapper/
│ │ ├── config.xml # Tizen app manifest
│ │ ├── icon.png # app icon (resized to 117×117 at build time)
│ │ └── videoPlayer.html # AVPlay-driven replacement for dist's
│ ├── extras/
│ │ ├── tizen-bootstrap.js # patches: config + URL/auth/WebSocket
│ │ ├── tizen-sw.js # image-auth Service Worker
│ │ ├── tizen.css # AVPlay surface + TV focus styles
│ │ └── avplayVideoPlayer.js # AVPlay reference (from jellyfin-tizen)
│ └── build.sh # prepare + tizen build-web + tizen package
└── .github/workflows/
└── build-tizen.yml # CI: produces a signed .wgt per push
At build time, build.sh copies dist/* into a build directory, layers
our wrapper and extras on top (overwriting config.xml, videoPlayer.html,
and the index entry point), sed-injects our bootstrap into <head>,
strips Chorus2's own <script> tag, then runs the Tizen CLI to web-build
and package.
tizen-bootstrap.js is the integration point. It:
- Shows a first-launch setup screen and pings Kodi with the entered creds before saving anything
- Patches
XMLHttpRequest,fetch, andWebSocketso Chorus2's relative URLs land on the configured Kodi host with HTTP Basic auth - Registers a Service Worker (
tizen-sw.js) that catches<img>and CSSurl()loads (which bypass XHR/fetch) and adds the auth header - Registers Tizen media keys + implements spatial navigation for the arrow keys (Tizen 5 doesn't provide one)
- Dynamically loads
js/kodi-webinterface.jsonly after config is verified
videoPlayer.html (our replacement for the upstream video.js one) drives
webapis.avplay directly: open → setListener → SET_MODE_4K →
prepareAsync → setDisplayMethod(LETTER_BOX) → play. Basic Auth is
embedded into the AVPlay URL as http://user:pass@host:port/... because
AVPlay's open() only takes a URL.
Branches
tizen— default branch. Everything in this README lives here.master— upstream Chorus2 tracking branch, kept clean so we can pull updates fromxbmc/chorus2cleanly.
Acknowledgments
- Chorus2 by Jeremy Graham and the Kodi contributors — the web interface this app wraps. GPL-2.0.
- jellyfin-tizen-avplay — the AVPlay shim was originally written there. GPL-2.0.
- The Tizen Web Application docs and AVPlay API reference at docs.tizen.org.
License
GPL-2.0, inherited from Chorus2. See LICENSE.