- Python 53.8%
- JavaScript 21.5%
- Shell 10.1%
- HTML 10.1%
- Dockerfile 4.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| browser-extension | ||
| static | ||
| templates | ||
| .dockerignore | ||
| .gitignore | ||
| 99-tv-remote.rules | ||
| app.py | ||
| compose.yml | ||
| deploy.sh | ||
| Dockerfile | ||
| firefox-policy.json | ||
| launcher.py | ||
| README.md | ||
| remote-hotkey.py | ||
| requirements.txt | ||
| run.py | ||
| start-firefox.sh | ||
| TV Hub Apps.desktop | ||
| TV Hub.desktop | ||
| tv-hub-remote.service | ||
| tv-hub.container | ||
DISCLAIMER
This project was AI assisted, if you don't like that, don't use it. The VTM GO, Goplay and VRT MAX websites are never altered, the extension making it controleable with a remote is running completely local. The included VTM GO, Goplay and VRT MAX tiles on the UI are merely links to the respective websites, nothing more. I am in no way affiliated with DPG Media, VRT or Play media.
TV Hub
A Python TV launcher with a containerized Flask UI, containerized official Jellyfin Media Player and Firefox ESR clients, and a standard-library host-side launcher bridge. Launches websites for respective services using Firefox ESR client with self written extension to activate the remote control friendly controls. Long press esc shows an 'Exit to TV Hub' overlay in the web app, clicking yes goes back to the TV Hub. This is a mapping based on my particular remote. The apps added are also based on my personal usage. I just put the source here so anyone can use this freely if they wish to break free of their TV (or android tv/apple tv) spyware.
Adding A Site
To add another site to the TV Hub UI, add an entry to the APPS list in app.py, including its ID, display name, description, type, and accent color. Add the matching site URL to the web-app URL map in launcher.py, using the same ID. The new entry will then appear in the UI and use the existing launcher endpoint.
To make the site controllable with the Firefox remote-navigation extension, add its HTTPS domain and any required subdomains to the matches list in browser-extension/manifest.json. The existing browser-extension/content.js provides the general keyboard and remote navigation behavior for matching sites. Only add site-specific code there if the site requires custom handling for its controls or navigation. Rebuild the image after changing the extension so the updated extension is packaged into Firefox. This should mostly just work, but no guarantees, only tested with the sites that are currently added in my specific build.
Run From The Registry
The published image is:
forgejo.vosjes.cloud/yarrith/tv-hub-podman:latest
On a logged-in Wayland desktop with Podman and /dev/dri available, run the UI container with:
podman run --rm --name tv-hub-ui \
--network host \
--shm-size 1g \
-e WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}" \
-e XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \
-e DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=/run/user/$(id -u)/bus}" \
-e QT_QPA_PLATFORM=wayland \
-e QTWEBENGINE_DISABLE_SANDBOX=1 \
-v "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}:${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \
-v tv-hub-jellyfin-config:/root/.config \
-v tv-hub-jellyfin-data:/root/.local/share \
--device /dev/dri:/dev/dri \
forgejo.vosjes.cloud/yarrith/tv-hub-podman:latest
This publishes the hub at http://127.0.0.1:8080. The named volumes hold Jellyfin settings and browser profiles; they are created on the machine and are not part of the image. For the complete desktop experience, including the fullscreen hub browser and the localhost launch bridge, use the checkout-based python3 run.py flow below.
Autostart With Quadlet
Install the rootless Quadlet unit and enable it for the logged-in desktop user:
podman pull forgejo.vosjes.cloud/yarrith/tv-hub-podman:latest
mkdir -p ~/.config/containers/systemd
cp tv-hub.container ~/.config/containers/systemd/tv-hub.container
systemctl --user daemon-reload
systemctl --user enable --now tv-hub.service
The unit starts the registry image with the current user's Wayland runtime, D-Bus session, GPU, shared memory, and named persistent volumes. The image provides its own launch and return-home endpoints, so this deployment does not need the checkout-based host launcher bridge. It is enabled only after graphical-session.target is active, so it cannot block graphical login. Image pulls are deliberately not performed by the login service; pull updates manually before restarting it. Check it with:
systemctl --user status tv-hub.service
To update the image:
podman pull forgejo.vosjes.cloud/yarrith/tv-hub-podman:latest
systemctl --user restart tv-hub.service
After the service is running, install TV Hub Apps.desktop into ~/.local/share/applications/ or copy it there with:
cp "TV Hub Apps.desktop" ~/.local/share/applications/
Launching TV Hub Apps opens Firefox in kiosk mode at http://127.0.0.1:8080.
Remote Reset-To-Home Hotkey
A long press of the remote's Delete key (on my particular remote, maps to the backspace keyboard key) resets to the TV Hub home screen from anywhere on the desktop, not just from inside a TV Hub window -- it closes whatever web app is currently open and launches the hub fullscreen if it wasn't already running. This works by reading the remote's raw input events directly, independent of window focus, since it needs to fire even when no TV Hub window has focus (or none is open at all).
remote-hotkey.py hardcodes the remote's USB device path (/dev/input/by-id/usb-XING_WEI_2.4G_USB_USB_Composite_Device-if02-event-kbd) and its Backspace scancode; a different remote model will need both re-checked (cat /proc/bus/input/devices for the path, evtest on its keyboard interface for the scancode).
Install:
sudo cp 99-tv-remote.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
mkdir -p ~/.config/systemd/user
cp tv-hub-remote.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now tv-hub-remote.service
The udev rule grants the logged-in seat user dynamic access to the remote's input device (uaccess), so no group membership change or re-login is needed. Check it with:
systemctl --user status tv-hub-remote.service