From 1fb45740c56332d17ffaf22259cf4440302805cd Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Mon, 24 Jul 2023 11:38:28 +0300 Subject: [PATCH 01/25] fix: rename fields of ActionSearch::Search Signed-off-by: Lachezar Lechev --- src/runtime/msg/action.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/msg/action.rs b/src/runtime/msg/action.rs index 1dfb32c08..50547dfaf 100644 --- a/src/runtime/msg/action.rs +++ b/src/runtime/msg/action.rs @@ -131,6 +131,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, From 9e5829f70da795a1a96f621795272c87c7b265e4 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Aug 2023 16:29:50 +0200 Subject: [PATCH 02/25] feat: toggle notifications for library item --- src/models/ctx/update_library.rs | 19 +++++++++++++++++++ src/runtime/msg/action.rs | 1 + src/runtime/msg/event.rs | 3 +++ 3 files changed, 23 insertions(+) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index 3f87822e6..30ab002d9 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -78,6 +78,25 @@ pub fn update_library( })) .unchanged(), }, + Msg::Action(Action::Ctx(ActionCtx::ToggleLibraryItemNotifications(id))) => { + match library.items.get(id) { + Some(library_item) => { + let mut library_item = library_item.to_owned(); + library_item.state.notifications_disabled = + !library_item.state.notifications_disabled; + 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..55dff77ae 100644 --- a/src/runtime/msg/action.rs +++ b/src/runtime/msg/action.rs @@ -31,6 +31,7 @@ pub enum ActionCtx { AddToLibrary(MetaItemPreview), RemoveFromLibrary(String), RewindLibraryItem(String), + ToggleLibraryItemNotifications(MetaItemId), /// Dismiss all Notification for a given [`MetaItemId`]. DismissNotificationItem(MetaItemId), PushUserToAPI, diff --git a/src/runtime/msg/event.rs b/src/runtime/msg/event.rs index 4121bd901..1d13d053a 100644 --- a/src/runtime/msg/event.rs +++ b/src/runtime/msg/event.rs @@ -103,6 +103,9 @@ pub enum Event { LibraryItemRewinded { id: String, }, + LibraryItemNotificationsToggled { + id: String, + }, MagnetParsed { magnet: Url, }, From 213c02bb1cfe71e68af4a8d10362f46f55123cd5 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Aug 2023 18:31:28 +0200 Subject: [PATCH 03/25] refactor: rename notifications_disabled to no_notif --- src/models/ctx/update_library.rs | 3 +-- src/types/library/library_item.rs | 4 ++-- src/unit_tests/ctx/add_to_library.rs | 2 +- src/unit_tests/ctx/notifications/data.json | 2 +- src/unit_tests/ctx/pull_notifications.rs | 2 +- src/unit_tests/ctx/remove_from_library.rs | 2 +- src/unit_tests/ctx/rewind_library_item.rs | 2 +- .../deep_links/library_item_deep_links.rs | 12 ++++++------ src/unit_tests/serde/default_tokens_ext.rs | 2 +- src/unit_tests/serde/library_item_state.rs | 16 ++++++++-------- 10 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index 30ab002d9..c5114b278 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -82,8 +82,7 @@ pub fn update_library( match library.items.get(id) { Some(library_item) => { let mut library_item = library_item.to_owned(); - library_item.state.notifications_disabled = - !library_item.state.notifications_disabled; + library_item.state.no_notif = !library_item.state.no_notif; Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(library_item))) .join(Effects::msg(Msg::Event( Event::LibraryItemNotificationsToggled { id: id.to_owned() }, diff --git a/src/types/library/library_item.rs b/src/types/library/library_item.rs index 9a628b6f1..1c872fd47 100644 --- a/src/types/library/library_item.rs +++ b/src/types/library/library_item.rs @@ -51,7 +51,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 +141,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..1556b2c27 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\":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/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, From bbc2dd573f69ad486db43f89cc2a27f88cf055d8 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 2 Aug 2023 09:02:27 +0200 Subject: [PATCH 04/25] refactor: add state argument to ToggleLibraryItemNotifications --- src/models/ctx/update_library.rs | 4 ++-- src/runtime/msg/action.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index c5114b278..99357a363 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -78,11 +78,11 @@ pub fn update_library( })) .unchanged(), }, - Msg::Action(Action::Ctx(ActionCtx::ToggleLibraryItemNotifications(id))) => { + 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 = !library_item.state.no_notif; + 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() }, diff --git a/src/runtime/msg/action.rs b/src/runtime/msg/action.rs index 55dff77ae..29bbfb9f8 100644 --- a/src/runtime/msg/action.rs +++ b/src/runtime/msg/action.rs @@ -31,7 +31,7 @@ pub enum ActionCtx { AddToLibrary(MetaItemPreview), RemoveFromLibrary(String), RewindLibraryItem(String), - ToggleLibraryItemNotifications(MetaItemId), + ToggleLibraryItemNotifications(MetaItemId, bool), /// Dismiss all Notification for a given [`MetaItemId`]. DismissNotificationItem(MetaItemId), PushUserToAPI, From 397459c1e71d85ed864920034d447c6eb5930aa4 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 10:37:58 +0300 Subject: [PATCH 05/25] chore: run cargo fix and change edition to 2021 Signed-off-by: Lachezar Lechev --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b97e85f7e..266256f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "stremio-core" version = "0.1.0" authors = ["Smart Code OOD"] -edition = "2018" +edition = "2021" rust-version = "1.60" From 05afb407716416adbc11ecba273c9f2420570945 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 10:42:10 +0300 Subject: [PATCH 06/25] chore: stremio-derive to 2021 edition Signed-off-by: Lachezar Lechev --- stremio-derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stremio-derive/Cargo.toml b/stremio-derive/Cargo.toml index a447db2d9..c37e99081 100644 --- a/stremio-derive/Cargo.toml +++ b/stremio-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "stremio-derive" version = "0.1.0" -edition = "2018" +edition = "2021" publish = false [lib] From f9160e71137202a988654305282fe4b1e727b241 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 10:48:31 +0300 Subject: [PATCH 07/25] chore: update core deps. Signed-off-by: Lachezar Lechev --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 266256f74..a808fe1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ 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 +51,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" From 6204b9e821d02afb041e310d1075e11d70f4ed9e Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 11:01:28 +0300 Subject: [PATCH 08/25] chore: Update dependencies of stremio-derive Signed-off-by: Lachezar Lechev --- stremio-derive/Cargo.toml | 12 ++++++------ stremio-derive/src/lib.rs | 11 +++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/stremio-derive/Cargo.toml b/stremio-derive/Cargo.toml index c37e99081..e9e3d4e4b 100644 --- a/stremio-derive/Cargo.toml +++ b/stremio-derive/Cargo.toml @@ -8,9 +8,9 @@ publish = false 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())) } From 3dae40eaa34275e3b11af9141efba5dfb43d1c70 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 13:35:48 +0300 Subject: [PATCH 09/25] feat: export derive Model macro with feature Signed-off-by: Lachezar Lechev --- Cargo.toml | 4 ++++ src/constants.rs | 2 +- src/lib.rs | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b97e85f7e..8689de36b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,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,6 +28,9 @@ doctest = false # see https://github.com/rustwasm/wasm-bindgen/issues/2833 env-future-send = [] +# Exports the Model derive macro from `stremio-derive` +derive = [] + [dependencies] stremio-derive = { path = "stremio-derive" } stremio-watched-bitfield = { path = "stremio-watched-bitfield" } 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/lib.rs b/src/lib.rs index 6875e3ecb..714428dbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,15 @@ +//! # 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; pub mod deep_links; pub mod models; From cfd614543835b0954aa20afa9b884b95ebb44e6e Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 13:54:14 +0300 Subject: [PATCH 10/25] feat: move analytics to core behind a feature Signed-off-by: Lachezar Lechev --- Cargo.toml | 3 + src/analytics.rs | 174 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 3 files changed, 179 insertions(+) create mode 100644 src/analytics.rs diff --git a/Cargo.toml b/Cargo.toml index 8689de36b..62ea44f67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,9 @@ 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" } diff --git a/src/analytics.rs b/src/analytics.rs new file mode 100644 index 000000000..2191dcdcb --- /dev/null +++ b/src/analytics.rs @@ -0,0 +1,174 @@ +use std::{ + collections::VecDeque, + marker::PhantomData, + sync::{Arc, Mutex}, +}; + +use derivative::Derivative; +use enclose::enclose; +use futures::{ + future::{self, Either}, + Future, FutureExt, +}; +use serde::Serialize; + +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 crate::{runtime::EnvFutureExt, types::True}; + +#[derive(Clone, PartialEq, Serialize, Debug)] +struct Event { + #[serde(flatten)] + data: serde_json::Value, + #[serde(rename = "eventName")] + name: String, + #[serde(rename = "eventTime")] + time: i64, + #[serde(rename = "eventNumber")] + number: u64, + #[serde(rename = "app")] + context: serde_json::Value, +} + +#[derive(Clone, PartialEq, Debug)] +struct EventsBatch { + auth_key: AuthKey, + events: Vec, +} + +#[derive(Default)] +struct State { + number: u64, + queue: VecDeque, + pending: Option, +} + +impl State { + fn next_number(&mut self) -> u64 { + self.number = self.number.wrapping_add(1); + self.number + } + fn pop_batch(&mut self) -> Option { + self.queue.pop_front() + } + fn push_event(&mut self, event: Event, auth_key: AuthKey) { + match self.queue.back_mut() { + Some(batch) if batch.auth_key == auth_key => { + batch.events.push(event); + } + _ => self.queue.push_back(EventsBatch { + auth_key, + events: vec![event], + }), + }; + } + fn revert_pending(&mut self) { + if let Some(batch) = self.pending.take() { + self.queue.push_front(batch); + }; + } +} + +#[derive(Derivative)] +#[derivative(Default)] +pub struct Analytics { + state: Arc>, + env: PhantomData, +} + +impl Analytics { + pub fn emit( + &self, + name: String, + data: serde_json::Value, + ctx: &Ctx, + streaming_server: &StreamingServer, + path: &str, + ) { + let mut state = self.state.lock().expect("analytics state lock failed"); + let auth_key = match ctx.profile.auth_key() { + Some(auth_key) => auth_key.to_owned(), + _ => return, + }; + let event = Event { + name, + data, + number: state.next_number(), + time: E::now().timestamp_millis(), + context: E::analytics_context(ctx, streaming_server, path), + }; + state.push_event(event, auth_key); + } + pub fn send_next_batch(&self) -> impl Future { + let mut state = self.state.lock().expect("analytics state lock failed"); + if state.pending.is_none() { + let batch = state.pop_batch(); + if let Some(batch) = batch { + state.pending = Some(batch.to_owned()); + return Either::Left( + send_events_batch_to_api::(&batch) + .map(|result| match result { + Ok(APIResult::Err { error }) if error.code != 1 => Err(()), + Err(EnvError::Fetch(_)) | Err(EnvError::Serde(_)) => Err(()), + _ => Ok(()), + }) + .then(enclose!((self.state => state) move |result| async move { + let mut state = state.lock().expect("analytics state lock failed"); + if state.pending == Some(batch) { + match result { + Ok(_) => { + state.pending = None; + } + Err(_) => { + state.revert_pending(); + } + }; + }; + })), + ); + }; + }; + Either::Right(future::ready(())) + } + pub fn flush(&self) -> impl Future { + let mut state = self.state.lock().expect("analytics state lock failed"); + state.pending = None; + future::join_all( + state + .queue + .drain(..) + .map(|batch| send_events_batch_to_api::(&batch)), + ) + .map(|_| ()) + } +} + +fn send_events_batch_to_api( + batch: &EventsBatch, +) -> TryEnvFuture> { + #[cfg(debug_assertions)] + if cfg!(debug_assertions) { + E::log(format!("send_events_batch_to_api: {:#?}", &batch)); + return future::ok(APIResult::Ok { + result: SuccessResponse { success: True }, + }) + .boxed_env(); + }; + + fetch_api::(&APIRequest::Events { + auth_key: batch.auth_key.to_owned(), + events: batch + .events + .iter() + .map(|value| serde_json::to_value(value).unwrap()) + .collect(), + }) +} diff --git a/src/lib.rs b/src/lib.rs index 714428dbd..0ea067de4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,8 @@ 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; From 3ca015a470c5e6f10661e2d001c099fdd2661766 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 4 Aug 2023 13:55:52 +0300 Subject: [PATCH 11/25] chore: remove stremio-analytics Signed-off-by: Lachezar Lechev --- Cargo.toml | 1 - stremio-analytics/Cargo.toml | 16 ---- stremio-analytics/src/lib.rs | 166 ----------------------------------- 3 files changed, 183 deletions(-) delete mode 100644 stremio-analytics/Cargo.toml delete mode 100644 stremio-analytics/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 62ea44f67..7722ce9f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ rust-version = "1.60" [workspace] members = [ "stremio-derive", - "stremio-analytics", "stremio-watched-bitfield", ] 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-analytics/src/lib.rs b/stremio-analytics/src/lib.rs deleted file mode 100644 index fce13a01d..000000000 --- a/stremio-analytics/src/lib.rs +++ /dev/null @@ -1,166 +0,0 @@ -use derivative::Derivative; -use enclose::enclose; -use futures::future::Either; -use futures::{future, 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; -#[cfg(debug_assertions)] -use stremio_core::types::True; - -#[derive(Clone, PartialEq, Serialize, Debug)] -struct Event { - #[serde(flatten)] - data: serde_json::Value, - #[serde(rename = "eventName")] - name: String, - #[serde(rename = "eventTime")] - time: i64, - #[serde(rename = "eventNumber")] - number: u64, - #[serde(rename = "app")] - context: serde_json::Value, -} - -#[derive(Clone, PartialEq, Debug)] -struct EventsBatch { - auth_key: AuthKey, - events: Vec, -} - -#[derive(Default)] -struct State { - number: u64, - queue: VecDeque, - pending: Option, -} - -impl State { - fn next_number(&mut self) -> u64 { - self.number = self.number.wrapping_add(1); - self.number - } - fn pop_batch(&mut self) -> Option { - self.queue.pop_front() - } - fn push_event(&mut self, event: Event, auth_key: AuthKey) { - match self.queue.back_mut() { - Some(batch) if batch.auth_key == auth_key => { - batch.events.push(event); - } - _ => self.queue.push_back(EventsBatch { - auth_key, - events: vec![event], - }), - }; - } - fn revert_pending(&mut self) { - if let Some(batch) = self.pending.take() { - self.queue.push_front(batch); - }; - } -} - -#[derive(Derivative)] -#[derivative(Default)] -pub struct Analytics { - state: Arc>, - env: PhantomData, -} - -impl Analytics { - pub fn emit( - &self, - name: String, - data: serde_json::Value, - ctx: &Ctx, - streaming_server: &StreamingServer, - path: &str, - ) { - let mut state = self.state.lock().expect("analytics state lock failed"); - let auth_key = match ctx.profile.auth_key() { - Some(auth_key) => auth_key.to_owned(), - _ => return, - }; - let event = Event { - name, - data, - number: state.next_number(), - time: E::now().timestamp_millis(), - context: E::analytics_context(ctx, streaming_server, path), - }; - state.push_event(event, auth_key); - } - pub fn send_next_batch(&self) -> impl Future { - let mut state = self.state.lock().expect("analytics state lock failed"); - if state.pending.is_none() { - let batch = state.pop_batch(); - if let Some(batch) = batch { - state.pending = Some(batch.to_owned()); - return Either::Left( - send_events_batch_to_api::(&batch) - .map(|result| match result { - Ok(APIResult::Err { error }) if error.code != 1 => Err(()), - Err(EnvError::Fetch(_)) | Err(EnvError::Serde(_)) => Err(()), - _ => Ok(()), - }) - .then(enclose!((self.state => state) move |result| async move { - let mut state = state.lock().expect("analytics state lock failed"); - if state.pending == Some(batch) { - match result { - Ok(_) => { - state.pending = None; - } - Err(_) => { - state.revert_pending(); - } - }; - }; - })), - ); - }; - }; - Either::Right(future::ready(())) - } - pub fn flush(&self) -> impl Future { - let mut state = self.state.lock().expect("analytics state lock failed"); - state.pending = None; - future::join_all( - state - .queue - .drain(..) - .map(|batch| send_events_batch_to_api::(&batch)), - ) - .map(|_| ()) - } -} - -fn send_events_batch_to_api( - batch: &EventsBatch, -) -> TryEnvFuture> { - #[cfg(debug_assertions)] - if cfg!(debug_assertions) { - E::log(format!("send_events_batch_to_api: {:#?}", &batch)); - return future::ok(APIResult::Ok { - result: SuccessResponse { success: True }, - }) - .boxed_env(); - }; - - fetch_api::(&APIRequest::Events { - auth_key: batch.auth_key.to_owned(), - events: batch - .events - .iter() - .map(|value| serde_json::to_value(value).unwrap()) - .collect(), - }) -} From c68395fd5441baf91bb98d5a79c8efa9401f835c Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Mon, 7 Aug 2023 11:25:26 +0300 Subject: [PATCH 12/25] chore: Add docs for deep_links Signed-off-by: Lachezar Lechev --- src/deep_links/mod.rs | 47 +++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) 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, From 1f1af6b7cccd4ee1977d3dd3d0121154a6e06c4c Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 10:54:58 +0300 Subject: [PATCH 13/25] feat: update Toggle notif. for LibraryItem Signed-off-by: Lachezar Lechev --- src/models/ctx/update_library.rs | 35 +++++++++++----------- src/runtime/msg/action.rs | 49 ++++++++++++++++++++----------- src/runtime/msg/event.rs | 9 +++--- src/types/library/library_item.rs | 4 ++- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index 99357a363..0579a925a 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -78,24 +78,25 @@ 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::ToggleLibraryItemNotifications { + id, + no_notifications, + })) => match library.items.get(id) { + Some(library_item) => { + let mut library_item = library_item.to_owned(); + library_item.state.no_notif = *no_notifications; + 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 29bbfb9f8..5301da08b 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,7 +41,12 @@ pub enum ActionCtx { AddToLibrary(MetaItemPreview), RemoveFromLibrary(String), RewindLibraryItem(String), - ToggleLibraryItemNotifications(MetaItemId, bool), + #[serde(rename_all = "camelCase")] + ToggleLibraryItemNotifications { + id: LibraryItemId, + /// If set to `true` it will disable notifications for the LibraryItem. + no_notifications: bool, + }, /// Dismiss all Notification for a given [`MetaItemId`]. DismissNotificationItem(MetaItemId), PushUserToAPI, diff --git a/src/runtime/msg/event.rs b/src/runtime/msg/event.rs index 1d13d053a..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,16 +96,16 @@ pub enum Event { settings: Settings, }, LibraryItemAdded { - id: String, + id: LibraryItemId, }, LibraryItemRemoved { - id: String, + id: LibraryItemId, }, LibraryItemRewinded { - id: String, + id: LibraryItemId, }, LibraryItemNotificationsToggled { - id: String, + id: LibraryItemId, }, MagnetParsed { magnet: Url, diff --git a/src/types/library/library_item.rs b/src/types/library/library_item.rs index 1c872fd47..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)] From cd475e1d5119960d2da7c1b13fbfad3b2e81f23b Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 13:42:27 +0300 Subject: [PATCH 14/25] fix: revert named fields for action Signed-off-by: Lachezar Lechev --- src/models/ctx/update_library.rs | 35 ++++++++++++++++---------------- src/runtime/msg/action.rs | 8 ++------ 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index 0579a925a..99357a363 100644 --- a/src/models/ctx/update_library.rs +++ b/src/models/ctx/update_library.rs @@ -78,25 +78,24 @@ pub fn update_library( })) .unchanged(), }, - Msg::Action(Action::Ctx(ActionCtx::ToggleLibraryItemNotifications { - id, - no_notifications, - })) => match library.items.get(id) { - Some(library_item) => { - let mut library_item = library_item.to_owned(); - library_item.state.no_notif = *no_notifications; - Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(library_item))) - .join(Effects::msg(Msg::Event( - Event::LibraryItemNotificationsToggled { id: id.to_owned() }, - ))) - .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(), } - _ => 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 5301da08b..b7c402418 100644 --- a/src/runtime/msg/action.rs +++ b/src/runtime/msg/action.rs @@ -41,12 +41,8 @@ pub enum ActionCtx { AddToLibrary(MetaItemPreview), RemoveFromLibrary(String), RewindLibraryItem(String), - #[serde(rename_all = "camelCase")] - ToggleLibraryItemNotifications { - id: LibraryItemId, - /// If set to `true` it will disable notifications for the LibraryItem. - no_notifications: bool, - }, + /// 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, From c953490c8a2636e57b8e57f83fa483951ee25547 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:17:40 +0300 Subject: [PATCH 15/25] chore: bump MSRV to 1.61 Signed-off-by: Lachezar Lechev --- .github/workflows/msrv.yml | 12 +++++++----- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index d094dcac7..839798909 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -1,4 +1,4 @@ -name: MSRV 1.60 build +name: MSRV build on: # will checkout the default branch `development` @@ -12,16 +12,18 @@ concurrency: cancel-in-progress: true env: - RUST_MSRV_VERSION: '1.60' + RUST_MSRV_VERSION: '1.61' 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 + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ RUST_MSRV_VERSION }} - name: Checkout uses: actions/checkout@v3 diff --git a/Cargo.toml b/Cargo.toml index 4c139836b..500c8bed5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Smart Code OOD"] edition = "2021" -rust-version = "1.60" +rust-version = "1.61" [workspace] members = [ From 46941d398835a288dbb54682abdd67401b5bc075 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:18:03 +0300 Subject: [PATCH 16/25] ci(docs): build docs using dtolnay/rust-toolchain Signed-off-by: Lachezar Lechev --- .github/workflows/docs.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ecb1d090b..3c356b3cb 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 + 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: From 0c437e001fbd1f22605112123c15b1d7a0dc4ee4 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:19:49 +0300 Subject: [PATCH 17/25] ci(build): use dtolnay/rust-toolchain@stable Signed-off-by: Lachezar Lechev --- .github/workflows/build.yml | 8 +++----- .github/workflows/docs.yml | 2 +- .github/workflows/msrv.yml | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c863d648d..5ca26e666 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,21 +7,19 @@ concurrency: group: ${{ 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 3c356b3cb..fe87a5d8f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: with: submodules: true - - name: Rust setup + - name: Rust setup (nightly) uses: dtolnay/rust-toolchain@master with: # Use nightly to build the docs with `--cfg docsrs` diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 839798909..7c9ff3dfa 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Rust setup + - name: Rust setup (MSRV) uses: dtolnay/rust-toolchain@master with: toolchain: ${{ RUST_MSRV_VERSION }} From 0e9255bc7fd77f155e7b0818ce9cf19237864e2e Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:24:35 +0300 Subject: [PATCH 18/25] ci(MSRV): allow action to be ran manually Signed-off-by: Lachezar Lechev --- .github/workflows/msrv.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 7c9ff3dfa..b4c86a8c1 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -5,6 +5,8 @@ on: schedule: # run every Friday at 17:00 - cron: '00 17 * * 5' + # Or ran manually + workflow_dispatch: # Stops the running workflow of previous pushes concurrency: From 81039812278e65baf165f9a8838ab737413dd103 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:32:18 +0300 Subject: [PATCH 19/25] fix(docs): intra doc link Signed-off-by: Lachezar Lechev --- src/types/addon/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, } From 5b8dc5a0646e3d0817dcb5902db24a3babadf954 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:29:46 +0300 Subject: [PATCH 20/25] fix(ci): msrv workflow env. variable Signed-off-by: Lachezar Lechev --- .github/workflows/msrv.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index b4c86a8c1..a3ca40e2b 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -25,7 +25,7 @@ jobs: - name: Rust setup (MSRV) uses: dtolnay/rust-toolchain@master with: - toolchain: ${{ RUST_MSRV_VERSION }} + toolchain: ${{ env.RUST_MSRV_VERSION }} - name: Checkout uses: actions/checkout@v3 From a46823d9e2b7d76503abe355a3448e1ea77a2a93 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:38:25 +0300 Subject: [PATCH 21/25] fix(ci): bump MSRV to 1.67.1, cancel per workflows Signed-off-by: Lachezar Lechev --- .github/workflows/build.yml | 3 ++- .github/workflows/msrv.yml | 5 +++-- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ca26e666..0e3b7c553 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,8 @@ 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 jobs: diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index a3ca40e2b..4b632613f 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -10,11 +10,12 @@ on: # 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.61' + RUST_MSRV_VERSION: '1.67.1' jobs: build: diff --git a/Cargo.toml b/Cargo.toml index 500c8bed5..818106e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Smart Code OOD"] edition = "2021" -rust-version = "1.61" +rust-version = "1.67.1" [workspace] members = [ From 1178940669a2b086351c5dc857d51cd210433613 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 8 Aug 2023 14:52:13 +0300 Subject: [PATCH 22/25] feat: README - add badges Signed-off-by: Lachezar Lechev --- README.md | 3 +++ 1 file changed, 3 insertions(+) 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. From 24eb758f5a4acfe9e032ae0c66fd861b693b72d4 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Aug 2023 10:46:09 +0200 Subject: [PATCH 23/25] refactor(update_library): use now time from env --- src/models/ctx/update_library.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/models/ctx/update_library.rs b/src/models/ctx/update_library.rs index 6d12b64fa..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(), From 4518818e76bd93c0ea034dcd3a1f3377af286407 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Aug 2023 10:46:36 +0200 Subject: [PATCH 24/25] fix(test): rewind_library_item --- src/unit_tests/ctx/rewind_library_item.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unit_tests/ctx/rewind_library_item.rs b/src/unit_tests/ctx/rewind_library_item.rs index 1556b2c27..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,\"noNotif\":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() From 8e750a84ed03880b9fda7f3ce41262291cfc8af1 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Aug 2023 11:31:25 +0200 Subject: [PATCH 25/25] refactor(continue_watching_preview): don't update items on UpdateLibraryItem --- src/models/continue_watching_preview.rs | 2 -- 1 file changed, 2 deletions(-) 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)