feat: Update SoundPlayed model to accept nullable user_id and enhance sound tracking in MusicPlayerService
This commit is contained in:
@@ -3,18 +3,27 @@
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import vlc
|
||||
from flask import current_app, request
|
||||
|
||||
from app.models.playlist import Playlist
|
||||
from app.models.sound import Sound
|
||||
from app.models.sound_played import SoundPlayed
|
||||
from app.services.logging_service import LoggingService
|
||||
from app.services.socketio_service import socketio_service
|
||||
|
||||
logger = LoggingService.get_logger(__name__)
|
||||
|
||||
# Constants
|
||||
TRACK_START_THRESHOLD_MS = 500 # 500 milliseconds - threshold for considering a track as "starting fresh"
|
||||
STATE_CHANGE_THRESHOLD_MS = (
|
||||
1000 # 1 second threshold for state change detection
|
||||
)
|
||||
|
||||
|
||||
class MusicPlayerService:
|
||||
"""Service for managing a VLC music player with playlist support."""
|
||||
@@ -47,6 +56,9 @@ class MusicPlayerService:
|
||||
self._track_ending_handled = (
|
||||
False # Flag to prevent duplicate ending triggers
|
||||
)
|
||||
self._track_play_tracked = (
|
||||
False # Flag to track if current track play has been logged
|
||||
)
|
||||
|
||||
def start_vlc_instance(self) -> bool:
|
||||
"""Start a VLC instance with Python bindings."""
|
||||
@@ -216,12 +228,41 @@ class MusicPlayerService:
|
||||
self.current_track_index = index
|
||||
# Reset track ending flag when loading a new track
|
||||
self._track_ending_handled = False
|
||||
self._track_play_tracked = (
|
||||
False # Reset play tracking for new track
|
||||
)
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading track at index {index}: {e}")
|
||||
return False
|
||||
|
||||
def _track_sound_play(self, sound_id: int) -> None:
|
||||
"""Track that a sound has been played."""
|
||||
try:
|
||||
# Use stored app instance or current_app
|
||||
app_to_use = self.app or current_app
|
||||
if app_to_use:
|
||||
with app_to_use.app_context():
|
||||
# Get the sound and increment its play count
|
||||
sound = Sound.query.get(sound_id)
|
||||
if sound:
|
||||
sound.play_count += 1
|
||||
sound.updated_at = datetime.now(tz=ZoneInfo("UTC"))
|
||||
logger.info(
|
||||
f"Incremented play count for sound '{sound.name}' (ID: {sound_id})"
|
||||
)
|
||||
|
||||
# Create a sound played record without user_id (anonymous play)
|
||||
SoundPlayed.create_play_record(
|
||||
user_id=None, sound_id=sound_id, commit=True
|
||||
)
|
||||
logger.info(
|
||||
f"Created anonymous play record for sound ID: {sound_id}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error tracking sound play for sound {sound_id}: {e}")
|
||||
|
||||
def _get_sound_file_path(self, sound: Sound) -> Optional[str]:
|
||||
"""Get the file path for a sound, preferring normalized version."""
|
||||
try:
|
||||
@@ -268,6 +309,9 @@ class MusicPlayerService:
|
||||
result = self.player.play()
|
||||
if result == 0: # Success
|
||||
self.is_playing = True
|
||||
self._track_play_tracked = (
|
||||
False # Track when we first start playing
|
||||
)
|
||||
self._emit_player_state()
|
||||
return True
|
||||
return False
|
||||
@@ -430,9 +474,10 @@ class MusicPlayerService:
|
||||
if not self.current_playlist_id:
|
||||
return None
|
||||
|
||||
# Ensure we have Flask app context
|
||||
if current_app:
|
||||
with current_app.app_context():
|
||||
# Use stored app instance or current_app
|
||||
app_to_use = self.app or current_app
|
||||
if app_to_use:
|
||||
with app_to_use.app_context():
|
||||
playlist = Playlist.query.get(self.current_playlist_id)
|
||||
if playlist and 0 <= self.current_track_index < len(
|
||||
playlist.playlist_sounds
|
||||
@@ -610,11 +655,24 @@ class MusicPlayerService:
|
||||
elif self.is_playing and not old_playing:
|
||||
self._track_ending_handled = False
|
||||
|
||||
# Track play event when song starts playing from beginning (but only once per track load)
|
||||
# Only track if we're playing, haven't tracked yet, and current time is near the start (< 5 seconds)
|
||||
if (
|
||||
self.is_playing
|
||||
and not self._track_play_tracked
|
||||
and self.current_time >= 0
|
||||
and self.current_time < TRACK_START_THRESHOLD_MS
|
||||
):
|
||||
current_track = self.get_current_track()
|
||||
if current_track:
|
||||
self._track_sound_play(current_track["id"])
|
||||
self._track_play_tracked = True
|
||||
|
||||
# Emit updates if state changed significantly or periodically
|
||||
state_changed = (
|
||||
old_playing != self.is_playing
|
||||
or abs(old_time - self.current_time)
|
||||
> 1000 # More than 1 second difference
|
||||
> STATE_CHANGE_THRESHOLD_MS # More than 1 second difference
|
||||
)
|
||||
|
||||
# Always emit if playing to keep frontend updated
|
||||
|
||||
Reference in New Issue
Block a user