Files
sdb-back/app/models/sound.py
JSC 7128ca727b Add new sound files and update dependencies
- Added various sound files to the soundboard, including insults, quotes, and sound effects.
- Introduced new dependencies: ffmpeg-python (version 0.2.0) and pydub (version 0.25.1) for audio processing.
- Updated the lock file to reflect the new packages and their respective versions.
- Added .gitignore files in the sounds/stream and sounds/temp directories to exclude unnecessary files.
2025-07-02 17:09:43 +02:00

226 lines
6.7 KiB
Python

"""Sound model for storing sound file information."""
from datetime import datetime
from enum import Enum
from typing import Optional
from app.database import db
from sqlalchemy import Boolean, DateTime, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
class SoundType(Enum):
"""Sound type enumeration."""
SDB = "SDB" # Soundboard sound
SAY = "SAY" # Text-to-speech
STR = "STR" # Stream sound
class Sound(db.Model):
"""Sound model for storing sound file information."""
__tablename__ = "sounds"
id: Mapped[int] = mapped_column(primary_key=True)
# Sound type (SDB, SAY, or STR)
type: Mapped[str] = mapped_column(String(3), nullable=False)
# Basic sound information
name: Mapped[str] = mapped_column(String(255), nullable=False)
filename: Mapped[str] = mapped_column(String(500), nullable=False)
duration: Mapped[int] = mapped_column(Integer, nullable=False)
size: Mapped[int] = mapped_column(Integer, nullable=False) # Size in bytes
hash: Mapped[str] = mapped_column(String(64), nullable=False) # SHA256 hash
# Normalized sound information
normalized_filename: Mapped[str | None] = mapped_column(
String(500),
nullable=True,
)
normalized_duration: Mapped[int | None] = mapped_column(
Integer,
nullable=True,
)
normalized_size: Mapped[int | None] = mapped_column(
Integer,
nullable=True,
)
normalized_hash: Mapped[str | None] = mapped_column(
String(64),
nullable=True,
)
# Sound properties
is_normalized: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=False,
)
is_music: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=False,
)
is_deletable: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=True,
)
# Usage tracking
play_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
# Timestamps
created_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
nullable=False,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
nullable=False,
)
def __repr__(self) -> str:
"""String representation of Sound."""
return f"<Sound {self.name} ({self.type}) - {self.play_count} plays>"
def to_dict(self) -> dict:
"""Convert sound to dictionary."""
return {
"id": self.id,
"type": self.type,
"name": self.name,
"filename": self.filename,
"duration": self.duration,
"size": self.size,
"hash": self.hash,
"normalized_filename": self.normalized_filename,
"normalized_duration": self.normalized_duration,
"normalized_size": self.normalized_size,
"normalized_hash": self.normalized_hash,
"is_normalized": self.is_normalized,
"is_music": self.is_music,
"is_deletable": self.is_deletable,
"play_count": self.play_count,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
def increment_play_count(self) -> None:
"""Increment the play count for this sound."""
self.play_count += 1
self.updated_at = datetime.utcnow()
db.session.commit()
def set_normalized_info(
self,
normalized_filename: str,
normalized_duration: float,
normalized_size: int,
normalized_hash: str,
) -> None:
"""Set normalized sound information."""
self.normalized_filename = normalized_filename
self.normalized_duration = normalized_duration
self.normalized_size = normalized_size
self.normalized_hash = normalized_hash
self.is_normalized = True
self.updated_at = datetime.utcnow()
def clear_normalized_info(self) -> None:
"""Clear normalized sound information."""
self.normalized_filename = None
self.normalized_duration = None
self.normalized_hash = None
self.normalized_size = None
self.is_normalized = False
self.updated_at = datetime.utcnow()
def update_file_info(
self,
filename: str,
duration: float,
size: int,
hash_value: str,
) -> None:
"""Update file information for existing sound."""
self.filename = filename
self.duration = duration
self.size = size
self.hash = hash_value
self.updated_at = datetime.utcnow()
@classmethod
def find_by_hash(cls, hash_value: str) -> Optional["Sound"]:
"""Find sound by hash."""
return cls.query.filter_by(hash=hash_value).first()
@classmethod
def find_by_name(cls, name: str) -> Optional["Sound"]:
"""Find sound by name."""
return cls.query.filter_by(name=name).first()
@classmethod
def find_by_filename(cls, filename: str) -> Optional["Sound"]:
"""Find sound by filename."""
return cls.query.filter_by(filename=filename).first()
@classmethod
def find_by_type(cls, sound_type: str) -> list["Sound"]:
"""Find all sounds by type."""
return cls.query.filter_by(type=sound_type).all()
@classmethod
def get_most_played(cls, limit: int = 10) -> list["Sound"]:
"""Get the most played sounds."""
return cls.query.order_by(cls.play_count.desc()).limit(limit).all()
@classmethod
def get_music_sounds(cls) -> list["Sound"]:
"""Get all music sounds."""
return cls.query.filter_by(is_music=True).all()
@classmethod
def get_deletable_sounds(cls) -> list["Sound"]:
"""Get all deletable sounds."""
return cls.query.filter_by(is_deletable=True).all()
@classmethod
def create_sound(
cls,
sound_type: str,
name: str,
filename: str,
duration: float,
size: int,
hash_value: str,
is_music: bool = False,
is_deletable: bool = True,
commit: bool = True,
) -> "Sound":
"""Create a new sound."""
# Validate sound type
if sound_type not in [t.value for t in SoundType]:
raise ValueError(f"Invalid sound type: {sound_type}")
sound = cls(
type=sound_type,
name=name,
filename=filename,
duration=duration,
size=size,
hash=hash_value,
is_music=is_music,
is_deletable=is_deletable,
)
db.session.add(sound)
if commit:
db.session.commit()
return sound