Skip to main content

Understanding callbacks

Tafrigh provides comprehensive callbacks to monitor every stage of the transcription pipeline: preprocessing, splitting, and transcription. Use these callbacks to build progress indicators, logging systems, or custom error handling.

Complete callback example

This example demonstrates all available callbacks:
import { init, transcribe } from 'tafrigh';

init({ apiKeys: ['your-wit-ai-key'] });

const transcript = await transcribe('audio.mp3', {
  callbacks: {
    // Preprocessing callbacks
    onPreprocessingStarted: async (filePath) => {
      console.log(`Starting preprocessing: ${filePath}`);
    },
    onPreprocessingProgress: async (percent) => {
      console.log(`Preprocessing: ${percent}%`);
    },
    onPreprocessingFinished: async (filePath) => {
      console.log(`Finished preprocessing: ${filePath}`);
    },

    // Splitting callbacks
    onSplittingStarted: async (totalChunks) => {
      console.log(`Splitting into ${totalChunks} chunks`);
    },
    onSplittingProgress: async (chunkFilePath, chunkIndex) => {
      console.log(`Created chunk ${chunkIndex}: ${chunkFilePath}`);
    },
    onSplittingFinished: async () => {
      console.log('Splitting complete');
    },

    // Transcription callbacks
    onTranscriptionStarted: async (totalChunks) => {
      console.log(`Transcribing ${totalChunks} chunks`);
    },
    onTranscriptionProgress: async (chunkIndex) => {
      console.log(`Transcribed chunk ${chunkIndex}`);
    },
    onTranscriptionFinished: async (transcripts) => {
      console.log(`Complete! ${transcripts.length} segments`);
    },
  },
});

console.log(transcript);

Expected output

Starting preprocessing: /tmp/tafrigh/1709438400000.mp3
Preprocessing: 25%
Preprocessing: 50%
Preprocessing: 75%
Preprocessing: 100%
Finished preprocessing: /tmp/tafrigh/1709438400000.mp3
Splitting into 5 chunks
Created chunk 0: /tmp/tafrigh/chunk_0.wav
Created chunk 1: /tmp/tafrigh/chunk_1.wav
Created chunk 2: /tmp/tafrigh/chunk_2.wav
Created chunk 3: /tmp/tafrigh/chunk_3.wav
Created chunk 4: /tmp/tafrigh/chunk_4.wav
Splitting complete
Transcribing 5 chunks
Transcribed chunk 0
Transcribed chunk 1
Transcribed chunk 2
Transcribed chunk 3
Transcribed chunk 4
Complete! 5 segments

Building a progress bar

Create a visual progress indicator using callbacks:
import { init, transcribe } from 'tafrigh';

init({ apiKeys: ['your-wit-ai-key'] });

let totalChunks = 0;
let processedChunks = 0;

function updateProgress(stage, current, total) {
  const percent = Math.round((current / total) * 100);
  const bar = '='.repeat(percent / 2) + ' '.repeat(50 - percent / 2);
  console.log(`[${bar}] ${percent}% - ${stage}`);
}

const transcript = await transcribe('long-audio.mp3', {
  callbacks: {
    onPreprocessingProgress: async (percent) => {
      const bar = '='.repeat(percent / 2) + ' '.repeat(50 - percent / 2);
      console.log(`[${bar}] ${percent}% - Preprocessing`);
    },
    onTranscriptionStarted: async (total) => {
      totalChunks = total;
      processedChunks = 0;
    },
    onTranscriptionProgress: async (chunkIndex) => {
      processedChunks++;
      updateProgress('Transcribing', processedChunks, totalChunks);
    },
  },
});

console.log('Transcription complete!');

Expected output

[=========================                         ] 50% - Preprocessing
[==================================================] 100% - Preprocessing
[==========                                        ] 20% - Transcribing
[====================                              ] 40% - Transcribing
[==============================                    ] 60% - Transcribing
[========================================          ] 80% - Transcribing
[==================================================] 100% - Transcribing
Transcription complete!

Handling transcription errors

Use TranscriptionError to access partial results and retry failed chunks:
import { promises as fs } from 'node:fs';
import { init, transcribe, TranscriptionError } from 'tafrigh';

init({ apiKeys: ['your-wit-ai-key'] });

try {
  const transcript = await transcribe('large-file.mp3');
  console.log('Success:', transcript);
} catch (error) {
  if (error instanceof TranscriptionError) {
    console.error(`Failed to transcribe ${error.failures.length} chunk(s)`);
    console.log(`Successfully transcribed ${error.transcripts.length} chunks`);
    
    // Access partial transcripts
    console.log('Partial results:', error.transcripts);
    
    // Access failed chunk information
    error.failures.forEach((failure, index) => {
      console.error(`Failed chunk ${failure.index}:`, failure.error);
    });
    
    // Clean up temporary directory
    if (error.outputDir) {
      await fs.rm(error.outputDir, { recursive: true });
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

Resuming failed transcriptions

Automatically retry failed chunks without re-processing successful ones:
import { promises as fs } from 'node:fs';
import {
  init,
  transcribe,
  TranscriptionError,
  resumeFailedTranscriptions,
} from 'tafrigh';

init({ apiKeys: ['key1', 'key2', 'key3'] });

try {
  const transcript = await transcribe('large-file.mp3');
  console.log('All chunks transcribed successfully');
} catch (error) {
  if (error instanceof TranscriptionError) {
    console.log(`Initial run: ${error.transcripts.length} succeeded, ${error.failures.length} failed`);
    
    // Retry only the failed chunks
    const { failures, transcripts } = await resumeFailedTranscriptions(error, {
      retries: 3,
      concurrency: 2,
    });
    
    if (failures.length === 0) {
      console.log('All chunks transcribed after retry!');
      console.log('Complete transcript:', transcripts);
    } else {
      console.error(`Still have ${failures.length} failures after retry`);
      console.log('Partial transcript:', transcripts);
    }
    
    // Clean up temporary directory
    if (error.outputDir) {
      await fs.rm(error.outputDir, { recursive: true });
    }
  }
}
The resumeFailedTranscriptions function merges successful transcripts from both the initial attempt and retry, sorted by timestamp. The temporary directory is preserved until you explicitly delete it.

Real-time progress updates

Send progress updates to a client application:
import { init, transcribe } from 'tafrigh';
import { EventEmitter } from 'events';

const progressEmitter = new EventEmitter();

init({ apiKeys: ['your-wit-ai-key'] });

// Listen for progress events
progressEmitter.on('progress', ({ stage, percent }) => {
  console.log(`${stage}: ${percent}%`);
  // Send to client via WebSocket, HTTP, etc.
});

const transcript = await transcribe('audio.mp3', {
  callbacks: {
    onPreprocessingProgress: async (percent) => {
      progressEmitter.emit('progress', { stage: 'preprocessing', percent });
    },
    onSplittingStarted: async (totalChunks) => {
      progressEmitter.emit('progress', { 
        stage: 'splitting', 
        percent: 0,
        totalChunks 
      });
    },
    onTranscriptionStarted: async (totalChunks) => {
      progressEmitter.emit('progress', { 
        stage: 'transcription', 
        percent: 0,
        totalChunks 
      });
    },
    onTranscriptionProgress: async (chunkIndex) => {
      progressEmitter.emit('progress', { 
        stage: 'transcription', 
        chunkIndex 
      });
    },
    onTranscriptionFinished: async (transcripts) => {
      progressEmitter.emit('progress', { 
        stage: 'complete', 
        percent: 100,
        totalSegments: transcripts.length 
      });
    },
  },
});

Logging to file

Save detailed logs for debugging:
import { promises as fs } from 'node:fs';
import { init, transcribe } from 'tafrigh';

const logFile = 'transcription-log.txt';

const log = async (message) => {
  const timestamp = new Date().toISOString();
  await fs.appendFile(logFile, `[${timestamp}] ${message}\n`);
};

init({ apiKeys: ['your-wit-ai-key'] });

const transcript = await transcribe('audio.mp3', {
  callbacks: {
    onPreprocessingStarted: async (filePath) => {
      await log(`Preprocessing started: ${filePath}`);
    },
    onPreprocessingFinished: async (filePath) => {
      await log(`Preprocessing finished: ${filePath}`);
    },
    onSplittingStarted: async (totalChunks) => {
      await log(`Splitting started: ${totalChunks} chunks`);
    },
    onSplittingProgress: async (chunkFilePath, chunkIndex) => {
      await log(`Created chunk ${chunkIndex}: ${chunkFilePath}`);
    },
    onTranscriptionStarted: async (totalChunks) => {
      await log(`Transcription started: ${totalChunks} chunks`);
    },
    onTranscriptionProgress: async (chunkIndex) => {
      await log(`Transcribed chunk ${chunkIndex}`);
    },
    onTranscriptionFinished: async (transcripts) => {
      await log(`Transcription complete: ${transcripts.length} segments`);
    },
  },
});

console.log('Check transcription-log.txt for details');

Performance monitoring

Track timing metrics for each stage:
import { init, transcribe } from 'tafrigh';

const timings = {
  preprocessing: { start: 0, end: 0 },
  splitting: { start: 0, end: 0 },
  transcription: { start: 0, end: 0 },
};

init({ apiKeys: ['key1', 'key2', 'key3'] });

const transcript = await transcribe('audio.mp3', {
  callbacks: {
    onPreprocessingStarted: async () => {
      timings.preprocessing.start = Date.now();
    },
    onPreprocessingFinished: async () => {
      timings.preprocessing.end = Date.now();
    },
    onSplittingStarted: async () => {
      timings.splitting.start = Date.now();
    },
    onSplittingFinished: async () => {
      timings.splitting.end = Date.now();
    },
    onTranscriptionStarted: async () => {
      timings.transcription.start = Date.now();
    },
    onTranscriptionFinished: async () => {
      timings.transcription.end = Date.now();
    },
  },
});

// Calculate durations
const preprocessingTime = timings.preprocessing.end - timings.preprocessing.start;
const splittingTime = timings.splitting.end - timings.splitting.start;
const transcriptionTime = timings.transcription.end - timings.transcription.start;
const totalTime = preprocessingTime + splittingTime + transcriptionTime;

console.log('Performance Metrics:');
console.log(`Preprocessing: ${preprocessingTime}ms`);
console.log(`Splitting: ${splittingTime}ms`);
console.log(`Transcription: ${transcriptionTime}ms`);
console.log(`Total: ${totalTime}ms`);

Expected output

Performance Metrics:
Preprocessing: 3421ms
Splitting: 892ms
Transcription: 12567ms
Total: 16880ms
Use multiple API keys and higher concurrency to significantly reduce transcription time for longer audio files.

Build docs developers (and LLMs) love