Giter Club home page Giter Club logo

stremio-core's Introduction

Stremio - the next generation media center

Build Workflow Status Latest MSRV workflow Status Latest deployed docs on GH pages

Stremio is a full-featured media center designed to help you organize and stream your favorite videos, movies and TV series. It will notify you for new episodes / movies, and allow you to find new content through Discover.

Stremio allows, using its Add-ons system, to play movies, TV series and channels instantly.

stremio-core

stremio-core is a rust crate that's designed to contain all the reusable logic between Stremio versions.

Goals

  • Flexibility - can be integrated into existing code bases, across the entire stack, and in different paradigms
    • use case: types can be used by add-ons
    • use case: can be used with existing user authentication as an addition to an existing app
    • use case: can use the Context model to manage the user authentication/addons, using it as a backbone to the entire Stremio app
  • Emphasis on correctness
  • No cruft / legacy - not burdened by obsolete decisions & solutions

Modules

  • types
  • addon_transport - handles communication with add-ons, implements legacy protocol adapter
  • state_types: types that describe application state; inspired by the Elm architecture
    • Effects and Update traits
    • runtime: helps using stremio-core in an application by handling the effects automatically
    • environment: a trait describes the environment (fetch, storage)
    • msg: messages: actions, events
    • models: all stateful models, such as Context (handling user authentication, add-ons), Library, CatalogFiltered, etc.
cargo clippy
cargo fmt

Optimizing WASM output

WASM output binary can get large, especially if we derive Serialize/Deserialize in places we don't need to

We can optimize it by running twiggy: twiggy top ..._bg.wasm and seeing what the biggest code size offenders are

Adding new actions

Defining actions and what middleware requests they should trigger is defined in src/state_types/msg/actions

stremio-core's People

Contributors

apurvtaneja avatar core1024 avatar doomheadshot avatar edde746 avatar elpiel avatar ensapra avatar glenviewjeff avatar jaruba avatar kkaskak avatar martinkavik avatar nklhtv avatar swetlasg avatar thebeastlt avatar tymmesyde avatar unclekingpin avatar yanivbir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stremio-core's Issues

rename to stremio-core

one repo that contains the following modules:

  • types: all the primitive types like MetaDetailed, MetaPreview, Stream, LibItem, NotifItem
  • state_types: Chain and ContainerMuxer
  • state_types/containers: CatalogGrouped, CatalogFiltered, Details, Streams
  • middlewares: ContextMiddleware, AddonMiddleware, PlayerMiddleware
  • library_addon: LibAddon

there's no need to split those into multiple crates - they all have the same deps

Player model

pub struct PlayerModel {
   // from the router, we will get (item_id, video_id, stream, meta_resourcereq)
   // @TODO maybe an addon transport_url too, there are use cases where we want information from the addon manifest
   // @TODO: Option or Loadable? Cause we may want to express "will not load" vs "loading"
   pub meta: Option<MetaItem>,
   pub next_video: Option<Video>,
   pub libitem: Option<LibItem>, // should we use Loadable? 
   pub stream: Stream,
}

As for handling the bingeing (or other complicated cases), we should:

fn update(&mut self, ctx: &Ctx, action: &Action) {
    match action {
        Msg::Player(item, PlayerEv::Ended) if item == Some(self.libitem) => {
              if let Some(next) = self.next_video && ctx.settings.bingeing  {
                    // trigger an Effect that resolves in Open
              }
        }

By default, the player will try to get an existing item from the library with this ID. If it does not exist, it will construct it from the loaded meta. If that doesn't load, it will operate without a libItem.

All extra roles: bingeing, saving library item state, marking episodes watched, marking notifications seen

It can operate without a libItem (just proxying the Player implementation) or with one, in which case it should load the library item, nad mutate it as the player advances

When the player starts playing it should request subtitles from the addon system and do addExtraSubtitles for each result and perhaps set selected subtitles when applying PlayerPreferences;
also send stream.subtitles via addExtraSubtitles

It should also mutate and persist PlayerPreferences itself, which should contain preferred subtitle language, preferred track for each language, preferred audio track, and subtitles delay

It should also catch and handle internal inconsistencies, most notably the playing stream being different than self.stream

bunch of design decisions related to the entire architecture of Stremio (notifications, calendar, library)

  • decision: streams for youtube should just be embedded in video.streams (the results of the addon itself)
  • API: consider filtering out jibberish in some way (other, type trailer, etc.)
    • also, pruning removed items that have not been touched for 2 years
    • decision: will use a planner function for v4.4 to stop syncing those items; and will prune them from our DB via a script; in stremio-core, it will be implemented as a simple filter in the LibAddon
  • decide what to use for Continue watching; decision: a catalog from the Library add-on; but
    • those things would be difficult if the manifest had to be updated, but that's easy since it's just a manifest() function within the same app
    • since the catalog will return a different response type (libItems), it should be filtered out of CatalogFiltered and CatalogGrouped; the catalog will be of type any, which will be filtered out of CatalogFiltered
    • the catalog can be refreshed individually by triggering a AddonRequest when the lib is updated
    • all catalogs in the LibAddon will contain the user ID in their ID, to avoid race conditions or cache issues
    • the Library will use a custom Load action, which will request from the appropriate catalog
  • calendar can be implemented via addons (library addon)
  • stremio-core: consider merging Streams and Detail, cause Streams should show streams from .video.streams if any; also this is tightly coupled with open(); decision: will be separate
    • separate containers, Detail will just output the meta to render, and the selected_video; Load action: Detail(..., Option<video_id>); if selected_id is None, the libItem.state.video_id will be used
    • Streams will just load streams from addons
    • UI will use both Detail and Streams, and it will trigger Load(Streams(...)) if the selected_video does not have .streams; if it does, it will render that
    • TryOpen will take the full video to determine whether to request streams from addons or not
  • rust-based shell? few binaries: app, server (maybe those should be in 1, with different modes) and autoupdater; the missing piece here is the web view; ultralight or servo are candidates
    • could happen and would be great, but it requires servo or ultralight becoming more mature
    • before that, we can implement the existing player protocol in C++ for MPV and Chromecast into the existing shell
  • addon spec: decide what extra properties with required: true will be allowed in CatalogFiltered; e.g. genre is one such example
    • adding an extra required prop could be used to make a catalog not appear in Board
    • in light of this, figure out whether behaviorHints noBoard/noDiscover are needed; probably not
    • full spec describing stremio-recognized extra props (search, searchIndex, genre, skip, possibly location, etc.)
    • Solution: in CatalogFiltered, filter catalogs by .extra.iter().all(|e| !e.is_required || e.options.map_or(|o| !o.is_empty(), false)); or also || e.name == "onlyDiscover" if we want a santinel value that just flags a catalog as only visible in Discover

Added later on:

  • there will be an API call to that returns toast notifications to be shown to the user, at certain lifecycle moments of the app (on open, every day, etc.); this will be used to inform the user of, for example, expired Trakt authentication, or a new stremio version: see relevant issue
  • Addons catalog should use a special addons resource, that can be implemented by any add-on (therefore allowing one addon to add items to the catalog, i.e. repositories); cinemeta will implement it for official/third-party add-ons
    • this adds a few benefits
      • repositories
      • more flexible control from the server side, including what the catalogs are (e.g. we can remove "third party" in some jurisdictions)
      • pagination, cause we can just use skip
      • we should consider a ManifestPreview, and pulling the whole manifest when installing
      • re-use code between addons/discover pages (CatalogFiltered)
      • addon dependencies: can help the addon dependencies problem, by allowing an "Install all" button for whole catalogs (we can use behaviorHints for this)
    • Decide: should we display the UI selectors as Type > Catalog or Catalog > Type; first one is the same as Discover; second one can be done via any catalog type plus extra prop for the type
      • which one is better for UX?
    • Implementation: should we reuse CatalogFiltered
      • we add .addon_catalogs to the manifest, and make CatalogFiltered generic over T: CatalogAdapter which has T::catalogs(x: &Descriptor) and T::resource() (that return x.manifest.catalogs, "catalog" or x.manifest.addon_catalogs, "addon_catalog"); we will have to use try_into for the response, as models/items_group does
      • another thing we can do is add a resource_name to each catalog; that way instead of manifest.addon_catalogs, we'll have them in manifest.catalogs but we'll just filter by resource_name
      • alternatively, addon catalogs will be hidden behind a required property, and we will instantiate CatalogFiltered with some extra prop already passed; this seems hackier
    • Should we introduce ManifestPreview
      • pro: faster loading of catalogs
      • pro/con: modals will always have to load the manifest; if the addon is down you won't be able to install it

Player msgs

Obsolete

A trait that depresents a player that follows the https://github.com/stremio/stremio-players spec

trait Player {
    fn dispatch(&Action) -> Result<(), Box<dyn Error>>;
    fn observe() -> EnvStream<&Event>; 
}

The WASM environment crate should contain an utility for converting a JS object into something that implements the Player trait in rust;

one way to do it would be to have a JS function makePlayer(emit) (that returns a { dispatch, drop }); other ways could be explored via wasm-bindgen magic (https://rustwasm.github.io/wasm-bindgen/contributing/design/js-objects-in-rust.html)


IMPORTANT NOTE: Disregard what's written above. With the new, closer ot Elm architecture, it may just be easier to make the Player impl entirely in JS, make it listen to messages (e.g. from the runtime receiver), and make it send messages to stremio-core by dispatch-ing them to the app model directly
as for player msgs, we'll create a file player.rs in the msg module that will include PlayerAction and PlayerEv

implement WatchedBitfield

  • just parses to struct { anchor_video, bitmap }
  • find a data structure for the bitmap; implementing Iterator would be nice cause of take_while and similar
  • .mark_watched(videos: &[Video], video: &Video, watched: bool)
    • identify if the anchor_video is within the same position of videos
      • it is -> continue
      • it is not -> re-create bitmap by rewriting the previous bitmap into a new bitmap of size videos.len() with an offset
      • set bitmap[videos.index_of(video)] = watched, but also resize bitmap in order to fit that index
      • set anchor_video to the last watched video
  • document assumptions: stable order from the addon

Stop showing items in continue watching section when fixed % of movie has been watched

Description
Movies should be removed from continue watching section when user has watched >= 95% of duration time

This task affects one model:

  • LibRecent (continue watching)

--

Alternatively, for better behavior with series, we should implement this in the Player model: when the user stops playing something, if it's over a threshold, consider it played and try setting the video_id to the next one (if any)

This way, when you watch 95% of an episode, and stop it, stremio will auto-set itself to the next one

This is currently implemented in https://github.com/Stremio/stremio/blob/development/src/controllers/player.js#L400

This involves the following models

  • Player

example UI via Elm

This is definitely less of a priority than #31 (which will be easier)

this can be done via:

  • using the containers as part of the model and updating them in the update itself, without a ContainerMuxer
  • the Action would be one of the variants of Msg
  • using subscriptions, we will bind the output of the chain; Cmds will be sent to the input of the chain
  • JSON decoders/encoders would need to be written for the interop

behaviorHints type

will be used in MetaItem, MetaPreview, Stream, Manifest

we can either use a Vec<(name, value)> or HashMap/BTreeMap; given how fast the latter is, there's no reason to not use it

or we can make a separate type for Manifest, MetaItem and etc. and capture additional fields

types: consider using Url

consider using Url for transport_url, addon logo/poster, meta poster/logo/background, stream url/external_url

The question here is whether we will benefit more than we're gonna risk from the added validation; the risk lies in that we might break certain add-on response working, even if only 1 of the fields is not a valid URL

design decision: code reuse between containers

a lot of containers (CatalogGrouped, Streams/Detail, Search) will use the same logic: when starting loading, add groups in state Loading, then as responses start coming in, update the given group

this could be mitigated by reusing generic code?

implement LibAddon

see also: #28

  • decide on storage scheme for libitems - bucket-based, full bucket and recent bucket
  • decide on storage scheme for lastvideos; single key JSON; not relevant anymore
  • figure out err handling for internal methods (update_item, push, sync); CtxError should be OK
  • implement push
  • implement sync
  • implement should_push and filtering out other type
  • msg: LibUpdate: update it in index (can fail with IdxError) AND push
  • syncing: https://github.com/Stremio/stremio-datastore-syncer/blob/master/lib/datastoreSyncer.js
  • LibAction, a sum action of all possible things you can do with the library
  • LibSync: update the index
  • move handling of all msgs to Library
  • LibSync: should .persist right after LibSyncPulled
  • LibItemUpdate - should update the index and join two effects: .push and .persist
  • we can literarily split all mutations - LibraryLoadable will have it's own update which takes a mut self
  • CtxUpdate -> sets to Loading, triggers a sync if there's a user and persists -> LibLoaded
  • CtxLoaded -> sets to Loading, triggers a load from storage -> LibLoaded
    • effects: load_from_storage, load_initial_api
      • would result in LibLoaded(AuthKey, items), to protect frm race conditions
        • may result in fatal errors
      • load_initial_api must persist
  • CtxUpdate (should it be renamed?) will NOT trigger library storage load (cause presumably there's none)
    • but it should trigger an initial sync
  • emit LibPersisted after each library mutation (after persisted)
  • LibBucket refactor
  • LibAdd / LibRemove
  • storage: recent bucket (load and save)
  • msg: LibAddFromMeta
  • go through code TODOs
  • Cinemeta Cloudflare worker to implement last-videos catalog
  • consider gzipping everything stored in storage
  • we can use the response from the last-videos catalogs to update poster and name for items, if needed; or simply rely on a message LibUpdateFromMeta(MetaPreview): #6
  • last videos: there should be an easy way to get # of notifications for a libitem
  • consider the libItem.temp property: is it still needed?

Continue watching:

show an item if item.state.video_id != item._id and timeOffset == 0 and there is a video_id; the goal is to show series that you have a next ep of
resetting state should work like this: if there is no next video, reset video_id to null;
first sort by the date of the first notification, second sort by lastWatched; the goal here is to show items with notifications (but only if overallTimeWatched > 0)

the classic reason to show an item in continue watching is if (!removed || temp) && timeOffset > 0; extend that by also requiring timeOffset < duration*0.95

All of this should be defined in lib_item.is_in_continue_watching()


Storage:

  • make it simple first, measure, then optimize; limit the size of the lib too
    • turns out, loading ~1400 items takes ~20ms in firefox (50-60ms in Chrome); amazing result; also, that amount of libitems is 1.4mb
  • library: bucket-based storage, segmented by recency and type OR classic Storage; buckets are slower on writes but help cleanups

web environment crate and improvements

move the environment implementation for the web browser to a common crate which can be either implemented as a feature to stremio-core or as a separate crate altogether

improvements:

  • fetch: optimize deserialization; curently we deserialize once in JS, then serialize to a string buffer to deserialize with serde on the WASM side; can be optimized
  • fetch: any response code other than 200 should return an error related to the response code
  • storage: consider gzipping storage

CatalogFiltered: is_addon_installed

UX: Discover: if we've opened an addon that is not installed, there should be an "This addon is not installed. Install now?" notification on top

we can do that via is_addon_installed in CatalogFiltered

addon SDK based on the types crate

an addon SDK that's based on the types crate from this repo

it can use proc macro to allow compile time validation of certain constraints: e.g. a manifest that mentions meta resource means you always have to implement a handler, or vice versa (if you implement a handler you need to put it in the manifest)

while under the hood it'd be best to use hyper (cause, performance), the API should be similar to Rocket:
https://github.com/SergioBenitez/Rocket
https://rocket.rs/
https://www.youtube.com/watch?v=t_FUZ34ahBE

Something else which is super useful in Rocket is the error messages at compile time: it can even detect routing conflicts

Settings model and structs

Settings can be stored in 4 places: localStorage, user, addonCollection, server

The Context model should only handle the ones that are in localStorage by default; settings that are specific to the environment (e.g. MPV settings) should be handled externally (in this example, by the player implementation) and hooked up on application level

ContextSettings: struct, stored in localStorage

Managed by the Context model, and indirectly by Settings model

It will contain:

  • language: preferred language
  • autoplay_next_vid: binge watching
  • server_url: default URL for the streaming server
  • use_external_player: use external player by default; perhaps this should be a device ID/type, so that it can work for Chromecast
  • player_esc_exits_fullscreen: this is not in the player settings cause it affects UI behavior in general
  • pause_on_lost_focus: whether to pause on minimize/lost focus; alternatively could be renamed to play_in_background
  • show_vid_overview: whether the video overview/synopsis will be readable by the user

ServerSettings: a struct filled with dynamic data, loaded/saved from/to the server

Managed by the Settings model

NOTE: because of the dynamic nature, we might not need a struct but rather a model that will load the settings types/values from the server

The server settings will be accessed from the server at URL ctx.settings.server_url

In practice, those settings are:

  • server_cache_location
  • server_cache_size
  • server_torrent_profile

However, those will be loaded dynamically from the server, along with the possible options, as they are in 4.x

WARNING: the ServerSettings may be replaced by streaming server as a protocol addon and addonSettings

PlayerSettings: struct, stored in localStorage

Managed by the Player model, which also manages PlayerPreferences and sends the settings/preferences to the implementation

The settings are:

  • subtitles_language: default subtitles language
  • subtitles_size
  • subtitles_fgcolor
  • subtitles_outline_color
  • subtitles_background

Those will be sent to the implementation through the Load player command. Not every implementation will support all of those

MPV settings

Managed (saved/loaded) by the mpv implementation:

  • player_mpv_hwdec
  • player_mpv_cache_size
  • player_mpv_separate_window

Addon settings

Managed by the Settings model

Auto play: this will be an autoplay flag for each addon in the collection; that, combined with the order of the addons in the collection, will determine autoplay behavior; the flag may contain details, such as { onlyGroups: ["hd"] } (more about groups: https://github.com/Stremio/stremio/issues/524)

Settings model

this model makes managing all settings convenient, including the server settings and add-on settings (preferred add-on and etc.)

This includes:

  • saving/loading ServerSettings to/from the server
  • managing add-on flags in order to set preferences

It might need to issue messages that will be handled in the Context model (e.g. UpdateSettings or ActionAddon::Install/ActionAddon::Update)

SettingsUI trait

A helper trait to describe a settings struct in the UI

The server settings are described dynamically by it. We may define this behavior as a trait so that player implementations can use this to enable the Settings model to easily control their settings too


To implement this, we can:

  • add a Settings struct to the Ctx under the settings property
  • Environment-specific settings: player implementations will manage their own settings OR this dynamic map would support nesting other maps into it, and we can use that for player-specific settings (e.g. player_mpv key) that will only be sent to the relative player implementation
  • the settings structs (ContextSettings, PlayerSettings) will have static properties (like autoplay settings) and if needed (see above), a field that holds everything else as a serde_json::Value or HashMap<String, Any>; serialize/deserialize will be done that way (see "capture additional fields")

final design decisions before v1.0

in light of using this in stremio-web, stremio-example-seed, stremio-example-sauron, stremio-example-ui and stremio-ng-example,

there are a few more design decisions to make before finalizing:

  • instead of CatalogMuxer, implement a #[derive] macro that will allow the user to fill a struct with fields that implement Container; that is much more ergonomic; however, this struct should not have interior mutability, and mutability will need to be handled by the user (implementation-specific) - DOING IT
  • should internal APIs use future Streams instead of callbacks - DOING IT
  • redux middleware style vs elm-style update function - GOING FOR ELM STYLE
    • elm-style update problem: many things are dependent on the user ctx, so we will have to somehow retrieve it before determining what tasks to issue; if we do a promise of the style with_ctx, then it's kinda ugly (adding async logic to update)
      • or, simple solution: we do update(&mut Model, &Ctx) -> Effects
      • we can still have loopback actions
    • a good reason for loopback actions is that you might want some environment side-effects, e.g. routing; receiving a RecommendedXXX (as a result to Open) to load should lead to a page that will trigger this Load
  • consider a AddonEffects trait (with a fn that returns AggrRequest) instead of addon_aggr_req; and in general, a mechanisms for Containers to define thier own Load parameters and effects; could be a trait associated type that we use in Actions - not needed
  • ...or, a more generalized Effects trait with fn effects(ctx: Ctx) -> Vec<EnvFuture<Action>> - not needed
  • should the update function mutate the struct or return a new struct? - mutate
  • consider StandardStremio - a struct with all containers a usual stremio app has - not needed, derive macro handles this great
  • AddonM can be enhanced with memoization to avoid re-requesting the same thing (e.g. when going from /detail/movie/tt213 to /detail/movie/tt213/tt213), but for now there's no point of doing this cause (1) the reducer will keep the old response and (2) the browser has a cache - not a problem with elm architecture
  • Problem: circular dependency: player needs addon system responses (from AddonM) but needs to emit to the library add-on (back in ContextM) or control it somehow; LibAddon itself needs to be connected after AddonM (to listen to AddonRequest/AddonResponse) - not a problem with elm architecture
    • solution 1: re-think the chain
    • solution 2: drop the chain, there'd be one middleware that manages everything
    • solution 3: use a loopback action
    • solution 4: the player should somehow capture a handle ot the LibAddon and use it directly
    • solution 5: the elm architecture (Player update will handle it's own side effects)
  • do we need a common trait between MetaItem, MetaPreview and LibItem? - #53
  • do we need LibItemPreview? - no
  • when saving the last stream, save the whole stream object but compressed OR save the stream hash; hash can be saved within the libitem - whole stream makes more sense, could be part of PlayerPreferences?; this decision may change
  • design decision: models (containers; or should we perhaps rename them to models) should contain raw, but complete data; their update function should focus on how they mutate, and do as little processing as possible; they will do sorting and filtering (e.g. Search) though
  • figure out environment-specific settings; perhaps use the Environment trait?

see also: #28

loopback messages

There are certain messages that are meant to be, for most apps/cases, looped back into the system

This means reacting to an Output message by triggering a corresponding Action

Current list of those:

  • AddonsChanged -> PushAddons, but only if there's an internet connection
  • LibUpdate(libitem) -> PushLibItem(libitem), but only if there's an internet connection; the PushLibItem will be ignored by the system if the user is not logged in
  • RecommendedLoad -> Load; the RecommendLoad may happen as a response to OpenMedia(url) or Open(libitem/metaitem/etc.)
  • InstallAndOpenAddon: this will install an add-on and then open it, so it will trigger a RecommendedLoad
  • certain kinds of User-related errors (that mean session expired) -> route to login screen

research search libraries for implementing searchIndex spec

spec: https://github.com/Stremio/stremio/issues/615

simsearch, thread: https://www.reddit.com/r/rust/comments/bdj65n/simsearch_a_new_simple_and_lightweight_fuzzy/

tantivy: according to the thread above it can work in memory; also see blog post; might be what we need, but it's more complicated to setup than semsearch

sonic: designed to work as a server, but see valeriansaliou/sonic#150

flexsearch.js: https://github.com/nextapps-de/flexsearch

tinysearch: https://endler.dev/2019/tinysearch/ - https://github.com/mre/tinysearch - doesn't do fuzzy search so it might be useless

we should also look into how firefox does it for it's URl bar (and chromium's omnibar); as well as the search in angularjs (which is what we use in v4.4 for videos) and crates.io

AddonCatalog, tests

The AddonCatalog should load addon collections: one of Installed, Official, External(url)

Actions are fairly straight forward: filters; and it should react to AddonRemoved/AddonInstalled

Behaviour after add-on uninstall

Perhaps marking every library item aware of which add-on it belongs to. This way we can identify if the add-on in question if it has been removes, so a proper message may be displayed.

Detail model

See this and follow the spec: https://github.com/Stremio/stremio/issues/655; some things of the spec are strictly view logic, but we should design the model and implement it here

it should have meta and libitem, other than all individual responses from addons;

furthermore, it should contain the streams within it; separate streams model is not necessarily a good idea, cause rendering streams depends on the meta (cause of video.streams and also the progress bar, libitem.state.timeOffset)

Context model tests

  • Register
  • remove add-ons, push
  • create a new context; expect it to be persisted after CtxLoad
  • Logout, expect it be persisted
  • Login, expect it to be synced and persisted

metadata: rename links to externalUrls

this replaces:

  • .fandango
  • .website
  • potentially others too, e.g. links to rottentomatoes and imdb
  • we can introduce .cinema, which is just a URL to any cinema showtimes

CatalogGrouped: laziness

rather than loading all groups at once, load them when they're in view

this helps implement two features:

  • loading the Board quicker (only loading what's in view)
  • limiting the number of loaded catalogs from an add-on (e.g. to 3), and toggling showing others with "show more from this add-on"

This can be done by introducing a mode field in the ActionLoad::CatalogGrouped variant, which can be either set to Normal (the default) or to Lazy(Vec<ResourceReq>); if the vector is empty, we will only initialize it and set everything to Loadable::NotLoaded; we will start loading those catalogs whose req is in the Vec<ResourceReq>

This can be invoked multiple times to request that more catalogs are loaded; the ones that are in a Loadable::Loading or Loadable::Loaded status will remain

This is not high priority

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.