Skip to content

Commit

Permalink
feat(player): add Seek player action
Browse files Browse the repository at this point in the history
Signed-off-by: Lachezar Lechev <lachezar@ambire.com>
  • Loading branch information
elpiel committed May 16, 2024
1 parent 4a0b991 commit 0a0db93
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 46 deletions.
154 changes: 108 additions & 46 deletions src/models/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,70 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
}))
.unchanged()
}
Msg::Action(Action::Player(ActionPlayer::Seek {
time,
duration,
device,
})) => match (&self.selected, &mut self.library_item) {
(
// make sure we have a Selected
Some(_selected),
Some(library_item),
) => {
// We might want to consider whether we want to update the LibraryItem for next video
// like we do for TimeChanged

// update the last_watched
library_item.state.last_watched = Some(E::now());
// seek logging
if library_item.r#type == "series" && time < &PLAYER_IGNORE_SEEK_AFTER {
self.seek_history.push(SeekLog {
from: library_item.state.time_offset,
to: *time,
});
}
// };
time.clone_into(&mut library_item.state.time_offset);
duration.clone_into(&mut library_item.state.duration);
// No need to check and flag the library item as watched,
// seeking does not update the time_watched!

// Nor there's a need to update removed and temp, this can only happen
// after we mark a LibraryItem as watched! Leave this to TimeChanged

// Update the analytics, we still want to keep the correct time and duration updated
if let Some(analytics_context) = &mut self.analytics_context {
library_item
.state
.video_id
.clone_into(&mut analytics_context.video_id);
analytics_context.time = Some(library_item.state.time_offset);
analytics_context.duration = Some(library_item.state.duration);
analytics_context.device_type = Some(device.to_owned());
analytics_context.device_name = Some(device.to_owned());
analytics_context.player_duration = Some(duration.to_owned());
};

// on seeking we want to make sure we send the correct Trakt events
let trakt_event_effects = match (self.loaded, self.paused) {
(true, Some(true)) => Effects::msg(Msg::Event(Event::TraktPaused {
context: self.analytics_context.as_ref().cloned().unwrap_or_default(),
}))
.unchanged(),
(true, Some(false)) => Effects::msg(Msg::Event(Event::TraktPlaying {
context: self.analytics_context.as_ref().cloned().unwrap_or_default(),
}))
.unchanged(),
_ => Effects::none(),
};

let push_to_library_effects =
push_to_library::<E>(&mut self.push_library_item_time, library_item);

trakt_event_effects.join(push_to_library_effects)
}
_ => Effects::none().unchanged(),
},
Msg::Action(Action::Player(ActionPlayer::TimeChanged {
time,
duration,
Expand All @@ -396,8 +460,6 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
}),
Some(library_item),
) => {
let seeking = library_item.state.time_offset.abs_diff(*time) > 1000;

// if we've selected a new video (like the next episode)
library_item.state.last_watched = Some(E::now());
if library_item.state.video_id != Some(video_id.to_owned()) {
Expand All @@ -409,20 +471,7 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
library_item.state.time_watched = 0;
library_item.state.flagged_watched = 0;
} else {
// else we have added to the currently selected video/stream
// seek logging
if seeking
&& library_item.r#type == "series"
&& time < &PLAYER_IGNORE_SEEK_AFTER
{
self.seek_history.push(SeekLog {
from: library_item.state.time_offset,
to: *time,
});
}

let time_watched =
1000.min(time.saturating_sub(library_item.state.time_offset));
let time_watched = time.saturating_sub(library_item.state.time_offset);
library_item.state.time_watched =
library_item.state.time_watched.saturating_add(time_watched);
library_item.state.overall_time_watched = library_item
Expand All @@ -444,13 +493,16 @@ 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());
}
};
}

if library_item.temp && library_item.state.times_watched == 0 {
library_item.removed = true;
};
}

if library_item.removed {
library_item.temp = true;
};
}

if let Some(analytics_context) = &mut self.analytics_context {
library_item
.state
Expand All @@ -462,34 +514,8 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
analytics_context.device_name = Some(device.to_owned());
analytics_context.player_duration = Some(duration.to_owned());
};
let trakt_event_effects = if seeking && self.loaded && self.paused.is_some() {
if self.paused.expect("paused is None") {
Effects::msg(Msg::Event(Event::TraktPaused {
context: self
.analytics_context
.as_ref()
.cloned()
.unwrap_or_default(),
}))
.unchanged()
} else {
Effects::msg(Msg::Event(Event::TraktPlaying {
context: self
.analytics_context
.as_ref()
.cloned()
.unwrap_or_default(),
}))
.unchanged()
}
} else {
Effects::none()
};

let push_to_library_effects =
push_to_library::<E>(&mut self.push_library_item_time, library_item);

trakt_event_effects.join(push_to_library_effects)
push_to_library::<E>(&mut self.push_library_item_time, library_item)
}
_ => Effects::none().unchanged(),
},
Expand Down Expand Up @@ -529,6 +555,42 @@ impl<E: Env + 'static> UpdateWithCtx<E> for Player {
};
trakt_event_effects.join(update_library_item_effects)
}
Msg::Action(Action::Player(ActionPlayer::PausedChanged { paused }))
if self.selected.is_some() =>
{
self.paused = Some(*paused);
let trakt_event_effects = if !self.loaded {
self.loaded = true;
Effects::msg(Msg::Event(Event::PlayerPlaying {
load_time: self
.load_time
.map(|load_time| {
E::now().timestamp_millis() - load_time.timestamp_millis()
})
.unwrap_or(-1),
context: self.analytics_context.as_ref().cloned().unwrap_or_default(),
}))
.unchanged()
} else if *paused {
Effects::msg(Msg::Event(Event::TraktPaused {
context: self.analytics_context.as_ref().cloned().unwrap_or_default(),
}))
.unchanged()
} else {
Effects::msg(Msg::Event(Event::TraktPlaying {
context: self.analytics_context.as_ref().cloned().unwrap_or_default(),
}))
.unchanged()
};
let update_library_item_effects = match &self.library_item {
Some(library_item) => Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(
library_item.to_owned(),
)))
.unchanged(),
_ => Effects::none().unchanged(),
};
trakt_event_effects.join(update_library_item_effects)
}
Msg::Action(Action::Player(ActionPlayer::NextVideo)) => {
let seek_history_effects = seek_update::<E>(
self.selected.as_ref(),
Expand Down
7 changes: 7 additions & 0 deletions src/runtime/msg/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ pub enum ActionPlayer {
StreamStateChanged {
state: StreamItemState,
},
/// Seek performed by the user when using the seekbar or
/// the shortcuts for seeking
Seek {
time: u64,
duration: u64,
device: String,
},
TimeChanged {
time: u64,
duration: u64,
Expand Down

0 comments on commit 0a0db93

Please sign in to comment.