dsh-audiogen 0.4.18 → 0.4.19
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 +378 -367
- package/lib/client.js.map +1 -1
- package/lib/index.js +18 -3
- package/package.json +1 -1
- package/src/client/voices-view.tsx +4 -0
- package/src/protocol.ts +3 -1
- package/src/routes.ts +1 -0
- package/src/voice-manager.ts +20 -3
package/lib/index.js
CHANGED
|
@@ -1238,7 +1238,10 @@ async function listElevenLabs(channel, options) {
|
|
|
1238
1238
|
} catch (error) {
|
|
1239
1239
|
failures.push(`共享音色库:${error instanceof Error ? error.message : String(error)}`);
|
|
1240
1240
|
}
|
|
1241
|
-
if (entries.length === 0 && failures.length > 0)
|
|
1241
|
+
if (entries.length === 0 && failures.length > 0) {
|
|
1242
|
+
const gatewayHint = gatewayVoiceLibraryHint(failures);
|
|
1243
|
+
throw new Error(`ElevenLabs 音色列表拉取失败:${failures.join(";").slice(0, 300)}${gatewayHint}`);
|
|
1244
|
+
}
|
|
1242
1245
|
const seen = /* @__PURE__ */ new Set();
|
|
1243
1246
|
const deduped = [];
|
|
1244
1247
|
for (const entry of entries) {
|
|
@@ -1246,13 +1249,23 @@ async function listElevenLabs(channel, options) {
|
|
|
1246
1249
|
seen.add(entry.voice_id);
|
|
1247
1250
|
deduped.push(entry);
|
|
1248
1251
|
}
|
|
1252
|
+
const note = failures.length === 0 ? void 0 : `部分端点失败(已忽略):${failures.join(";").slice(0, 300)}${gatewayVoiceLibraryHint(failures)}`;
|
|
1249
1253
|
return {
|
|
1250
1254
|
vendor: "elevenlabs",
|
|
1251
1255
|
voices: deduped,
|
|
1252
1256
|
truncated: false,
|
|
1253
|
-
...
|
|
1257
|
+
...note === void 0 ? {} : { note }
|
|
1254
1258
|
};
|
|
1255
1259
|
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Gateway (new-api 类) 通常不映射 ElevenLabs 音色库端点,其响应是
|
|
1262
|
+
* 404/Invalid URL。把这种失败翻译成可操作的建议,而不是一份原始 JSON。
|
|
1263
|
+
*/
|
|
1264
|
+
function gatewayVoiceLibraryHint(failures) {
|
|
1265
|
+
const raw = failures.join(" ").toLowerCase();
|
|
1266
|
+
if (!raw.includes("404") && !raw.includes("invalid url") && !raw.includes("not found")) return "";
|
|
1267
|
+
return "。该渠道网关未提供 ElevenLabs 音色库端点(/v1/voices、/v1/shared-voices):生成可用,但浏览/删除音色需要配置官方 API 地址 https://api.elevenlabs.io 的渠道";
|
|
1268
|
+
}
|
|
1256
1269
|
async function listVendorVoices(channel, options = {}) {
|
|
1257
1270
|
if (channel.apiUrl.trim() === "") throw new Error("渠道未配置 API 地址");
|
|
1258
1271
|
if (channel.apiKey.trim() === "") throw new Error("渠道未配置 API 密钥");
|
|
@@ -1324,7 +1337,7 @@ async function deleteVendorVoice(channel, voiceId) {
|
|
|
1324
1337
|
}
|
|
1325
1338
|
function cap(options) {
|
|
1326
1339
|
const limit = typeof options.limit === "number" && Number.isFinite(options.limit) ? Math.floor(options.limit) : 100;
|
|
1327
|
-
return Math.max(1, Math.min(
|
|
1340
|
+
return Math.max(1, Math.min(500, limit));
|
|
1328
1341
|
}
|
|
1329
1342
|
/** Which official shared-voice filters are set (for the MiniMax "not supported" note). */
|
|
1330
1343
|
function serverFilterNote(filters) {
|
|
@@ -1367,6 +1380,7 @@ function applyFilter(entries, options) {
|
|
|
1367
1380
|
}
|
|
1368
1381
|
return true;
|
|
1369
1382
|
});
|
|
1383
|
+
matched.sort((a, b) => a.deletable === b.deletable ? 0 : a.deletable ? -1 : 1);
|
|
1370
1384
|
return {
|
|
1371
1385
|
voices: matched.slice(0, count),
|
|
1372
1386
|
truncated: matched.length > count
|
|
@@ -2421,6 +2435,7 @@ function makeRoutes(deps) {
|
|
|
2421
2435
|
...str("language") === void 0 ? {} : { language: str("language") },
|
|
2422
2436
|
...str("keyword") === void 0 ? {} : { keyword: str("keyword") },
|
|
2423
2437
|
...str("source") === void 0 ? {} : { source: str("source") },
|
|
2438
|
+
...typeof body?.limit === "number" && Number.isFinite(body.limit) ? { limit: Math.floor(body.limit) } : {},
|
|
2424
2439
|
...Object.keys(filters).length === 0 ? {} : { serverFilters: filters }
|
|
2425
2440
|
});
|
|
2426
2441
|
writeJson(res, 200, {
|
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.19",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -76,6 +76,7 @@ export function VoicesView(props: {
|
|
|
76
76
|
const [freeUsersAllowed, setFreeUsersAllowed] = useState(false)
|
|
77
77
|
|
|
78
78
|
const [voices, setVoices] = useState<VoiceEntry[]>([])
|
|
79
|
+
const [truncated, setTruncated] = useState(false)
|
|
79
80
|
const [loading, setLoading] = useState(false)
|
|
80
81
|
const [error, setError] = useState<string | null>(null)
|
|
81
82
|
const [note, setNote] = useState<string | null>(null)
|
|
@@ -103,9 +104,11 @@ export function VoicesView(props: {
|
|
|
103
104
|
...(sort === '' ? {} : { sort }),
|
|
104
105
|
...(featured ? { featured: true } : {}),
|
|
105
106
|
...(freeUsersAllowed ? { free_users_allowed: true } : {}),
|
|
107
|
+
limit: 500,
|
|
106
108
|
})
|
|
107
109
|
if (response.ok) {
|
|
108
110
|
setVoices(response.voices ?? [])
|
|
111
|
+
setTruncated(response.truncated === true)
|
|
109
112
|
setNote(response.note ?? null)
|
|
110
113
|
} else {
|
|
111
114
|
setVoices([])
|
|
@@ -218,6 +221,7 @@ export function VoicesView(props: {
|
|
|
218
221
|
{loading ? <span className={css.stateNote}>正在拉取音色列表…</span> : null}
|
|
219
222
|
{!loading && error !== null ? <span className={css.stateNote} data-error>⚠ {error}</span> : null}
|
|
220
223
|
{!loading && note !== null ? <span className={css.stateNote}>{note}</span> : null}
|
|
224
|
+
{!loading && truncated ? <span className={css.stateNote}>已截断:仅显示前 {voices.length} 个,可用筛选条件缩小范围</span> : null}
|
|
221
225
|
{!loading && error === null ? <span className={css.count}>{voices.length} 个音色</span> : null}
|
|
222
226
|
</div>
|
|
223
227
|
|
package/src/protocol.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const AUDIOGEN_SETTINGS_NAMESPACE = 'dsh-audiogen'
|
|
9
9
|
|
|
10
10
|
/** Published package version shared by the host updater and the client UI. */
|
|
11
|
-
export const PLUGIN_VERSION = '0.4.
|
|
11
|
+
export const PLUGIN_VERSION = '0.4.19'
|
|
12
12
|
|
|
13
13
|
/** Same-origin route family (loopback-only, mirroring dsh-imagegen). */
|
|
14
14
|
export const SETTINGS_API = {
|
|
@@ -70,6 +70,8 @@ export interface VoicesListRequest {
|
|
|
70
70
|
language?: string
|
|
71
71
|
keyword?: string
|
|
72
72
|
source?: string
|
|
73
|
+
/** Max returned entries (default 100, hard cap 500). */
|
|
74
|
+
limit?: number
|
|
73
75
|
/** Official /v1/shared-voices server-side filters (ElevenLabs only). */
|
|
74
76
|
search?: string
|
|
75
77
|
use_case?: string
|
package/src/routes.ts
CHANGED
|
@@ -426,6 +426,7 @@ export function makeRoutes(deps: AudiogenRoutesDeps): WebRoute[] {
|
|
|
426
426
|
...(str('language') === undefined ? {} : { language: str('language')! }),
|
|
427
427
|
...(str('keyword') === undefined ? {} : { keyword: str('keyword')! }),
|
|
428
428
|
...(str('source') === undefined ? {} : { source: str('source')! }),
|
|
429
|
+
...(typeof body?.limit === 'number' && Number.isFinite(body.limit) ? { limit: Math.floor(body.limit) } : {}),
|
|
429
430
|
...(Object.keys(filters).length === 0 ? {} : { serverFilters: filters }),
|
|
430
431
|
})
|
|
431
432
|
writeJson(res, 200, {
|
package/src/voice-manager.ts
CHANGED
|
@@ -272,7 +272,10 @@ async function listElevenLabs(channel: AudioChannel, options: ListVoicesOptions)
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
if (entries.length === 0 && failures.length > 0) {
|
|
275
|
-
|
|
275
|
+
const gatewayHint = gatewayVoiceLibraryHint(failures)
|
|
276
|
+
throw new Error(
|
|
277
|
+
`ElevenLabs 音色列表拉取失败:${failures.join(';').slice(0, 300)}${gatewayHint}`,
|
|
278
|
+
)
|
|
276
279
|
}
|
|
277
280
|
// Owned voices come first and shadow shared ones with the same id.
|
|
278
281
|
const seen = new Set<string>()
|
|
@@ -282,14 +285,25 @@ async function listElevenLabs(channel: AudioChannel, options: ListVoicesOptions)
|
|
|
282
285
|
seen.add(entry.voice_id)
|
|
283
286
|
deduped.push(entry)
|
|
284
287
|
}
|
|
288
|
+
const note = failures.length === 0 ? undefined : `部分端点失败(已忽略):${failures.join(';').slice(0, 300)}${gatewayVoiceLibraryHint(failures)}`
|
|
285
289
|
return {
|
|
286
290
|
vendor: 'elevenlabs',
|
|
287
291
|
voices: deduped,
|
|
288
292
|
truncated: false,
|
|
289
|
-
...(
|
|
293
|
+
...(note === undefined ? {} : { note }),
|
|
290
294
|
}
|
|
291
295
|
}
|
|
292
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Gateway (new-api 类) 通常不映射 ElevenLabs 音色库端点,其响应是
|
|
299
|
+
* 404/Invalid URL。把这种失败翻译成可操作的建议,而不是一份原始 JSON。
|
|
300
|
+
*/
|
|
301
|
+
function gatewayVoiceLibraryHint(failures: string[]): string {
|
|
302
|
+
const raw = failures.join(' ').toLowerCase()
|
|
303
|
+
if (!raw.includes('404') && !raw.includes('invalid url') && !raw.includes('not found')) return ''
|
|
304
|
+
return '。该渠道网关未提供 ElevenLabs 音色库端点(/v1/voices、/v1/shared-voices):生成可用,但浏览/删除音色需要配置官方 API 地址 https://api.elevenlabs.io 的渠道'
|
|
305
|
+
}
|
|
306
|
+
|
|
293
307
|
// ------------------------------------------------------ public surface
|
|
294
308
|
|
|
295
309
|
export async function listVendorVoices(
|
|
@@ -387,7 +401,7 @@ function cap(options: ListVoicesOptions): number {
|
|
|
387
401
|
const limit = typeof options.limit === 'number' && Number.isFinite(options.limit)
|
|
388
402
|
? Math.floor(options.limit)
|
|
389
403
|
: 100
|
|
390
|
-
return Math.max(1, Math.min(
|
|
404
|
+
return Math.max(1, Math.min(500, limit))
|
|
391
405
|
}
|
|
392
406
|
|
|
393
407
|
/** Which official shared-voice filters are set (for the MiniMax "not supported" note). */
|
|
@@ -438,5 +452,8 @@ function applyFilter(
|
|
|
438
452
|
}
|
|
439
453
|
return true
|
|
440
454
|
})
|
|
455
|
+
// Deletable (account-owned/custom) voices first so narrow default windows
|
|
456
|
+
// show the manageable ones; stable sort keeps provider order inside groups.
|
|
457
|
+
matched.sort((a, b) => (a.deletable === b.deletable ? 0 : a.deletable ? -1 : 1))
|
|
441
458
|
return { voices: matched.slice(0, count), truncated: matched.length > count }
|
|
442
459
|
}
|