Fix listener minutes tracking for accurate geo stats

- Add register-web-listener to now-playing-json API endpoint
  This keeps listeners registered during continuous playback instead of
  timing out after 5 minutes of inactivity

- Fix listen_minutes calculation to increment by listener_count per poll
  Previously incremented by 1 regardless of how many listeners, now properly
  tracks listener-minutes (1 minute per listener per 60s poll interval)

- Add migration 009 documenting the calculation fix
This commit is contained in:
glenneth 2026-01-11 20:00:21 +03:00 committed by Brian O'Reilly
parent 8b6209e4e0
commit 249844160f
3 changed files with 34 additions and 4 deletions

View File

@ -138,6 +138,8 @@
(define-api-with-limit asteroid/partial/now-playing-json (&optional mount) (:limit 2 :timeout 1) (define-api-with-limit asteroid/partial/now-playing-json (&optional mount) (:limit 2 :timeout 1)
"Get JSON with now playing info including track ID for favorites. "Get JSON with now playing info including track ID for favorites.
Optional MOUNT parameter specifies which stream to get metadata from." Optional MOUNT parameter specifies which stream to get metadata from."
;; Register web listener for geo stats (keeps listener active during playback)
(register-web-listener)
(with-error-handling (with-error-handling
(let* ((mount-name (or mount "asteroid.mp3")) (let* ((mount-name (or mount "asteroid.mp3"))
(now-playing-stats (icecast-now-playing *stream-base-url* mount-name))) (now-playing-stats (icecast-now-playing *stream-base-url* mount-name)))

View File

@ -268,18 +268,18 @@
(defun update-geo-stats (country-code listener-count &optional city) (defun update-geo-stats (country-code listener-count &optional city)
"Update geo stats for today, optionally including city. "Update geo stats for today, optionally including city.
listener_count tracks peak concurrent listeners (max seen today). listener_count tracks peak concurrent listeners (max seen today).
listen_minutes increments by 1 per poll (approximates total listen time)." listen_minutes increments by listener_count per poll (1 minute per listener per poll)."
(when country-code (when country-code
(handler-case (handler-case
(with-db (with-db
(let ((city-sql (if city (format nil "'~a'" city) "NULL"))) (let ((city-sql (if city (format nil "'~a'" city) "NULL")))
(postmodern:execute (postmodern:execute
(format nil "INSERT INTO listener_geo_stats (date, country_code, city, listener_count, listen_minutes) (format nil "INSERT INTO listener_geo_stats (date, country_code, city, listener_count, listen_minutes)
VALUES (CURRENT_DATE, '~a', ~a, ~a, 1) VALUES (CURRENT_DATE, '~a', ~a, ~a, ~a)
ON CONFLICT (date, country_code, city) ON CONFLICT (date, country_code, city)
DO UPDATE SET listener_count = GREATEST(listener_geo_stats.listener_count, ~a), DO UPDATE SET listener_count = GREATEST(listener_geo_stats.listener_count, ~a),
listen_minutes = listener_geo_stats.listen_minutes + 1" listen_minutes = listener_geo_stats.listen_minutes + ~a"
country-code city-sql listener-count listener-count)))) country-code city-sql listener-count listener-count listener-count listener-count))))
(error (e) (error (e)
(log:error "Failed to update geo stats: ~a" e))))) (log:error "Failed to update geo stats: ~a" e)))))

View File

@ -0,0 +1,28 @@
-- Migration: Fix Listen Minutes Calculation
-- Version: 009
-- Date: 2026-01-11
-- Description: Document the fix for listen_minutes calculation
--
-- ISSUE: listen_minutes was incrementing by 1 per poll regardless of listener count.
-- This meant a country with 5 listeners for 1 hour would only show 60 minutes,
-- not 300 listener-minutes.
--
-- FIX: Application code in listener-stats.lisp now increments listen_minutes by
-- the listener_count value (1 minute per listener per poll interval).
--
-- ADDITIONAL FIX: register-web-listener is now called from the now-playing-json API
-- endpoint, keeping listeners registered during continuous playback
-- instead of timing out after 5 minutes.
--
-- No schema changes required - this migration documents the application logic fix.
-- Add a comment to the table for future reference
COMMENT ON COLUMN listener_geo_stats.listen_minutes IS
'Total listener-minutes: increments by listener_count per poll (1 min per listener per 60s poll)';
-- Success message
DO $$
BEGIN
RAISE NOTICE 'Migration 009: listen_minutes calculation fix documented';
RAISE NOTICE 'listen_minutes now represents true listener-minutes (count * time)';
END $$;