⚠️ Under Active Development — This project is a work in progress. Expect breaking changes, incomplete features, and bugs.
An open-source, community-driven media aggregator with a Netflix-style interface. Built with Tauri v2 + React + Vite, featuring a built-in web player with full playback controls.
- Horizon-inspired UI — Top navbar with tabs and scroll auto-hide, crossfade hero carousel, catalog hub with genre/sort filters (Discover), full-width search with history suggestions, redesigned cards with hover-play
- Built-in web player — HTML5 + hls.js playback (VixSrc) with a settings menu (quality, playback speed, audio, captions), server selection, ±10s skip, prev/next episode, and fullscreen. Optional switch to VidSrc / CineSrc embedded players.
- Playback controls — Play/Pause, seek, volume/mute, fullscreen, quality menu, subtitle menu
- VixSrc streaming — All video playback is resolved through VixSrc (vixsrc.to) and played in the built-in web player. No addons or debrid required.
- Music — Search songs, albums and artists (via Piped), with full artist album lists (years, newest first) and album tracks scraped directly from YouTube Music. Playback runs through the hidden YouTube IFrame Player with prev/next, seek and a persisted volume slider.
- Server-side TMDB proxy — Metadata (trending, popular, genres, search, details) is fetched through the Rust backend via Tauri IPC, with the API key kept out of the webview and 1-hour in-memory caching to cut API quota.
- Customizable — 12 accent colors, 6 theme skins, persisted via localStorage
- TV remote navigation — Spatial D-pad navigation (directional focus), visible focus rings, auto-focus and media-key support for Android TV / Firestick remotes
- Android — Builds as a single universal APK for phones, Android TV and Firestick
- Favourites — Save media locally with localStorage persistence
┌───────────────────────────────────────────────┐
│ Tauri v2 Desktop App │
│ ┌─────────────────────────────────────────┐ │
│ │ React SPA (Vite) — WebView │ │
│ │ Port 5173 (dev) / dist/ (production) │ │
│ │ │ │
│ │ ┌─── WebPlayer (hls.js) ─────────┐ │ │
│ │ │ Play/Pause · Seek · Volume │ │ │
│ │ │ Quality · Subtitles · Fullscreen│ │ │
│ │ └──────────────────────────────────┘ │ │
│ │ ▲ │ │
│ │ resolve_vixsrc (Rust backend) │ │
│ └─────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
- Frontend SPA (
src/) — Vite-powered React application with Tailwind CSS. Video plays in a<video>element via hls.js inside the WebView. - Web player —
WebPlayercomponent plays the VixSrc HLS master playlist directly in the browser (VixSrc serves CORS-enabled playlists/segments), avoiding server-side CDN throttling. - Streaming —
resolve_vixsrc(Rust backend) resolves a direct HLS master playlist through VixSrc's 3-step flow; the web player then handles playback.
| Layer | Technology |
|---|---|
| Shell | Tauri v2 (WebView2 on Windows, Android WebView) |
| UI framework | React 18 + Vite 5 |
| Styling | Tailwind CSS |
| Video engine | HTML5 + hls.js (in-webview) |
| Player | Custom WebPlayer React component (quality, subtitles, fullscreen) |
| Music search | Piped API (public instances with fallback + live list refresh) |
| Music playback | YouTube IFrame Player (hidden audio player) |
| Media metadata | TMDB API (server-side proxy via Rust backend, in-memory cache) |
| Streaming | VixSrc (vixsrc.to) — direct HLS resolved by the Rust backend |
- Node.js 18+
- Rust (latest stable)
- npm
- For Android builds: JDK 17 + Android SDK/NDK (see "Build for Android")
git clone https://github.com/Wookiee-/tagflix.git
cd tagflix
npm installnpm run tauri devThis starts both the Vite dev server and the Tauri desktop app with hot-reload.
npm run tauri buildOutput will be in src-tauri/target/release/.
Prerequisites: JDK 17, Android SDK (platform + build-tools + NDK), with
JAVA_HOME, ANDROID_HOME and ANDROID_SDK_ROOT set.
npx tauri android init # once — generates src-tauri/gen/android
npx tauri android build --apk --target aarch64 # signed release APKThe signed release APK is written to
src-tauri/gen/android/app/build/outputs/apk/universal/release/.
Debug APKs (auto-signed, for quick sideloading) are built with --debug.
Signing:
tauri android buildsigns with the keystore configured insrc-tauri/gen/android/keystore.properties(gitignored). Keep the keystore file safe — the same key is required for future updates to install smoothly.
- Search — Songs / Albums / Artists tabs, backed by Piped's
music_*search filters. The public instance list is refreshed dynamically from the Piped docs and tried in order until one responds. - Artists — The real album list (with years, newest first) is scraped from the artist's YouTube Music channel page — no more partial/incorrect results.
- Albums — Track lists come from the YouTube playlist page, or the YouTube Music innertube API for album ids.
- Playback — A hidden YouTube IFrame Player plays each track; the now-playing bar has play/pause, previous/next, seek and a volume slider (persisted).
Copy .env.example to .env and fill in your API keys:
cp .env.example .envEdit .env with your API keys:
# TMDB API — SERVER-SIDE ONLY. Never prefix with VITE_ (that would bundle the
# key into the frontend and expose it to clients). Read by the Rust backend.
TMDB_API_KEY=your_tmdb_api_key
# TMDB_ACCESS_TOKEN=your_tmdb_v4_read_access_token
VITE_TRAKT_CLIENT_ID=your_trakt_client_idAll TMDB requests are proxied through the Rust backend via Tauri IPC, so your key never reaches the webview. List responses (trending, popular, genres, discover) are cached in memory for 1 hour to cut API quota usage.
If no key is configured, the app automatically falls back to a set of public shared TMDB keys (with automatic rotation on 401/403) so it works out of the box — your own key always takes priority.
Playback uses a selectable player / source (Settings → Player):
- Web Player (VixSrc) — the built-in hls.js player. VixSrc HLS is resolved directly through the Rust backend and played in the browser with resolution/subtitle defaults, server selection (Server1/Server2), skip buttons, and a fullscreen settings menu.
- VidSrc (vidsrc.tw) — VidSrc's own embedded player in a full-screen iframe.
- CineSrc (cinesrc.st) — CineSrc's own embedded player in a full-screen iframe, honoring the default resolution setting.
No addons, API keys, or debrid services are required.
tagflix/
src-tauri/ Tauri v2 Rust backend
src/
lib.rs Main library with IPC commands
main.rs Entry point
tmdb.rs Server-side TMDB proxy with in-memory cache
vixsrc.rs VixSrc stream resolver (direct HLS)
piped.rs Piped API client (music search, dynamic instances)
youtube.rs YouTube playlist / artist / album scraping
trakt.rs Trakt API integration
capabilities/
default.json Tauri permissions (window, fullscreen)
gen/android/ Generated Android project (gradle, AndroidManifest)
src/ React SPA frontend
components/
WebPlayer.jsx HTML5 + hls.js player (quality, subtitles, fullscreen)
MediaCard.jsx Media card with hover-play overlay
MediaGrid.jsx Grid layout for media items
SplashScreen.jsx Startup splash screen
ErrorBoundary.jsx React error boundary
hooks/
useTagflix.js Main app state hook
useTVNavigation.jsx Spatial D-pad navigation for TV remotes
pages/
Home.jsx Landing page with hero carousel + catalog rows
Detail.jsx Media detail page (Play resolves VixSrc stream)
Player.jsx Route wrapper for WebPlayer
Music.jsx Music search + player (YouTube IFrame Player)
Discover.jsx Catalog hub (type/sort/genre filters, infinite scroll)
Movies.jsx Movies browse page
TVShows.jsx TV shows browse page
Search.jsx Search with history suggestions
Favourites.jsx Saved favourites
Settings.jsx App settings
services/
api.js Tauri IPC bridge
tmdbApi.js TMDB client (via IPC proxy)
continueWatching.js Resume progress for the home "Continue Watching" row
public/ Static assets
| Key | Action |
|---|---|
| Space / K | Play/Pause |
| Arrow Left | Skip back 10 seconds |
| Arrow Right | Skip forward 10 seconds |
| Arrow Up | Volume up |
| Arrow Down | Volume down |
| M | Toggle mute |
| F | Toggle fullscreen |
This project is licensed under the GNU General Public License v3.0.