feat: add Text to Speech (TTS) functionality with provider selection, generation, and history management

- Implemented CreateTTSDialog for generating TTS from user input.
- Added TTSHeader for search, sorting, and creation controls.
- Created TTSList to display TTS history with filtering and sorting capabilities.
- Developed TTSLoadingStates for handling loading and error states.
- Introduced TTSRow for individual TTS entries with play and delete options.
- Built TTSTable for structured display of TTS history.
- Integrated TTS service API for generating and managing TTS data.
- Added TTSPage to encapsulate the TTS feature with pagination and state management.
This commit is contained in:
JSC
2025-09-20 23:11:21 +02:00
parent da4566c789
commit 6f477a1aa7
10 changed files with 1344 additions and 0 deletions

129
src/lib/api/services/tts.ts Normal file
View File

@@ -0,0 +1,129 @@
import { apiClient } from '../client'
export interface TTSRequest {
text: string
provider?: string
options?: Record<string, any>
}
export interface TTSResponse {
id: number
text: string
provider: string
options: Record<string, any>
sound_id: number | null
user_id: number
created_at: string
}
export interface TTSGenerateResponse {
message: string
tts: TTSResponse
}
export interface TTSProvider {
name: string
file_extension: string
supported_languages: string[]
option_schema: Record<string, any>
}
export interface TTSProvidersResponse {
[key: string]: TTSProvider
}
export type TTSSortField = 'created_at' | 'text' | 'provider'
export type TTSSortOrder = 'asc' | 'desc'
export interface GetTTSHistoryParams {
search?: string
sort_by?: TTSSortField
sort_order?: TTSSortOrder
page?: number
limit?: number
}
export interface GetTTSHistoryResponse {
tts: TTSResponse[]
total: number
total_pages: number
current_page: number
}
export const ttsService = {
async generateTTS(request: TTSRequest): Promise<TTSGenerateResponse> {
return await apiClient.post('/api/v1/tts/generate', request)
},
async getTTSHistory(params?: GetTTSHistoryParams): Promise<GetTTSHistoryResponse> {
const searchParams = new URLSearchParams()
// Backend currently only supports limit and offset, not page-based pagination
if (params?.limit) {
searchParams.append('limit', params.limit.toString())
}
if (params?.page && params?.limit) {
// Convert page to offset
const offset = (params.page - 1) * params.limit
searchParams.append('offset', offset.toString())
}
const url = searchParams.toString()
? `/api/v1/tts/history?${searchParams.toString()}`
: '/api/v1/tts/history'
const ttsArray: TTSResponse[] = await apiClient.get(url)
// Apply client-side filtering and sorting since backend doesn't support them yet
let filteredTTS = ttsArray
if (params?.search) {
const search = params.search.toLowerCase()
filteredTTS = filteredTTS.filter(tts =>
tts.text.toLowerCase().includes(search) ||
tts.provider.toLowerCase().includes(search)
)
}
if (params?.sort_by && params?.sort_order) {
filteredTTS.sort((a, b) => {
let aValue = a[params.sort_by as keyof TTSResponse]
let bValue = b[params.sort_by as keyof TTSResponse]
// Convert dates to timestamps for comparison
if (params.sort_by === 'created_at') {
aValue = new Date(aValue as string).getTime()
bValue = new Date(bValue as string).getTime()
}
const comparison = aValue > bValue ? 1 : -1
return params.sort_order === 'asc' ? comparison : -comparison
})
}
// Calculate pagination info
const limit = params?.limit || 50
const currentPage = params?.page || 1
const total = filteredTTS.length
const totalPages = Math.ceil(total / limit)
return {
tts: filteredTTS,
total,
total_pages: totalPages,
current_page: currentPage,
}
},
async getProviders(): Promise<TTSProvidersResponse> {
return await apiClient.get('/api/v1/tts/providers')
},
async getProvider(name: string): Promise<TTSProvider> {
return await apiClient.get(`/api/v1/tts/providers/${name}`)
},
async deleteTTS(ttsId: number): Promise<{ message: string }> {
return await apiClient.delete(`/api/v1/tts/${ttsId}`)
},
}