JavaScript SDK Examples

Real-world examples and practical implementations using the VerbalisAI JavaScript SDK.

Basic Examples

Simple Transcription Script (Node.js)

#!/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();

Batch File Processor

#!/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);

Web Applications

Express.js Server

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}`);
});

React Application

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;

Data Processing

Meeting Analysis Tool

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();
}

Podcast Processing Pipeline

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;