diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c863d648d..0e3b7c553 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,24 +4,23 @@ on: [push] # Stops the running workflow of previous pushes concurrency: - group: ${{ github.ref }} + # cancel per workflow + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - RUST_VERSION: stable - jobs: build: name: Lint, test and build runs-on: ubuntu-latest steps: - - name: Set default Rust version - ${{ env.RUST_VERSION }} - run: rustup default ${{ env.RUST_VERSION }} - name: Checkout uses: actions/checkout@v3 + - name: Rust setup (stable) + uses: dtolnay/rust-toolchain@stable + - name: Lint - rustfmt run: cargo fmt --all -- --check diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ecb1d090b..fe87a5d8f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,20 +12,24 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - uses: actions-rs/toolchain@v1 + + - name: Rust setup (nightly) + uses: dtolnay/rust-toolchain@master with: # Use nightly to build the docs with `--cfg docsrs` toolchain: nightly - profile: minimal components: rust-docs + - name: Build docs # Building locally: # for `--enable-index-page` it is required to pass `-Z unstable-options` to rustdocs run: RUSTDOCFLAGS="--cfg docsrs -Z unstable-options --enable-index-page" cargo +nightly doc --all-features --no-deps --workspace + - name: Prepare /docs run: | rm -rf ./docs mv target/doc ./docs + - name: Deploy gh-pages uses: peaceiris/actions-gh-pages@v3 with: diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index d094dcac7..4b632613f 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -1,27 +1,32 @@ -name: MSRV 1.60 build +name: MSRV build on: # will checkout the default branch `development` schedule: # run every Friday at 17:00 - cron: '00 17 * * 5' + # Or ran manually + workflow_dispatch: # Stops the running workflow of previous pushes concurrency: - group: ${{ github.ref }} + # cancel per workflow + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: - RUST_MSRV_VERSION: '1.60' + RUST_MSRV_VERSION: '1.67.1' jobs: build: - name: Test and build --release + name: Test and build runs-on: ubuntu-latest steps: - - name: Set default Rust version - ${{ env.RUST_MSRV_VERSION }} - run: rustup default ${{ env.RUST_MSRV_VERSION }} + - name: Rust setup (MSRV) + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_MSRV_VERSION }} - name: Checkout uses: actions/checkout@v3 diff --git a/Cargo.toml b/Cargo.toml index b97e85f7e..818106e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,13 @@ name = "stremio-core" version = "0.1.0" authors = ["Smart Code OOD"] -edition = "2018" +edition = "2021" -rust-version = "1.60" +rust-version = "1.67.1" [workspace] members = [ "stremio-derive", - "stremio-analytics", "stremio-watched-bitfield", ] @@ -20,6 +19,7 @@ doctest = false # TODO: env-future-send should be enabled by default # but our `TestEnv` for `unit_tests` uses a MutexGuard which is not Send. # default = ["env-future-send"] +default = [] # Adds `Send` marker trait to the `Env` trait methods and `EnvFuture`. # It's required for environments that do not support `Send`. @@ -27,13 +27,19 @@ doctest = false # see https://github.com/rustwasm/wasm-bindgen/issues/2833 env-future-send = [] +# Exports the Model derive macro from `stremio-derive` +derive = [] + +# Enable core analytics +analytics = [] + [dependencies] stremio-derive = { path = "stremio-derive" } stremio-watched-bitfield = { path = "stremio-watched-bitfield" } stremio-official-addons = "=2.0.10" # (De)Serialization -serde = "1.0.*" +serde = { version = "1", features = ["derive"]} serde_json = "1.0.*" serde_url_params = "0.2.*" serde_bencode = "0.2.*" @@ -51,20 +57,20 @@ percent-encoding = "2.1" chrono = { version = "0.4", features = ["serde"] } semver = { version = "1", features = ["serde"] } -base64 = { version = "0.21"} +base64 = "0.21" sha1 = "0.10" either = "1.6" enclose = "1.1" derivative = "2.2" derive_more = "0.99" -boolinator = "2.4.*" -strum = { version = "0.24", features = ["derive"] } +boolinator = "2.4" +strum = { version = "0.25", features = ["derive"] } lazysort = "0.2" lazy_static = "1.4" once_cell = "1.4" -itertools = "0.10" +itertools = "0.11" magnet-url = "2.0" hex = "0.4" diff --git a/README.md b/README.md index 8c6d96bb4..8326b3392 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ ## Stremio - the next generation media center +[![Build Workflow Status](https://img.shields.io/github/actions/workflow/status/Stremio/stremio-core/build.yml?label=Build)](https://github.com/Stremio/stremio-core/actions/workflows/build.yml) +[![Latest MSRV workflow Status](https://img.shields.io/github/actions/workflow/status/Stremio/stremio-core/msrv.yml?label=MSRV)](https://github.com/Stremio/stremio-core/actions/workflows/msrv.yml) +[![Latest deployed docs on GH pages](https://img.shields.io/github/actions/workflow/status/Stremio/stremio-core/docs.yml?event=workflow_dispatch&label=Latest%20deployed%20Docs)](https://stremio.github.io/stremio-core) 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. diff --git a/stremio-analytics/src/lib.rs b/src/analytics.rs similarity index 89% rename from stremio-analytics/src/lib.rs rename to src/analytics.rs index fce13a01d..2191dcdcb 100644 --- a/stremio-analytics/src/lib.rs +++ b/src/analytics.rs @@ -1,20 +1,28 @@ +use std::{ + collections::VecDeque, + marker::PhantomData, + sync::{Arc, Mutex}, +}; + use derivative::Derivative; use enclose::enclose; -use futures::future::Either; -use futures::{future, Future, FutureExt}; +use futures::{ + future::{self, Either}, + Future, FutureExt, +}; use serde::Serialize; -use std::collections::VecDeque; -use std::marker::PhantomData; -use std::sync::{Arc, Mutex}; -use stremio_core::models::ctx::Ctx; -use stremio_core::models::streaming_server::StreamingServer; -#[cfg(debug_assertions)] -use stremio_core::runtime::EnvFutureExt; -use stremio_core::runtime::{Env, EnvError, TryEnvFuture}; -use stremio_core::types::api::{fetch_api, APIRequest, APIResult, SuccessResponse}; -use stremio_core::types::profile::AuthKey; + +use crate::{ + models::{ctx::Ctx, streaming_server::StreamingServer}, + runtime::{Env, EnvError, TryEnvFuture}, + types::{ + api::{fetch_api, APIRequest, APIResult, SuccessResponse}, + profile::AuthKey, + }, +}; + #[cfg(debug_assertions)] -use stremio_core::types::True; +use crate::{runtime::EnvFutureExt, types::True}; #[derive(Clone, PartialEq, Serialize, Debug)] struct Event { diff --git a/src/constants.rs b/src/constants.rs index a5468a54e..31eb0aa8a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,7 +34,7 @@ pub const SCHEMA_VERSION: u32 = 7; pub const IMDB_LINK_CATEGORY: &str = "imdb"; pub const GENRES_LINK_CATEGORY: &str = "Genres"; pub const CINEMETA_TOP_CATALOG_ID: &str = "top"; -/// Only found in Cinemeta catalogs, i.e. [`CINEMETA_CATALOGS_URL`] +/// Only found in Cinemeta catalogs, i.e. [`CINEMETA_CATALOGS_URL`](struct@CINEMETA_CATALOGS_URL) pub const CINEMETA_FEED_CATALOG_ID: &str = "feed.json"; pub const IMDB_TITLE_PATH: &str = "title"; pub const YOUTUBE_ADDON_ID_PREFIX: &str = "yt_id:"; diff --git a/src/deep_links/mod.rs b/src/deep_links/mod.rs index 6c00c45f6..b205d1284 100644 --- a/src/deep_links/mod.rs +++ b/src/deep_links/mod.rs @@ -1,19 +1,26 @@ -mod error_link; - -use crate::constants::URI_COMPONENT_ENCODE_SET; -use crate::deep_links::error_link::ErrorLink; -use crate::models::installed_addons_with_filters::InstalledAddonsRequest; -use crate::models::library_with_filters::LibraryRequest; -use crate::types::addon::{ExtraValue, ResourcePath, ResourceRequest}; -use crate::types::library::LibraryItem; -use crate::types::profile::Settings; -use crate::types::query_params_encode; -use crate::types::resource::{MetaItem, MetaItemPreview, Stream, StreamSource, Video}; use percent_encoding::utf8_percent_encode; use regex::Regex; use serde::Serialize; use url::Url; +use crate::{ + constants::URI_COMPONENT_ENCODE_SET, + models::{ + installed_addons_with_filters::InstalledAddonsRequest, library_with_filters::LibraryRequest, + }, + types::{ + addon::{ExtraValue, ResourcePath, ResourceRequest}, + library::LibraryItem, + profile::Settings, + query_params_encode, + resource::{MetaItem, MetaItemPreview, Stream, StreamSource, Video}, + }, +}; + +pub use error_link::ErrorLink; + +mod error_link; + #[derive(Default, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct OpenPlayerLink { @@ -42,6 +49,12 @@ pub struct ExternalPlayerLink { } impl From<(&Stream, &Option, &Settings)> for ExternalPlayerLink { + /// Create an [`ExternalPlayerLink`] using the [`Stream`], + /// the server url (from [`StreamingServer::base_url`] which indicates a running or not server) + /// and the user's [`Settings`] in order to use the [`Settings::player_type`] for generating a + /// player-specific url. + /// + /// [`StreamingServer::base_url`]: crate::models::streaming_server::StreamingServer::base_url fn from((stream, streaming_server_url, settings): (&Stream, &Option, &Settings)) -> Self { let http_regex = Regex::new(r"https?://").unwrap(); let download = stream.download_url(); @@ -301,6 +314,12 @@ pub struct StreamDeepLinks { } impl From<(&Stream, &Option, &Settings)> for StreamDeepLinks { + /// Create a [`StreamDeepLinks`] using the [`Stream`], + /// the server url (from [`StreamingServer::base_url`] which indicates a running or not server) + /// and the user's [`Settings`] in order to use the [`Settings::player_type`] for generating a + /// player-specific url. + /// + /// [`StreamingServer::base_url`]: crate::models::streaming_server::StreamingServer::base_url fn from((stream, streaming_server_url, settings): (&Stream, &Option, &Settings)) -> Self { StreamDeepLinks { player: stream @@ -326,6 +345,12 @@ impl &Settings, )> for StreamDeepLinks { + /// Create a [`StreamDeepLinks`] using the [`Stream`], stream request, meta request, + /// the server url (from [`StreamingServer::base_url`] which indicates a running or not server) + /// and the user's [`Settings`] in order to use the [`Settings::player_type`] for generating a + /// player-specific url. + /// + /// [`StreamingServer::base_url`]: crate::models::streaming_server::StreamingServer::base_url fn from( (stream, stream_request, meta_request, streaming_server_url, settings): ( &Stream, diff --git a/src/lib.rs b/src/lib.rs index 6875e3ecb..0ea067de4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,18 @@ +//! # Stremio core #![allow(clippy::module_inception)] // Do not allow broken intra doc links #![deny(rustdoc::broken_intra_doc_links)] // TODO: Fix async tests that trigger this lock warning #![cfg_attr(test, allow(clippy::await_holding_lock))] +// Re-export of the derive macro for Model which removes the need for +// depending on `stremio-derive` everywhere. +#[cfg(feature = "derive")] +pub use stremio_derive::Model; + pub mod addon_transport; +#[cfg(feature = "analytics")] +pub mod analytics; pub mod deep_links; pub mod models; pub mod runtime; diff --git a/src/models/continue_watching_preview.rs b/src/models/continue_watching_preview.rs index dc664abdc..79799d7c3 100644 --- a/src/models/continue_watching_preview.rs +++ b/src/models/continue_watching_preview.rs @@ -35,8 +35,6 @@ impl UpdateWithCtx for ContinueWatchingPreview { match msg { // library has changed Msg::Internal(Internal::LibraryChanged(true)) - // LibraryItem has been updated (this message alters the `mtime` and will re-order the CW list) - | Msg::Internal(Internal::UpdateLibraryItem(_)) // notifications have been updated | Msg::Internal(Internal::NotificationsChanged) => { library_items_update(&mut self.library_items, &ctx.library, &ctx.notifications) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index d6e2047f1..5cb953288 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -10,7 +10,6 @@ use crate::types::api::{ }; use crate::types::library::{LibraryBucket, LibraryBucketRef, LibraryItem}; use crate::types::profile::{AuthKey, Profile}; -use chrono::Utc; use futures::future::Either; use futures::{future, FutureExt, TryFutureExt}; use std::collections::HashMap; @@ -67,7 +66,7 @@ pub fn update_library( Some(library_item) => { let mut library_item = library_item.to_owned(); library_item.state.time_offset = 0; - library_item.state.last_watched = Some(Utc::now()); + library_item.state.last_watched = Some(E::now()); Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(library_item))) .join(Effects::msg(Msg::Event(Event::LibraryItemRewinded { id: id.to_owned(), @@ -80,6 +79,24 @@ pub fn update_library( })) .unchanged(), }, + Msg::Action(Action::Ctx(ActionCtx::ToggleLibraryItemNotifications(id, state))) => { + match library.items.get(id) { + Some(library_item) => { + let mut library_item = library_item.to_owned(); + library_item.state.no_notif = *state; + Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(library_item))) + .join(Effects::msg(Msg::Event( + Event::LibraryItemNotificationsToggled { id: id.to_owned() }, + ))) + .unchanged() + } + _ => Effects::msg(Msg::Event(Event::Error { + error: CtxError::from(OtherError::LibraryItemNotFound), + source: Box::new(Event::LibraryItemNotificationsToggled { id: id.to_owned() }), + })) + .unchanged(), + } + } Msg::Action(Action::Ctx(ActionCtx::SyncLibraryWithAPI)) => match auth_key { Some(auth_key) => Effects::one(plan_sync_with_api::(library, auth_key)).unchanged(), _ => Effects::msg(Msg::Event(Event::Error { diff --git a/src/runtime/msg/action.rs b/src/runtime/msg/action.rs index 20eb23014..96c7ff428 100644 --- a/src/runtime/msg/action.rs +++ b/src/runtime/msg/action.rs @@ -1,22 +1,32 @@ -use crate::models::addon_details::Selected as AddonDetailsSelected; -use crate::models::catalog_with_filters::Selected as CatalogWithFiltersSelected; -use crate::models::catalogs_with_extra::Selected as CatalogsWithExtraSelected; -use crate::models::installed_addons_with_filters::Selected as InstalledAddonsWithFiltersSelected; -use crate::models::library_by_type::Selected as LibraryByTypeSelected; -use crate::models::library_with_filters::Selected as LibraryWithFiltersSelected; -use crate::models::meta_details::Selected as MetaDetailsSelected; -use crate::models::player::Selected as PlayerSelected; -use crate::models::streaming_server::{ - Settings as StreamingServerSettings, StatisticsRequest as StreamingServerStatisticsRequest, -}; -use crate::types::addon::Descriptor; -use crate::types::api::AuthRequest; -use crate::types::profile::Settings as ProfileSettings; -use crate::types::resource::{MetaItemId, MetaItemPreview}; -use serde::Deserialize; use std::ops::Range; + +use serde::Deserialize; use url::Url; +use crate::{ + models::{ + addon_details::Selected as AddonDetailsSelected, + catalog_with_filters::Selected as CatalogWithFiltersSelected, + catalogs_with_extra::Selected as CatalogsWithExtraSelected, + installed_addons_with_filters::Selected as InstalledAddonsWithFiltersSelected, + library_by_type::Selected as LibraryByTypeSelected, + library_with_filters::Selected as LibraryWithFiltersSelected, + meta_details::Selected as MetaDetailsSelected, + player::Selected as PlayerSelected, + streaming_server::{ + Settings as StreamingServerSettings, + StatisticsRequest as StreamingServerStatisticsRequest, + }, + }, + types::{ + addon::Descriptor, + api::AuthRequest, + library::LibraryItemId, + profile::Settings as ProfileSettings, + resource::{MetaItemId, MetaItemPreview}, + }, +}; + #[derive(Clone, Deserialize, Debug)] #[serde(tag = "action", content = "args")] pub enum ActionCtx { @@ -31,6 +41,8 @@ pub enum ActionCtx { AddToLibrary(MetaItemPreview), RemoveFromLibrary(String), RewindLibraryItem(String), + /// If boolean is set to `true` it will disable notifications for the LibraryItem. + ToggleLibraryItemNotifications(LibraryItemId, bool), /// Dismiss all Notification for a given [`MetaItemId`]. DismissNotificationItem(MetaItemId), PushUserToAPI, @@ -141,6 +153,7 @@ pub enum ActionLoad { #[serde(tag = "action", content = "args")] pub enum ActionSearch { /// Request for Search queries + #[serde(rename_all = "camelCase")] Search { search_query: String, max_results: usize, diff --git a/src/runtime/msg/event.rs b/src/runtime/msg/event.rs index 4121bd901..6797b387b 100644 --- a/src/runtime/msg/event.rs +++ b/src/runtime/msg/event.rs @@ -1,6 +1,7 @@ use crate::models::ctx::CtxError; use crate::models::player::AnalyticsContext as PlayerAnalyticsContext; use crate::types::api::AuthRequest; +use crate::types::library::LibraryItemId; use crate::types::profile::{AuthKey, Settings, UID}; use serde::Serialize; use url::Url; @@ -95,13 +96,16 @@ pub enum Event { settings: Settings, }, LibraryItemAdded { - id: String, + id: LibraryItemId, }, LibraryItemRemoved { - id: String, + id: LibraryItemId, }, LibraryItemRewinded { - id: String, + id: LibraryItemId, + }, + LibraryItemNotificationsToggled { + id: LibraryItemId, }, MagnetParsed { magnet: Url, diff --git a/src/types/addon/request.rs b/src/types/addon/request.rs index bf87851d9..baf5d78b3 100644 --- a/src/types/addon/request.rs +++ b/src/types/addon/request.rs @@ -76,7 +76,7 @@ pub struct ResourcePath { /// Extra query parameters to be passed to the endpoint /// /// When calling the endpoint using the [`AddonHTTPTransport`](crate::addon_transport::AddonHTTPTransport), - /// they will be encoded using [`query_params_encode`](crate::types::query_params_encode). + /// they will be encoded using [`query_params_encode()`](crate::types::query_params_encode()). pub extra: Vec, } diff --git a/src/types/library/library_item.rs b/src/types/library/library_item.rs index 9a628b6f1..a8230b968 100644 --- a/src/types/library/library_item.rs +++ b/src/types/library/library_item.rs @@ -12,12 +12,14 @@ use crate::{ types::resource::{MetaItemBehaviorHints, MetaItemPreview, PosterShape, Video}, }; +pub type LibraryItemId = String; + #[serde_as] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct LibraryItem { #[serde(rename = "_id")] - pub id: String, + pub id: LibraryItemId, pub name: String, pub r#type: String, #[serde(default)] @@ -51,7 +53,7 @@ impl LibraryItem { self.r#type != "other" && (!self.removed || self.temp) && self.state.time_offset > 0 } pub fn should_pull_notifications(&self) -> bool { - !self.state.notifications_disabled + !self.state.no_notif && self.r#type != "other" && self.r#type != "movie" && self.behavior_hints.default_video_id.is_none() @@ -141,7 +143,7 @@ pub struct LibraryItemState { /// /// Default: receive notifications #[serde(default)] - pub notifications_disabled: bool, + pub no_notif: bool, } impl LibraryItemState { diff --git a/src/unit_tests/ctx/add_to_library.rs b/src/unit_tests/ctx/add_to_library.rs index cfd2fe987..4eaba0af8 100644 --- a/src/unit_tests/ctx/add_to_library.rs +++ b/src/unit_tests/ctx/add_to_library.rs @@ -31,7 +31,7 @@ fn actionctx_addtolibrary() { url, method, body, .. } if url == "https://api.strem.io/api/datastorePut" && method == "POST" - && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":false,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-01T00:00:00Z\",\"state\":{\"lastWatched\":\"2020-01-01T00:00:00Z\",\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"notificationsDisabled\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => + && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":false,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-01T00:00:00Z\",\"state\":{\"lastWatched\":\"2020-01-01T00:00:00Z\",\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"noNotif\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => { future::ok(Box::new(APIResult::Ok { result: SuccessResponse { success: True {} }, diff --git a/src/unit_tests/ctx/notifications/data.json b/src/unit_tests/ctx/notifications/data.json index abd144e8f..f3e900dd0 100644 --- a/src/unit_tests/ctx/notifications/data.json +++ b/src/unit_tests/ctx/notifications/data.json @@ -86,7 +86,7 @@ "duration": 101, "video_id": "tt1:1:5", "lastVideoReleased": "2020-01-01T00:00:00.000Z", - "notificationsDisabled": false + "noNotif": false } } ], diff --git a/src/unit_tests/ctx/pull_notifications.rs b/src/unit_tests/ctx/pull_notifications.rs index 0cd81e694..5276d9cee 100644 --- a/src/unit_tests/ctx/pull_notifications.rs +++ b/src/unit_tests/ctx/pull_notifications.rs @@ -202,7 +202,7 @@ fn pull_notifications() { last_video_released: Some( Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), ), - notifications_disabled: false, + no_notif: false, }, behavior_hints: Default::default(), }], diff --git a/src/unit_tests/ctx/remove_from_library.rs b/src/unit_tests/ctx/remove_from_library.rs index 395ac077a..59c2eae85 100644 --- a/src/unit_tests/ctx/remove_from_library.rs +++ b/src/unit_tests/ctx/remove_from_library.rs @@ -29,7 +29,7 @@ fn actionctx_removefromlibrary() { url, method, body, .. } if url == "https://api.strem.io/api/datastorePut" && method == "POST" - && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":true,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-02T00:00:00Z\",\"state\":{\"lastWatched\":null,\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"notificationsDisabled\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => + && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":true,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-02T00:00:00Z\",\"state\":{\"lastWatched\":null,\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"noNotif\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => { future::ok(Box::new(APIResult::Ok { result: SuccessResponse { success: True {} }, diff --git a/src/unit_tests/ctx/rewind_library_item.rs b/src/unit_tests/ctx/rewind_library_item.rs index 4e66848a4..2e2ca7cbc 100644 --- a/src/unit_tests/ctx/rewind_library_item.rs +++ b/src/unit_tests/ctx/rewind_library_item.rs @@ -29,7 +29,7 @@ fn actionctx_rewindlibraryitem() { url, method, body, .. } if url == "https://api.strem.io/api/datastorePut" && method == "POST" - && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":false,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-02T00:00:00Z\",\"state\":{\"lastWatched\":null,\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"notificationsDisabled\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => + && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"changes\":[{\"_id\":\"id\",\"name\":\"name\",\"type\":\"type\",\"poster\":null,\"posterShape\":\"poster\",\"removed\":false,\"temp\":false,\"_ctime\":\"2020-01-01T00:00:00Z\",\"_mtime\":\"2020-01-02T00:00:00Z\",\"state\":{\"lastWatched\":\"2020-01-02T00:00:00Z\",\"timeWatched\":0,\"timeOffset\":0,\"overallTimeWatched\":0,\"timesWatched\":0,\"flaggedWatched\":0,\"duration\":0,\"video_id\":null,\"watched\":null,\"lastVideoReleased\":null,\"noNotif\":false},\"behaviorHints\":{\"defaultVideoId\":null,\"featuredVideoId\":null,\"hasScheduledVideos\":false}}]}" => { future::ok(Box::new(APIResult::Ok { result: SuccessResponse { success: True {} }, @@ -58,6 +58,7 @@ fn actionctx_rewindlibraryitem() { mtime: Utc.with_ymd_and_hms(2020, 1, 2, 0, 0, 0).unwrap(), state: LibraryItemState { time_offset: 0, + last_watched: Some(Utc.with_ymd_and_hms(2020, 1, 2, 0, 0, 0).unwrap()), ..LibraryItemState::default() }, ..library_item.to_owned() diff --git a/src/unit_tests/deep_links/library_item_deep_links.rs b/src/unit_tests/deep_links/library_item_deep_links.rs index 94d3b581a..f9dc57399 100644 --- a/src/unit_tests/deep_links/library_item_deep_links.rs +++ b/src/unit_tests/deep_links/library_item_deep_links.rs @@ -30,7 +30,7 @@ fn library_item_deep_links() { video_id: None, watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: Default::default(), }; @@ -68,7 +68,7 @@ fn library_item_deep_links_state_video_id_no_time_offset() { video_id: Some("video_id".to_string()), watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: Default::default(), }; @@ -106,7 +106,7 @@ fn library_item_deep_links_state_video_id() { video_id: Some("video_id".to_string()), watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: Default::default(), }; @@ -146,7 +146,7 @@ fn library_item_deep_links_behavior_hints_default_video_id() { video_id: None, watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: MetaItemBehaviorHints { default_video_id: Some("bh_video_id".to_string()), @@ -188,7 +188,7 @@ fn library_item_deep_links_state_and_behavior_hints_default_video_id() { video_id: Some("video_id".to_string()), watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: MetaItemBehaviorHints { default_video_id: Some("bh_video_id".to_string()), @@ -230,7 +230,7 @@ fn library_item_deep_links_state_no_time_offset_and_behavior_hints_default_video video_id: Some("video_id".to_string()), watched: None, last_video_released: None, - notifications_disabled: true, + no_notif: true, }, behavior_hints: MetaItemBehaviorHints { default_video_id: Some("bh_video_id".to_string()), diff --git a/src/unit_tests/serde/default_tokens_ext.rs b/src/unit_tests/serde/default_tokens_ext.rs index db20f8559..b630dd2ba 100644 --- a/src/unit_tests/serde/default_tokens_ext.rs +++ b/src/unit_tests/serde/default_tokens_ext.rs @@ -46,7 +46,7 @@ impl DefaultTokens for LibraryItemState { Token::None, Token::Str("lastVideoReleased"), Token::None, - Token::Str("notificationsDisabled"), + Token::Str("noNotif"), Token::Bool(false), Token::StructEnd, ] diff --git a/src/unit_tests/serde/library_item_state.rs b/src/unit_tests/serde/library_item_state.rs index 9d0e910f6..d2721eef6 100644 --- a/src/unit_tests/serde/library_item_state.rs +++ b/src/unit_tests/serde/library_item_state.rs @@ -17,7 +17,7 @@ fn library_item_state() { video_id: Some("tt2934286:1:5".to_owned()), watched: Some("tt2934286:1:5:5:eJyTZwAAAEAAIA==".parse().unwrap()), last_video_released: Some(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap()), - notifications_disabled: true, + no_notif: true, }, LibraryItemState { last_watched: None, @@ -30,7 +30,7 @@ fn library_item_state() { video_id: None, watched: None, last_video_released: None, - notifications_disabled: false, + no_notif: false, }, ], &[ @@ -63,7 +63,7 @@ fn library_item_state() { Token::Str("lastVideoReleased"), Token::Some, Token::Str("2020-01-01T00:00:00Z"), - Token::Str("notificationsDisabled"), + Token::Str("noNotif"), Token::Bool(true), Token::StructEnd, Token::Struct { @@ -90,7 +90,7 @@ fn library_item_state() { Token::None, Token::Str("lastVideoReleased"), Token::None, - Token::Str("notificationsDisabled"), + Token::Str("noNotif"), Token::Bool(false), Token::StructEnd, Token::SeqEnd, @@ -109,7 +109,7 @@ fn library_item_state() { video_id: None, watched: None, last_video_released: None, - notifications_disabled: false, + no_notif: false, }, LibraryItemState { last_watched: None, @@ -122,7 +122,7 @@ fn library_item_state() { video_id: None, watched: None, last_video_released: None, - notifications_disabled: false, + no_notif: false, }, ], &[ @@ -143,7 +143,7 @@ fn library_item_state() { Token::U32(0), Token::Str("duration"), Token::U64(0), - Token::Str("notificationsDisabled"), + Token::Str("noNotif"), Token::Bool(false), Token::StructEnd, Token::Struct { @@ -174,7 +174,7 @@ fn library_item_state() { Token::Str("lastVideoReleased"), Token::Some, Token::Str(""), - Token::Str("notificationsDisabled"), + Token::Str("noNotif"), Token::Bool(false), Token::StructEnd, Token::SeqEnd, diff --git a/stremio-analytics/Cargo.toml b/stremio-analytics/Cargo.toml deleted file mode 100644 index 770091eb7..000000000 --- a/stremio-analytics/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "stremio-analytics" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -doctest = false - -[dependencies] -stremio-core = { path="../" } -serde = "1.0.*" -serde_json = "1.0.*" -futures = "0.3.*" -enclose = "1.1.*" -derivative = "2.2.*" diff --git a/stremio-derive/Cargo.toml b/stremio-derive/Cargo.toml index a447db2d9..e9e3d4e4b 100644 --- a/stremio-derive/Cargo.toml +++ b/stremio-derive/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "stremio-derive" version = "0.1.0" -edition = "2018" +edition = "2021" publish = false [lib] proc-macro = true [dependencies] -syn = "1.0.*" -quote = "1.0.*" -proc-macro2 = "1.0.*" -proc-macro-crate = "0.1.*" -proc_macro_roids = "0.7.*" -case = "1.0.*" +syn = "2" +quote = "1" +proc-macro2 = "1" +proc-macro-crate = "1" +proc_macro_roids = "0.8" +case = "1" diff --git a/stremio-derive/src/lib.rs b/stremio-derive/src/lib.rs index ff2551e28..6bcbe6228 100644 --- a/stremio-derive/src/lib.rs +++ b/stremio-derive/src/lib.rs @@ -1,7 +1,7 @@ use case::CaseExt; use proc_macro::TokenStream; use proc_macro2::Span; -use proc_macro_crate::crate_name; +use proc_macro_crate::{crate_name, FoundCrate}; use proc_macro_roids::IdentExt; use quote::quote; use std::borrow::Cow; @@ -30,7 +30,7 @@ pub fn model_derive(input: TokenStream) -> TokenStream { let env_ident = input .attrs .iter() - .find(|attr| attr.path.is_ident("model")) + .find(|attr| attr.path().is_ident("model")) .expect("model attribute required") .parse_args::() .expect("model attribute parse failed"); @@ -136,10 +136,13 @@ pub fn model_derive(input: TokenStream) -> TokenStream { } } -fn get_core_ident() -> Result { +fn get_core_ident() -> Result { let core_crate_name = match env::var("CARGO_PKG_NAME") { Ok(cargo_pkg_name) if cargo_pkg_name == CORE_CRATE_ORIGINAL_NAME => Cow::Borrowed("crate"), - _ => Cow::Owned(crate_name(CORE_CRATE_ORIGINAL_NAME)?), + _ => match crate_name(CORE_CRATE_ORIGINAL_NAME)? { + FoundCrate::Itself => Cow::Borrowed("crate"), + FoundCrate::Name(name) => Cow::Owned(name), + }, }; Ok(Ident::new(&core_crate_name, Span::call_site())) }