Transcodage systématique MP3 128kbps
J'ai implémenté une solution complète pour optimiser ton système audio : 1. Backend - Transcodage & Waveforms Nouveau module de transcodage (transcoder.py): Transcodage automatique en MP3 128kbps via FFmpeg Stockage dans dossier transcoded/ Compression ~70-90% selon format source Waveforms pré-calculées (waveform_generator.py): Génération lors du scan (800 points) Stockage JSON dans dossier waveforms/ Chargement instantané Schema BDD mis à jour (schema.py): filepath : fichier original (download) stream_filepath : MP3 128kbps (streaming) waveform_filepath : JSON pré-calculé Scanner amélioré (scanner.py): Transcode automatiquement chaque fichier Pré-calcule la waveform Stocke les 3 chemins en BDD 2. API - Endpoints Endpoint /api/library/scan (library.py): POST pour lancer un scan Tâche en arrière-plan Statut consultable via GET /api/library/scan/status Streaming optimisé (audio.py): Utilise stream_filepath (MP3 128kbps) en priorité Fallback sur fichier original si absent Waveform chargée depuis JSON pré-calculé 3. Frontend - Interface Bouton Rescan (page.tsx): Dans le header à droite Icône qui tourne pendant le scan Affichage progression en temps réel Reload automatique après scan 4. Base de données Migration appliquée (20251223_003_add_stream_waveform_paths.py): ALTER TABLE audio_tracks ADD COLUMN stream_filepath VARCHAR; ALTER TABLE audio_tracks ADD COLUMN waveform_filepath VARCHAR; CREATE INDEX idx_stream_filepath ON audio_tracks (stream_filepath); 🚀 Utilisation Via l'interface web Clique sur le bouton "Rescan" dans le header Le scan démarre automatiquement Tu vois la progression en temps réel La page se recharge automatiquement à la fin Via CLI (dans le container) docker-compose exec backend python -m src.cli.scanner /music 📊 Avantages ✅ Streaming ultra-rapide : MP3 128kbps = ~70-90% plus léger ✅ Waveform instantanée : Pré-calculée, pas de latence ✅ Download qualité : Fichier original préservé ✅ Rescan facile : Bouton dans l'UI ✅ Prêt pour serveur distant : Optimisé pour la bande passante
This commit is contained in:
@@ -15,6 +15,8 @@ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from src.core.audio_processor import extract_all_features
|
||||
from src.core.essentia_classifier import EssentiaClassifier
|
||||
from src.core.transcoder import AudioTranscoder
|
||||
from src.core.waveform_generator import save_waveform_to_file
|
||||
from src.models.database import SessionLocal
|
||||
from src.models.schema import AudioTrack
|
||||
from src.utils.logging import get_logger
|
||||
@@ -53,12 +55,13 @@ def find_audio_files(directory: str) -> List[Path]:
|
||||
return audio_files
|
||||
|
||||
|
||||
def analyze_and_store(file_path: Path, classifier: EssentiaClassifier, db) -> bool:
|
||||
def analyze_and_store(file_path: Path, classifier: EssentiaClassifier, transcoder: AudioTranscoder, db) -> bool:
|
||||
"""Analyze an audio file and store it in the database.
|
||||
|
||||
Args:
|
||||
file_path: Path to audio file
|
||||
classifier: Essentia classifier instance
|
||||
transcoder: Audio transcoder instance
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
@@ -85,9 +88,31 @@ def analyze_and_store(file_path: Path, classifier: EssentiaClassifier, db) -> bo
|
||||
# Get instruments
|
||||
instruments = classifier.predict_instruments(str(file_path))
|
||||
|
||||
# Transcode to MP3 128kbps for streaming
|
||||
logger.info(" → Transcoding to MP3 128kbps for streaming...")
|
||||
stream_path = transcoder.transcode_to_mp3(
|
||||
str(file_path),
|
||||
bitrate="128k",
|
||||
overwrite=False
|
||||
)
|
||||
|
||||
# Pre-compute waveform
|
||||
logger.info(" → Generating waveform...")
|
||||
waveform_dir = file_path.parent / "waveforms"
|
||||
waveform_dir.mkdir(parents=True, exist_ok=True)
|
||||
waveform_path = waveform_dir / f"{file_path.stem}.waveform.json"
|
||||
|
||||
waveform_success = save_waveform_to_file(
|
||||
str(file_path),
|
||||
str(waveform_path),
|
||||
num_peaks=800
|
||||
)
|
||||
|
||||
# Create track record
|
||||
track = AudioTrack(
|
||||
filepath=str(file_path),
|
||||
stream_filepath=stream_path,
|
||||
waveform_filepath=str(waveform_path) if waveform_success else None,
|
||||
filename=file_path.name,
|
||||
duration_seconds=features['duration_seconds'],
|
||||
tempo_bpm=features['tempo_bpm'],
|
||||
@@ -115,6 +140,8 @@ def analyze_and_store(file_path: Path, classifier: EssentiaClassifier, db) -> bo
|
||||
logger.info(f"✓ Added to database: {file_path.name}")
|
||||
logger.info(f" Genre: {genre_result['primary']}, Mood: {mood_result['primary']}, "
|
||||
f"Tempo: {features['tempo_bpm']:.1f} BPM")
|
||||
logger.info(f" Stream: {stream_path}")
|
||||
logger.info(f" Waveform: {'✓' if waveform_success else '✗'}")
|
||||
|
||||
return True
|
||||
|
||||
@@ -153,6 +180,15 @@ def main():
|
||||
logger.info("Initializing Essentia classifier...")
|
||||
classifier = EssentiaClassifier()
|
||||
|
||||
# Initialize transcoder
|
||||
logger.info("Initializing audio transcoder...")
|
||||
transcoder = AudioTranscoder()
|
||||
|
||||
# Check FFmpeg availability
|
||||
if not transcoder.check_ffmpeg_available():
|
||||
logger.error("FFmpeg is required for transcoding. Please install FFmpeg and try again.")
|
||||
return
|
||||
|
||||
# Process files
|
||||
db = SessionLocal()
|
||||
success_count = 0
|
||||
@@ -162,7 +198,7 @@ def main():
|
||||
for i, file_path in enumerate(audio_files, 1):
|
||||
logger.info(f"[{i}/{len(audio_files)}] Processing...")
|
||||
|
||||
if analyze_and_store(file_path, classifier, db):
|
||||
if analyze_and_store(file_path, classifier, transcoder, db):
|
||||
success_count += 1
|
||||
else:
|
||||
error_count += 1
|
||||
|
||||
Reference in New Issue
Block a user