chatterbox-ui/storage_service.py

111 lines
4.1 KiB
Python

"""
Project storage service for saving and loading Chatterbox TTS projects.
"""
import json
import os
import asyncio
from pathlib import Path
from typing import List, Optional
from datetime import datetime
from models import DialogProject, DialogLine
class ProjectStorage:
"""Handles saving and loading projects to/from JSON files."""
def __init__(self, storage_dir: str = "projects"):
self.storage_dir = Path(storage_dir)
self.storage_dir.mkdir(exist_ok=True)
async def save_project(self, project: DialogProject) -> bool:
"""Save a project to a JSON file."""
try:
project_file = self.storage_dir / f"{project.id}.json"
# Convert to dict and ensure timestamps are strings
project_data = project.dict()
project_data["last_modified"] = datetime.now().isoformat()
# Ensure created_at is set if not already
if not project_data.get("created_at"):
project_data["created_at"] = datetime.now().isoformat()
with open(project_file, 'w', encoding='utf-8') as f:
json.dump(project_data, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"Error saving project {project.id}: {e}")
return False
async def load_project(self, project_id: str) -> Optional[DialogProject]:
"""Load a project from a JSON file."""
try:
project_file = self.storage_dir / f"{project_id}.json"
if not project_file.exists():
return None
with open(project_file, 'r', encoding='utf-8') as f:
project_data = json.load(f)
# Validate that audio files still exist
for line in project_data.get("lines", []):
if line.get("audio_url"):
audio_path = Path("dialog_output") / line["audio_url"].split("/")[-1]
if not audio_path.exists():
line["audio_url"] = None
line["status"] = "pending"
return DialogProject(**project_data)
except Exception as e:
print(f"Error loading project {project_id}: {e}")
return None
async def list_projects(self) -> List[dict]:
"""List all saved projects with metadata."""
projects = []
for project_file in self.storage_dir.glob("*.json"):
try:
with open(project_file, 'r', encoding='utf-8') as f:
project_data = json.load(f)
projects.append({
"id": project_data["id"],
"name": project_data["name"],
"created_at": project_data.get("created_at"),
"last_modified": project_data.get("last_modified"),
"line_count": len(project_data.get("lines", [])),
"has_audio": any(line.get("audio_url") for line in project_data.get("lines", []))
})
except Exception as e:
print(f"Error reading project file {project_file}: {e}")
continue
# Sort by last modified (most recent first)
projects.sort(key=lambda x: x.get("last_modified", ""), reverse=True)
return projects
async def delete_project(self, project_id: str) -> bool:
"""Delete a saved project."""
try:
project_file = self.storage_dir / f"{project_id}.json"
if project_file.exists():
project_file.unlink()
return True
return False
except Exception as e:
print(f"Error deleting project {project_id}: {e}")
return False
async def project_exists(self, project_id: str) -> bool:
"""Check if a project exists in storage."""
project_file = self.storage_dir / f"{project_id}.json"
return project_file.exists()
# Global storage instance
project_storage = ProjectStorage()