From e0241df8afde0483fc36cb2300b53127672486dc Mon Sep 17 00:00:00 2001 From: tslil Date: Sun, 12 Jul 2026 18:20:30 +0100 Subject: Graph based --- src/trajectory.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/trajectory.rs (limited to 'src/trajectory.rs') diff --git a/src/trajectory.rs b/src/trajectory.rs new file mode 100644 index 0000000..214e16c --- /dev/null +++ b/src/trajectory.rs @@ -0,0 +1,98 @@ +use serde::{Deserialize, Serialize}; +use std::{collections::HashSet, time::SystemTime}; + +use crate::learner::Learning; + +#[derive(Serialize, Deserialize, Debug)] +struct LastData { + timestamp: SystemTime, + album: String, +} + +#[derive(Serialize, Deserialize, Default)] +pub struct Trajectory { + last_data: Option, + streak_kind: Option, + history: Vec, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub enum Action { + Skip, + More, +} + +impl From> for Action { + fn from(learning: Option<&Learning>) -> Action { + if let Some(ts) = learning { + match ts { + Learning::SkipExtend | Learning::MoreToSkip(_, _) => Action::Skip, + Learning::MoreExtend(_, _) | Learning::SkipToMore(_, _) => Action::More, + } + } else { + Action::Skip + } + } +} + +impl Trajectory { + pub fn prune(&mut self, valid: &HashSet) { + self.history.retain_mut(|x| valid.contains(x)); + + if let Some(ref last_data) = self.last_data + && !valid.contains(&last_data.album) + { + self.last_data = None + } + } + + pub fn slice(&self) -> &[String] { + &self.history + } + + pub fn log(&mut self, new_album: &str) { + self.last_data = Some(LastData { + timestamp: SystemTime::now(), + album: new_album.to_string(), + }) + } + + pub fn step(&mut self, skip_window_secs: u64) -> Option { + if let Some(ref last_data) = self.last_data { + let action = if SystemTime::now() + .duration_since(last_data.timestamp) + .ok() + .is_some_and(|d| d.as_secs() < skip_window_secs) + { + Action::Skip + } else { + Action::More + }; + + let last_album = last_data.album.clone(); + let current_streak = self.history.clone(); + + let learning = if let Some(ref kind) = self.streak_kind + && kind == &action + { + self.history.push(last_album.clone()); + match action { + Action::Skip => Learning::SkipExtend, + Action::More => Learning::MoreExtend(current_streak, last_album), + } + } else { + self.history.clear(); + self.history.push(last_album.clone()); + match action { + Action::Skip => Learning::MoreToSkip(current_streak, last_album), + Action::More => Learning::SkipToMore(current_streak, last_album), + } + }; + + self.streak_kind = Some(action); + Some(learning) + } else { + None + } + } +} -- cgit v1.2.3