@tsavo/creatify-api-ts
Version:
TypeScript client for the Creatify AI API
228 lines (207 loc) • 7.9 kB
text/typescript
import { CreatifyApiOptions } from '../types';
import { Avatar } from '../types';
import { ICreatifyApiClient } from '../types/api-client';
import { apiClientFactory } from '../client-factory';
/**
* Client for interacting with the Creatify AI Avatar API
* @see https://creatify.mintlify.app/api-reference/lipsync
*/
export class AvatarApi {
private client: ICreatifyApiClient;
/**
* Create a new AvatarApi instance
* @param options API client options
* @param clientFactory Optional factory for creating API clients (useful for testing)
*/
constructor(options: CreatifyApiOptions, clientFactory = apiClientFactory) {
this.client = clientFactory.createClient(options);
}
/**
* Get a list of all available avatars
* @returns Promise resolving to an array of avatar information
* @see https://creatify.mintlify.app/api-reference/personas/get-apipersonas
*/
async getAvatars(filters?: {
age_range?: 'child' | 'teen' | 'adult' | 'senior';
gender?: 'm' | 'f' | 'nb';
location?: 'outdoor' | 'fantasy' | 'indoor' | 'other';
style?: 'selfie' | 'presenter' | 'other';
}): Promise<Avatar.AvatarInfo[]> {
try {
const avatars = await this.client.get<Avatar.AvatarInfo[]>('/api/personas/', filters);
// Ensure avatars is an array
if (!Array.isArray(avatars)) {
return [];
}
// Add avatar_id property for backward compatibility and explicitly add a name property
return avatars.map(avatar => ({
...avatar,
avatar_id: avatar.id,
// Force the name property to be 'Avatar ID' if it's missing, empty or undefined
name: avatar.name && avatar.name.trim() ? avatar.name : `Avatar ${avatar.id}`,
}));
} catch (error) {
console.error('Error fetching avatars:', error);
return [];
}
}
/**
* Get a list of available avatars with pagination
* @param page Page number (starts from 1)
* @param limit Number of items per page
* @param filters Optional filtering parameters
* @returns Promise resolving to paginated avatar information
* @see https://creatify.mintlify.app/api-reference/personas/get-apipersonas-paginated
*/
async getAvatarsPaginated(
page: number = 1,
limit: number = 20,
filters?: {
age_range?: 'child' | 'teen' | 'adult' | 'senior';
gender?: 'm' | 'f' | 'nb';
location?: 'outdoor' | 'fantasy' | 'indoor' | 'other';
style?: 'selfie' | 'presenter' | 'other';
}
): Promise<{
count: number;
next: string | null;
previous: string | null;
results: Avatar.AvatarInfo[];
}> {
return this.client.get('/api/personas-paginated/', {
page,
limit,
...filters,
});
}
/**
* Get a list of all available voices
* @returns Promise resolving to an array of voice information
* @see https://creatify.mintlify.app/api-reference/voices/get-apivoices
*/
async getVoices(): Promise<Avatar.VoiceInfo[]> {
try {
const voices = await this.client.get<Avatar.VoiceInfo[]>('/api/voices/');
// Ensure voices is an array
if (!Array.isArray(voices)) {
return [];
}
// Process voice data to handle different API response formats
return voices.map(voice => {
// Create a normalized version of each voice with consistent properties
const normalizedVoice: Avatar.VoiceInfo = {
// Ensure ID is available in a consistent field
id: voice.id || voice.voice_id || '',
voice_id: voice.voice_id || voice.id || '',
name: voice.name || 'Unnamed Voice',
language: voice.language || '',
gender: voice.gender || '',
accents: voice.accents || [],
// Extract preview URL from the first accent if not directly available
preview_url:
voice.preview_url ||
(voice.accents && voice.accents.length > 0 && voice.accents[0].preview_url) ||
'',
};
return normalizedVoice;
});
} catch (error) {
console.error('Error fetching voices:', error);
return [];
}
}
/**
* Create a lipsync video with an AI avatar speaking the provided text
* @param params Parameters for the lipsync video generation
* @returns Promise resolving to the lipsync task response
* @see https://creatify.mintlify.app/api-reference/lipsync
*/
async createLipsync(params: Avatar.LipsyncParams): Promise<Avatar.LipsyncResponse> {
try {
return await this.client.post<Avatar.LipsyncResponse>('/api/lipsyncs/', params);
} catch (error) {
console.error('Error creating lipsync:', error);
throw error;
}
}
/**
* Get the status and result of a lipsync task
* @param id ID of the lipsync task
* @returns Promise resolving to the lipsync task status and result
* @see https://creatify.mintlify.app/api-reference/lipsync
*/
async getLipsync(id: string): Promise<Avatar.LipsyncResultResponse> {
try {
return await this.client.get<Avatar.LipsyncResultResponse>(`/api/lipsyncs/${id}/`);
} catch (error) {
console.error(`Error fetching lipsync result for ID ${id}:`, error);
// Return a default response to prevent crashes
return {
id,
status: 'error',
error_message:
error instanceof Error ? error.message : 'An error occurred with the API request',
} as Avatar.LipsyncResultResponse;
}
}
/**
* Get all lipsync tasks
* @returns Promise resolving to an array of lipsync tasks
* @see https://creatify.mintlify.app/api-reference/lipsync
*/
async getLipsyncs(): Promise<Avatar.LipsyncResultResponse[]> {
try {
const results = await this.client.get<Avatar.LipsyncResultResponse[]>('/api/lipsyncs/');
return Array.isArray(results) ? results : [];
} catch (error) {
console.error('Error fetching lipsync tasks:', error);
return [];
}
}
/**
* Create a multi-avatar lipsync video with multiple AI avatars speaking
* @param params Parameters for the multi-avatar lipsync video generation
* @returns Promise resolving to the lipsync task response
* @see https://creatify.mintlify.app/api-reference/lipsync-multi-avatar
*/
async createMultiAvatarLipsync(
params: Avatar.MultiAvatarLipsyncParams
): Promise<Avatar.LipsyncResponse> {
try {
return await this.client.post<Avatar.LipsyncResponse>('/api/lipsyncs/multi_avatar/', params);
} catch (error) {
console.error('Error creating multi-avatar lipsync:', error);
throw error;
}
}
/**
* Create a lipsync video and wait for it to complete
* @param params Parameters for the lipsync video generation
* @param pollInterval How often to check status in ms (default 2000)
* @param maxAttempts Maximum number of polling attempts (default 30)
* @returns Promise resolving to the completed lipsync task
*/
async createAndWaitForLipsync(
params: Avatar.LipsyncParams,
pollInterval: number = 2000,
maxAttempts: number = 30
): Promise<Avatar.LipsyncResultResponse> {
// Create the lipsync task
const response = await this.createLipsync(params);
// Poll for completion
let attempts = 0;
let result = await this.getLipsync(response.id);
while (attempts < maxAttempts && result.status !== 'done' && result.status !== 'error') {
// Wait for the specified interval
await new Promise(resolve => setTimeout(resolve, pollInterval));
// Check the status again
result = await this.getLipsync(response.id);
attempts++;
}
// Check if we reached max attempts without completion
if (attempts >= maxAttempts && result.status !== 'done' && result.status !== 'error') {
throw new Error(`Lipsync task ${response.id} did not complete within the timeout period`);
}
return result;
}
}