- Implemented VLC player API endpoints for playing and stopping sounds. - Added tests for successful playback, error handling, and authentication scenarios. - Created utility function to get sound file paths based on sound properties. - Refactored player service to utilize shared sound path utility. - Enhanced test coverage for sound file path utility with various sound types. - Introduced tests for VLC player service, including subprocess handling and play count tracking.
67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
"""Audio file utility functions shared across audio processing services."""
|
|
|
|
import hashlib
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
import ffmpeg # type: ignore[import-untyped]
|
|
|
|
from app.core.logging import get_logger
|
|
|
|
if TYPE_CHECKING:
|
|
from app.models.sound import Sound
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
def get_file_hash(file_path: Path) -> str:
|
|
"""Calculate SHA-256 hash of a file."""
|
|
hash_sha256 = hashlib.sha256()
|
|
with file_path.open("rb") as f:
|
|
for chunk in iter(lambda: f.read(4096), b""):
|
|
hash_sha256.update(chunk)
|
|
return hash_sha256.hexdigest()
|
|
|
|
|
|
def get_file_size(file_path: Path) -> int:
|
|
"""Get file size in bytes."""
|
|
return file_path.stat().st_size
|
|
|
|
|
|
def get_audio_duration(file_path: Path) -> int:
|
|
"""Get audio duration in milliseconds using ffmpeg."""
|
|
try:
|
|
probe = ffmpeg.probe(str(file_path))
|
|
duration = float(probe["format"]["duration"])
|
|
return int(duration * 1000) # Convert to milliseconds
|
|
except Exception as e:
|
|
logger.warning("Failed to get duration for %s: %s", file_path, e)
|
|
return 0
|
|
|
|
|
|
def get_sound_file_path(sound: "Sound") -> Path:
|
|
"""Get the file path for a sound based on its type and normalization status.
|
|
|
|
Args:
|
|
sound: The Sound object to get the path for
|
|
|
|
Returns:
|
|
Path: The full path to the sound file
|
|
|
|
"""
|
|
# Determine the correct subdirectory based on sound type
|
|
if sound.type.upper() == "EXT":
|
|
subdir = "extracted"
|
|
elif sound.type.upper() == "SDB":
|
|
subdir = "soundboard"
|
|
elif sound.type.upper() == "TTS":
|
|
subdir = "text_to_speech"
|
|
else:
|
|
# Fallback to lowercase type
|
|
subdir = sound.type.lower()
|
|
|
|
# Use normalized file if available, otherwise original
|
|
if sound.is_normalized and sound.normalized_filename:
|
|
return Path("sounds/normalized") / subdir / sound.normalized_filename
|
|
return Path("sounds/originals") / subdir / sound.filename
|