feat: Replace pydub with ffmpeg for audio duration and metadata extraction in sound services
This commit is contained in:
@@ -7,7 +7,6 @@ import re
|
||||
from pathlib import Path
|
||||
|
||||
import ffmpeg
|
||||
from pydub import AudioSegment
|
||||
|
||||
from app.database import db
|
||||
from app.models.sound import Sound
|
||||
@@ -632,9 +631,17 @@ class SoundNormalizerService:
|
||||
# Calculate file hash
|
||||
file_hash = SoundNormalizerService._calculate_file_hash(file_path)
|
||||
|
||||
# Get duration using pydub
|
||||
audio = AudioSegment.from_wav(file_path)
|
||||
duration = len(audio) # Duration in milliseconds
|
||||
# Get duration using ffmpeg
|
||||
probe = ffmpeg.probe(file_path)
|
||||
audio_stream = next(
|
||||
(s for s in probe['streams'] if s['codec_type'] == 'audio'),
|
||||
None
|
||||
)
|
||||
|
||||
if audio_stream and 'duration' in audio_stream:
|
||||
duration = int(float(audio_stream['duration']) * 1000) # Convert to milliseconds
|
||||
else:
|
||||
duration = 0
|
||||
|
||||
return {
|
||||
"duration": duration,
|
||||
|
||||
@@ -4,8 +4,7 @@ import hashlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from pydub import AudioSegment
|
||||
from pydub.utils import mediainfo
|
||||
import ffmpeg
|
||||
|
||||
from app.database import db
|
||||
from app.models.sound import Sound
|
||||
@@ -281,32 +280,31 @@ class SoundScannerService:
|
||||
|
||||
@staticmethod
|
||||
def _extract_audio_metadata(file_path: str) -> dict:
|
||||
"""Extract metadata from audio file using pydub and mediainfo."""
|
||||
"""Extract metadata from audio file using ffmpeg-python."""
|
||||
try:
|
||||
# Get file size
|
||||
file_size = Path(file_path).stat().st_size
|
||||
|
||||
# Load audio file with pydub for basic info
|
||||
audio = AudioSegment.from_file(file_path)
|
||||
# Use ffmpeg to probe audio metadata
|
||||
probe = ffmpeg.probe(file_path)
|
||||
audio_stream = next(
|
||||
(s for s in probe['streams'] if s['codec_type'] == 'audio'),
|
||||
None
|
||||
)
|
||||
|
||||
if not audio_stream:
|
||||
raise ValueError("No audio stream found in file")
|
||||
|
||||
# Extract basic metadata from AudioSegment
|
||||
duration = len(audio)
|
||||
channels = audio.channels
|
||||
sample_rate = audio.frame_rate
|
||||
|
||||
# Use mediainfo for more accurate bitrate information
|
||||
bitrate = None
|
||||
try:
|
||||
info = mediainfo(file_path)
|
||||
if info and "bit_rate" in info:
|
||||
bitrate = int(info["bit_rate"])
|
||||
elif info and "bitrate" in info:
|
||||
bitrate = int(info["bitrate"])
|
||||
except (ValueError, KeyError, TypeError):
|
||||
# Fallback to calculated bitrate if mediainfo fails
|
||||
if duration > 0:
|
||||
file_size_bits = file_size * 8
|
||||
bitrate = int(file_size_bits / duration / 1000)
|
||||
# Extract metadata from ffmpeg probe
|
||||
duration = int(float(audio_stream.get('duration', 0)) * 1000) # Convert to milliseconds
|
||||
channels = int(audio_stream.get('channels', 0))
|
||||
sample_rate = int(audio_stream.get('sample_rate', 0))
|
||||
bitrate = int(audio_stream.get('bit_rate', 0)) if audio_stream.get('bit_rate') else None
|
||||
|
||||
# Fallback bitrate calculation if not available
|
||||
if not bitrate and duration > 0:
|
||||
file_size_bits = file_size * 8
|
||||
bitrate = int(file_size_bits / (duration / 1000))
|
||||
|
||||
return {
|
||||
"duration": duration,
|
||||
|
||||
@@ -15,7 +15,6 @@ dependencies = [
|
||||
"flask-migrate==4.1.0",
|
||||
"flask-socketio==5.5.1",
|
||||
"flask-sqlalchemy==3.1.1",
|
||||
"pydub==0.25.1",
|
||||
"python-dotenv==1.1.1",
|
||||
"python-vlc>=3.0.21203",
|
||||
"requests==2.32.4",
|
||||
|
||||
11
uv.lock
generated
11
uv.lock
generated
@@ -505,15 +505,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydub"
|
||||
version = "0.25.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
@@ -645,7 +636,6 @@ dependencies = [
|
||||
{ name = "flask-migrate" },
|
||||
{ name = "flask-socketio" },
|
||||
{ name = "flask-sqlalchemy" },
|
||||
{ name = "pydub" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "python-vlc" },
|
||||
{ name = "requests" },
|
||||
@@ -671,7 +661,6 @@ requires-dist = [
|
||||
{ name = "flask-migrate", specifier = "==4.1.0" },
|
||||
{ name = "flask-socketio", specifier = "==5.5.1" },
|
||||
{ name = "flask-sqlalchemy", specifier = "==3.1.1" },
|
||||
{ name = "pydub", specifier = "==0.25.1" },
|
||||
{ name = "python-dotenv", specifier = "==1.1.1" },
|
||||
{ name = "python-vlc", specifier = ">=3.0.21203" },
|
||||
{ name = "requests", specifier = "==2.32.4" },
|
||||
|
||||
Reference in New Issue
Block a user