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/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) throw new Error(`ElevenLabs 音色列表拉取失败:${failures.join(";").slice(0, 300)}`);
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
- ...failures.length === 0 ? {} : { note: `部分端点失败(已忽略):${failures.join(";").slice(0, 300)}` }
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(200, limit));
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.18",
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.18'
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, {
@@ -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
- throw new Error(`ElevenLabs 音色列表拉取失败:${failures.join(';').slice(0, 300)}`)
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
- ...(failures.length === 0 ? {} : { note: `部分端点失败(已忽略):${failures.join(';').slice(0, 300)}` }),
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(200, limit))
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
  }