@neosapience/typecast-js 0.1.4 → 0.1.6

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.cjs CHANGED
@@ -1,13 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var axios = require('axios');
4
-
5
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
-
7
- var axios__default = /*#__PURE__*/_interopDefault(axios);
8
-
9
- // src/client.ts
10
-
11
3
  // src/errors.ts
12
4
  var TypecastAPIError = class _TypecastAPIError extends Error {
13
5
  constructor(message, statusCode, response) {
@@ -54,58 +46,134 @@ var TypecastAPIError = class _TypecastAPIError extends Error {
54
46
  // src/client.ts
55
47
  var TypecastClient = class {
56
48
  constructor(config = {}) {
57
- this.config = {
49
+ const finalConfig = {
58
50
  baseHost: process.env.TYPECAST_API_HOST || "https://api.typecast.ai",
59
51
  apiKey: process.env.TYPECAST_API_KEY || "",
60
52
  ...config
61
53
  };
62
- this.client = axios__default.default.create({
63
- baseURL: this.config.baseHost,
64
- headers: {
65
- "X-API-KEY": this.config.apiKey,
66
- "Content-Type": "application/json"
54
+ this.baseHost = finalConfig.baseHost;
55
+ this.headers = {
56
+ "X-API-KEY": finalConfig.apiKey,
57
+ "Content-Type": "application/json"
58
+ };
59
+ }
60
+ /**
61
+ * Handle HTTP error responses
62
+ */
63
+ async handleResponse(response) {
64
+ if (!response.ok) {
65
+ let errorData;
66
+ try {
67
+ errorData = await response.json();
68
+ } catch {
67
69
  }
68
- });
69
- this.client.interceptors.response.use(
70
- (response) => response,
71
- (error) => {
72
- if (error.response) {
73
- throw TypecastAPIError.fromResponse(
74
- error.response.status,
75
- error.response.statusText,
76
- error.response.data
77
- );
70
+ throw TypecastAPIError.fromResponse(
71
+ response.status,
72
+ response.statusText,
73
+ errorData
74
+ );
75
+ }
76
+ return response.json();
77
+ }
78
+ /**
79
+ * Build URL with query parameters
80
+ */
81
+ buildUrl(path, params) {
82
+ const url = new URL(path, this.baseHost);
83
+ if (params) {
84
+ Object.entries(params).forEach(([key, value]) => {
85
+ if (value !== void 0 && value !== null) {
86
+ url.searchParams.append(key, String(value));
78
87
  }
79
- throw error;
80
- }
81
- );
88
+ });
89
+ }
90
+ return url.toString();
82
91
  }
92
+ /**
93
+ * Convert text to speech
94
+ * @param request - TTS request parameters including text, voice_id, model, and optional settings
95
+ * @returns TTSResponse containing audio data, duration, and format
96
+ */
83
97
  async textToSpeech(request) {
84
- const response = await this.client.post("/v1/text-to-speech", request, {
85
- responseType: "arraybuffer"
98
+ const response = await fetch(this.buildUrl("/v1/text-to-speech"), {
99
+ method: "POST",
100
+ headers: this.headers,
101
+ body: JSON.stringify(request)
86
102
  });
87
- const contentType = String(response.headers["content-type"] || "audio/wav");
103
+ if (!response.ok) {
104
+ let errorData;
105
+ try {
106
+ errorData = await response.json();
107
+ } catch {
108
+ }
109
+ throw TypecastAPIError.fromResponse(
110
+ response.status,
111
+ response.statusText,
112
+ errorData
113
+ );
114
+ }
115
+ const contentType = response.headers.get("content-type") || "audio/wav";
88
116
  const formatFromHeader = contentType.split("/")[1] || "wav";
89
117
  const format = formatFromHeader === "mp3" ? "mp3" : "wav";
90
- const durationHeader = response.headers["x-audio-duration"];
91
- const duration = typeof durationHeader === "string" ? Number(durationHeader) : 0;
118
+ const durationHeader = response.headers.get("x-audio-duration");
119
+ const duration = durationHeader ? Number(durationHeader) : 0;
120
+ const audioData = await response.arrayBuffer();
92
121
  return {
93
- audioData: response.data,
122
+ audioData,
94
123
  duration,
95
124
  format
96
125
  };
97
126
  }
127
+ /**
128
+ * Get available voices (V1 API)
129
+ * @param model - Optional model filter (e.g., 'ssfm-v21', 'ssfm-v30')
130
+ * @returns List of available voices with their emotions
131
+ * @deprecated Use getVoicesV2() for enhanced metadata and filtering options
132
+ */
98
133
  async getVoices(model) {
99
- const response = await this.client.get("/v1/voices", {
100
- params: model ? { model } : void 0
101
- });
102
- return response.data;
134
+ const response = await fetch(
135
+ this.buildUrl("/v1/voices", model ? { model } : void 0),
136
+ { headers: this.headers }
137
+ );
138
+ return this.handleResponse(response);
103
139
  }
140
+ /**
141
+ * Get voice by ID (V1 API)
142
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
143
+ * @param model - Optional model filter
144
+ * @returns Voice information including available emotions
145
+ * @deprecated Use getVoicesV2() for enhanced metadata
146
+ */
104
147
  async getVoiceById(voiceId, model) {
105
- const response = await this.client.get(`/v1/voices/${voiceId}`, {
106
- params: model ? { model } : void 0
107
- });
108
- return response.data;
148
+ const response = await fetch(
149
+ this.buildUrl(`/v1/voices/${voiceId}`, model ? { model } : void 0),
150
+ { headers: this.headers }
151
+ );
152
+ return this.handleResponse(response);
153
+ }
154
+ /**
155
+ * Get voices with enhanced metadata (V2 API)
156
+ * Returns voices with model-grouped emotions and additional metadata
157
+ * @param filter - Optional filter options (model, gender, age, use_cases)
158
+ */
159
+ async getVoicesV2(filter) {
160
+ const response = await fetch(
161
+ this.buildUrl("/v2/voices", filter),
162
+ { headers: this.headers }
163
+ );
164
+ return this.handleResponse(response);
165
+ }
166
+ /**
167
+ * Get a specific voice by ID with enhanced metadata (V2 API)
168
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
169
+ * @returns Voice information with model-grouped emotions and metadata
170
+ */
171
+ async getVoiceV2(voiceId) {
172
+ const response = await fetch(
173
+ this.buildUrl(`/v2/voices/${voiceId}`),
174
+ { headers: this.headers }
175
+ );
176
+ return this.handleResponse(response);
109
177
  }
110
178
  };
111
179
 
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":["axios"],"mappings":"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","file":"index.cjs","sourcesContent":["import { ApiErrorResponse } from './types';\n\nexport class TypecastAPIError extends Error {\n public readonly statusCode: number;\n public readonly response?: ApiErrorResponse;\n\n constructor(message: string, statusCode: number, response?: ApiErrorResponse) {\n super(message);\n this.name = 'TypecastAPIError';\n this.statusCode = statusCode;\n this.response = response;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TypecastAPIError);\n }\n }\n\n static fromResponse(statusCode: number, statusText: string, data?: ApiErrorResponse): TypecastAPIError {\n let message: string;\n\n switch (statusCode) {\n case 400:\n message = 'Bad Request - The request was invalid or cannot be served';\n break;\n case 401:\n message = 'Unauthorized - Invalid or missing API key';\n break;\n case 402:\n message = 'Payment Required - Insufficient credits to complete the request';\n break;\n case 404:\n message = 'Not Found - The requested resource does not exist';\n break;\n case 422:\n message = 'Validation Error - The request data failed validation';\n break;\n case 500:\n message = 'Internal Server Error - Something went wrong on the server';\n break;\n default:\n message = `API request failed with status ${statusCode}: ${statusText}`;\n }\n\n if (data?.detail) {\n const detailStr = typeof data.detail === 'string' \n ? data.detail \n : JSON.stringify(data.detail);\n message += ` - ${detailStr}`;\n }\n\n return new TypecastAPIError(message, statusCode, data);\n }\n}\n\n","import axios, { AxiosInstance, AxiosError } from 'axios';\nimport { ClientConfig, TTSRequest, TTSResponse, ApiErrorResponse } from './types';\nimport { VoicesResponse } from './types/Voices';\nimport { TypecastAPIError } from './errors';\n\nexport class TypecastClient {\n private client: AxiosInstance;\n private config: ClientConfig;\n\n constructor(config: Partial<ClientConfig> = {}) {\n this.config = {\n baseHost: process.env.TYPECAST_API_HOST || 'https://api.typecast.ai',\n apiKey: process.env.TYPECAST_API_KEY || '',\n ...config,\n };\n this.client = axios.create({\n baseURL: this.config.baseHost,\n headers: {\n 'X-API-KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n },\n });\n\n // Add response interceptor for error handling\n this.client.interceptors.response.use(\n (response) => response,\n (error: AxiosError<ApiErrorResponse>) => {\n if (error.response) {\n throw TypecastAPIError.fromResponse(\n error.response.status,\n error.response.statusText,\n error.response.data\n );\n }\n throw error;\n }\n );\n }\n\n async textToSpeech(request: TTSRequest): Promise<TTSResponse> {\n const response = await this.client.post<ArrayBuffer>('/v1/text-to-speech', request, {\n responseType: 'arraybuffer',\n });\n\n const contentType = String(response.headers['content-type'] || 'audio/wav');\n const formatFromHeader = contentType.split('/')[1] || 'wav';\n const format: 'wav' | 'mp3' = formatFromHeader === 'mp3' ? 'mp3' : 'wav';\n\n const durationHeader: unknown = response.headers['x-audio-duration'];\n const duration = typeof durationHeader === 'string' ? Number(durationHeader) : 0;\n\n return {\n audioData: response.data,\n duration,\n format,\n };\n }\n\n async getVoices(model?: string): Promise<VoicesResponse[]> {\n const response = await this.client.get<VoicesResponse[]>('/v1/voices', {\n params: model ? { model } : undefined,\n });\n return response.data;\n }\n\n async getVoiceById(voiceId: string, model?: string): Promise<VoicesResponse[]> {\n const response = await this.client.get<VoicesResponse[]>(`/v1/voices/${voiceId}`, {\n params: model ? { model } : undefined,\n });\n return response.data;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import { ApiErrorResponse } from './types';\n\nexport class TypecastAPIError extends Error {\n public readonly statusCode: number;\n public readonly response?: ApiErrorResponse;\n\n constructor(message: string, statusCode: number, response?: ApiErrorResponse) {\n super(message);\n this.name = 'TypecastAPIError';\n this.statusCode = statusCode;\n this.response = response;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TypecastAPIError);\n }\n }\n\n static fromResponse(statusCode: number, statusText: string, data?: ApiErrorResponse): TypecastAPIError {\n let message: string;\n\n switch (statusCode) {\n case 400:\n message = 'Bad Request - The request was invalid or cannot be served';\n break;\n case 401:\n message = 'Unauthorized - Invalid or missing API key';\n break;\n case 402:\n message = 'Payment Required - Insufficient credits to complete the request';\n break;\n case 404:\n message = 'Not Found - The requested resource does not exist';\n break;\n case 422:\n message = 'Validation Error - The request data failed validation';\n break;\n case 500:\n message = 'Internal Server Error - Something went wrong on the server';\n break;\n default:\n message = `API request failed with status ${statusCode}: ${statusText}`;\n }\n\n if (data?.detail) {\n const detailStr = typeof data.detail === 'string' \n ? data.detail \n : JSON.stringify(data.detail);\n message += ` - ${detailStr}`;\n }\n\n return new TypecastAPIError(message, statusCode, data);\n }\n}\n\n","import { ClientConfig, TTSRequest, TTSResponse, ApiErrorResponse } from './types';\nimport { VoicesResponse, VoiceV2Response, VoicesV2Filter } from './types/Voices';\nimport { TypecastAPIError } from './errors';\n\nexport class TypecastClient {\n private baseHost: string;\n private headers: Record<string, string>;\n\n constructor(config: Partial<ClientConfig> = {}) {\n const finalConfig: ClientConfig = {\n baseHost: process.env.TYPECAST_API_HOST || 'https://api.typecast.ai',\n apiKey: process.env.TYPECAST_API_KEY || '',\n ...config,\n };\n this.baseHost = finalConfig.baseHost;\n this.headers = {\n 'X-API-KEY': finalConfig.apiKey,\n 'Content-Type': 'application/json',\n };\n }\n\n /**\n * Handle HTTP error responses\n */\n private async handleResponse<T>(response: Response): Promise<T> {\n if (!response.ok) {\n let errorData: ApiErrorResponse | undefined;\n try {\n errorData = (await response.json()) as ApiErrorResponse;\n } catch {\n // Response body is not JSON\n }\n throw TypecastAPIError.fromResponse(\n response.status,\n response.statusText,\n errorData\n );\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Build URL with query parameters\n */\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const url = new URL(path, this.baseHost);\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n return url.toString();\n }\n\n /**\n * Convert text to speech\n * @param request - TTS request parameters including text, voice_id, model, and optional settings\n * @returns TTSResponse containing audio data, duration, and format\n */\n async textToSpeech(request: TTSRequest): Promise<TTSResponse> {\n const response = await fetch(this.buildUrl('/v1/text-to-speech'), {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n let errorData: ApiErrorResponse | undefined;\n try {\n errorData = (await response.json()) as ApiErrorResponse;\n } catch {\n // Response body is not JSON\n }\n throw TypecastAPIError.fromResponse(\n response.status,\n response.statusText,\n errorData\n );\n }\n\n const contentType = response.headers.get('content-type') || 'audio/wav';\n const formatFromHeader = contentType.split('/')[1] || 'wav';\n const format: 'wav' | 'mp3' = formatFromHeader === 'mp3' ? 'mp3' : 'wav';\n\n const durationHeader = response.headers.get('x-audio-duration');\n const duration = durationHeader ? Number(durationHeader) : 0;\n\n const audioData = await response.arrayBuffer();\n\n return {\n audioData,\n duration,\n format,\n };\n }\n\n /**\n * Get available voices (V1 API)\n * @param model - Optional model filter (e.g., 'ssfm-v21', 'ssfm-v30')\n * @returns List of available voices with their emotions\n * @deprecated Use getVoicesV2() for enhanced metadata and filtering options\n */\n async getVoices(model?: string): Promise<VoicesResponse[]> {\n const response = await fetch(\n this.buildUrl('/v1/voices', model ? { model } : undefined),\n { headers: this.headers }\n );\n return this.handleResponse<VoicesResponse[]>(response);\n }\n\n /**\n * Get voice by ID (V1 API)\n * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')\n * @param model - Optional model filter\n * @returns Voice information including available emotions\n * @deprecated Use getVoicesV2() for enhanced metadata\n */\n async getVoiceById(voiceId: string, model?: string): Promise<VoicesResponse[]> {\n const response = await fetch(\n this.buildUrl(`/v1/voices/${voiceId}`, model ? { model } : undefined),\n { headers: this.headers }\n );\n return this.handleResponse<VoicesResponse[]>(response);\n }\n\n /**\n * Get voices with enhanced metadata (V2 API)\n * Returns voices with model-grouped emotions and additional metadata\n * @param filter - Optional filter options (model, gender, age, use_cases)\n */\n async getVoicesV2(filter?: VoicesV2Filter): Promise<VoiceV2Response[]> {\n const response = await fetch(\n this.buildUrl('/v2/voices', filter as Record<string, unknown>),\n { headers: this.headers }\n );\n return this.handleResponse<VoiceV2Response[]>(response);\n }\n\n /**\n * Get a specific voice by ID with enhanced metadata (V2 API)\n * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')\n * @returns Voice information with model-grouped emotions and metadata\n */\n async getVoiceV2(voiceId: string): Promise<VoiceV2Response> {\n const response = await fetch(\n this.buildUrl(`/v2/voices/${voiceId}`),\n { headers: this.headers }\n );\n return this.handleResponse<VoiceV2Response>(response);\n }\n}\n"]}
package/lib/index.d.cts CHANGED
@@ -25,18 +25,44 @@ interface ClientConfig {
25
25
  apiKey: string;
26
26
  }
27
27
 
28
- type TTSModel = 'ssfm-v21';
28
+ type TTSModel = 'ssfm-v21' | 'ssfm-v30';
29
29
  /**
30
30
  * Language code following ISO 639-3 standard
31
31
  * Supported languages for text-to-speech conversion
32
+ *
33
+ * ssfm-v21: 27 languages
34
+ * ssfm-v30: 37 languages (includes all v21 languages plus additional ones)
32
35
  */
33
- type LanguageCode = 'eng' | 'kor' | 'jpn' | 'spa' | 'deu' | 'fra' | 'ita' | 'pol' | 'nld' | 'rus' | 'ell' | 'tam' | 'tgl' | 'fin' | 'zho' | 'slk' | 'ara' | 'hrv' | 'ukr' | 'ind' | 'dan' | 'swe' | 'msa' | 'ces' | 'por' | 'bul' | 'ron';
36
+ type LanguageCode = 'eng' | 'kor' | 'jpn' | 'spa' | 'deu' | 'fra' | 'ita' | 'pol' | 'nld' | 'rus' | 'ell' | 'tam' | 'tgl' | 'fin' | 'zho' | 'slk' | 'ara' | 'hrv' | 'ukr' | 'ind' | 'dan' | 'swe' | 'msa' | 'ces' | 'por' | 'bul' | 'ron' | 'ben' | 'hin' | 'hun' | 'nan' | 'nor' | 'pan' | 'tha' | 'tur' | 'vie' | 'yue';
34
37
  /**
35
- * Emotion and style settings for the generated speech
38
+ * Emotion preset types
39
+ * ssfm-v21: normal, happy, sad, angry
40
+ * ssfm-v30: normal, happy, sad, angry, whisper, toneup, tonedown
41
+ */
42
+ type EmotionPreset = 'normal' | 'happy' | 'sad' | 'angry' | 'whisper' | 'toneup' | 'tonedown';
43
+ /**
44
+ * Emotion and style settings for ssfm-v21 model
36
45
  */
37
46
  interface Prompt {
38
47
  /** Emotion preset for the voice (default: 'normal') */
39
- emotion_preset?: 'happy' | 'sad' | 'normal' | 'angry' | 'tonemid' | 'toneup';
48
+ emotion_preset?: EmotionPreset;
49
+ /**
50
+ * Emotion intensity
51
+ * @min 0.0
52
+ * @max 2.0
53
+ * @default 1.0
54
+ */
55
+ emotion_intensity?: number;
56
+ }
57
+ /**
58
+ * Preset-based emotion control for ssfm-v30 model
59
+ * Use this when you want to specify a specific emotion preset
60
+ */
61
+ interface PresetPrompt {
62
+ /** Must be 'preset' for preset-based emotion control */
63
+ emotion_type: 'preset';
64
+ /** Emotion preset to apply (default: 'normal') */
65
+ emotion_preset?: EmotionPreset;
40
66
  /**
41
67
  * Emotion intensity
42
68
  * @min 0.0
@@ -45,17 +71,44 @@ interface Prompt {
45
71
  */
46
72
  emotion_intensity?: number;
47
73
  }
74
+ /**
75
+ * Context-aware emotion inference for ssfm-v30 model
76
+ * The model analyzes surrounding context to infer appropriate emotion
77
+ */
78
+ interface SmartPrompt {
79
+ /** Must be 'smart' for context-aware emotion inference */
80
+ emotion_type: 'smart';
81
+ /** Text that comes BEFORE the main text (max 2000 chars) */
82
+ previous_text?: string;
83
+ /** Text that comes AFTER the main text (max 2000 chars) */
84
+ next_text?: string;
85
+ }
86
+ /**
87
+ * Union type for all prompt types
88
+ * - Prompt: Basic emotion control (ssfm-v21 compatible)
89
+ * - PresetPrompt: Explicit preset emotion control (ssfm-v30)
90
+ * - SmartPrompt: Context-aware emotion inference (ssfm-v30)
91
+ */
92
+ type TTSPrompt = Prompt | PresetPrompt | SmartPrompt;
48
93
  /**
49
94
  * Audio output settings for controlling the final audio characteristics
50
95
  */
51
96
  interface Output {
52
97
  /**
53
- * Output volume
98
+ * Relative volume scaling of the output audio.
99
+ * Cannot be used simultaneously with target_lufs.
54
100
  * @min 0
55
101
  * @max 200
56
102
  * @default 100
57
103
  */
58
104
  volume?: number;
105
+ /**
106
+ * Target loudness in LUFS for absolute loudness normalization.
107
+ * Cannot be used simultaneously with volume.
108
+ * @min -70
109
+ * @max 0
110
+ */
111
+ target_lufs?: number;
59
112
  /**
60
113
  * Audio pitch adjustment in semitones
61
114
  * @min -12
@@ -92,7 +145,7 @@ interface TTSRequest {
92
145
  /** Language code (ISO 639-3). If not provided, will be auto-detected based on text content */
93
146
  language?: LanguageCode;
94
147
  /** Emotion and style settings for the generated speech */
95
- prompt?: Prompt;
148
+ prompt?: TTSPrompt;
96
149
  /** Audio output settings */
97
150
  output?: Output;
98
151
  /** Random seed for reproducible results (same seed + same parameters = same output) */
@@ -110,20 +163,112 @@ interface TTSResponse {
110
163
  format: 'wav' | 'mp3';
111
164
  }
112
165
 
166
+ /**
167
+ * V1 Voices response (deprecated, use VoiceV2Response instead)
168
+ */
113
169
  interface VoicesResponse {
114
170
  voice_id: string;
115
171
  voice_name: string;
116
172
  model: TTSModel;
117
173
  emotions: string[];
118
174
  }
175
+ /**
176
+ * Gender classification for voices
177
+ */
178
+ type GenderEnum = 'male' | 'female';
179
+ /**
180
+ * Age group classification for voices
181
+ */
182
+ type AgeEnum = 'child' | 'teenager' | 'young_adult' | 'middle_age' | 'elder';
183
+ /**
184
+ * Use case categories for voices
185
+ */
186
+ type UseCaseEnum = 'Announcer' | 'Anime' | 'Audiobook' | 'Conversational' | 'Documentary' | 'E-learning' | 'Rapper' | 'Game' | 'Tiktok/Reels' | 'News' | 'Podcast' | 'Voicemail' | 'Ads';
187
+ /**
188
+ * Model information with supported emotions
189
+ */
190
+ interface ModelInfo {
191
+ /** TTS model version (e.g., ssfm-v21, ssfm-v30) */
192
+ version: TTSModel;
193
+ /** List of supported emotions for this model */
194
+ emotions: string[];
195
+ }
196
+ /**
197
+ * V2 Voice response with enhanced metadata
198
+ */
199
+ interface VoiceV2Response {
200
+ /** Unique voice identifier */
201
+ voice_id: string;
202
+ /** Human-readable name of the voice */
203
+ voice_name: string;
204
+ /** List of supported TTS models with their available emotions */
205
+ models: ModelInfo[];
206
+ /** Voice gender classification */
207
+ gender?: GenderEnum | null;
208
+ /** Voice age group classification */
209
+ age?: AgeEnum | null;
210
+ /** List of use case categories this voice is suitable for */
211
+ use_cases?: string[];
212
+ }
213
+ /**
214
+ * Filter options for V2 voices endpoint
215
+ */
216
+ interface VoicesV2Filter {
217
+ /** Filter by TTS model */
218
+ model?: TTSModel;
219
+ /** Filter by gender */
220
+ gender?: GenderEnum;
221
+ /** Filter by age group */
222
+ age?: AgeEnum;
223
+ /** Filter by use case */
224
+ use_cases?: UseCaseEnum;
225
+ }
119
226
 
120
227
  declare class TypecastClient {
121
- private client;
122
- private config;
228
+ private baseHost;
229
+ private headers;
123
230
  constructor(config?: Partial<ClientConfig>);
231
+ /**
232
+ * Handle HTTP error responses
233
+ */
234
+ private handleResponse;
235
+ /**
236
+ * Build URL with query parameters
237
+ */
238
+ private buildUrl;
239
+ /**
240
+ * Convert text to speech
241
+ * @param request - TTS request parameters including text, voice_id, model, and optional settings
242
+ * @returns TTSResponse containing audio data, duration, and format
243
+ */
124
244
  textToSpeech(request: TTSRequest): Promise<TTSResponse>;
245
+ /**
246
+ * Get available voices (V1 API)
247
+ * @param model - Optional model filter (e.g., 'ssfm-v21', 'ssfm-v30')
248
+ * @returns List of available voices with their emotions
249
+ * @deprecated Use getVoicesV2() for enhanced metadata and filtering options
250
+ */
125
251
  getVoices(model?: string): Promise<VoicesResponse[]>;
252
+ /**
253
+ * Get voice by ID (V1 API)
254
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
255
+ * @param model - Optional model filter
256
+ * @returns Voice information including available emotions
257
+ * @deprecated Use getVoicesV2() for enhanced metadata
258
+ */
126
259
  getVoiceById(voiceId: string, model?: string): Promise<VoicesResponse[]>;
260
+ /**
261
+ * Get voices with enhanced metadata (V2 API)
262
+ * Returns voices with model-grouped emotions and additional metadata
263
+ * @param filter - Optional filter options (model, gender, age, use_cases)
264
+ */
265
+ getVoicesV2(filter?: VoicesV2Filter): Promise<VoiceV2Response[]>;
266
+ /**
267
+ * Get a specific voice by ID with enhanced metadata (V2 API)
268
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
269
+ * @returns Voice information with model-grouped emotions and metadata
270
+ */
271
+ getVoiceV2(voiceId: string): Promise<VoiceV2Response>;
127
272
  }
128
273
 
129
274
  declare class TypecastAPIError extends Error {
@@ -133,4 +278,4 @@ declare class TypecastAPIError extends Error {
133
278
  static fromResponse(statusCode: number, statusText: string, data?: ApiErrorResponse): TypecastAPIError;
134
279
  }
135
280
 
136
- export { type ApiErrorResponse, type ClientConfig, type LanguageCode, type Output, type Prompt, type TTSModel, type TTSRequest, type TTSResponse, TypecastAPIError, TypecastClient, type ValidationError, type ValidationErrorResponse, type VoicesResponse };
281
+ export { type AgeEnum, type ApiErrorResponse, type ClientConfig, type EmotionPreset, type GenderEnum, type LanguageCode, type ModelInfo, type Output, type PresetPrompt, type Prompt, type SmartPrompt, type TTSModel, type TTSPrompt, type TTSRequest, type TTSResponse, TypecastAPIError, TypecastClient, type UseCaseEnum, type ValidationError, type ValidationErrorResponse, type VoiceV2Response, type VoicesResponse, type VoicesV2Filter };
package/lib/index.d.ts CHANGED
@@ -25,18 +25,44 @@ interface ClientConfig {
25
25
  apiKey: string;
26
26
  }
27
27
 
28
- type TTSModel = 'ssfm-v21';
28
+ type TTSModel = 'ssfm-v21' | 'ssfm-v30';
29
29
  /**
30
30
  * Language code following ISO 639-3 standard
31
31
  * Supported languages for text-to-speech conversion
32
+ *
33
+ * ssfm-v21: 27 languages
34
+ * ssfm-v30: 37 languages (includes all v21 languages plus additional ones)
32
35
  */
33
- type LanguageCode = 'eng' | 'kor' | 'jpn' | 'spa' | 'deu' | 'fra' | 'ita' | 'pol' | 'nld' | 'rus' | 'ell' | 'tam' | 'tgl' | 'fin' | 'zho' | 'slk' | 'ara' | 'hrv' | 'ukr' | 'ind' | 'dan' | 'swe' | 'msa' | 'ces' | 'por' | 'bul' | 'ron';
36
+ type LanguageCode = 'eng' | 'kor' | 'jpn' | 'spa' | 'deu' | 'fra' | 'ita' | 'pol' | 'nld' | 'rus' | 'ell' | 'tam' | 'tgl' | 'fin' | 'zho' | 'slk' | 'ara' | 'hrv' | 'ukr' | 'ind' | 'dan' | 'swe' | 'msa' | 'ces' | 'por' | 'bul' | 'ron' | 'ben' | 'hin' | 'hun' | 'nan' | 'nor' | 'pan' | 'tha' | 'tur' | 'vie' | 'yue';
34
37
  /**
35
- * Emotion and style settings for the generated speech
38
+ * Emotion preset types
39
+ * ssfm-v21: normal, happy, sad, angry
40
+ * ssfm-v30: normal, happy, sad, angry, whisper, toneup, tonedown
41
+ */
42
+ type EmotionPreset = 'normal' | 'happy' | 'sad' | 'angry' | 'whisper' | 'toneup' | 'tonedown';
43
+ /**
44
+ * Emotion and style settings for ssfm-v21 model
36
45
  */
37
46
  interface Prompt {
38
47
  /** Emotion preset for the voice (default: 'normal') */
39
- emotion_preset?: 'happy' | 'sad' | 'normal' | 'angry' | 'tonemid' | 'toneup';
48
+ emotion_preset?: EmotionPreset;
49
+ /**
50
+ * Emotion intensity
51
+ * @min 0.0
52
+ * @max 2.0
53
+ * @default 1.0
54
+ */
55
+ emotion_intensity?: number;
56
+ }
57
+ /**
58
+ * Preset-based emotion control for ssfm-v30 model
59
+ * Use this when you want to specify a specific emotion preset
60
+ */
61
+ interface PresetPrompt {
62
+ /** Must be 'preset' for preset-based emotion control */
63
+ emotion_type: 'preset';
64
+ /** Emotion preset to apply (default: 'normal') */
65
+ emotion_preset?: EmotionPreset;
40
66
  /**
41
67
  * Emotion intensity
42
68
  * @min 0.0
@@ -45,17 +71,44 @@ interface Prompt {
45
71
  */
46
72
  emotion_intensity?: number;
47
73
  }
74
+ /**
75
+ * Context-aware emotion inference for ssfm-v30 model
76
+ * The model analyzes surrounding context to infer appropriate emotion
77
+ */
78
+ interface SmartPrompt {
79
+ /** Must be 'smart' for context-aware emotion inference */
80
+ emotion_type: 'smart';
81
+ /** Text that comes BEFORE the main text (max 2000 chars) */
82
+ previous_text?: string;
83
+ /** Text that comes AFTER the main text (max 2000 chars) */
84
+ next_text?: string;
85
+ }
86
+ /**
87
+ * Union type for all prompt types
88
+ * - Prompt: Basic emotion control (ssfm-v21 compatible)
89
+ * - PresetPrompt: Explicit preset emotion control (ssfm-v30)
90
+ * - SmartPrompt: Context-aware emotion inference (ssfm-v30)
91
+ */
92
+ type TTSPrompt = Prompt | PresetPrompt | SmartPrompt;
48
93
  /**
49
94
  * Audio output settings for controlling the final audio characteristics
50
95
  */
51
96
  interface Output {
52
97
  /**
53
- * Output volume
98
+ * Relative volume scaling of the output audio.
99
+ * Cannot be used simultaneously with target_lufs.
54
100
  * @min 0
55
101
  * @max 200
56
102
  * @default 100
57
103
  */
58
104
  volume?: number;
105
+ /**
106
+ * Target loudness in LUFS for absolute loudness normalization.
107
+ * Cannot be used simultaneously with volume.
108
+ * @min -70
109
+ * @max 0
110
+ */
111
+ target_lufs?: number;
59
112
  /**
60
113
  * Audio pitch adjustment in semitones
61
114
  * @min -12
@@ -92,7 +145,7 @@ interface TTSRequest {
92
145
  /** Language code (ISO 639-3). If not provided, will be auto-detected based on text content */
93
146
  language?: LanguageCode;
94
147
  /** Emotion and style settings for the generated speech */
95
- prompt?: Prompt;
148
+ prompt?: TTSPrompt;
96
149
  /** Audio output settings */
97
150
  output?: Output;
98
151
  /** Random seed for reproducible results (same seed + same parameters = same output) */
@@ -110,20 +163,112 @@ interface TTSResponse {
110
163
  format: 'wav' | 'mp3';
111
164
  }
112
165
 
166
+ /**
167
+ * V1 Voices response (deprecated, use VoiceV2Response instead)
168
+ */
113
169
  interface VoicesResponse {
114
170
  voice_id: string;
115
171
  voice_name: string;
116
172
  model: TTSModel;
117
173
  emotions: string[];
118
174
  }
175
+ /**
176
+ * Gender classification for voices
177
+ */
178
+ type GenderEnum = 'male' | 'female';
179
+ /**
180
+ * Age group classification for voices
181
+ */
182
+ type AgeEnum = 'child' | 'teenager' | 'young_adult' | 'middle_age' | 'elder';
183
+ /**
184
+ * Use case categories for voices
185
+ */
186
+ type UseCaseEnum = 'Announcer' | 'Anime' | 'Audiobook' | 'Conversational' | 'Documentary' | 'E-learning' | 'Rapper' | 'Game' | 'Tiktok/Reels' | 'News' | 'Podcast' | 'Voicemail' | 'Ads';
187
+ /**
188
+ * Model information with supported emotions
189
+ */
190
+ interface ModelInfo {
191
+ /** TTS model version (e.g., ssfm-v21, ssfm-v30) */
192
+ version: TTSModel;
193
+ /** List of supported emotions for this model */
194
+ emotions: string[];
195
+ }
196
+ /**
197
+ * V2 Voice response with enhanced metadata
198
+ */
199
+ interface VoiceV2Response {
200
+ /** Unique voice identifier */
201
+ voice_id: string;
202
+ /** Human-readable name of the voice */
203
+ voice_name: string;
204
+ /** List of supported TTS models with their available emotions */
205
+ models: ModelInfo[];
206
+ /** Voice gender classification */
207
+ gender?: GenderEnum | null;
208
+ /** Voice age group classification */
209
+ age?: AgeEnum | null;
210
+ /** List of use case categories this voice is suitable for */
211
+ use_cases?: string[];
212
+ }
213
+ /**
214
+ * Filter options for V2 voices endpoint
215
+ */
216
+ interface VoicesV2Filter {
217
+ /** Filter by TTS model */
218
+ model?: TTSModel;
219
+ /** Filter by gender */
220
+ gender?: GenderEnum;
221
+ /** Filter by age group */
222
+ age?: AgeEnum;
223
+ /** Filter by use case */
224
+ use_cases?: UseCaseEnum;
225
+ }
119
226
 
120
227
  declare class TypecastClient {
121
- private client;
122
- private config;
228
+ private baseHost;
229
+ private headers;
123
230
  constructor(config?: Partial<ClientConfig>);
231
+ /**
232
+ * Handle HTTP error responses
233
+ */
234
+ private handleResponse;
235
+ /**
236
+ * Build URL with query parameters
237
+ */
238
+ private buildUrl;
239
+ /**
240
+ * Convert text to speech
241
+ * @param request - TTS request parameters including text, voice_id, model, and optional settings
242
+ * @returns TTSResponse containing audio data, duration, and format
243
+ */
124
244
  textToSpeech(request: TTSRequest): Promise<TTSResponse>;
245
+ /**
246
+ * Get available voices (V1 API)
247
+ * @param model - Optional model filter (e.g., 'ssfm-v21', 'ssfm-v30')
248
+ * @returns List of available voices with their emotions
249
+ * @deprecated Use getVoicesV2() for enhanced metadata and filtering options
250
+ */
125
251
  getVoices(model?: string): Promise<VoicesResponse[]>;
252
+ /**
253
+ * Get voice by ID (V1 API)
254
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
255
+ * @param model - Optional model filter
256
+ * @returns Voice information including available emotions
257
+ * @deprecated Use getVoicesV2() for enhanced metadata
258
+ */
126
259
  getVoiceById(voiceId: string, model?: string): Promise<VoicesResponse[]>;
260
+ /**
261
+ * Get voices with enhanced metadata (V2 API)
262
+ * Returns voices with model-grouped emotions and additional metadata
263
+ * @param filter - Optional filter options (model, gender, age, use_cases)
264
+ */
265
+ getVoicesV2(filter?: VoicesV2Filter): Promise<VoiceV2Response[]>;
266
+ /**
267
+ * Get a specific voice by ID with enhanced metadata (V2 API)
268
+ * @param voiceId - The voice ID (e.g., 'tc_62a8975e695ad26f7fb514d1')
269
+ * @returns Voice information with model-grouped emotions and metadata
270
+ */
271
+ getVoiceV2(voiceId: string): Promise<VoiceV2Response>;
127
272
  }
128
273
 
129
274
  declare class TypecastAPIError extends Error {
@@ -133,4 +278,4 @@ declare class TypecastAPIError extends Error {
133
278
  static fromResponse(statusCode: number, statusText: string, data?: ApiErrorResponse): TypecastAPIError;
134
279
  }
135
280
 
136
- export { type ApiErrorResponse, type ClientConfig, type LanguageCode, type Output, type Prompt, type TTSModel, type TTSRequest, type TTSResponse, TypecastAPIError, TypecastClient, type ValidationError, type ValidationErrorResponse, type VoicesResponse };
281
+ export { type AgeEnum, type ApiErrorResponse, type ClientConfig, type EmotionPreset, type GenderEnum, type LanguageCode, type ModelInfo, type Output, type PresetPrompt, type Prompt, type SmartPrompt, type TTSModel, type TTSPrompt, type TTSRequest, type TTSResponse, TypecastAPIError, TypecastClient, type UseCaseEnum, type ValidationError, type ValidationErrorResponse, type VoiceV2Response, type VoicesResponse, type VoicesV2Filter };