apick-api 3.10.0 → 4.0.0
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/CHANGELOG.md +16 -0
- package/README.md +468 -466
- package/docs/guide.en.md +363 -359
- package/docs/guide.ko.md +371 -367
- package/docs/tts.md +34 -0
- package/package.json +1 -1
- package/src/index.cjs +33 -13
- package/src/index.d.ts +37 -9
package/docs/tts.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Gemini·ChatGPT TTS
|
|
2
|
+
|
|
3
|
+
기존 APICK 목소리와 자동 전환은 종료되었습니다. `createTtsJob`은 Gemini 기본 목소리 `Kore`를 사용합니다. `getTtsQuality`, `retryTtsJob`, `downloadTtsCandidate`의 기존 경로는 HTTP 409를 반환합니다.
|
|
4
|
+
|
|
5
|
+
Legacy APICK voices and automatic fallback have retired. `createTtsJob` now defaults to Gemini voice `Kore`. The legacy quality, retry and candidate endpoints return HTTP 409.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const job = await client.createTtsJob('기온은 오 도입니다.', { voiceId: 'Kore', normalizeText: false, idempotencyKey: 'tts-001' });
|
|
9
|
+
const dialogue = await client.createOpenAiTtsJob({
|
|
10
|
+
utterances: [{ speaker: '진행', text: '반갑습니다.' }, { speaker: '손님', text: '안녕하세요.' }],
|
|
11
|
+
speakers: { 진행: { voice_id: 'alloy' }, 손님: { voice_id: 'nova', emotion: 'calm' } },
|
|
12
|
+
multi_speaker: true, normalize_text: false
|
|
13
|
+
}, { idempotencyKey: 'tts-002' });
|
|
14
|
+
const voices = await client.listGeminiTtsVoices();
|
|
15
|
+
const otherVoices = await client.listOpenAiTtsVoices();
|
|
16
|
+
const options = await client.getTtsOptions();
|
|
17
|
+
const quote = await client.quoteTts({ engine: 'gemini', text: '본문', voice_id: 'Kore' });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`style`, `emotion`, `tone`, `accent`, `pace`(0.5–2), `pitch`(-12–12), `volume_gain_db`(-12–12)는 최상위·화자·발화에 지정할 수 있습니다. `speakers`는 화자 이름별 설정이고 `multi_speaker: true`는 Gemini 최대 2명, ChatGPT 최대 8명입니다. 엔진에 따라 표현 효과가 달라지며 정확한 속도 배율·반음·dB 적용을 보장하지 않습니다. 특히 ChatGPT는 수치의 방향을 낭독 지시로 전달합니다.
|
|
21
|
+
|
|
22
|
+
Style, emotion, tone, accent, pace, pitch and volume options can be set per request, speaker or utterance. Gemini supports up to two native speakers; ChatGPT supports up to eight speakers across utterances. Numeric acoustic adjustments are not guaranteed: ChatGPT receives directional instructions rather than precise rate, semitone or decibel controls.
|
|
23
|
+
|
|
24
|
+
정규화는 기본 켜짐이며 추가 스킬 요금이 합산됩니다. `normalize_text: false`(SDK 문자열 호출은 `normalizeText: false`)로 끌 수 있습니다. 정규화 입력은 발화 사이 줄바꿈을 포함해 최대 2,000자, 정규화 없이 최대 8,000자입니다. 견적은 `quoteTts`, 현재 목소리는 엔진별 목록, 옵션은 `getTtsOptions`로 조회합니다.
|
|
25
|
+
|
|
26
|
+
Normalization is enabled by default and costs extra. Disable it with `normalize_text: false` (`normalizeText: false` for the string SDK helper). Input limits are 2,000 characters including utterance separators with normalization, or 8,000 without it. Query a quote, engine-specific voices and supported options before submitting.
|
|
27
|
+
|
|
28
|
+
실제 AI 원가 × 고정된 환율 × 1.4에 정규화 요금을 더해 정산합니다. Gemini 프로모션 입력/출력 단가는 2026-12-31까지 백만 토큰당 $0.50/$6.00이며 2027-01-01 00:00 UTC부터 $1.00/$12.00입니다. 약 $0.54/시간은 참고값이며 시간 단위 청구가 아닙니다. 엔진별 요금이 다릅니다.
|
|
29
|
+
|
|
30
|
+
AI usage is charged at verified cost × the pinned exchange rate × 1.4, plus normalization. Gemini promotional input/output rates are $0.50/$6.00 per million tokens through 2026-12-31, then $1.00/$12.00 from 2027-01-01 00:00 UTC. The approximate hourly figure is informational; billing uses actual usage and differs by engine.
|
|
31
|
+
|
|
32
|
+
두 엔진은 같은 작업 조회·취소·MP3·원문 ASS 계약을 사용합니다. `billing`의 예약·해제·확정·환불을 확인하세요. MP3는 24kHz 모노 48kbps, 결과는 완료 후 24시간 보관합니다. MP3와 ASS는 각각 완료된 다운로드 1회만 허용하며 전송 중단은 재시도할 수 있습니다. 접수 응답 유실 시 같은 입력과 멱등 키를 재사용하세요. 서버·공급자 최종 실패는 정규화까지 환불하며, 연결 종료는 작업 취소가 아닙니다. 취소 시 이미 수행된 작업분을 정산합니다.
|
|
33
|
+
|
|
34
|
+
Both engines share job status, cancellation, MP3 and original-text ASS downloads. Check reserved, released, captured and refunded amounts in `billing`. MP3 is 24 kHz mono at 48 kbps. Files are kept for 24 hours; each permits one completed download, and interrupted transfers can be retried. Reuse the same input and idempotency key after a lost response. Final server/provider failures refund synthesis and normalization; disconnecting does not cancel work. Explicit cancellation settles work already performed.
|
package/package.json
CHANGED
package/src/index.cjs
CHANGED
|
@@ -118,9 +118,9 @@ function normalizeTtsJobId(value) {
|
|
|
118
118
|
return jobId;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function normalizeTtsVoice(value) {
|
|
122
|
-
const voiceId = requiredString('voiceId', value);
|
|
123
|
-
if (
|
|
121
|
+
function normalizeTtsVoice(value) {
|
|
122
|
+
const voiceId = requiredString('voiceId', value, 160);
|
|
123
|
+
if (TTS_VOICE_ID_SET.has(voiceId)) throw new RangeError('This voiceId has retired. Select a current Gemini voice with listGeminiTtsVoices().');
|
|
124
124
|
return voiceId;
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -379,15 +379,17 @@ class ApickClient {
|
|
|
379
379
|
Accept: definition.output === 'binary' ? '*/*' : 'application/json',
|
|
380
380
|
Authorization: `Bearer ${this.#apiKey}`
|
|
381
381
|
};
|
|
382
|
-
const method = definition.method || 'POST';
|
|
382
|
+
const method = definition.method || 'POST';
|
|
383
|
+
if (definition.idempotencyKey) headers['X-Idempotency-Key'] = requiredString('idempotencyKey', definition.idempotencyKey, 128);
|
|
383
384
|
const request = {
|
|
384
385
|
method,
|
|
385
386
|
headers,
|
|
386
387
|
signal: controller.signal,
|
|
387
388
|
redirect: 'error'
|
|
388
389
|
};
|
|
389
|
-
if (method !== 'GET') {
|
|
390
|
-
|
|
390
|
+
if (method !== 'GET') {
|
|
391
|
+
if (definition.json) { headers['Content-Type']='application/json'; request.body=JSON.stringify(payload||{}); }
|
|
392
|
+
else request.body = formData || require('./form.cjs').createForm(payload || {}, definition.endpoint);
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
let response;
|
|
@@ -628,13 +630,31 @@ class ApickClient {
|
|
|
628
630
|
return this._call('createVideoJob', null, null, { endpoint:'/rest/'+model+'/jobs/'+jobId+'/result', method:'GET', output:'binary', filename:jobId+'.mp4', timeoutMs:60_000 });
|
|
629
631
|
}
|
|
630
632
|
|
|
631
|
-
createTtsJob(text, options) {
|
|
632
|
-
const config = options || {};
|
|
633
|
-
|
|
634
|
-
voice_id: normalizeTtsVoice(config.voiceId || '
|
|
635
|
-
text: requiredString('text', text,
|
|
636
|
-
}
|
|
637
|
-
|
|
633
|
+
createTtsJob(text, options) {
|
|
634
|
+
const config = options || {};
|
|
635
|
+
const payload = typeof text==='object' && text!==null ? {...text} : {
|
|
636
|
+
voice_id: normalizeTtsVoice(config.voiceId || 'Kore'),
|
|
637
|
+
text: requiredString('text', text, config.normalizeText === false ? 8000 : 2000)
|
|
638
|
+
};
|
|
639
|
+
if(config.normalizeText!==undefined)payload.normalize_text=config.normalizeText;
|
|
640
|
+
if(config.fallbackPolicy!==undefined||config.fallbackOptions!==undefined||payload.fallback_policy!==undefined||payload.fallback_options!==undefined)throw new TypeError('TTS fallback has retired. Use createGeminiTtsJob or createOpenAiTtsJob.');
|
|
641
|
+
if(payload.normalize_text!==undefined&&typeof payload.normalize_text!=='boolean')throw new TypeError('normalizeText must be boolean.');
|
|
642
|
+
if(payload.voice_id!==undefined)normalizeTtsVoice(payload.voice_id);
|
|
643
|
+
return this._call('createTtsJob',payload,null,{json:true,idempotencyKey:config.idempotencyKey});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
createGeminiTtsJob(input, options) {
|
|
647
|
+
if(!input||typeof input!=='object'||Array.isArray(input))throw new TypeError('Gemini TTS input is required.');
|
|
648
|
+
return this._call('createTtsJob', input, null, {endpoint:'/rest/tts/gemini/jobs',json:true,idempotencyKey:options?.idempotencyKey});
|
|
649
|
+
}
|
|
650
|
+
listGeminiTtsVoices() { return this._call('createTtsJob',null,null,{endpoint:'/rest/tts/gemini/voices',method:'GET'}); }
|
|
651
|
+
createOpenAiTtsJob(input, options) {
|
|
652
|
+
if(!input||typeof input!=='object'||Array.isArray(input))throw new TypeError('OpenAI TTS input is required.');
|
|
653
|
+
return this._call('createTtsJob',input,null,{endpoint:'/rest/tts/openai/jobs',json:true,idempotencyKey:options?.idempotencyKey});
|
|
654
|
+
}
|
|
655
|
+
listOpenAiTtsVoices() { return this._call('createTtsJob',null,null,{endpoint:'/rest/tts/openai/voices',method:'GET'}); }
|
|
656
|
+
getTtsOptions() { return this._call('createTtsJob',null,null,{endpoint:'/rest/tts/options',method:'GET'}); }
|
|
657
|
+
quoteTts(input) { return this._call('createTtsJob',input,null,{endpoint:'/rest/tts/quote',json:true}); }
|
|
638
658
|
|
|
639
659
|
getTtsJob(jobId) {
|
|
640
660
|
const id = normalizeTtsJobId(jobId);
|
package/src/index.d.ts
CHANGED
|
@@ -37,7 +37,8 @@ export interface ImageAiOptions { imageCount?: number; size?: ImageAiSize; outpu
|
|
|
37
37
|
export interface ImageAiGenerateOptions extends ImageAiOptions { referenceImage?: string|BinaryInput|ArrayBuffer|ArrayBufferView; referenceFilename?: string; referenceContentType?: 'image/png'|'image/jpeg'|'image/webp'; }
|
|
38
38
|
export interface ImageAiEditOptions extends ImageAiOptions { filename?: string; contentType?: 'image/png'|'image/jpeg'|'image/webp'; }
|
|
39
39
|
export interface ImageAiResultImage { index:number; b64_json:string; mime_type:'image/png'|'image/jpeg'|'image/webp'; width:number; height:number; }
|
|
40
|
-
export interface ImageAiResultData { request_id:string; image_count:number; images:ImageAiResultImage[]; idempotent_replay?:boolean; }
|
|
40
|
+
export interface ImageAiResultData { request_id:string; image_count:number; images:ImageAiResultImage[]; idempotent_replay?:boolean; }
|
|
41
|
+
export interface ImageAiPendingData {request_id:string;job_id:string;status:'processing';billing_status:'pending';reserved_point:number;}
|
|
41
42
|
export interface ImageAiJobData { job_id:string; status:ImageAiStatus; requested_count:number; completed_count?:number; failed_count?:number; prepaid_point?:number; charged_point?:number; refunded_point?:number; result_available?:boolean; expires_at?:string|null; error_code?:ApickImageErrorCode|null; }
|
|
42
43
|
|
|
43
44
|
export interface MaskResidentNumberOptions extends OcrOptions {
|
|
@@ -199,15 +200,33 @@ export interface YoutubeSubtitleList {
|
|
|
199
200
|
}
|
|
200
201
|
export interface YoutubeSubtitleOptions { format?: 'vtt' | 'srt' | 'txt'; type?: 'any' | 'manual' | 'auto'; }
|
|
201
202
|
|
|
202
|
-
|
|
203
|
+
/** @deprecated Retired APICK voice IDs. Query listGeminiTtsVoices or listOpenAiTtsVoices. */
|
|
204
|
+
export const TTS_VOICE_IDS: readonly [
|
|
203
205
|
'v2_ann_m_30s_01', 'v2_ann_m_30s_02', 'v2_ann_m_30s_04', 'v2_ann_m_30s_05', 'v2_ann_f_30s_02', 'v2_ann_f_30s_03', 'v2_ann_f_30s_04', 'v2_ann_f_30s_05', 'v2_m_teen_01', 'v2_m_young_01', 'v2_m_mid_01', 'v2_m_senior_01', 'v2_f_young_01', 'v2_f_senior_01'
|
|
204
206
|
];
|
|
205
|
-
export type TtsVoiceId =
|
|
207
|
+
export type TtsVoiceId = string;
|
|
208
|
+
/** @deprecated Automatic fallback is no longer supported. */
|
|
209
|
+
export type TtsFallbackPolicy = 'never' | 'queue_full' | 'busy';
|
|
210
|
+
export interface TtsFallbackOptions { voice_id?: string; style?: string; }
|
|
211
|
+
/** @deprecated Retired APICK request shape. Use GeminiTtsInput or OpenAiTtsInput. */
|
|
212
|
+
export interface ApickTtsInput { voice_id: TtsVoiceId; text?: string; utterances?: Array<{text:string;emotion?:string;speed?:number;pause_after_ms?:number}>; normalize_text?:boolean; fallback_policy?:TtsFallbackPolicy; fallback_options?:TtsFallbackOptions; }
|
|
213
|
+
export interface TtsStyleOptions { style?:string; emotion?:'neutral'|'calm'|'cheerful'|'excited'|'sad'|'serious'|'friendly'|'empathetic'|'confident'|'gentle'|'whisper'|'narration'; tone?:'narration'|'news'|'audiobook'|'documentary'|'ad'|'conversation'|'announcement'|'tutorial'|'storytelling'; accent?:'standard'|'seoul'|'gyeongsang'|'jeolla'|'chungcheong'; pace?:number; pitch?:number; volume_gain_db?:number; }
|
|
214
|
+
export interface TtsSpeakerOptions extends TtsStyleOptions { voice_id?:string; }
|
|
215
|
+
export interface TtsUtterance extends TtsSpeakerOptions { text:string; speaker?:string; }
|
|
216
|
+
export interface GeminiTtsInput extends TtsStyleOptions { voice_id?:string; text?:string; utterances?:TtsUtterance[]; speakers?:Record<string,TtsSpeakerOptions>; multi_speaker?:boolean; normalize_text?:boolean; language_code?:'ko'|'ko-KR'; }
|
|
217
|
+
export interface OpenAiTtsInput extends GeminiTtsInput {}
|
|
218
|
+
export interface TtsOptions { voiceId?:TtsVoiceId; normalizeText?:boolean; idempotencyKey?:string; }
|
|
219
|
+
export interface GeminiTtsVoice { voice_id:string; name:string; gender:string|null; tone:string; age_band:string|null; age_years?:number|null; age_verified?:boolean; age_basis?:'catalog_description'|'reviewed'; age_source?:string; description?:string; language_code?:string|null; reviewed_at?:string; library:string; }
|
|
220
|
+
export interface TtsQuote { synthesis:number; normalization:number; estimated_total:number; maximum:number; synthesis_maximum:number; normalization_maximum:number; estimated_only:true; currency:'POINT'; fallback_price_may_change:boolean; }
|
|
206
221
|
|
|
207
222
|
export interface TtsJobData {
|
|
208
223
|
job_id: string;
|
|
209
224
|
status: 'waiting' | 'processing' | 'completed' | 'cancelled' | 'failed';
|
|
210
|
-
voice_id?:
|
|
225
|
+
voice_id?: string;
|
|
226
|
+
synthesis?: {engine:'apick'|'gemini'|'openai';model:string;voice_id:string;fallback_reason:string|null;age_verified:boolean;notice:string|null};
|
|
227
|
+
normalization?: {enabled:boolean;run_id:string|null;status:string;skill_id:string|null};
|
|
228
|
+
billing?: {status:string;synthesis:number;skill:number;total:number;reserved:number;refunded:number;released:number;currency:'POINT'};
|
|
229
|
+
expires_at?:string|null;
|
|
211
230
|
character_count?: number;
|
|
212
231
|
result_available?: boolean;
|
|
213
232
|
subtitles_available?: boolean;
|
|
@@ -290,20 +309,29 @@ export class ApickClient {
|
|
|
290
309
|
youtubeThumbnail(url: string): Promise<ApickBinaryResult>;
|
|
291
310
|
youtubeSubtitleList(url: string): Promise<ApickResult<YoutubeSubtitleList>>;
|
|
292
311
|
youtubeSubtitle(url: string, lang: string, options?: YoutubeSubtitleOptions): Promise<ApickBinaryResult>;
|
|
293
|
-
createTtsJob(text: string, options?:
|
|
312
|
+
createTtsJob(text: string | GeminiTtsInput, options?: TtsOptions): Promise<ApickResult<TtsJobData>>;
|
|
313
|
+
createGeminiTtsJob(input:GeminiTtsInput, options?:{idempotencyKey?:string}):Promise<ApickResult<TtsJobData>>;
|
|
314
|
+
listGeminiTtsVoices():Promise<ApickResult<{voices:GeminiTtsVoice[];complete:boolean;checked_at:string|null}>>;
|
|
315
|
+
createOpenAiTtsJob(input:OpenAiTtsInput, options?:{idempotencyKey?:string}):Promise<ApickResult<TtsJobData>>;
|
|
316
|
+
listOpenAiTtsVoices():Promise<ApickResult<{voices:GeminiTtsVoice[];complete:boolean;checked_at:string|null}>>;
|
|
317
|
+
getTtsOptions():Promise<ApickResult<{engines:Record<'gemini'|'openai',Record<string,unknown>>}>>;
|
|
318
|
+
quoteTts(input:GeminiTtsInput&{engine?:'gemini'|'openai'}):Promise<ApickResult<TtsQuote>>;
|
|
294
319
|
getTtsJob(jobId: string): Promise<ApickResult<TtsJobData>>;
|
|
295
320
|
cancelTtsJob(jobId: string): Promise<ApickResult<TtsJobData>>;
|
|
296
321
|
downloadTtsResult(jobId: string): Promise<ApickBinaryResult>;
|
|
297
322
|
downloadTtsSubtitles(jobId: string): Promise<ApickBinaryResult>;
|
|
298
|
-
|
|
299
|
-
|
|
323
|
+
/** @deprecated Retired endpoint; the service returns HTTP 409. */
|
|
324
|
+
getTtsQuality(jobId: string): Promise<ApickResult<TtsQualityData>>;
|
|
325
|
+
/** @deprecated Retired endpoint; the service returns HTTP 409. */
|
|
326
|
+
retryTtsJob(jobId: string, utteranceIds: string[], idempotencyKey: string): Promise<ApickResult<TtsJobData>>;
|
|
327
|
+
/** @deprecated Retired endpoint; the service returns HTTP 409. */
|
|
300
328
|
downloadTtsCandidate(jobId: string, candidateId: string): Promise<ApickBinaryResult>;
|
|
301
329
|
htmlToPdf(html: string, options?: { pagination?: boolean }): Promise<ApickBinaryResult>;
|
|
302
330
|
jsonToExcel(data: unknown[], options?: { sheetName?: string }): Promise<ApickBinaryResult>;
|
|
303
331
|
summarize(text: string): Promise<ApickResult>;
|
|
304
332
|
polish(text: string): Promise<ApickResult>;
|
|
305
|
-
generateImages(prompt:string, options?:ImageAiGenerateOptions): Promise<ApickResult<ImageAiResultData>>;
|
|
306
|
-
editImages(image:string|BinaryInput|ArrayBuffer|ArrayBufferView, prompt:string, options?:ImageAiEditOptions): Promise<ApickResult<ImageAiResultData>>;
|
|
333
|
+
generateImages(prompt:string, options?:ImageAiGenerateOptions): Promise<ApickResult<ImageAiResultData|ImageAiPendingData>>;
|
|
334
|
+
editImages(image:string|BinaryInput|ArrayBuffer|ArrayBufferView, prompt:string, options?:ImageAiEditOptions): Promise<ApickResult<ImageAiResultData|ImageAiPendingData>>;
|
|
307
335
|
createImageGenerationJob(prompt:string, options?:ImageAiGenerateOptions): Promise<ApickResult<ImageAiJobData>>;
|
|
308
336
|
createImageEditJob(image:string|BinaryInput|ArrayBuffer|ArrayBufferView, prompt:string, options?:ImageAiEditOptions): Promise<ApickResult<ImageAiJobData>>;
|
|
309
337
|
getImageJob(jobId:string): Promise<ApickResult<ImageAiJobData>>;
|