Track video clicks as engagement signals

- stats: started_count now includes any video opened (last_watched_at set)
  not just ones with saved progress seconds
- VideoPlayer: fires updateProgress immediately on open so even a
  click-and-back sets last_watched_at and counts as a started video

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 01:19:47 +02:00
parent fadb0fffcd
commit 1b010d4081
2 changed files with 11 additions and 1 deletions

View File

@@ -140,7 +140,7 @@ def get_stats(
text(""" text("""
SELECT COUNT(*) AS n FROM user_videos SELECT COUNT(*) AS n FROM user_videos
WHERE user_id = :uid AND watched = 0 WHERE user_id = :uid AND watched = 0
AND watch_progress_seconds > 0 AND (watch_progress_seconds > 0 OR last_watched_at IS NOT NULL)
"""), """),
{"uid": uid}, {"uid": uid},
).mappings().first() ).mappings().first()

View File

@@ -126,6 +126,16 @@ export default function VideoPlayer() {
} }
}, [dlStatus?.status]); // eslint-disable-line react-hooks/exhaustive-deps }, [dlStatus?.status]); // eslint-disable-line react-hooks/exhaustive-deps
// Record a "clicked" impression as soon as we have the video id — even if the
// user closes immediately before playback starts.
const clickedRef = useRef(false);
useEffect(() => {
if (video?.id && !clickedRef.current && !video.is_watched) {
clickedRef.current = true;
updateProgress(video.id, { watch_progress_seconds: video.watch_progress_seconds ?? 0 });
}
}, [video?.id]); // eslint-disable-line react-hooks/exhaustive-deps
// ── Trigger download on open ────────────────────────────────────────────── // ── Trigger download on open ──────────────────────────────────────────────
const downloadMut = useMutation({ const downloadMut = useMutation({
mutationFn: (ytId) => createDownload(ytId), mutationFn: (ytId) => createDownload(ytId),