dsh-audiogen 0.4.25 → 0.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +377 -506
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/voices-view.tsx +3 -85
- package/src/client/voices.module.css +0 -85
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-audiogen",
|
|
3
3
|
"description": "AI audio generation plugin for the dsh web GUI: multi-vendor TTS/music/sound-effect channels (OpenAI-compatible, ElevenLabs, MiniMax, Stability AI and custom), per-channel model/voice catalogs, Agent tool and a sidebar AI 音频 panel.",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.26",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
import { useEffect, useMemo, useState } from 'react'
|
|
13
13
|
import type { AudiogenApi } from './api.ts'
|
|
14
14
|
import type { AudiogenScope } from './settings-scope.ts'
|
|
15
|
-
import type { VoiceEntry
|
|
16
|
-
import { tt } from './helpers.ts'
|
|
15
|
+
import type { VoiceEntry } from '../protocol.ts'
|
|
17
16
|
import { AudioPlayer } from './audio-player.tsx'
|
|
18
|
-
import { MicIcon, SearchIcon, TrashIcon, CheckIcon
|
|
17
|
+
import { MicIcon, SearchIcon, TrashIcon, CheckIcon } from './icons.tsx'
|
|
19
18
|
import css from './voices.module.css'
|
|
20
19
|
|
|
21
20
|
export interface VoicesReusePayload {
|
|
@@ -100,26 +99,6 @@ export function VoicesView(props: {
|
|
|
100
99
|
const [note, setNote] = useState<string | null>(null)
|
|
101
100
|
const [deletingId, setDeletingId] = useState<string | null>(null)
|
|
102
101
|
|
|
103
|
-
// AI 推荐记录(每次推荐自动落盘,最多保留 50 条;关闭面板后仍可回看)
|
|
104
|
-
const [records, setRecords] = useState<VoiceRecommendRecord[]>([])
|
|
105
|
-
const [recordsOpen, setRecordsOpen] = useState(false)
|
|
106
|
-
|
|
107
|
-
const loadRecords = async (): Promise<void> => {
|
|
108
|
-
try {
|
|
109
|
-
const response = await api.voiceRecommendHistory(30)
|
|
110
|
-
setRecords(response.entries ?? [])
|
|
111
|
-
} catch {
|
|
112
|
-
// best-effort:记录加载失败不打扰主流程
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const removeRecord = async (id: string): Promise<void> => {
|
|
117
|
-
await api.voiceRecommendHistoryRemove(id).catch(() => { /* best-effort */ })
|
|
118
|
-
void loadRecords()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
useEffect(() => { void loadRecords() }, [api])
|
|
122
|
-
|
|
123
102
|
/** 当前的筛选载荷(查询音色列表用)。 */
|
|
124
103
|
const currentFilterPayload = (): Record<string, unknown> => ({
|
|
125
104
|
channel: channelId,
|
|
@@ -201,20 +180,6 @@ export function VoicesView(props: {
|
|
|
201
180
|
})
|
|
202
181
|
}
|
|
203
182
|
|
|
204
|
-
/** 从推荐记录里复用音色:切到记录对应的渠道,再回填 TTS 表单。 */
|
|
205
|
-
const reuseFromRecord = (record: VoiceRecommendRecord, voice: { voice_id: string }): void => {
|
|
206
|
-
if (record.channel_id !== undefined && record.channel_id !== '' && channels.some(item => item.id === record.channel_id)) {
|
|
207
|
-
setChannelId(record.channel_id)
|
|
208
|
-
}
|
|
209
|
-
const channel = channels.find(item => item.id === (record.channel_id ?? channelId))
|
|
210
|
-
const ttsModel = channel?.models.find(model => model.category === 'tts' || /tts|speech|voice|t2a/i.test(model.alias))
|
|
211
|
-
onReuseVoice({
|
|
212
|
-
mode: 'tts',
|
|
213
|
-
voiceId: voice.voice_id,
|
|
214
|
-
...(ttsModel === undefined || ttsModel.alias === '' ? {} : { model: ttsModel.alias }),
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
183
|
return (
|
|
219
184
|
<div className={css.view}>
|
|
220
185
|
<div className={css.toolbar}>
|
|
@@ -254,56 +219,9 @@ export function VoicesView(props: {
|
|
|
254
219
|
</div>
|
|
255
220
|
|
|
256
221
|
<p className={css.stateNote}>
|
|
257
|
-
按需求描述(prompt)推荐音色请使用生成页左侧「🎤
|
|
222
|
+
按需求描述(prompt)推荐音色请使用生成页左侧「🎤 音色推荐」模式;这里仅浏览/筛选/删除音色。
|
|
258
223
|
</p>
|
|
259
224
|
|
|
260
|
-
{records.length > 0 ? (
|
|
261
|
-
<details
|
|
262
|
-
className={css.recordBox}
|
|
263
|
-
open={recordsOpen}
|
|
264
|
-
onToggle={(event) => setRecordsOpen((event.target as HTMLDetailsElement).open)}
|
|
265
|
-
>
|
|
266
|
-
<summary className={css.recordSummary}>
|
|
267
|
-
<ListIcon /> 最近 AI 推荐({records.length})
|
|
268
|
-
<span className={css.recordHint}>每次推荐自动记录,点「用此音色生成」可直接回填 TTS 表单</span>
|
|
269
|
-
</summary>
|
|
270
|
-
<div className={css.recordList}>
|
|
271
|
-
{records.map(record => (
|
|
272
|
-
<div className={`${css.card} ${css.recordCard}`} key={record.id}>
|
|
273
|
-
<div className={css.cardHead}>
|
|
274
|
-
<span className={css.voiceName} title={record.requirement}>{record.requirement}</span>
|
|
275
|
-
<span className={css.badge} data-source={record.vendor}>{record.channel}</span>
|
|
276
|
-
</div>
|
|
277
|
-
<div className={css.meta}>
|
|
278
|
-
<span>{new Date(record.createdAt).toLocaleString()}</span>
|
|
279
|
-
<span>候选池 {record.candidate_count}</span>
|
|
280
|
-
<span>推荐 {record.recommendations.length} 条</span>
|
|
281
|
-
</div>
|
|
282
|
-
<div className={css.recordVoices}>
|
|
283
|
-
{record.recommendations.map((voice, index) => (
|
|
284
|
-
<div className={css.recordVoiceRow} key={`${record.id}:${voice.voice_id}`}>
|
|
285
|
-
<span className={css.rank}>{index + 1}</span>
|
|
286
|
-
<span className={css.recordVoiceName} title={voice.name}>{voice.name}</span>
|
|
287
|
-
<span className={css.voiceId} title={voice.voice_id}>ID: {voice.voice_id}</span>
|
|
288
|
-
{voice.descriptive === undefined ? null : <span className={css.reasonInline}>descriptive: {voice.descriptive}</span>}
|
|
289
|
-
{voice.reason === '' ? null : <span className={css.reasonInline}>{voice.reason}</span>}
|
|
290
|
-
<button type="button" className={css.useBtn} onClick={() => reuseFromRecord(record, voice)}>
|
|
291
|
-
<MicIcon /> 用此音色生成
|
|
292
|
-
</button>
|
|
293
|
-
</div>
|
|
294
|
-
))}
|
|
295
|
-
</div>
|
|
296
|
-
<div className={css.actions}>
|
|
297
|
-
<button type="button" className={css.deleteBtn} onClick={() => void removeRecord(record.id)}>
|
|
298
|
-
<TrashIcon /> 删除记录
|
|
299
|
-
</button>
|
|
300
|
-
</div>
|
|
301
|
-
</div>
|
|
302
|
-
))}
|
|
303
|
-
</div>
|
|
304
|
-
</details>
|
|
305
|
-
) : null}
|
|
306
|
-
|
|
307
225
|
<details className={css.advanced}>
|
|
308
226
|
<summary className={css.advancedSummary}>官方共享库筛选(ElevenLabs /v1/shared-voices,其它渠道无效)</summary>
|
|
309
227
|
<div className={css.advancedGrid}>
|
|
@@ -323,94 +323,9 @@
|
|
|
323
323
|
background: color-mix(in srgb, var(--dsw-accent, #2563eb) 4%, var(--dsw-alias-bg, #fff));
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
.rank {
|
|
327
|
-
display: inline-flex;
|
|
328
|
-
align-items: center;
|
|
329
|
-
justify-content: center;
|
|
330
|
-
width: 20px;
|
|
331
|
-
height: 20px;
|
|
332
|
-
border-radius: 999px;
|
|
333
|
-
background: var(--dsw-accent, #2563eb);
|
|
334
|
-
color: #fff;
|
|
335
|
-
font-size: 11px;
|
|
336
|
-
font-weight: 600;
|
|
337
|
-
flex: none;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
326
|
.reason {
|
|
341
327
|
margin: 0;
|
|
342
328
|
font-size: 12.5px;
|
|
343
329
|
line-height: 1.5;
|
|
344
330
|
color: var(--dsw-accent, #2563eb);
|
|
345
331
|
}
|
|
346
|
-
|
|
347
|
-
/* ------------------------------------------------------ AI 推荐记录 */
|
|
348
|
-
.recordBox {
|
|
349
|
-
border: 1px solid var(--dsw-alias-border-l2, #e5e7eb);
|
|
350
|
-
border-radius: 10px;
|
|
351
|
-
background: var(--dsw-alias-bg-layer-2, #f9fafb);
|
|
352
|
-
padding: 8px 12px;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
.recordSummary {
|
|
356
|
-
display: flex;
|
|
357
|
-
align-items: center;
|
|
358
|
-
gap: 6px;
|
|
359
|
-
cursor: pointer;
|
|
360
|
-
font-size: 13px;
|
|
361
|
-
font-weight: 600;
|
|
362
|
-
color: var(--dsw-alias-fg, #111827);
|
|
363
|
-
user-select: none;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.recordHint {
|
|
367
|
-
font-size: 11.5px;
|
|
368
|
-
font-weight: 400;
|
|
369
|
-
color: var(--dsw-alias-fg-l3, #6b7280);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.recordList {
|
|
373
|
-
display: flex;
|
|
374
|
-
flex-direction: column;
|
|
375
|
-
gap: 8px;
|
|
376
|
-
margin-top: 8px;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.recordCard {
|
|
380
|
-
border-left: 3px solid var(--dsw-accent, #2563eb);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.recordVoices {
|
|
384
|
-
display: flex;
|
|
385
|
-
flex-direction: column;
|
|
386
|
-
gap: 4px;
|
|
387
|
-
margin-top: 6px;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
.recordVoiceRow {
|
|
391
|
-
display: flex;
|
|
392
|
-
align-items: center;
|
|
393
|
-
gap: 8px;
|
|
394
|
-
flex-wrap: wrap;
|
|
395
|
-
padding: 4px 2px;
|
|
396
|
-
border-radius: 6px;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
.recordVoiceRow:hover {
|
|
400
|
-
background: color-mix(in srgb, var(--dsw-accent, #2563eb) 5%, transparent);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
.recordVoiceName {
|
|
404
|
-
font-size: 13px;
|
|
405
|
-
font-weight: 500;
|
|
406
|
-
color: var(--dsw-alias-fg, #111827);
|
|
407
|
-
overflow: hidden;
|
|
408
|
-
text-overflow: ellipsis;
|
|
409
|
-
white-space: nowrap;
|
|
410
|
-
max-width: 220px;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
.reasonInline {
|
|
414
|
-
font-size: 12px;
|
|
415
|
-
color: var(--dsw-accent, #2563eb);
|
|
416
|
-
}
|