Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions src/models/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use crate::types::api::{
};
use crate::types::library::{LibraryBucket, LibraryItem};
use crate::types::player::{IntroData, IntroOutro};
use crate::types::profile::Profile;
use crate::types::profile::{AuthKey, Profile};
use crate::types::rating::{Rating, RatingSendRequest, RatingSendResponse};
use crate::types::resource::{
MetaItem, SeriesInfo, Stream, StreamSource, StreamUrls, Subtitles, Video,
};
Expand Down Expand Up @@ -519,7 +520,8 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
duration.clone_into(&mut library_item.state.duration);
}

if library_item.state.flagged_watched == 0
// Watched threshold for marking an episode/movie as watched
let should_send_watched = if library_item.state.flagged_watched == 0
&& library_item.state.time_watched as f64
> library_item.state.duration as f64 * WATCHED_THRESHOLD_COEF
{
Expand All @@ -531,7 +533,33 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
watched_bit_field.set_video(video_id, true);
library_item.state.watched = Some(watched_bit_field.into());
}
}

true
} else {
false
};

// send Watched for MetaDetail id
// single episode should mark the item as watched.
let send_watched_effects = match (
should_send_watched,
ctx.profile.auth_key(),
self.selected.as_ref(),
) {
(
true,
Some(auth_key),
Some(Selected {
meta_request:
Some(ResourceRequest {
path: meta_path, ..
}),
..
}),
) => Effects::one(send_watched::<E>(auth_key.to_owned(), meta_path))
.unchanged(),
_ => Effects::none().unchanged(),
};

if library_item.temp && library_item.state.times_watched == 0 {
library_item.removed = true;
Expand All @@ -553,7 +581,10 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
analytics_context.player_duration = Some(duration.to_owned());
};

push_to_library::<E>(&mut self.push_library_item_time, library_item)
send_watched_effects.join(push_to_library::<E>(
&mut self.push_library_item_time,
library_item,
))
}
_ => Effects::none().unchanged(),
},
Expand Down Expand Up @@ -1546,6 +1577,30 @@ fn get_skip_gaps<E: Env + 'static>(skip_gaps_request: SkipGapsRequest) -> Effect
)
.into()
}

/// Sends watched state for meta item on every Watched state change of the library item
fn send_watched<E: Env + 'static>(auth_key: AuthKey, meta_path: &ResourcePath) -> Effect {
let meta_id = meta_path.id.to_owned();

let request = RatingSendRequest {
auth_key,
meta_item_id: meta_id.to_owned(),
meta_item_type: meta_path.r#type.to_owned(),
rating: Some(Rating::Watched),
};

EffectFuture::Concurrent(
E::fetch::<_, RatingSendResponse>(request.into())
.map(enclose::enclose!((meta_id) move |result| {
Msg::Internal(Internal::WatchedSendResult(
meta_id, result,
))
}))
.boxed_env(),
)
.into()
}

#[cfg(test)]
mod tests {
use chrono::Utc;
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/msg/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,9 @@ pub enum Internal {
DismissedEventsChanged,
RatingGetStatusResult(MetaItemId, Result<RatingGetStatusResponse, EnvError>),
RatingSendResult(MetaItemId, Result<RatingSendResponse, EnvError>),
/// Internal to core, sends the watched update when needed:
/// Mark video as watched
/// Mark Season as watched (meta item)
/// Mark move as watched (meta item)
WatchedSendResult(MetaItemId, Result<RatingSendResponse, EnvError>),
}