Real-world examples and use cases with the VerbalisAI JavaScript SDK
Real-world examples and practical implementations using the VerbalisAI JavaScript SDK.
#!/usr/bin/env node
/**
* Simple transcription script
* Usage: node transcribe.js <audio_url>
*/
import { VerbalisAI } from '@verbalisai/sdk';
async function main() {
const audioUrl = process.argv[2];
if (!audioUrl) {
console.log('Usage: node transcribe.js <audio_url>');
process.exit(1);
}
try {
const client = new VerbalisAI();
console.log(`Starting transcription of: ${audioUrl}`);
const transcription = await client.transcriptions.create({
audioUrl: audioUrl,
model: 'mini'
});
console.log('\nTranscription completed!');
console.log(`Duration: ${transcription.duration} seconds`);
console.log(`Text: ${transcription.text}`);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
main();
#!/usr/bin/env node
/**
* Batch transcription processor
* Processes all audio files in a directory
*/
import { VerbalisAI } from '@verbalisai/sdk';
import fs from 'fs/promises';
import path from 'path';
async function processDirectory(directoryPath, outputDir = 'transcriptions') {
const client = new VerbalisAI();
try {
// Create output directory
await fs.mkdir(outputDir, { recursive: true });
// Find audio files
const audioExtensions = new Set(['.mp3', '.wav', '.flac', '.m4a', '.ogg']);
const files = await fs.readdir(directoryPath);
const audioFiles = files.filter(file =>
audioExtensions.has(path.extname(file).toLowerCase())
);
console.log(`Found ${audioFiles.length} audio files`);
// Process files
const results = [];
for (const audioFile of audioFiles) {
try {
console.log(`Processing: ${audioFile}`);
const filePath = path.join(directoryPath, audioFile);
// Upload file
const fileStream = await fs.readFile(filePath);
const fileInfo = await client.files.upload({
file: new Blob([fileStream]),
filename: audioFile
});
// Transcribe
const transcription = await client.transcriptions.create({
audioUrl: fileInfo.url,
model: 'mini',
topics: true
});
// Save transcription
const outputFile = path.join(outputDir, `${path.parse(audioFile).name}_transcription.txt`);
const content = [
`File: ${audioFile}`,
`Duration: ${transcription.duration} seconds`,
transcription.topics ? `Topics: ${transcription.topics.join(', ')}` : '',
'',
'Transcription:',
transcription.text
].filter(Boolean).join('\n');
await fs.writeFile(outputFile, content, 'utf-8');
results.push({
file: audioFile,
status: 'success',
output: outputFile
});
console.log(`✅ Completed: ${audioFile}`);
} catch (error) {
console.log(`❌ Failed: ${audioFile} - ${error.message}`);
results.push({
file: audioFile,
status: 'failed',
error: error.message
});
}
}
// Summary
const successful = results.filter(r => r.status === 'success').length;
const failed = results.filter(r => r.status === 'failed').length;
console.log(`\nBatch processing complete:`);
console.log(`✅ Successful: ${successful}`);
console.log(`❌ Failed: ${failed}`);
return results;
} catch (error) {
console.error(`Directory processing failed: ${error.message}`);
throw error;
}
}
// Usage
if (process.argv.length !== 3) {
console.log('Usage: node batch-processor.js <directory_path>');
process.exit(1);
}
const directory = process.argv[2];
processDirectory(directory).catch(console.error);
import express from 'express';
import multer from 'multer';
import { VerbalisAI } from '@verbalisai/sdk';
import cors from 'cors';
const app = express();
const client = new VerbalisAI();
// Middleware
app.use(cors());
app.use(express.json());
// Configure multer for file uploads
const upload = multer({
limits: {
fileSize: 100 * 1024 * 1024 // 100MB max
},
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('audio/')) {
cb(null, true);
} else {
cb(new Error('Only audio files are allowed'));
}
}
});
// In-memory storage for demo (use database in production)
const transcriptionStatus = new Map();
app.get('/', (req, res) => {
res.send(`
<html>
<head><title>VerbalisAI Transcription Service</title></head>
<body>
<h1>Audio Transcription Service</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="audio" accept="audio/*" required>
<button type="submit">Upload & Transcribe</button>
</form>
</body>
</html>
`);
});
app.post('/upload', upload.single('audio'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file provided' });
}
console.log(`Processing file: ${req.file.originalname}`);
// Upload to VerbalisAI
const fileInfo = await client.files.upload({
file: req.file.buffer,
filename: req.file.originalname
});
// Start transcription (non-blocking)
const transcription = await client.transcriptions.create({
audioUrl: fileInfo.url,
model: 'pro',
diarize: true,
topics: true,
summarization: true,
waitUntilComplete: false
});
// Store status
transcriptionStatus.set(transcription.id, {
status: 'processing',
filename: req.file.originalname,
startedAt: new Date()
});
// Start monitoring in background
monitorTranscription(transcription.id);
res.json({
transcriptionId: transcription.id,
status: 'processing',
message: `Transcription started for ${req.file.originalname}`
});
} catch (error) {
console.error('Upload error:', error);
res.status(500).json({ error: error.message });
}
});
async function monitorTranscription(transcriptionId) {
while (true) {
try {
const transcription = await client.transcriptions.get(transcriptionId);
if (transcription.status === 'completed') {
transcriptionStatus.set(transcriptionId, {
status: 'completed',
transcription: transcription,
completedAt: new Date()
});
break;
} else if (transcription.status === 'failed') {
transcriptionStatus.set(transcriptionId, {
status: 'failed',
error: transcription.error,
failedAt: new Date()
});
break;
}
await new Promise(resolve => setTimeout(resolve, 5000)); // Check every 5 seconds
} catch (error) {
transcriptionStatus.set(transcriptionId, {
status: 'error',
error: error.message,
errorAt: new Date()
});
break;
}
}
}
app.get('/status/:transcriptionId', (req, res) => {
const { transcriptionId } = req.params;
if (!transcriptionStatus.has(transcriptionId)) {
return res.status(404).json({ error: 'Transcription not found' });
}
const statusInfo = transcriptionStatus.get(transcriptionId);
if (statusInfo.status === 'completed') {
const transcription = statusInfo.transcription;
return res.json({
status: 'completed',
text: transcription.text,
duration: transcription.duration,
topics: transcription.topics,
summary: transcription.summary?.text,
segments: transcription.segments.map(s => ({
text: s.text,
start: s.start,
end: s.end,
speakerId: s.speakerId
}))
});
} else {
return res.json(statusInfo);
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
import React, { useState, useCallback, useRef } from 'react';
import { VerbalisAI } from '@verbalisai/sdk';
const TranscriptionApp = () => {
const [client] = useState(() => new VerbalisAI({
apiKey: process.env.REACT_APP_VERBALISAI_API_KEY
}));
const [transcriptions, setTranscriptions] = useState([]);
const [uploading, setUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const fileInputRef = useRef(null);
const handleFileUpload = useCallback(async (files) => {
if (!files || files.length === 0) return;
setUploading(true);
setUploadProgress(0);
try {
const uploadPromises = Array.from(files).map(async (file, index) => {
// Upload file
const fileInfo = await client.files.upload({
file,
filename: file.name,
onProgress: (progress) => {
const totalProgress = ((index + progress.percentage / 100) / files.length) * 100;
setUploadProgress(totalProgress);
}
});
// Start transcription
const transcription = await client.transcriptions.create({
audioUrl: fileInfo.url,
model: 'pro',
topics: true,
summarization: true,
diarize: true,
waitUntilComplete: false
});
return {
id: transcription.id,
filename: file.name,
status: 'processing',
startedAt: new Date(),
fileInfo
};
});
const newTranscriptions = await Promise.all(uploadPromises);
setTranscriptions(prev => [...newTranscriptions, ...prev]);
// Start monitoring each transcription
newTranscriptions.forEach(t => monitorTranscription(t.id));
} catch (error) {
console.error('Upload failed:', error);
alert(`Upload failed: ${error.message}`);
} finally {
setUploading(false);
setUploadProgress(0);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
}
}, [client]);
const monitorTranscription = useCallback(async (transcriptionId) => {
const checkStatus = async () => {
try {
const transcription = await client.transcriptions.get(transcriptionId);
setTranscriptions(prev => prev.map(t =>
t.id === transcriptionId
? {
...t,
status: transcription.status,
...(transcription.status === 'completed' ? {
result: transcription,
completedAt: new Date()
} : {}),
...(transcription.status === 'failed' ? {
error: transcription.error,
failedAt: new Date()
} : {})
}
: t
));
if (transcription.status === 'processing') {
setTimeout(checkStatus, 5000); // Check again in 5 seconds
}
} catch (error) {
console.error('Status check failed:', error);
setTranscriptions(prev => prev.map(t =>
t.id === transcriptionId
? { ...t, status: 'error', error: error.message }
: t
));
}
};
checkStatus();
}, [client]);
const handleDrop = useCallback((e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files).filter(file =>
file.type.startsWith('audio/')
);
if (files.length > 0) {
handleFileUpload(files);
}
}, [handleFileUpload]);
const handleFileSelect = useCallback((e) => {
handleFileUpload(e.target.files);
}, [handleFileUpload]);
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-4xl mx-auto px-4">
<h1 className="text-3xl font-bold text-gray-900 mb-8">
VerbalisAI Transcription
</h1>
{/* Upload Area */}
<div
className={`border-2 border-dashed rounded-lg p-8 text-center mb-8 transition-colors ${
uploading
? 'border-gray-300 bg-gray-100 cursor-not-allowed'
: 'border-blue-300 bg-blue-50 hover:bg-blue-100 cursor-pointer'
}`}
onDrop={handleDrop}
onDragOver={(e) => e.preventDefault()}
onClick={() => !uploading && fileInputRef.current?.click()}
>
<input
ref={fileInputRef}
type="file"
multiple
accept="audio/*"
onChange={handleFileSelect}
disabled={uploading}
className="hidden"
/>
{uploading ? (
<div>
<div className="text-lg font-medium text-gray-700 mb-4">
Uploading files...
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
/>
</div>
<div className="text-sm text-gray-600 mt-2">
{uploadProgress.toFixed(1)}%
</div>
</div>
) : (
<div>
<div className="text-lg font-medium text-gray-700 mb-2">
Drag & drop audio files here or click to select
</div>
<div className="text-sm text-gray-600">
Supported formats: MP3, WAV, FLAC, M4A, OGG
</div>
</div>
)}
</div>
{/* Transcriptions List */}
{transcriptions.length > 0 && (
<div className="space-y-4">
<h2 className="text-xl font-semibold text-gray-900">
Transcriptions
</h2>
{transcriptions.map((transcription) => (
<TranscriptionCard
key={transcription.id}
transcription={transcription}
/>
))}
</div>
)}
</div>
</div>
);
};
const TranscriptionCard = ({ transcription }) => {
const [expanded, setExpanded] = useState(false);
const getStatusColor = (status) => {
switch (status) {
case 'completed': return 'text-green-600 bg-green-100';
case 'processing': return 'text-yellow-600 bg-yellow-100';
case 'failed':
case 'error': return 'text-red-600 bg-red-100';
default: return 'text-gray-600 bg-gray-100';
}
};
return (
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-lg font-medium text-gray-900">
{transcription.filename}
</h3>
<div className="text-sm text-gray-600">
Started: {transcription.startedAt.toLocaleString()}
</div>
</div>
<span className={`px-3 py-1 rounded-full text-sm font-medium ${getStatusColor(transcription.status)}`}>
{transcription.status}
</span>
</div>
{transcription.status === 'completed' && transcription.result && (
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<span className="font-medium">Duration:</span>
<div>{transcription.result.duration}s</div>
</div>
{transcription.result.topics && (
<div>
<span className="font-medium">Topics:</span>
<div>{transcription.result.topics.length}</div>
</div>
)}
<div>
<span className="font-medium">Speakers:</span>
<div>
{new Set(transcription.result.segments
.map(s => s.speakerId)
.filter(Boolean)
).size || 1}
</div>
</div>
<div>
<span className="font-medium">Completed:</span>
<div>{transcription.completedAt?.toLocaleString()}</div>
</div>
</div>
{transcription.result.topics && transcription.result.topics.length > 0 && (
<div>
<span className="font-medium text-sm">Topics: </span>
<div className="flex flex-wrap gap-2 mt-1">
{transcription.result.topics.map((topic, index) => (
<span
key={index}
className="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded"
>
{topic}
</span>
))}
</div>
</div>
)}
{transcription.result.summary && (
<div>
<span className="font-medium text-sm">Summary:</span>
<div className="mt-1 p-3 bg-gray-50 rounded text-sm">
<pre className="whitespace-pre-wrap font-sans">
{transcription.result.summary.text}
</pre>
</div>
</div>
)}
<div>
<button
onClick={() => setExpanded(!expanded)}
className="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
{expanded ? 'Hide' : 'Show'} Full Transcript
</button>
{expanded && (
<div className="mt-2 p-4 bg-gray-50 rounded max-h-64 overflow-y-auto">
<pre className="whitespace-pre-wrap text-sm font-sans">
{transcription.result.text}
</pre>
</div>
)}
</div>
</div>
)}
{transcription.status === 'processing' && (
<div className="flex items-center text-sm text-gray-600">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-yellow-600 mr-2"></div>
Processing...
</div>
)}
{(transcription.status === 'failed' || transcription.status === 'error') && (
<div className="text-sm text-red-600">
Error: {transcription.error || 'Processing failed'}
</div>
)}
</div>
);
};
export default TranscriptionApp;
import { VerbalisAI } from '@verbalisai/sdk';
import fs from 'fs/promises';
class MeetingAnalyzer {
constructor() {
this.client = new VerbalisAI();
}
async analyzeMeeting(audioFilePath) {
console.log(`Analyzing meeting: ${audioFilePath}`);
try {
// Upload audio file
const audioBuffer = await fs.readFile(audioFilePath);
const fileInfo = await this.client.files.upload({
file: new Blob([audioBuffer]),
filename: audioFilePath.split('/').pop(),
tags: ['meeting', 'analysis']
});
// Transcribe with full analysis
const transcription = await this.client.transcriptions.create({
audioUrl: fileInfo.url,
model: 'pro',
diarize: true,
topics: true,
summarization: true,
summaryType: 'bullets',
entityDetection: true,
entityTypes: ['person', 'organization', 'date', 'phoneNumber', 'email']
});
// Extract meeting insights
const analysis = {
metadata: {
filename: audioFilePath.split('/').pop(),
duration: transcription.duration,
analyzedAt: new Date().toISOString(),
transcriptionId: transcription.id
},
participants: this.extractParticipants(transcription),
topics: transcription.topics,
summary: transcription.summary?.text,
actionItems: this.extractActionItems(transcription.text),
keyDecisions: this.extractDecisions(transcription.text),
entities: this.groupEntities(transcription.entities),
conversationFlow: this.analyzeConversationFlow(transcription.segments),
speakingTime: this.calculateSpeakingTime(transcription.segments),
fullTranscript: transcription.text,
timestampedSegments: transcription.segments.map(s => ({
speaker: s.speakerId,
text: s.text,
start: s.start,
end: s.end
}))
};
return analysis;
} catch (error) {
console.error('Meeting analysis failed:', error);
throw error;
}
}
extractParticipants(transcription) {
const speakers = new Set();
transcription.segments.forEach(segment => {
if (segment.speakerId) {
speakers.add(segment.speakerId);
}
});
return Array.from(speakers).sort().map(speaker => ({
id: speaker,
label: `Participant ${speaker.slice(-1)}`
}));
}
extractActionItems(text) {
const actionKeywords = [
'action item', 'todo', 'to do', 'follow up', 'will do',
'should do', 'need to', 'must do', 'assigned to',
'deadline', 'by next week', 'by friday'
];
const sentences = text.split('. ');
const actionItems = [];
sentences.forEach(sentence => {
const sentenceLower = sentence.toLowerCase();
if (actionKeywords.some(keyword => sentenceLower.includes(keyword))) {
actionItems.push(sentence.trim());
}
});
return actionItems;
}
extractDecisions(text) {
const decisionKeywords = [
'decided', 'decision', 'agreed', 'consensus',
'approved', 'rejected', 'concluded', 'determined'
];
const sentences = text.split('. ');
const decisions = [];
sentences.forEach(sentence => {
const sentenceLower = sentence.toLowerCase();
if (decisionKeywords.some(keyword => sentenceLower.includes(keyword))) {
decisions.push(sentence.trim());
}
});
return decisions;
}
groupEntities(entities) {
const grouped = {};
entities.forEach(entity => {
if (!grouped[entity.type]) {
grouped[entity.type] = [];
}
grouped[entity.type].push({
text: entity.text,
confidence: entity.confidence
});
});
return grouped;
}
analyzeConversationFlow(segments) {
if (!segments || segments.length === 0) return {};
const speakerTransitions = [];
let currentSpeaker = null;
segments.forEach(segment => {
if (segment.speakerId !== currentSpeaker) {
speakerTransitions.push({
from: currentSpeaker,
to: segment.speakerId,
timestamp: segment.start
});
currentSpeaker = segment.speakerId;
}
});
return {
totalTransitions: speakerTransitions.length,
transitions: speakerTransitions.slice(0, 10) // First 10 transitions
};
}
calculateSpeakingTime(segments) {
const speakingTime = {};
segments.forEach(segment => {
const speaker = segment.speakerId || 'Unknown';
const duration = segment.end - segment.start;
if (!speakingTime[speaker]) {
speakingTime[speaker] = 0;
}
speakingTime[speaker] += duration;
});
// Convert to percentages
const totalTime = Object.values(speakingTime).reduce((sum, time) => sum + time, 0);
const speakingPercentages = {};
if (totalTime > 0) {
Object.entries(speakingTime).forEach(([speaker, time]) => {
speakingPercentages[speaker] = (time / totalTime) * 100;
});
}
return {
absoluteTime: speakingTime,
percentages: speakingPercentages
};
}
}
// Usage
async function main() {
const analyzer = new MeetingAnalyzer();
try {
const analysis = await analyzer.analyzeMeeting('./meeting-recording.mp3');
// Save analysis results
const outputFile = `meeting_analysis_${Date.now()}.json`;
await fs.writeFile(outputFile, JSON.stringify(analysis, null, 2));
console.log('Meeting analysis completed!');
console.log(`Results saved to: ${outputFile}`);
// Print summary
console.log('\n--- Meeting Summary ---');
console.log(`Duration: ${analysis.metadata.duration} seconds`);
console.log(`Participants: ${analysis.participants.length}`);
console.log(`Topics: ${analysis.topics.slice(0, 5).join(', ')}`);
console.log(`Action Items: ${analysis.actionItems.length}`);
console.log(`Key Decisions: ${analysis.keyDecisions.length}`);
} catch (error) {
console.error('Error analyzing meeting:', error);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
import { VerbalisAI } from '@verbalisai/sdk';
import fs from 'fs/promises';
import path from 'path';
class PodcastProcessor {
constructor() {
this.client = new VerbalisAI();
}
async processPodcastEpisode(audioFile, metadata = {}) {
console.log(`Processing podcast episode: ${audioFile}`);
try {
// Upload audio
const audioBuffer = await fs.readFile(audioFile);
const fileInfo = await this.client.files.upload({
file: new Blob([audioBuffer]),
filename: path.basename(audioFile),
tags: ['podcast', 'episode'],
folder: 'podcasts'
});
// Transcribe with speaker identification
const transcription = await this.client.transcriptions.create({
audioUrl: fileInfo.url,
model: 'pro',
diarize: true,
topics: true,
summarization: true,
summaryType: 'paragraphs',
entityDetection: true,
entityTypes: ['person', 'organization', 'product', 'location']
});
// Generate podcast-specific outputs
const result = {
episodeInfo: {
filename: path.basename(audioFile),
duration: transcription.duration,
processedAt: new Date().toISOString(),
...metadata
},
content: {
fullTranscript: transcription.text,
summary: transcription.summary?.text,
topics: transcription.topics,
keyEntities: this.extractKeyEntities(transcription.entities)
},
speakers: this.analyzeSpeakers(transcription.segments),
highlights: this.extractHighlights(transcription.segments),
chapters: this.generateChapters(transcription.segments, transcription.topics),
seo: {
titleSuggestions: this.generateTitleSuggestions(transcription.topics),
description: this.generateDescription(transcription.summary),
keywords: transcription.topics.slice(0, 10)
},
socialMedia: {
twitterThread: this.generateTwitterThread(transcription.text),
instagramCaption: this.generateInstagramCaption(transcription.summary),
youtubeDescription: this.generateYoutubeDescription(transcription)
}
};
return result;