feat: Drama Studio 프로젝트 초기 구조 설정
- FastAPI 백엔드 (audio-studio-api) - Next.js 프론트엔드 (audio-studio-ui) - Qwen3-TTS 엔진 (audio-studio-tts) - MusicGen 서비스 (audio-studio-musicgen) - Docker Compose 개발/운영 환경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
169
audio-studio-api/app/database.py
Normal file
169
audio-studio-api/app/database.py
Normal file
@ -0,0 +1,169 @@
|
||||
"""데이터베이스 연결 설정
|
||||
|
||||
MongoDB (motor async) + GridFS (오디오 저장)
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase, AsyncIOMotorGridFSBucket
|
||||
from redis.asyncio import Redis
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Database:
|
||||
"""데이터베이스 연결 관리"""
|
||||
|
||||
def __init__(self):
|
||||
self.client: Optional[AsyncIOMotorClient] = None
|
||||
self.db: Optional[AsyncIOMotorDatabase] = None
|
||||
self.gridfs: Optional[AsyncIOMotorGridFSBucket] = None
|
||||
self.redis: Optional[Redis] = None
|
||||
|
||||
async def connect(self):
|
||||
"""데이터베이스 연결"""
|
||||
# MongoDB
|
||||
mongodb_url = os.getenv("MONGODB_URL", "mongodb://localhost:27017/")
|
||||
db_name = os.getenv("DB_NAME", "audio_studio")
|
||||
|
||||
logger.info(f"MongoDB 연결 중: {db_name}")
|
||||
self.client = AsyncIOMotorClient(mongodb_url)
|
||||
self.db = self.client[db_name]
|
||||
|
||||
# GridFS (오디오 파일 저장용)
|
||||
self.gridfs = AsyncIOMotorGridFSBucket(self.db, bucket_name="audio_files")
|
||||
|
||||
# 연결 테스트
|
||||
await self.client.admin.command("ping")
|
||||
logger.info("MongoDB 연결 성공")
|
||||
|
||||
# Redis
|
||||
redis_url = os.getenv("REDIS_URL", "redis://localhost:6379")
|
||||
logger.info("Redis 연결 중...")
|
||||
self.redis = Redis.from_url(redis_url, decode_responses=True)
|
||||
|
||||
# 연결 테스트
|
||||
await self.redis.ping()
|
||||
logger.info("Redis 연결 성공")
|
||||
|
||||
# 인덱스 생성
|
||||
await self._create_indexes()
|
||||
|
||||
async def _create_indexes(self):
|
||||
"""컬렉션 인덱스 생성"""
|
||||
# voices 컬렉션
|
||||
await self.db.voices.create_index("voice_id", unique=True)
|
||||
await self.db.voices.create_index("owner_id")
|
||||
await self.db.voices.create_index("type")
|
||||
await self.db.voices.create_index("language")
|
||||
await self.db.voices.create_index("is_public")
|
||||
|
||||
# tts_generations 컬렉션
|
||||
await self.db.tts_generations.create_index("generation_id", unique=True)
|
||||
await self.db.tts_generations.create_index("user_id")
|
||||
await self.db.tts_generations.create_index("voice_id")
|
||||
await self.db.tts_generations.create_index("created_at")
|
||||
|
||||
# sound_effects 컬렉션
|
||||
await self.db.sound_effects.create_index("source_id")
|
||||
await self.db.sound_effects.create_index("categories")
|
||||
await self.db.sound_effects.create_index("tags")
|
||||
|
||||
# music_tracks 컬렉션
|
||||
await self.db.music_tracks.create_index("source")
|
||||
await self.db.music_tracks.create_index("genre")
|
||||
await self.db.music_tracks.create_index("mood")
|
||||
|
||||
logger.info("인덱스 생성 완료")
|
||||
|
||||
async def disconnect(self):
|
||||
"""데이터베이스 연결 해제"""
|
||||
if self.client:
|
||||
self.client.close()
|
||||
logger.info("MongoDB 연결 해제")
|
||||
|
||||
if self.redis:
|
||||
await self.redis.close()
|
||||
logger.info("Redis 연결 해제")
|
||||
|
||||
# ========================================
|
||||
# 컬렉션 접근자
|
||||
# ========================================
|
||||
|
||||
@property
|
||||
def voices(self):
|
||||
"""voices 컬렉션"""
|
||||
return self.db.voices
|
||||
|
||||
@property
|
||||
def tts_generations(self):
|
||||
"""tts_generations 컬렉션"""
|
||||
return self.db.tts_generations
|
||||
|
||||
@property
|
||||
def sound_effects(self):
|
||||
"""sound_effects 컬렉션"""
|
||||
return self.db.sound_effects
|
||||
|
||||
@property
|
||||
def music_tracks(self):
|
||||
"""music_tracks 컬렉션"""
|
||||
return self.db.music_tracks
|
||||
|
||||
@property
|
||||
def user_voice_library(self):
|
||||
"""user_voice_library 컬렉션"""
|
||||
return self.db.user_voice_library
|
||||
|
||||
# ========================================
|
||||
# GridFS 오디오 저장
|
||||
# ========================================
|
||||
|
||||
async def save_audio(
|
||||
self,
|
||||
audio_bytes: bytes,
|
||||
filename: str,
|
||||
content_type: str = "audio/wav",
|
||||
metadata: dict = None,
|
||||
) -> str:
|
||||
"""오디오 파일을 GridFS에 저장
|
||||
|
||||
Returns:
|
||||
file_id (str)
|
||||
"""
|
||||
file_id = await self.gridfs.upload_from_stream(
|
||||
filename,
|
||||
audio_bytes,
|
||||
metadata={
|
||||
"content_type": content_type,
|
||||
**(metadata or {}),
|
||||
}
|
||||
)
|
||||
return str(file_id)
|
||||
|
||||
async def get_audio(self, file_id: str) -> bytes:
|
||||
"""GridFS에서 오디오 파일 읽기"""
|
||||
from bson import ObjectId
|
||||
from io import BytesIO
|
||||
|
||||
buffer = BytesIO()
|
||||
await self.gridfs.download_to_stream(ObjectId(file_id), buffer)
|
||||
buffer.seek(0)
|
||||
return buffer.read()
|
||||
|
||||
async def delete_audio(self, file_id: str):
|
||||
"""GridFS에서 오디오 파일 삭제"""
|
||||
from bson import ObjectId
|
||||
await self.gridfs.delete(ObjectId(file_id))
|
||||
|
||||
|
||||
# 싱글톤 인스턴스
|
||||
db = Database()
|
||||
|
||||
|
||||
# FastAPI 의존성
|
||||
async def get_db() -> Database:
|
||||
"""데이터베이스 인스턴스 반환 (의존성 주입용)"""
|
||||
return db
|
||||
Reference in New Issue
Block a user