Skip to main content

Overview

The LibraryWithFilters model provides a filtered and sorted view of the user’s library. It supports filtering by content type, sorting by various criteria, and pagination. The model is generic over a filter type that determines which library items are included.

Structure

Fields

selected
Option<Selected>
Currently selected filters and sort options
selectable
Selectable
required
Available filter and sort options
catalog
Vec<LibraryItem>
required
Filtered, sorted, and paginated library items
filter
PhantomData<F>
Phantom data for the filter type parameter

Selected

Represents the current selection state:
pub struct Selected {
    pub request: LibraryRequest,
}

LibraryRequest

pub struct LibraryRequest {
    pub r#type: Option<String>,
    pub sort: Sort,
    pub page: LibraryRequestPage,
}
type
Option<String>
Content type filter (“movie”, “series”, etc.). None means all types.
sort
Sort
required
Sort order for library items (default: LastWatched)
page
LibraryRequestPage
required
Current page number (starts at 1)

Sort

Available sorting options:
pub enum Sort {
    LastWatched,    // Most recently watched first (default)
    Name,           // Alphabetical A-Z
    NameReverse,    // Alphabetical Z-A
    TimesWatched,   // Most watched first
    Watched,        // Watched items first, then unwatched
    NotWatched,     // Unwatched items first, then watched
}

Sort Behavior

Sorts by state.last_watched in descending order. Most recently watched items appear first.Source: src/models/library_with_filters.rs:69
Sorts alphabetically by item name (case-insensitive), A to Z.Source: src/models/library_with_filters.rs:89
Sorts alphabetically by item name (case-insensitive), Z to A.Source: src/models/library_with_filters.rs:90
Sorts by state.times_watched in descending order. Most watched items appear first.Source: src/models/library_with_filters.rs:70
Watched items first (by watched() status), then unwatched. Within each group, sorted by last_watched descending, with ctime as fallback.Source: src/models/library_with_filters.rs:73
Unwatched items first, then watched. Within each group, sorted by last_watched ascending, with ctime as fallback.Source: src/models/library_with_filters.rs:81

Selectable

Provides available options:
pub struct Selectable {
    pub types: Vec<SelectableType>,
    pub sorts: Vec<SelectableSort>,
    pub next_page: Option<SelectablePage>,
}
types
Vec<SelectableType>
required
Available content types found in the filtered library
sorts
Vec<SelectableSort>
required
All available sort options with selection state
next_page
Option<SelectablePage>
Request for next page if more items are available

LibraryFilter

The generic parameter F must implement the LibraryFilter trait:
pub trait LibraryFilter {
    fn predicate(library_item: &LibraryItem, notifications: &NotificationsBucket) -> bool;
}

Built-in Filters

ContinueWatchingFilter

Includes items that are in progress or have notifications:
impl LibraryFilter for ContinueWatchingFilter {
    fn predicate(library_item: &LibraryItem, notifications: &NotificationsBucket) -> bool {
        library_item.is_in_continue_watching() || 
        notifications.items.get(&library_item.id).filter(|n| !n.is_empty()).is_some()
    }
}
Use this filter for “Continue Watching” sections.

NotRemovedFilter

Includes all items that have not been marked as removed:
impl LibraryFilter for NotRemovedFilter {
    fn predicate(library_item: &LibraryItem, _notifications: &NotificationsBucket) -> bool {
        !library_item.removed
    }
}
Use this filter for the main library view.

Constructor

new()

Creates a new library browser instance:
pub fn new(
    library: &LibraryBucket,
    notifications: &NotificationsBucket
) -> (Self, Effects)
Returns: A tuple of the initialized model and effects to compute selectable options.

Update Implementation

Implements UpdateWithCtx<E> to handle library operations:

Supported Messages

Loads the library with specified filters:
  • Updates selected request
  • Applies type and sort filters
  • Paginates results
  • Updates selectable options
Source: src/models/library_with_filters.rs:173
Clears all library filter state:
  • Resets selected to None
  • Clears catalog
  • Recomputes selectable options
Source: src/models/library_with_filters.rs:191
Loads the next page of results:
  • Increments page number
  • Extends catalog with additional items
  • Updates next_page availability
Source: src/models/library_with_filters.rs:209
Recomputes filtered catalog when library is updated:
  • Reapplies current filters
  • Updates selectable types
  • Adjusts pagination
Source: src/models/library_with_filters.rs:235

Pagination

Library pagination works differently from catalog pagination:
1

Initial Load

Load first page (page 1) with CATALOG_PAGE_SIZE items
2

Check for More

If there’s an item at position page * CATALOG_PAGE_SIZE, next page is available
3

Load Next Page

Increment page number and take page * CATALOG_PAGE_SIZE items total
4

Update State

Catalog contains all items from page 1 to current page
Unlike catalog browsing, library filtering happens locally and instantly. All filtering and sorting is done in-memory.

Type Selection

Types are derived from the filtered library:
  1. Filter library items using the filter predicate
  2. Extract unique types from filtered items
  3. Sort types by TYPE_PRIORITIES (movie, series, etc.)
  4. Add “All Types” option (type = None) at the beginning

Usage Example

use stremio_core::models::library_with_filters::{LibraryWithFilters, ContinueWatchingFilter};
use stremio_core::runtime::UpdateWithCtx;

// Create continue watching browser
let (mut library, effects) = LibraryWithFilters::<ContinueWatchingFilter>::new(
    &ctx.library,
    &ctx.notifications,
);

// Load with default sorting
let selected = Selected {
    request: LibraryRequest {
        r#type: None,  // All types
        sort: Sort::LastWatched,
        page: LibraryRequestPage::default(),  // Page 1
    },
};

let effects = library.update(
    &Msg::Action(Action::Load(ActionLoad::LibraryWithFilters(selected))),
    &ctx
);

// Change sort order
let effects = library.update(
    &Msg::Action(Action::Load(ActionLoad::LibraryWithFilters(Selected {
        request: LibraryRequest {
            r#type: None,
            sort: Sort::Name,
            page: LibraryRequestPage::default(),
        },
    }))),
    &ctx
);
use stremio_core::models::library_with_filters::{LibraryWithFilters, NotRemovedFilter};

// Create full library browser
let (mut library, effects) = LibraryWithFilters::<NotRemovedFilter>::new(
    &ctx.library,
    &ctx.notifications,
);

// Filter by movies only
let selected = Selected {
    request: LibraryRequest {
        r#type: Some("movie".to_string()),
        sort: Sort::Name,
        page: LibraryRequestPage::default(),
    },
};

let effects = library.update(
    &Msg::Action(Action::Load(ActionLoad::LibraryWithFilters(selected))),
    &ctx
);

Performance Considerations

All filtering and sorting happens on every library change. For large libraries (1000+ items), consider:
  • Using more specific filters to reduce the working set
  • Implementing pagination in your UI to avoid rendering all items
  • Monitoring Internal::LibraryChanged message frequency

Testing

The module includes comprehensive tests for sort behavior:
  • Watched vs. not watched ordering
  • Series handling with times_watched and flagged_watched
  • Last watched timestamps
  • Creation time fallbacks
Source: src/models/library_with_filters.rs:371

See Also

Build docs developers (and LLMs) love