@geepers/media
v1.0.0
Published
Headless TypeScript library for LLM media generation — images, video, and speech
Maintainers
Readme
@geepers/media
Headless TypeScript library for LLM media generation — images, video, and speech. Drop it into any project; bring your own UI.
Works with any API gateway that follows the standard REST shape. No framework dependencies. No React. Just fetch.
Install
npm install @geepers/media
# or
pnpm add @geepers/mediaQuick start
import { ImageGenerator, TTSGenerator, VideoGenerator } from '@geepers/media';
const config = { baseUrl: 'https://api.example.com/v1' };
// Image
const images = new ImageGenerator(config);
const result = await images.generate({ prompt: 'a red fox in the snow', provider: 'openai' });
console.log(result[0].url);
// Speech
const tts = new TTSGenerator(config);
const audio = await tts.generate({ text: 'Hello world', provider: 'openai', voice: 'alloy' });
console.log(audio.audioUrl);
// Video
const video = new VideoGenerator(config);
const job = await video.generate({ prompt: 'ocean waves at sunset', provider: 'xai' });
console.log(job.videoUrl);ImageGenerator
Supports URL and base64 image responses, seed control, batch generation with per-image callbacks, and automatic retry on moderation failures (when you supply a rewrite function).
import { ImageGenerator } from '@geepers/media';
const gen = new ImageGenerator({
baseUrl: 'https://api.example.com/v1',
headers: { 'X-API-Key': process.env.API_KEY! },
});
// Single image
const [img] = await gen.generate({
prompt: 'a geometric pattern in red and black',
provider: 'xai',
model: 'aurora',
seed: 42,
params: { aspectRatio: '16:9', resolution: '2k' },
});
// Batch — images stream in via onImage as each one finishes
const { images, successCount } = await gen.generateBatch({
prompt: 'abstract watercolor landscape',
provider: 'openai',
quantity: 8,
params: { size: '1024x1024', quality: 'high' },
onProgress: (done, total) => console.log(`${done}/${total}`),
onImage: (img) => saveToGallery(img),
});Provider params
| Provider | Type | Key params |
|----------|------|-----------|
| xAI | XaiImageParams | aspectRatio, resolution |
| OpenAI (DALL-E 3 / gpt-image-1) | OpenAiImageParams | size, quality, style |
| Gemini | GeminiImageParams | aspectRatio, negativePrompt |
VideoGenerator
Two usage patterns: fire-and-poll for streaming UI updates, or generate() for a simple await.
import { VideoGenerator } from '@geepers/media';
const gen = new VideoGenerator({
baseUrl: 'https://api.example.com/v1',
pollIntervalMs: 5_000,
pollTimeoutMs: 600_000,
});
// Wait for completion
const job = await gen.generate(
{ prompt: 'timelapse of clouds', provider: 'xai', params: { duration: 8, aspectRatio: '16:9' } },
(update) => console.log(update.status), // called on each status change
);
console.log(job.videoUrl);
// Fire-and-poll (useful when you want to update UI manually)
const pending = await gen.start({ prompt: 'ocean waves', provider: 'openai' });
const { videoUrl } = await gen.poll(pending.requestId, pending.provider, (status, url) => {
setStatus(status);
if (url) setVideoUrl(url);
});
// Extend an existing xAI video
const extended = await gen.extend(job.videoUrl!, 'xai');
await gen.poll(extended.requestId, 'xai');TTSGenerator
Multi-provider, multi-voice TTS with batch support and xAI speech tags.
import { TTSGenerator, wrapWithTag } from '@geepers/media';
const tts = new TTSGenerator({ baseUrl: 'https://api.example.com/v1' });
// Fetch available providers and voices
const { providers } = await tts.getVoiceConfig();
// Single clip
const audio = await tts.generate({
text: 'Welcome back.',
provider: 'openai',
voice: 'nova',
model: 'tts-1-hd',
speed: 1.1,
});
// xAI speech tags
const taggedText = `Hello. ${wrapWithTag('', 'pause')} ${wrapWithTag('This part is quiet.', 'whisper')}`;
const xaiAudio = await tts.generate({
text: taggedText,
provider: 'xai',
voice: 'Aria',
codec: 'mp3',
sampleRate: 24000,
language: 'en',
});
// Batch
const { audio: clips } = await tts.generateBatch({
provider: 'openai',
voice: 'alloy',
texts: ['First sentence.', 'Second sentence.', 'Third sentence.'],
onAudio: (clip) => console.log('ready:', clip.audioUrl),
});withRetry / withAutoRetry
Standalone retry utilities — use them with any async function, not just media generation.
import { withRetry, withAutoRetry } from '@geepers/media';
// Basic exponential backoff
const data = await withRetry(() => fetch(url).then(r => r.json()), {
maxRetries: 3,
baseDelayMs: 500,
onRetry: (attempt, err) => console.warn(`Retry ${attempt}:`, err),
});
// Moderation-aware rewrite-and-retry
const { result, attempts } = await withAutoRetry(
(prompt) => generateImage(prompt),
'original prompt text',
{
maxRetries: 3,
rewritePrompt: async (prompt, attempt) => callMyRewriter(prompt, attempt),
onRewriteSuccess: (original, rewritten) => saveRuleToDb(original, rewritten),
},
);API reference
Full TypeScript types are exported for every class, method, and option. Run tsc or check dist/*.d.ts for the full interface surface.
License
MIT — Luke Steuber
