Skip to content

Commit

Permalink
Merge pull request #535 from Stremio/fix/remove-stream-addon-uninstall
Browse files Browse the repository at this point in the history
Remove stream from bucket upon addon uninstall
  • Loading branch information
elpiel authored Oct 12, 2023
2 parents aa43b09 + 63b5aee commit 97bd700
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 4 deletions.
9 changes: 6 additions & 3 deletions src/models/ctx/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
Some(auth_key) => Effects::one(delete_session::<E>(auth_key)).unchanged(),
_ => Effects::none().unchanged(),
};
let profile_effects = update_profile::<E>(&mut self.profile, &self.status, msg);
let profile_effects =
update_profile::<E>(&mut self.profile, &mut self.streams, &self.status, msg);
let library_effects =
update_library::<E>(&mut self.library, &self.profile, &self.status, msg);
let streams_effects = update_streams::<E>(&mut self.streams, &self.status, msg);
Expand Down Expand Up @@ -110,7 +111,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(notifications_effects)
}
Msg::Internal(Internal::CtxAuthResult(auth_request, result)) => {
let profile_effects = update_profile::<E>(&mut self.profile, &self.status, msg);
let profile_effects =
update_profile::<E>(&mut self.profile, &mut self.streams, &self.status, msg);
let library_effects =
update_library::<E>(&mut self.library, &self.profile, &self.status, msg);
let trakt_addon_effects = update_trakt_addon::<E>(
Expand Down Expand Up @@ -157,7 +159,8 @@ impl<E: Env + 'static> Update<E> for Ctx {
.join(ctx_effects)
}
_ => {
let profile_effects = update_profile::<E>(&mut self.profile, &self.status, msg);
let profile_effects =
update_profile::<E>(&mut self.profile, &mut self.streams, &self.status, msg);
let library_effects =
update_library::<E>(&mut self.library, &self.profile, &self.status, msg);
let streams_effects = update_streams::<E>(&mut self.streams, &self.status, msg);
Expand Down
8 changes: 8 additions & 0 deletions src/models/ctx/update_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ use crate::types::api::{
fetch_api, APIError, APIRequest, APIResult, CollectionResponse, SuccessResponse,
};
use crate::types::profile::{Auth, AuthKey, Profile, Settings, User};
use crate::types::streams::StreamsBucket;
use enclose::enclose;
use futures::{future, FutureExt, TryFutureExt};
use std::collections::HashSet;

pub fn update_profile<E: Env + 'static>(
profile: &mut Profile,
streams: &mut StreamsBucket,
status: &CtxStatus,
msg: &Msg,
) -> Effects {
Expand Down Expand Up @@ -159,6 +161,12 @@ pub fn update_profile<E: Env + 'static>(
if let Some(addon_position) = addon_position {
if !profile.addons[addon_position].flags.protected && !addon.flags.protected {
profile.addons.remove(addon_position);

// Remove stream related to this addon from the streams bucket
streams
.items
.retain(|_key, item| item.stream_transport_url != addon.transport_url);

let push_to_api_effects = match profile.auth_key() {
Some(auth_key) => Effects::one(push_addons_to_api::<E>(
profile.addons.to_owned(),
Expand Down
149 changes: 148 additions & 1 deletion src/unit_tests/ctx/uninstall_addon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::types::api::{APIResult, SuccessResponse};
use crate::types::library::LibraryBucket;
use crate::types::notifications::NotificationsBucket;
use crate::types::profile::{Auth, AuthKey, GDPRConsent, Profile, User};
use crate::types::streams::StreamsBucket;
use crate::types::resource::{Stream, StreamBehaviorHints, StreamSource};
use crate::types::streams::{StreamsBucket, StreamsItem, StreamsItemKey};
use crate::types::True;
use crate::unit_tests::{
default_fetch_handler, Request, TestEnv, FETCH_HANDLER, REQUESTS, STORAGE,
Expand All @@ -18,6 +19,51 @@ use std::any::Any;
use stremio_derive::Model;
use url::Url;

fn create_addon_descriptor(transport_url: &str) -> Descriptor {
Descriptor {
manifest: Manifest {
id: "id".to_owned(),
version: Version::new(0, 0, 1),
name: "name".to_owned(),
contact_email: None,
description: None,
logo: None,
background: None,
types: vec![],
resources: vec![],
id_prefixes: None,
catalogs: vec![],
addon_catalogs: vec![],
behavior_hints: Default::default(),
},
transport_url: Url::parse(transport_url).unwrap(),
flags: Default::default(),
}
}

fn create_addon_streams_item(addon: &Descriptor) -> StreamsItem {
let stream = Stream {
source: StreamSource::Url {
url: "https://source_url".parse().unwrap(),
},
name: None,
description: None,
thumbnail: None,
subtitles: vec![],
behavior_hints: StreamBehaviorHints::default(),
};

StreamsItem {
stream,
r#type: "movie".to_owned(),
meta_id: "tt123456".to_owned(),
video_id: "tt123456:1:0".to_owned(),
meta_transport_url: addon.transport_url.clone(),
stream_transport_url: addon.transport_url.clone(),
mtime: TestEnv::now(),
}
}

#[test]
fn actionctx_uninstalladdon() {
#[derive(Model, Clone, Default)]
Expand Down Expand Up @@ -370,3 +416,104 @@ fn actionctx_uninstalladdon_not_installed() {
"No requests have been sent"
);
}

#[test]
fn actionctx_uninstalladdon_streams_bucket() {
#[derive(Model, Clone, Default)]
#[model(TestEnv)]
struct TestModel {
ctx: Ctx,
}

let addon = create_addon_descriptor("https://transport_url");
let addon_2 = create_addon_descriptor("https://transport_url_2");

let profile = Profile {
addons: vec![addon.to_owned()],
..Default::default()
};
let _env_mutex = TestEnv::reset().expect("Should have exclusive lock to TestEnv");
STORAGE.write().unwrap().insert(
PROFILE_STORAGE_KEY.to_owned(),
serde_json::to_string(&profile).unwrap(),
);

let streams_item_key = StreamsItemKey {
meta_id: "tt123456".to_owned(),
video_id: "tt123456:1:0".to_owned(),
};

let streams_item_key_2 = StreamsItemKey {
meta_id: "tt123456".to_owned(),
video_id: "tt123456:1:1".to_owned(),
};

let stream_item = create_addon_streams_item(&addon);
let stream_item_2 = create_addon_streams_item(&addon_2);

let mut streams = StreamsBucket::default();
streams.items.insert(streams_item_key.clone(), stream_item);
streams
.items
.insert(streams_item_key_2.clone(), stream_item_2);

let (runtime, _rx) = Runtime::<TestEnv, _>::new(
TestModel {
ctx: Ctx::new(
profile,
LibraryBucket::default(),
streams,
NotificationsBucket::new::<TestEnv>(None, vec![]),
),
},
vec![],
1000,
);
TestEnv::run(|| {
runtime.dispatch(RuntimeAction {
field: None,
action: Action::Ctx(ActionCtx::UninstallAddon(addon)),
})
});
assert!(
runtime.model().unwrap().ctx.profile.addons.is_empty(),
"addons updated successfully in memory"
);
assert!(
STORAGE
.read()
.unwrap()
.get(PROFILE_STORAGE_KEY)
.map_or(false, |data| {
serde_json::from_str::<Profile>(data)
.unwrap()
.addons
.is_empty()
}),
"addons updated successfully in storage"
);
assert!(
REQUESTS.read().unwrap().is_empty(),
"No requests have been sent"
);
assert!(
!runtime
.model()
.unwrap()
.ctx
.streams
.items
.contains_key(&streams_item_key),
"stream item was removed from the bucket"
);
assert!(
runtime
.model()
.unwrap()
.ctx
.streams
.items
.contains_key(&streams_item_key_2),
"stream item still is in the bucket"
);
}

0 comments on commit 97bd700

Please sign in to comment.