diff --git a/qwen3-tts-backend/api/cache.py b/qwen3-tts-backend/api/cache.py deleted file mode 100644 index e669396..0000000 --- a/qwen3-tts-backend/api/cache.py +++ /dev/null @@ -1,156 +0,0 @@ -import logging -import json -from pathlib import Path -from typing import Optional -from fastapi import APIRouter, Depends, HTTPException, Request -from sqlalchemy.orm import Session -from slowapi import Limiter -from slowapi.util import get_remote_address - -from core.config import settings -from core.database import get_db -from core.cache_manager import VoiceCacheManager -from api.auth import get_current_user -from db.crud import list_cache_entries, delete_cache_entry -from db.models import VoiceCache, User -from utils.metrics import cache_metrics - -logger = logging.getLogger(__name__) -router = APIRouter(prefix="/cache", tags=["cache"]) - -limiter = Limiter(key_func=get_remote_address) - - -@router.get("/voices") -@limiter.limit("30/minute") -async def list_user_caches( - request: Request, - skip: int = 0, - limit: int = 100, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - caches = list_cache_entries(db, current_user.id, skip=skip, limit=limit) - - result = [] - for cache in caches: - meta_data = json.loads(cache.meta_data) if cache.meta_data else {} - cache_file = Path(cache.cache_path) - file_size_mb = cache_file.stat().st_size / (1024 * 1024) if cache_file.exists() else 0 - - result.append({ - 'id': cache.id, - 'ref_audio_hash': cache.ref_audio_hash, - 'created_at': cache.created_at.isoformat(), - 'last_accessed': cache.last_accessed.isoformat(), - 'access_count': cache.access_count, - 'metadata': meta_data, - 'size_mb': round(file_size_mb, 2) - }) - - return { - 'caches': result, - 'total': len(result) - } - - -@router.delete("/voices/{cache_id}") -@limiter.limit("30/minute") -async def delete_user_cache( - request: Request, - cache_id: int, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - cache = db.query(VoiceCache).filter( - VoiceCache.id == cache_id, - VoiceCache.user_id == current_user.id - ).first() - - if not cache: - raise HTTPException(status_code=404, detail="Cache not found") - - cache_file = Path(cache.cache_path) - if cache_file.exists(): - cache_file.unlink() - - success = delete_cache_entry(db, cache_id, current_user.id) - - if not success: - raise HTTPException(status_code=500, detail="Failed to delete cache") - - logger.info(f"Cache deleted: id={cache_id}, user={current_user.id}") - - return { - 'message': 'Cache deleted successfully', - 'cache_id': cache_id - } - - -@router.delete("/voices") -@limiter.limit("10/minute") -async def cleanup_expired_caches( - request: Request, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - cache_manager = await VoiceCacheManager.get_instance() - deleted_count = await cache_manager.cleanup_expired(db) - - logger.info(f"Expired cache cleanup: user={current_user.id}, deleted={deleted_count}") - - return { - 'message': 'Expired caches cleaned up', - 'deleted_count': deleted_count - } - - -@router.post("/voices/prune") -@limiter.limit("10/minute") -async def prune_caches( - request: Request, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - cache_manager = await VoiceCacheManager.get_instance() - deleted_count = await cache_manager.enforce_max_entries(current_user.id, db) - - logger.info(f"LRU prune: user={current_user.id}, deleted={deleted_count}") - - return { - 'message': 'LRU pruning completed', - 'deleted_count': deleted_count - } - - -@router.get("/stats") -@limiter.limit("30/minute") -async def get_cache_stats( - request: Request, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - stats = cache_metrics.get_stats(db, settings.CACHE_DIR) - - user_stats = None - for user_stat in stats['users']: - if user_stat['user_id'] == current_user.id: - user_stats = user_stat - break - - if user_stats is None: - user_cache_count = db.query(VoiceCache).filter( - VoiceCache.user_id == current_user.id - ).count() - user_stats = { - 'user_id': current_user.id, - 'hits': 0, - 'misses': 0, - 'hit_rate': 0.0, - 'cache_entries': user_cache_count - } - - return { - 'global': stats['global'], - 'user': user_stats - } diff --git a/qwen3-tts-backend/api/metrics.py b/qwen3-tts-backend/api/metrics.py deleted file mode 100644 index 728f270..0000000 --- a/qwen3-tts-backend/api/metrics.py +++ /dev/null @@ -1,21 +0,0 @@ -import logging -from fastapi import APIRouter - -from core.metrics import MetricsCollector - -logger = logging.getLogger(__name__) -router = APIRouter(prefix="/metrics", tags=["metrics"]) - - -@router.get("") -async def get_metrics(): - metrics = await MetricsCollector.get_instance() - data = await metrics.get_metrics() - return data - - -@router.post("/reset") -async def reset_metrics(): - metrics = await MetricsCollector.get_instance() - await metrics.reset() - return {"message": "Metrics reset successfully"} diff --git a/qwen3-tts-backend/api/tts.py b/qwen3-tts-backend/api/tts.py index 768f0ba..2e5b601 100644 --- a/qwen3-tts-backend/api/tts.py +++ b/qwen3-tts-backend/api/tts.py @@ -665,13 +665,6 @@ async def create_voice_clone_job( } -@router.get("/models") -@limiter.limit("30/minute") -async def list_models(request: Request): - model_manager = await ModelManager.get_instance() - return model_manager.get_model_info() - - @router.get("/speakers") @limiter.limit("30/minute") async def list_speakers(request: Request, backend: Optional[str] = "local"): diff --git a/qwen3-tts-backend/api/voice_designs.py b/qwen3-tts-backend/api/voice_designs.py index edd0c84..167421d 100644 --- a/qwen3-tts-backend/api/voice_designs.py +++ b/qwen3-tts-backend/api/voice_designs.py @@ -13,7 +13,6 @@ from db import crud from schemas.voice_design import ( VoiceDesignCreate, VoiceDesignResponse, - VoiceDesignUpdate, VoiceDesignListResponse ) @@ -58,45 +57,6 @@ async def list_voice_designs( designs = crud.list_voice_designs(db, current_user.id, backend_type, skip, limit) return VoiceDesignListResponse(designs=[VoiceDesignResponse.from_orm(d) for d in designs], total=len(designs)) -@router.get("/{design_id}", response_model=VoiceDesignResponse) -@limiter.limit("30/minute") -async def get_voice_design( - request: Request, - design_id: int, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - design = crud.get_voice_design(db, design_id, current_user.id) - if not design: - raise HTTPException(status_code=404, detail="Voice design not found") - return VoiceDesignResponse.from_orm(design) - -@router.patch("/{design_id}", response_model=VoiceDesignResponse) -@limiter.limit("30/minute") -async def update_voice_design( - request: Request, - design_id: int, - data: VoiceDesignUpdate, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - design = crud.update_voice_design(db, design_id, current_user.id, data.name) - if not design: - raise HTTPException(status_code=404, detail="Voice design not found") - return VoiceDesignResponse.from_orm(design) - -@router.delete("/{design_id}", status_code=status.HTTP_204_NO_CONTENT) -@limiter.limit("30/minute") -async def delete_voice_design( - request: Request, - design_id: int, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db) -): - success = crud.delete_voice_design(db, design_id, current_user.id) - if not success: - raise HTTPException(status_code=404, detail="Voice design not found") - @router.post("/{design_id}/prepare-clone") @limiter.limit("10/minute") async def prepare_voice_clone_prompt( diff --git a/qwen3-tts-backend/db/crud.py b/qwen3-tts-backend/db/crud.py index 182e55c..df615de 100644 --- a/qwen3-tts-backend/db/crud.py +++ b/qwen3-tts-backend/db/crud.py @@ -329,25 +329,3 @@ def update_voice_design_usage(db: Session, design_id: int, user_id: int) -> Opti db.refresh(design) return design -def update_voice_design( - db: Session, - design_id: int, - user_id: int, - name: Optional[str] = None -) -> Optional[VoiceDesign]: - design = get_voice_design(db, design_id, user_id) - if not design: - return None - if name is not None: - design.name = name - db.commit() - db.refresh(design) - return design - -def delete_voice_design(db: Session, design_id: int, user_id: int) -> bool: - design = get_voice_design(db, design_id, user_id) - if not design: - return False - design.is_active = False - db.commit() - return True diff --git a/qwen3-tts-backend/main.py b/qwen3-tts-backend/main.py index 61244e4..9dc071b 100644 --- a/qwen3-tts-backend/main.py +++ b/qwen3-tts-backend/main.py @@ -15,7 +15,7 @@ from core.config import settings from core.database import init_db from core.model_manager import ModelManager from core.cleanup import run_scheduled_cleanup -from api import auth, jobs, tts, cache, metrics, users, voice_designs +from api import auth, jobs, tts, users, voice_designs from apscheduler.schedulers.asyncio import AsyncIOScheduler logging.basicConfig( @@ -130,8 +130,6 @@ if settings.LOG_LEVEL == "debug": app.include_router(auth.router) app.include_router(jobs.router) app.include_router(tts.router) -app.include_router(cache.router) -app.include_router(metrics.router) app.include_router(users.router) app.include_router(voice_designs.router) diff --git a/qwen3-tts-backend/schemas/voice_design.py b/qwen3-tts-backend/schemas/voice_design.py index 2e3f599..604deed 100644 --- a/qwen3-tts-backend/schemas/voice_design.py +++ b/qwen3-tts-backend/schemas/voice_design.py @@ -10,9 +10,6 @@ class VoiceDesignCreate(BaseModel): meta_data: Optional[Dict[str, Any]] = None preview_text: Optional[str] = None -class VoiceDesignUpdate(BaseModel): - name: Optional[str] = Field(None, min_length=1, max_length=100) - class VoiceDesignResponse(BaseModel): id: int user_id: int diff --git a/qwen3-tts-frontend/src/components/tts/VoiceDesignForm.tsx b/qwen3-tts-frontend/src/components/tts/VoiceDesignForm.tsx index a2f3497..e14224e 100644 --- a/qwen3-tts-frontend/src/components/tts/VoiceDesignForm.tsx +++ b/qwen3-tts-frontend/src/components/tts/VoiceDesignForm.tsx @@ -132,10 +132,12 @@ const VoiceDesignForm = forwardRef((_props, ref) => { try { const backend = preferences?.default_backend || 'local' + const text = watch('text') const design = await voiceDesignApi.create({ name: saveDesignName, instruct: instruct, - backend_type: backend + backend_type: backend, + preview_text: text || `${saveDesignName}的声音` }) toast.success('音色设计已保存') diff --git a/qwen3-tts-frontend/src/lib/api.ts b/qwen3-tts-frontend/src/lib/api.ts index 09d4265..17c8fee 100644 --- a/qwen3-tts-frontend/src/lib/api.ts +++ b/qwen3-tts-frontend/src/lib/api.ts @@ -399,13 +399,6 @@ export const voiceDesignApi = { return response.data }, - get: async (id: number): Promise => { - const response = await apiClient.get( - API_ENDPOINTS.VOICE_DESIGNS.GET(id) - ) - return response.data - }, - create: async (data: VoiceDesignCreate): Promise => { const response = await apiClient.post( API_ENDPOINTS.VOICE_DESIGNS.CREATE, @@ -414,18 +407,6 @@ export const voiceDesignApi = { return response.data }, - update: async (id: number, name: string): Promise => { - const response = await apiClient.patch( - API_ENDPOINTS.VOICE_DESIGNS.UPDATE(id), - { name } - ) - return response.data - }, - - delete: async (id: number): Promise => { - await apiClient.delete(API_ENDPOINTS.VOICE_DESIGNS.DELETE(id)) - }, - prepareClone: async (id: number): Promise<{ message: string; cache_id: number; ref_text: string }> => { const response = await apiClient.post<{ message: string; cache_id: number; ref_text: string }>( API_ENDPOINTS.VOICE_DESIGNS.PREPARE_CLONE(id) diff --git a/qwen3-tts-frontend/src/lib/constants.ts b/qwen3-tts-frontend/src/lib/constants.ts index e67ae10..105a14f 100644 --- a/qwen3-tts-frontend/src/lib/constants.ts +++ b/qwen3-tts-frontend/src/lib/constants.ts @@ -30,9 +30,6 @@ export const API_ENDPOINTS = { VOICE_DESIGNS: { LIST: '/voice-designs', CREATE: '/voice-designs', - GET: (id: number) => `/voice-designs/${id}`, - UPDATE: (id: number) => `/voice-designs/${id}`, - DELETE: (id: number) => `/voice-designs/${id}`, PREPARE_CLONE: (id: number) => `/voice-designs/${id}/prepare-clone`, }, } as const