UNPKG

@tsavo/creatify-api-ts

Version:

TypeScript client for the Creatify AI API

240 lines (202 loc) 5.99 kB
import { Creatify } from '../index'; import { CreatifyApiOptions, Avatar, TextToSpeech } from '../types'; import { ICreatifyApiClientFactory } from '../types/api-client'; import { apiClientFactory } from '../client-factory'; /** * A utility class to simplify avatar video creation */ export class VideoCreatorWithFactory { private creatify: Creatify; private avatarCache: Record<string, Avatar.AvatarInfo> = {}; private voiceCache: Record<string, TextToSpeech.Voice> = {}; private avatarsLoaded = false; private voicesLoaded = false; /** * Create a new VideoCreator instance * @param apiIdOrOptions API ID or full options object * @param apiKey API key (only required if apiIdOrOptions is a string) * @param clientFactory Optional client factory for testing purposes */ constructor( apiIdOrOptions: string | CreatifyApiOptions, apiKey?: string | ICreatifyApiClientFactory, _clientFactory: ICreatifyApiClientFactory = apiClientFactory ) { // Handle different constructor argument formats const options: CreatifyApiOptions = typeof apiIdOrOptions === 'string' ? { apiId: apiIdOrOptions, apiKey: apiKey as string } : apiIdOrOptions; this.creatify = new Creatify(options); } /** * Preload avatars and voices for faster video creation */ async preload() { await Promise.all([this.loadAvatars(), this.loadVoices()]); return { avatars: Object.keys(this.avatarCache).length, voices: Object.keys(this.voiceCache).length, }; } /** * Load and cache all available avatars */ async loadAvatars() { if (this.avatarsLoaded) return Object.values(this.avatarCache); const avatars = await this.creatify.avatar.getAvatars(); for (const avatar of avatars) { this.avatarCache[avatar.id] = avatar; } this.avatarsLoaded = true; return avatars; } /** * Load and cache all available voices */ async loadVoices() { if (this.voicesLoaded) return Object.values(this.voiceCache); const voices = await this.creatify.avatar.getVoices(); for (const voice of voices) { this.voiceCache[voice.voice_id] = voice; } this.voicesLoaded = true; return voices; } /** * Create a video with an avatar speaking the provided text * @param params Video creation parameters * @param pollingInterval Optional interval for checking video status (ms) */ async createVideo(params: VideoCreationParams, pollingInterval = 2000) { // Extract parameters const { text, avatarId, voiceId, aspectRatio } = params; // Create the lipsync video return this.creatify.avatar.createAndWaitForLipsync( { text, creator: avatarId, voice_id: voiceId, aspect_ratio: aspectRatio, }, pollingInterval ); } /** * Find an avatar by name or partial name match * @param name Name or partial name to search for */ async findAvatarByName(name: string): Promise<Avatar.AvatarInfo | null> { if (!this.avatarsLoaded) { await this.loadAvatars(); } name = name.toLowerCase(); // First try exact match for (const avatar of Object.values(this.avatarCache)) { if (avatar.name?.toLowerCase() === name) { return avatar; } } // Then try partial match for (const avatar of Object.values(this.avatarCache)) { if (avatar.name?.toLowerCase().includes(name)) { return avatar; } } return null; } /** * Find a voice by name or partial name match * @param name Name or partial name to search for */ async findVoiceByName(name: string): Promise<TextToSpeech.Voice | null> { if (!this.voicesLoaded) { await this.loadVoices(); } name = name.toLowerCase(); // First try exact match for (const voice of Object.values(this.voiceCache)) { if (voice.name.toLowerCase() === name) { return voice; } } // Then try partial match for (const voice of Object.values(this.voiceCache)) { if (voice.name.toLowerCase().includes(name)) { return voice; } } return null; } /** * Create a video using avatar and voice names instead of IDs * @param params Simplified video creation parameters * @param pollingInterval Optional interval for checking video status (ms) */ async createVideoWithNames(params: SimplifiedVideoCreationParams, pollingInterval = 2000) { const { text, avatarName, voiceName, aspectRatio } = params; // Find the avatar and voice by name const [avatar, voice] = await Promise.all([ this.findAvatarByName(avatarName), this.findVoiceByName(voiceName), ]); if (!avatar) { throw new Error(`Avatar not found with name: ${avatarName}`); } if (!voice) { throw new Error(`Voice not found with name: ${voiceName}`); } // Create the video return this.createVideo( { text, avatarId: avatar.id || avatar.avatar_id || '', voiceId: voice.id || voice.voice_id || '', aspectRatio, }, pollingInterval ); } } /** * Parameters for creating a video */ export interface VideoCreationParams { /** * The text for the avatar to speak */ text: string; /** * The ID of the avatar to use */ avatarId: string; /** * The ID of the voice to use */ voiceId: string; /** * Optional aspect ratio for the video (default: '16:9') */ aspectRatio?: '16:9' | '1:1' | '9:16'; } /** * Simplified parameters for creating a video using names instead of IDs */ export interface SimplifiedVideoCreationParams { /** * The text for the avatar to speak */ text: string; /** * The name of the avatar to use */ avatarName: string; /** * The name of the voice to use */ voiceName: string; /** * Optional aspect ratio for the video (default: '16:9') */ aspectRatio?: '16:9' | '1:1' | '9:16'; }