Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Stremio/stremio-core/llms.txt
Use this file to discover all available pages before exploring further.
Models are the heart of Stremio Core’s state management. Each model is a self-contained state machine that responds to messages and produces effects.
What is a Model?
A model in Stremio Core is a struct that:
- Holds application state
- Implements the
Update or UpdateWithCtx trait
- Responds to messages by updating state and returning effects
// src/runtime/update.rs:8
pub trait Model<E: Env>: Clone {
type Field: Send + Sync + Serialize + for<'de> Deserialize<'de>;
fn update(&mut self, msg: &Msg) -> (Vec<Effect>, Vec<Self::Field>);
fn update_field(&mut self, msg: &Msg, field: &Self::Field) -> (Vec<Effect>, Vec<Self::Field>);
}
Core Application Model: Ctx
The Ctx model is the central state container that holds:
// src/models/ctx/ctx.rs:46
pub struct Ctx {
pub profile: Profile,
pub library: LibraryBucket,
pub notifications: NotificationsBucket,
pub streams: StreamsBucket,
pub streaming_server_urls: ServerUrlsBucket,
pub search_history: SearchHistoryBucket,
pub dismissed_events: DismissedEventsBucket,
pub status: CtxStatus,
pub trakt_addon: Option<DescriptorLoadable>,
pub notification_catalogs: Vec<ResourceLoadable<Vec<MetaItem>>>,
pub events: Events,
}
Creating a Ctx Instance
// src/models/ctx/ctx.rs:80
impl Ctx {
pub fn new(
profile: Profile,
library: LibraryBucket,
streams: StreamsBucket,
streaming_server_urls: ServerUrlsBucket,
notifications: NotificationsBucket,
search_history: SearchHistoryBucket,
dismissed_events: DismissedEventsBucket,
) -> Self {
Self {
profile,
library,
streams,
streaming_server_urls,
search_history,
dismissed_events,
notifications,
trakt_addon: None,
notification_catalogs: vec![],
status: CtxStatus::Ready,
events: Events {
modal: Loadable::Loading,
notification: Loadable::Loading,
},
}
}
}
Feature Models
Feature models handle specific parts of the application:
CatalogWithFilters
Manages catalog browsing with type, catalog, and extra filters:
// src/models/catalog_with_filters.rs:127
pub struct CatalogWithFilters<T> {
pub selected: Option<Selected>, // Current filter selection
pub selectable: Selectable, // Available filters
pub catalog: Catalog<T>, // Loaded catalog pages
}
Key features:
- Generic over content type (MetaItemPreview, Descriptor, etc.)
- Pagination support with lazy loading
- Dynamic filter generation based on installed addons
Loads and displays detailed information about content:
pub struct MetaDetails {
pub selected: Option<Selected>,
pub meta_items: Vec<ResourceLoadable<MetaItem>>,
pub streams: StreamsResourceLoadable,
// ...
}
Player
Manages video playback state:
pub struct Player {
pub selected: Option<Selected>,
pub library_item: LibraryItemDeepLinks,
pub meta_item: MetaItemDeepLinks,
pub next_video: Option<NextVideo>,
// ...
}
State Update Pattern
Models follow a consistent update pattern:
1. Match the Message
fn update(&mut self, msg: &Msg, ctx: &Ctx) -> Effects {
match msg {
Msg::Action(Action::Load(ActionLoad::CatalogWithFilters(selected))) => {
// Handle load action
}
Msg::Action(Action::Unload) => {
// Handle unload action
}
Msg::Internal(Internal::ResourceRequestResult(request, result)) => {
// Handle async result
}
_ => Effects::none().unchanged(),
}
}
2. Update State
// Update the model's fields
self.selected = Some(new_selection);
self.catalog = vec![new_page];
3. Generate Effects
// Load data from addon
let catalog_effects = catalog_update::<E, _>(
&mut self.catalog,
CatalogPageRequest::First,
&selected.request,
);
// Update derived state
let selectable_effects = selectable_update(
&mut self.selectable,
&self.selected,
&self.catalog,
&ctx.profile,
);
// Combine effects
catalog_effects.join(selectable_effects)
Composing State Updates
Complex models often delegate to helper functions:
// src/models/ctx/ctx.rs:269
let profile_effects =
update_profile::<E>(&mut self.profile, &mut self.streams, &self.status, msg);
let library_effects =
update_library::<E>(&mut self.library, &self.profile, &self.status, msg);
let streams_effects = update_streams::<E>(&mut self.streams, &self.status, msg);
profile_effects
.join(library_effects)
.join(streams_effects)
This keeps the main update function readable while allowing each subsystem to manage its own state.
Resource Loading Pattern
Many models use the ResourceLoadable type to track async operations:
pub struct ResourceLoadable<T> {
pub request: ResourceRequest,
pub content: Option<Loadable<T, EnvError>>,
}
pub enum Loadable<T, E> {
Loading,
Ready(T),
Err(E),
}
Loading Resources
// src/models/catalog_with_filters.rs:274
fn catalog_update<E, T>(
catalog: &mut Catalog<T>,
page_request: CatalogPageRequest,
request: &ResourceRequest,
) -> Effects {
let mut page = ResourceLoadable {
request: request.to_owned(),
content: None, // Will be set to Loading by resource_update
};
let effects = resource_update_with_vector_content::<E, _>(
&mut page,
ResourceAction::ResourceRequested { request },
);
match page_request {
CatalogPageRequest::First => *catalog = vec![page],
CatalogPageRequest::Next => catalog.extend(vec![page]),
};
effects
}
Handling Async Results
When effects complete, they send Internal messages back to the model:
Msg::Internal(Internal::ResourceRequestResult(request, result)) => {
self.catalog
.iter_mut()
.find(|page| page.request == *request)
.map(|page| {
resource_update_with_vector_content::<E, _>(
page,
ResourceAction::ResourceRequestResult { request, result },
)
})
.unwrap_or_else(|| Effects::none().unchanged())
}
State Persistence
The Ctx model persists critical state to storage:
// Profile changes trigger storage update
Effects::future(EffectFuture::Concurrent(
E::set_storage(PROFILE_STORAGE_KEY, Some(&self.profile))
.map(|_| Msg::Internal(Internal::ProfileChanged))
.boxed_env(),
))
Model Lifecycle
Load
User dispatches Action::Load(...) with initial parameters
Initialize
Model sets up state and generates initial effects
Process
Effects execute and send results back via Internal messages
Update
Model processes results and updates state
Unload
User dispatches Action::Unload to clean up resources
Best Practices
Single Responsibility
Each model should handle one feature or concept
Immutable Messages
Never mutate the message parameter in update functions
Pure Logic
Keep business logic in pure helper functions when possible
Effect Composition
Use .join() to combine effects from different subsystems
Next Steps
Elm Architecture
Review the Effect, Update, and Msg pattern
Effects and Runtime
Learn how the runtime executes effects