Implementing the Audio/Video Transcription Process with TypeScript

Implementing the Audio/Video Transcription Process with TypeScript

Welcome back! Let's continue our path to implementing the Audio/Video Transcriber using the OpenAI Whisper API! In this lesson, we will wrap up the main functionality by putting together the media file split functionality we've done in the previous lesson and the Whisper API call on a small media chunk that we've done in the previous course. On top of that, we will make sure to properly handle all potential errors and ensure we don't leave any redundant garbage on our disk to avoid wasting disk space. Ensuring robust error handling and cleanup is key to avoiding data loss and maintaining efficiency, even in unexpected scenarios.

Let's step in to see how exciting this all is!

Building the Transcription Process

Let's examine our main transcription function and understand how it handles errors and cleanup:

TypeScript
function transcribe(filePath: string): string | null {
  /** Transcribe a large media file by splitting it into chunks */
  let chunks: string[] = [];
  try {
    // splitMedia is implemented in the previous lesson
    // it splits a large media file into smaller chunks
    chunks = splitMedia(filePath, 1);
    const transcriptions: string[] = [];
    
    for (const chunk of chunks) {
      // transcribeSmallMedia uses OpenAI Whisper API
      // to transcribe chunks under 25MB
      const text = transcribeSmallMedia(chunk);
      if (text) {
        transcriptions.push(text);
      }
    }
    return transcriptions.join(' ');
  } catch (e) {
    console.error(`Error processing large file: ${e}`);
    return null;
  } finally {
    // Clean up all chunks in the finally block
    for (const chunk of chunks) {
      cleanupTempFiles(chunk);
    }
  }
}

The function works in several steps:

  1. We initialize an empty chunks array outside the try block to ensure it's accessible in the finally block.
  2. Using splitMedia (implemented in our previous lesson), we split the large media file into manageable chunks.
  3. For each chunk, we use transcribeSmallMedia (which wraps the OpenAI Whisper API call we learned about earlier) to get the text transcription.
  4. Finally, we join all transcriptions into a single text.

Notice how we've placed the chunks array initialization outside the try block. This ensures that even if an error occurs during splitting or transcription, we'll still have access to any chunks that were created, allowing us to clean them up properly.

Cleanup Process Implementation

The cleanup process is handled by our cleanupTempFiles function, which uses Node.js file system operations:

TypeScript
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';

// Promisify fs functions for cleaner async/await usage
const fsUnlink = promisify(fs.unlink);
const fsRmdir = promisify(fs.rmdir);
const fsReaddir = promisify(fs.readdir);
const fsStat = promisify(fs.stat);

async function cleanupTempFiles(filePath: string): Promise<void> {
  /**Clean up temporary files and directories*/
  try {
    const stats = await fsStat(filePath);
    
    if (stats.isFile()) {
      await fsUnlink(filePath);
    } else if (stats.isDirectory()) {
      await cleanupDirectory(filePath);
    }
  } catch (e) {
    console.warn(`Warning: Could not clean up ${filePath}: ${e}`);
  }
}

async function cleanupDirectory(dirPath: string): Promise<void> {
  /**Recursively clean up a directory and its contents*/
  try {
    const files = await fsReaddir(dirPath);
    
    // Process all files in the directory
    for (const file of files) {
      const curPath = path.join(dirPath, file);
      const stats = await fsStat(curPath);
      
      if (stats.isFile()) {
        await fsUnlink(curPath);
      } else if (stats.isDirectory()) {
        // Recursively clean subdirectories
        await cleanupDirectory(curPath);
      }
    }
    
    // Remove the now-empty directory
    await fsRmdir(dirPath);
  } catch (e) {
    console.warn(`Warning: Error during directory cleanup: ${e}`);
  }
}

The cleanup is guaranteed to work because:

  1. We initialize the chunks array before any operations.
  2. We use a finally block, which executes regardless of success or failure.
  3. Each cleanup operation is wrapped in its own try-catch block.
  4. We handle both files and directories systematically using Node.js fs module.
  5. Any cleanup failures are logged but don't prevent the cleanup of other files.
  6. We use asynchronous file operations with proper error handling.

Our implementation leverages TypeScript's async/await pattern with promisified Node.js filesystem functions for better readability and error handling. We've separated the directory cleanup into its own function to handle the recursive deletion of directories and their contents.

Lesson Summary

In this lesson, we've learned how to implement a robust error handling and cleanup system for our media file transcription process. We've covered:

  • Building a comprehensive transcription function that gracefully handles errors.
  • Implementing systematic cleanup of temporary files and directories using Node.js fs module.
  • Using try-catch-finally blocks to ensure cleanup occurs regardless of execution outcome.
  • Proper initialization of variables to ensure accessibility in cleanup blocks.
  • Using async/await for asynchronous file operations with proper error handling.
  • Logging errors without disrupting the cleanup process.

These practices are fundamental for creating reliable applications that efficiently manage system resources and provide clear feedback when issues occur. As you move to the practice section, you'll have the opportunity to implement these concepts in real-world scenarios.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal