Skip to content

stats: count plays with the scrobbler's relative threshold, not a 30 s floor - #242

Merged
ingoau merged 1 commit into
t3/deskhand-mobile-issue-sweepfrom
t3/fix-listening-stats-play-threshold
Nov 10, 2026
Merged

ingoau merged 1 commit into
t3/deskhand-mobile-issue-sweepfrom
t3/fix-listening-stats-play-threshold

Conversation

@ingoau

@ingoau ingoau commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Problem

The Listening stats screen showed "No listening yet" for every window (7 days / 30 days / Year) — even 15+ minutes after fully playing an album. Verified on the emulator against a live Navidrome 0.64.2: play history rows existed, the queue History listed the plays, and the server accepted every scrobble (playCount=1 per track), yet the screen stayed empty.

Root cause

Two components disagreed about what a "play" is:

  • The recorder (outbox/scrobbler.rs) writes a play_history row once, at the Last.fm threshold crossing — 50% of duration, capped at 4 min — and the row's played_ms is that crossing value, never updated afterwards.
  • The stats view (stats::listening_stats) filtered rows with an absolute StatsOptions::min_played_ms = 30_000.

For a 30-second track the row carries played_ms ≈ 15_000 — below the floor — so every play was silently dropped. Longer tracks were misjudged too (a 5 min track records 150 s and only counted by luck of the floor being smaller than typical thresholds).

Fix

  • stats/mod.rs: drop the absolute min_played_ms option and add stats::is_play, which applies the scrobbler's own threshold_ms (relative: min(duration/2, 4 min); <30 s tracks never count; unknown duration falls back to the absolute 30 s floor). Used by listening_stats_with_extras and streaks.
  • sim/device.rs: scrobble_threshold_ms re-exports the scrobbler's threshold_ms instead of duplicating the rule.

Tests

New play_threshold_matches_the_scrobbler covers: fully-heard 30 s track counts; 10% partial play of a 5 min track does not; 4-minute cap on a 1-hour track; sub-30 s and unknown-duration edges. Existing fixtures updated where they relied on the old inconsistent floor. cargo test -p hocket-core passes (873+), fmt/clippy clean.

On-device verification: rebuilt the APK with this commit, reinstalled on the emulator — the Listening screen now shows 32 plays / 8 min listened with the hour-of-day and weekday charts.

Generated by GLM-5.3-Flash in OpenCode

…s floor

The Listening screen filtered play-history rows with an absolute
min_played_ms of 30 s, but history rows only record the time heard
when the scrobble threshold is crossed: 50% of the track or 4 minutes.
A fully played 30 s track therefore carried played_ms ~15 000 and was
thrown away, so the Listening screen stayed empty for short tracks no
matter how often they were played (and mid-length tracks were judged
against a different rule than the scrobbler uses).

Replace the floor with stats::is_play, which applies the scrobbler's
own threshold (crate::outbox::scrobbler::threshold_ms): known duration
=> relative 50%/4-min rule, tracks under 30 s never play, unknown
duration => fall back to the absolute 30 s floor. The sim device now
re-exports the scrobbler's threshold instead of duplicating the rule.

Window filtering is unchanged; tests cover the fully heard 30 s track,
a 10% partial play of a 5 min track, the 4-minute cap on long tracks,
and the unknown-duration fallback.

Fixes: manual-testing finding on Android, t3/deskhand-mobile-issue-sweep

Generated by glm-5.3-flash in [OpenCode](https://opencode.ai)
Co-authored-by: glm-5.3-flash <opencode-agent[bot]@users.noreply.github.com>
@ingoau
ingoau merged commit eac16f9 into t3/deskhand-mobile-issue-sweep Nov 10, 2026
13 checks passed
@ingoau
ingoau deleted the t3/fix-listening-stats-play-threshold branch November 10, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant