Architecture
MovieBox-Tui is a terminal client (ratatui + crossterm + tokio) for streaming movies, series and TV channels from multiple providers. This document describes the shape of the code and how data flows through it.
Module map
src/
main.rs entry point: logging init, panic hook, raw mode,
alternate screen, App::new + App::run
lib.rs crate root, module declarations
cache.rs disk cache: provider-namespaced, TTL'd, atomic writes
config.rs Config load/save (config.json)
download.rs download engine (resume, ranges, segments, retry)
favorites.rs starred-titles persistence (favorites.json)
history.rs watch history persistence
logging.rs file logging (rotation, sanitization)
models.rs shared domain models (SearchResult, BrowseMetrics, StreamPool,
Notification, SubjectIdentity)
net.rs fallback DNS resolution, HTTP client builder, URL validation
player.rs player detection (OnceLock) and command construction (mpv/VLC/IINA/Android)
providers/
mod.rs provider module tree
models.rs shared typed models (ProviderKind, CatalogItem,
MediaDetails, Release, PlaybackSource, …)
moviebox/ primary provider (client + request signing)
fourkhdhub/ 4KHDHub provider (client, hubcloud resolver, parser)
bdix/circleftp/ BDIX CircleFTP provider
bdix/dhakaflix/ BDIX DhakaFlix provider
addons/ Community HTTP addons provider
tv/ Live TV / IPTV provider (M3U parser and models)
service.rs MovieBoxService headless engine (search, details, streams, captions)
updater/ GitHub release update check (mod, check, download, verify,
extract, apply, artifact)
tui/
app/ the application object and all behavior
mod.rs App struct, App::new, helpers
run.rs terminal event loop (run), rendering (draw),
and handle_action dispatcher (thin routing table)
network.rs poster fetch + provider dispatch helpers
search.rs search command routing, search request setup,
provider search dispatch, poster prefetch
playback.rs player launching + playback actions
download.rs download orchestration actions
favorites.rs favorite toggle/open actions, /favorites virtual list
requests.rs suggest/history/homepage/details/preview/
episode-stream actions
navigation.rs list navigation, submit actions, provider helpers
tv.rs TV mode: playlist manager + playback
addons.rs Addon mode: addon manager + HTTP addon actions
keyboard.rs raw key-event handling
mouse.rs mouse click handling and hitbox routing
system.rs help, refresh, cache, theme, updates, focus, resize
state.rs AppState: all UI state + in-memory LRU caches
action.rs the Action enum (event/message model)
commands.rs Slash command registry, parsing, and suggestions
event.rs EventHandler: input events + tick → Action channel
overlay.rs popups, pickers, notifications
screens/ render-only modules (home, details, help)
terminal.rs terminal capability probes
theme.rs color themes
widgets/ reusable widgets (badge, input, modal, poster, scrollbar, settings)
text.rs grapheme-safe text helpers and zero-allocation cursor slicing
The event loop
App::run (in app/run.rs) owns the only loop:
- If
clear_terminal_before_drawis set, the terminal buffer is cleared. - If
state.dirty, the screen is drawn (App::draw). tokio::select!waits for either:- an
Actionfrom theEventHandler(keyboard/mouse/focus/resize/tick), or - an
Actionpushed by a background task (network results, downloads, posters).
- an
EventHandler (event.rs) spawns one task that reads crossterm events and a Tick
interval, forwarding them into the action channel (capacity 128).
Async model
- The tokio runtime is multi-threaded (
rt-multi-thread). - State is single-threaded: all mutations to
AppStatehappen insidehandle_action, which is driven by the single event-loop task. Actions are serialized through the channel, so there are no data races on UI state. - Network uses async
reqwestclients (one per provider) plus per-provider signing. - Blocking work (disk cache reads/writes, image decoding, M3U parsing, watch-history
save, log cleanup) runs on
tokio::task::spawn_blockingso the event loop is never blocked. - Background tasks send
Actionmessages back (e.g.SearchSuccess,EpisodeStreamsReady,DownloadCompleted), whichhandle_actionconsumes.
Data flow — a typical search
- User types a query; the
Keyhandler updatessearch_queryand sendsAction::Search. handle_actionresolves the active provider, dispatches to the provider client (async), and spawns the request in a background task.- On success the task sends
Action::SearchSuccess;handle_actionstoressearch_results, marksdirty, and writes the provider search cache. - Posters for result rows are fetched by background tasks and delivered via
SearchPosterLoaded/PosterSuccess; image protocols are cached per terminal. App::drawrenders the results;dirtyis cleared.
Playback flow
- User selects a result →
Action::PlayStream(moviebox) or 4KHDHub/BDIX resolve. - The provider resolves a
PlaybackSource(url + optional headers/subtitle). launch_playerbuilds the player command (player.rs), optionally downloads the subtitle to a temp file, and spawns the player with null stdin/stdout and piped stderr; a blocking task waits and reports crashes.- Playback is handed to mpv / VLC / IINA / Android intent per the active player.
Configuration and persistence
config.json— settings (mode persistence, mode toggles, theme, provider, auto-update,default_player, download directory, BDIX) in the config dir.addons_config.json— installed HTTP community addons in the config dir.tv_config.json— user M3U playlist sources (URLs or file paths) in the config dir.history.json— watch history in the system data dir.favorites.json: starred titles in the system data dir, independent ofhistory.json.playback/— temporary playback states for session crash/kill reconciliation in the system data dir.scripts/— bundled player scripts (moviebox_tracker.lua) in the system data dir.- Cache lives under the system cache dir, keyed per provider.
- Logs live under the system data dir with rotation.
See config.md and logging.md for exact locations and formats.