Files

52 lines
1.8 KiB
Go
Raw Permalink Normal View History

2026-05-22 20:26:11 -04:00
package userstore
// ProgressThresholds bundles the watched and min-resume threshold percentages.
// Zero values mean "use defaults" (90% watched, 5% min-resume).
type ProgressThresholds struct {
WatchedPct int // mark completed above this % (default 90)
MinResumePct int // discard progress below this % (default 5)
}
// WatchedFraction converts a watched-threshold percentage (e.g. 90) to a
// fraction (0.9). If pct <= 0, returns the default of 0.9 (90%).
func WatchedFraction(pct int) float64 {
if pct <= 0 {
pct = 90
}
return float64(pct) / 100.0
}
// MinResumeFraction converts a min-resume percentage (e.g. 5) to a fraction
// (0.05). If pct <= 0, returns the default of 0.05 (5%).
func MinResumeFraction(pct int) float64 {
if pct <= 0 {
pct = 5
}
return float64(pct) / 100.0
}
// ResolveProgressState classifies one progress event against the thresholds:
// skip (below the min-resume floor — discard entirely), completed (above the
// watched threshold — the returned position resets to 0 so completed rows
// hold no resume point, matching MarkWatched), or a plain in-progress update.
// The single home of the rule shared by both store backends and the
// offline-sync ingest; `completed` remains a one-way latch at the write site.
func ResolveProgressState(position, duration float64, t ProgressThresholds) (pos float64, completed, skip bool) {
// Clamp malformed client input so negative values can never classify — or
// persist — as real progress on any backend.
if position < 0 {
position = 0
}
if duration < 0 {
duration = 0
}
if duration > 0 && position > 0 && position/duration < MinResumeFraction(t.MinResumePct) {
return position, false, true
}
completed = duration > 0 && position/duration > WatchedFraction(t.WatchedPct)
if completed {
position = 0
}
return position, completed, false
}