Spaces:
Sleeping
Sleeping
| import Dexie, { type Table } from "dexie"; | |
| export const HISTORY_CAP = 50; | |
| export type VoiceRecord = { | |
| id?: number; | |
| name: string; | |
| blob: Blob; | |
| sampleRate: number; | |
| durationMs: number; | |
| createdAt: number; | |
| isFavorite: boolean; | |
| }; | |
| export type SpeakerRef = { letter: "A" | "B" | "C" | "D"; voiceId: number }; | |
| export type HistoryRecord = { | |
| id?: number; | |
| text: string; | |
| modelId: string; | |
| voiceId?: number; | |
| language?: string; | |
| params: Record<string, unknown>; | |
| audioBlob: Blob; | |
| createdAt: number; | |
| kind?: "single" | "dialog"; | |
| seedUsed?: number; | |
| speakers?: SpeakerRef[]; | |
| }; | |
| class DB extends Dexie { | |
| voices!: Table<VoiceRecord, number>; | |
| history!: Table<HistoryRecord, number>; | |
| constructor() { | |
| super("chatterbox-voice-studio"); | |
| this.version(1).stores({ | |
| voices: "++id, name, createdAt, isFavorite", | |
| history: "++id, createdAt", | |
| }); | |
| this.version(2).stores({ | |
| voices: "++id, name, createdAt, isFavorite", | |
| history: "++id, createdAt", | |
| }).upgrade(async (tx) => { | |
| // Backfill new fields on existing rows so listings stay consistent. | |
| await tx.table("history").toCollection().modify((r: HistoryRecord) => { | |
| if (!r.kind) r.kind = "single"; | |
| }); | |
| }); | |
| } | |
| } | |
| export const db = new DB(); | |
| export async function addVoice( | |
| v: Omit<VoiceRecord, "id" | "createdAt" | "isFavorite"> & Partial<Pick<VoiceRecord, "isFavorite">>, | |
| ): Promise<number> { | |
| return db.voices.add({ | |
| ...v, | |
| isFavorite: v.isFavorite ?? false, | |
| createdAt: Date.now(), | |
| }); | |
| } | |
| export async function listVoices(): Promise<VoiceRecord[]> { | |
| return db.voices.orderBy("createdAt").reverse().toArray(); | |
| } | |
| export async function deleteVoice(id: number): Promise<void> { | |
| await db.voices.delete(id); | |
| } | |
| export async function setFavorite(id: number, fav: boolean): Promise<void> { | |
| await db.voices.update(id, { isFavorite: fav }); | |
| } | |
| export async function addHistory( | |
| h: Omit<HistoryRecord, "id" | "createdAt">, | |
| ): Promise<number> { | |
| const id = await db.history.add({ ...h, createdAt: Date.now() }); | |
| const count = await db.history.count(); | |
| if (count > HISTORY_CAP) { | |
| const overflow = count - HISTORY_CAP; | |
| const oldest = await db.history.orderBy("createdAt").limit(overflow).primaryKeys(); | |
| await db.history.bulkDelete(oldest); | |
| } | |
| return id; | |
| } | |
| export async function listHistory(): Promise<HistoryRecord[]> { | |
| return db.history.orderBy("createdAt").reverse().toArray(); | |
| } | |
| export async function clearHistory(): Promise<void> { | |
| await db.history.clear(); | |
| } | |