{"version":3,"file":"index.mjs","names":["logger: Logger","WIT_AI_API_KEYS: string[]","finalObject: WitAiResponse","currentObject: WitAiResponse","response: WitAiResponse","failures: FailedTranscription[]","transcripts: Segment[]","outputDir: string | undefined","fs"],"sources":["../src/utils/logger.ts","../src/apiKeys.ts","../src/errors.ts","../src/utils/mapping.ts","../src/utils/retry.ts","../src/wit.ai.ts","../src/transcriber.ts","../src/utils/constants.ts","../src/utils/validation.ts","../src/index.ts"],"sourcesContent":["import process from 'node:process';\nimport pino, { type Logger } from 'pino';\n\nconst logger: Logger = pino({\n    base: { hostname: undefined, pid: undefined }, // This will remove pid and hostname but keep time\n    level: process.env.LOG_LEVEL || 'info',\n});\n\nexport default logger;\n","import process from 'node:process';\n\nimport logger from './utils/logger.js';\n\n/**\n * Array storing Wit.ai API keys.\n * Keys can be provided either through code or through environment variables.\n * @internal\n */\nconst WIT_AI_API_KEYS: string[] = process.env.WIT_AI_API_KEYS ? process.env.WIT_AI_API_KEYS.split(' ') : [];\n\n/**\n * Tracks the current API key index for round-robin cycling.\n * @internal\n */\nlet currentKeyIndex = 0;\n\n/**\n * Returns the total number of available API keys.\n *\n * @returns {number} The count of available API keys\n * @internal\n */\nexport const getApiKeysCount = (): number => WIT_AI_API_KEYS.length;\n\n/**\n * Validates that at least one API key is available.\n * Throws an error if no keys are found.\n *\n * @throws {Error} If no API keys are available\n * @internal\n */\nconst validateApiKeys = (): void => {\n    if (getApiKeysCount() === 0) {\n        logger.error('At least one Wit.ai API key is required. Please set them in your environment variables.');\n        throw new Error('Empty wit.ai API keys');\n    }\n};\n\n/**\n * Returns the next API key in rotation, using a round-robin approach.\n * This distributes requests evenly across all available API keys.\n *\n * @returns {string} The next API key to use\n * @throws {Error} If no API keys are available\n * @internal\n */\nexport const getNextApiKey = (): string => {\n    validateApiKeys();\n\n    const key = WIT_AI_API_KEYS[currentKeyIndex];\n    currentKeyIndex = (currentKeyIndex + 1) % WIT_AI_API_KEYS.length;\n    return key;\n};\n\n/**\n * Sets the Wit.ai API keys to use for transcription.\n * This replaces any existing keys, including those from environment variables.\n *\n * @param {string[]} apiKeys - Array of Wit.ai API keys\n * @throws {Error} If the provided array is empty\n * @internal\n */\nexport const setApiKeys = (apiKeys: string[]) => {\n    WIT_AI_API_KEYS.length = 0;\n    WIT_AI_API_KEYS.push(...apiKeys);\n    currentKeyIndex = 0;\n\n    validateApiKeys();\n};\n","import type { AudioChunk } from 'ffmpeg-simplified';\n\nimport type { Segment } from './types.js';\n\nexport type FailedTranscription = {\n    chunk: AudioChunk;\n    error: unknown;\n    index: number;\n};\n\ntype TranscriptionErrorOptions = {\n    chunkFiles?: AudioChunk[];\n    failures: FailedTranscription[];\n    outputDir?: string;\n    transcripts: Segment[];\n};\n\nexport class TranscriptionError extends Error {\n    public readonly chunkFiles: AudioChunk[];\n\n    public readonly failures: FailedTranscription[];\n\n    public readonly outputDir?: string;\n\n    public readonly transcripts: Segment[];\n\n    constructor(message: string, { chunkFiles = [], failures, outputDir, transcripts }: TranscriptionErrorOptions) {\n        super(message);\n        this.name = 'TranscriptionError';\n        this.chunkFiles = chunkFiles;\n        this.failures = failures;\n        this.outputDir = outputDir;\n        this.transcripts = transcripts;\n    }\n\n    get failedChunks(): AudioChunk[] {\n        return this.failures.map((failure) => failure.chunk);\n    }\n\n    get hasFailures(): boolean {\n        return this.failures.length > 0;\n    }\n}\n","import type { TimeRange } from 'ffmpeg-simplified';\nimport type { Segment, WitAiResponse } from '@/types.js';\n\n/**\n * Maps the raw Wit.ai API response to a standardized Segment object.\n *\n * This function transforms the API response format to our internal representation,\n * converting millisecond timestamps to seconds and applying the correct offset\n * based on the chunk's position in the original audio file.\n *\n * @param {WitAiResponse} response - The raw response from Wit.ai API\n * @param {TimeRange} range - The time range of the chunk in the original audio\n * @returns {Segment} A standardized segment with adjusted timing information\n * @internal\n */\nexport const mapWitResponseToSegment = (response: WitAiResponse, { end, start }: TimeRange): Segment => {\n    // Convert API tokens to our internal Token format\n    const tokens = (response.tokens || [])\n        .filter((token) => token.token)\n        .map((token) => ({\n            ...(token.confidence && { confidence: token.confidence }),\n            end: token.end / 1000 + start, // Convert ms to seconds and add chunk offset\n            start: token.start / 1000 + start, // Convert ms to seconds and add chunk offset\n            text: token.token,\n        }));\n\n    // Build the segment object with proper timings\n    return {\n        ...(response.confidence && { confidence: response.confidence }),\n        end: tokens.at(-1)?.end ?? end, // Use last token end time or chunk end if no tokens\n        start: tokens[0]?.start ?? start, // Use first token start time or chunk start if no tokens\n        text: response.text!.trim(),\n        ...(tokens.length > 0 && { tokens: tokens }),\n    };\n};\n","import { setTimeout } from 'node:timers/promises';\n\nimport logger from './logger.js';\n\n/**\n * Default maximum number of retry attempts.\n * @internal\n */\nconst MAX_RETRIES = 5;\n\n/**\n * Default base delay in milliseconds for retry backoff.\n * @internal\n */\nconst BASE_DELAY_MS = 1000;\n\n/**\n * Executes a function with exponential backoff retry logic.\n *\n * If the function fails, this utility will retry the operation with\n * increasing delay intervals using an exponential backoff strategy.\n * The delay between attempts follows the formula: baseDelay * 2^(attempt-1)\n *\n * @template T The type of the return value from the function\n * @param {() => Promise<T>} fn - The async function to execute with retry logic\n * @param {number} [retries=MAX_RETRIES] - Maximum number of retry attempts\n * @param {number} [baseDelay=BASE_DELAY_MS] - Base delay in milliseconds\n * @returns {Promise<T>} The result of the successful function execution\n * @throws {Error} If all retry attempts fail, the last error is thrown\n * @internal\n */\nexport const exponentialBackoffRetry = async <T>(\n    fn: () => Promise<T>,\n    retries = MAX_RETRIES,\n    baseDelay = BASE_DELAY_MS,\n): Promise<T> => {\n    for (let attempt = 1; attempt <= retries; attempt++) {\n        try {\n            return await fn();\n        } catch (error) {\n            if (attempt < retries) {\n                const delay = baseDelay * 2 ** (attempt - 1);\n                logger.warn(`Attempt ${attempt} failed due to ${String(error)}. Retrying in ${delay}ms...`);\n                await setTimeout(delay); // Native setTimeout with promises\n            } else {\n                logger.error(`All ${retries} attempts failed.`);\n                throw error;\n            }\n        }\n    }\n    throw new Error('Exponential backoff failed unexpectedly'); // Fallback\n};\n","import fs from 'node:fs';\nimport https from 'node:https';\nimport { URL } from 'node:url';\n\nimport JSONStream from 'jsonstream-next';\n\nimport type { WitAiResponse } from './types.js';\n\ninterface SpeechToTextOptions {\n    apiKey: string;\n}\n\nconst MARKER_FINAL_TRANSCRIPTION = 'FINAL_TRANSCRIPTION';\n\nconst applyDictationChunk = (chunk: unknown, current: WitAiResponse, final: WitAiResponse): WitAiResponse => {\n    if (chunk === true) {\n        return {};\n    }\n\n    if (chunk === MARKER_FINAL_TRANSCRIPTION) {\n        if (current.tokens && current.tokens.length > 0) {\n            final.tokens?.push(...current.tokens);\n        }\n\n        final.text += ` ${current.text}`;\n        final.confidence = current.confidence;\n\n        return {};\n    }\n\n    if (typeof chunk === 'string') {\n        current.text = chunk;\n        return current;\n    }\n\n    if (chunk && typeof chunk === 'object') {\n        Object.assign(current, chunk);\n        return current;\n    }\n\n    return current;\n};\n\n/**\n * Builds the base HTTP headers required for Wit.ai speech endpoints.\n *\n * The content type is automatically adjusted based on the file extension so that\n * the Wit.ai API receives the correct media metadata for each request.\n *\n * @param {string} filePath - Absolute or relative path to the audio file being uploaded\n * @param {SpeechToTextOptions} options - Authentication options containing the Wit.ai API key\n * @returns {Record<string, string>} A header map containing authorization and content type information\n */\nconst getCommonHeaders = (filePath: string, options: SpeechToTextOptions): Record<string, string> => {\n    const result = {\n        Authorization: `Bearer ${options.apiKey}`,\n        'Content-Type': 'audio/wav',\n    };\n\n    if (filePath.endsWith('.mp3') || filePath.endsWith('.m4a') || filePath.endsWith('.mp4')) {\n        result['Content-Type'] = 'audio/mpeg3';\n    }\n\n    return result;\n};\n\n/**\n * Submits an audio file to the Wit.ai speech endpoint and returns the parsed transcript.\n *\n * This helper is best suited for smaller files where streaming chunked responses is unnecessary.\n *\n * @param {string} filePath - Path to the audio file that should be transcribed\n * @param {SpeechToTextOptions} options - Authentication options containing the Wit.ai API key\n * @returns {Promise<WitAiResponse>} The parsed transcription data returned from Wit.ai\n * @throws {Error} When the HTTP request fails or Wit.ai returns a non-success status code\n */\nexport const speechToText = async (filePath: string, options: SpeechToTextOptions): Promise<WitAiResponse> => {\n    const headers = {\n        ...getCommonHeaders(filePath, options),\n        Accept: 'application/vnd.wit.20200513+json',\n    };\n\n    return new Promise<WitAiResponse>((resolve, reject) => {\n        const stream = fs.createReadStream(filePath);\n        const url = new URL('https://api.wit.ai/speech');\n        const requestOptions = {\n            headers,\n            method: 'POST',\n        };\n\n        const req = https.request(url, requestOptions, (res) => {\n            let data = '';\n\n            res.on('data', (chunk) => {\n                data += chunk;\n            });\n\n            res.on('end', () => {\n                if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n                    const parsedData = JSON.parse(data);\n                    resolve({\n                        confidence: parsedData.speech?.confidence,\n                        text: parsedData.text,\n                        tokens: parsedData.speech?.tokens,\n                    });\n                } else {\n                    reject(new Error(`HTTP error! status: ${res.statusCode}`));\n                }\n            });\n        });\n\n        req.on('error', (err) => {\n            reject(err);\n        });\n\n        stream.pipe(req);\n    });\n};\n\n/**\n * Streams an audio file to the Wit.ai dictation endpoint and aggregates incremental responses.\n *\n * Unlike {@link speechToText}, this function processes streamed JSON markers from Wit.ai to\n * combine partial transcripts into a final result with token level detail.\n *\n * @param {string} filePath - Path to the audio file to be streamed\n * @param {SpeechToTextOptions} options - Authentication options containing the Wit.ai API key\n * @returns {Promise<WitAiResponse>} The combined final transcript with confidence metadata\n * @throws {Error} If the HTTP request fails, the server responds with an error, or the JSON stream errors\n */\nexport async function dictation(filePath: string, options: SpeechToTextOptions): Promise<WitAiResponse> {\n    const stream = fs.createReadStream(filePath);\n\n    const requestOptions = {\n        headers: {\n            ...getCommonHeaders(filePath, options),\n            Accept: 'application/vnd.wit.20200513+json',\n        },\n        hostname: 'api.wit.ai',\n        method: 'POST',\n        path: '/dictation?v=20240304',\n    };\n\n    return new Promise<WitAiResponse>((resolve, reject) => {\n        const req = https.request(requestOptions, (res) => {\n            if (res.statusCode !== 200) {\n                reject(new Error(`HTTP error! status: ${res.statusCode}`));\n                return;\n            }\n\n            const finalObject: WitAiResponse = { text: '', tokens: [] };\n            let currentObject: WitAiResponse = {};\n\n            const parser = JSONStream.parse('*');\n            res.pipe(parser);\n\n            parser.on('data', (chunk) => {\n                currentObject = applyDictationChunk(chunk, currentObject, finalObject);\n            });\n\n            parser.on('end', () => {\n                resolve(finalObject);\n            });\n\n            parser.on('error', reject);\n        });\n\n        req.on('error', reject);\n        stream.pipe(req);\n    });\n}\n","import type { AudioChunk } from 'ffmpeg-simplified';\n\nimport PQueue from 'p-queue';\n\nimport { getApiKeysCount, getNextApiKey } from './apiKeys.js';\nimport type { FailedTranscription, TranscriptionError } from './errors.js';\nimport type { Callbacks, Segment, WitAiResponse } from './types.js';\nimport logger from './utils/logger.js';\nimport { mapWitResponseToSegment } from './utils/mapping.js';\nimport { exponentialBackoffRetry } from './utils/retry.js';\nimport { dictation } from './wit.ai.js';\n\nconst maskText = (text: string) => {\n    return `${text.slice(0, 3)}*****${text[Math.floor(text.length / 2)]}*****${text.slice(-3)}`;\n};\n\n/**\n * Processes a single audio chunk and requests its transcription.\n *\n * @param {AudioChunk} chunk - The audio chunk to transcribe\n * @param {number} index - Index of the chunk in the original array\n * @param {Callbacks} [callbacks] - Callback functions for progress reporting\n * @param {number} [retries] - Number of retry attempts for failed requests\n * @returns {Promise<Segment|null>} - The transcribed segment or null if transcription failed/empty\n * @internal\n */\nconst requestNextTranscript = async (\n    chunk: AudioChunk,\n    index: number,\n    callbacks?: Callbacks,\n    retries?: number,\n): Promise<null | Segment> => {\n    const response: WitAiResponse = await exponentialBackoffRetry(() => {\n        const apiKey = getNextApiKey();\n        logger.info(`Calling dictation for ${chunk.filename} with key ${maskText(apiKey)}`);\n\n        return dictation(chunk.filename, { apiKey });\n    }, retries);\n\n    if (callbacks?.onTranscriptionProgress) {\n        callbacks.onTranscriptionProgress(index);\n    }\n\n    if (response.text?.trim()) {\n        return mapWitResponseToSegment(response, chunk.range);\n    }\n\n    return null;\n};\n\n/**\n * Transcribes audio chunks sequentially in a single thread.\n *\n * @param {AudioChunk[]} chunkFiles - Array of audio chunks to transcribe\n * @param {Callbacks} [callbacks] - Callback functions for progress reporting\n * @param {number} [retries] - Number of retry attempts for failed requests\n * @returns {Promise<Segment[]>} - Array of transcribed segments\n * @internal\n */\nconst transcribeAudioChunksInSingleThread = async (\n    chunkFiles: AudioChunk[],\n    callbacks?: Callbacks,\n    retries?: number,\n): Promise<TranscribeAudioChunksResult> => {\n    const failures: FailedTranscription[] = [];\n    const transcripts: Segment[] = [];\n\n    logger.debug(`transcribeAudioChunksInSingleThread for ${chunkFiles.length}`);\n\n    for (const [index, chunk] of chunkFiles.entries()) {\n        try {\n            const transcript = await requestNextTranscript(chunk, index, callbacks, retries);\n\n            if (transcript) {\n                transcripts.push(transcript);\n                logger.trace(`Transcript received for chunk: ${chunk.filename}`);\n            } else {\n                logger.warn(`Skipping empty transcript`);\n            }\n        } catch (error) {\n            logger.error(error, `Failed to transcribe chunk: ${chunk.filename}`);\n            failures.push({ chunk, error, index });\n\n            if (callbacks?.onTranscriptionProgress) {\n                callbacks.onTranscriptionProgress(index);\n            }\n        }\n    }\n\n    transcripts.sort((a: Segment, b: Segment) => a.start - b.start);\n\n    if (failures.length === 0 && callbacks?.onTranscriptionFinished) {\n        await callbacks.onTranscriptionFinished(transcripts);\n    }\n\n    return { failures, transcripts };\n};\n\n/**\n * Transcribes audio chunks concurrently with a specified concurrency limit.\n *\n * @param {AudioChunk[]} chunkFiles - Array of audio chunks to transcribe\n * @param {number} concurrency - Maximum number of concurrent transcription operations\n * @param {Callbacks} [callbacks] - Callback functions for progress reporting\n * @param {number} [retries] - Number of retry attempts for failed requests\n * @returns {Promise<Segment[]>} - Array of transcribed segments\n * @internal\n */\nconst transcribeAudioChunksWithConcurrency = async (\n    chunkFiles: AudioChunk[],\n    concurrency: number,\n    callbacks?: Callbacks,\n    retries?: number,\n): Promise<TranscribeAudioChunksResult> => {\n    logger.debug(`transcribeAudioChunksWithConcurrency ${concurrency}`);\n\n    const failures: FailedTranscription[] = [];\n    const transcripts: Segment[] = [];\n    const queue = new PQueue({ concurrency });\n\n    const processChunk = async (index: number, chunk: AudioChunk) => {\n        try {\n            const transcript = await requestNextTranscript(chunk, index, callbacks, retries);\n\n            if (transcript) {\n                transcripts.push(transcript);\n                logger.trace(`Transcript received for chunk: ${chunk.filename}`);\n            } else {\n                logger.warn(`Skipping empty transcript`);\n            }\n        } catch (error) {\n            logger.error(error, `Failed to transcribe chunk: ${chunk.filename}`);\n            failures.push({ chunk, error, index });\n\n            if (callbacks?.onTranscriptionProgress) {\n                callbacks.onTranscriptionProgress(index);\n            }\n        }\n    };\n\n    chunkFiles.forEach((chunk, index) => {\n        queue.add(() => processChunk(index, chunk));\n    });\n\n    await queue.onIdle(); // Wait until all tasks in the queue are processed\n\n    // Sort transcripts by their original order based on range to maintain chunk order\n    transcripts.sort((a: Segment, b: Segment) => a.start - b.start);\n\n    if (failures.length === 0 && callbacks?.onTranscriptionFinished) {\n        await callbacks.onTranscriptionFinished(transcripts);\n    }\n\n    return { failures, transcripts };\n};\n\n/**\n * Options for the transcribeAudioChunks function.\n */\ntype TranscribeAudioChunksOptions = {\n    /** Callback functions for progress reporting */\n    callbacks?: Callbacks;\n    /** Maximum number of concurrent transcription operations */\n    concurrency?: number;\n    /** Number of retry attempts for failed requests */\n    retries?: number;\n};\n\n/**\n * Transcribes an array of audio chunks, either sequentially or concurrently.\n *\n * Determines the optimal concurrency based on available API keys and\n * the specified concurrency limit, then dispatches to either the single-threaded\n * or concurrent implementation.\n *\n * @param {AudioChunk[]} chunkFiles - Array of audio chunks to transcribe\n * @param {TranscribeAudioChunksOptions} [options] - Configuration options\n * @returns {Promise<Segment[]>} - Array of transcribed segments\n * @internal\n */\nexport type TranscribeAudioChunksResult = {\n    failures: FailedTranscription[];\n    transcripts: Segment[];\n};\n\ntype ResumeOptions = Pick<TranscribeAudioChunksOptions, 'callbacks' | 'concurrency' | 'retries'>;\n\nexport const transcribeAudioChunks = async (\n    chunkFiles: AudioChunk[],\n    { callbacks, concurrency = 1, retries }: TranscribeAudioChunksOptions = {},\n): Promise<TranscribeAudioChunksResult> => {\n    const apiKeyCount = getApiKeysCount();\n    const maxConcurrency = concurrency && concurrency <= apiKeyCount ? concurrency : apiKeyCount;\n\n    if (callbacks?.onTranscriptionStarted) {\n        await callbacks?.onTranscriptionStarted(chunkFiles.length);\n    }\n\n    if (chunkFiles.length === 1 || concurrency === 1) {\n        return transcribeAudioChunksInSingleThread(chunkFiles, callbacks, retries);\n    }\n\n    return transcribeAudioChunksWithConcurrency(chunkFiles, maxConcurrency, callbacks, retries);\n};\n\nexport const resumeFailedTranscriptions = async (\n    error: Pick<TranscriptionError, 'failures' | 'transcripts'>,\n    options?: ResumeOptions,\n): Promise<TranscribeAudioChunksResult> => {\n    const failedChunks = error.failures\n        .slice()\n        .sort((a, b) => a.index - b.index)\n        .map((failure) => failure.chunk);\n\n    const { failures, transcripts } = await transcribeAudioChunks(failedChunks, options);\n\n    const combinedTranscripts = [...error.transcripts, ...transcripts];\n    combinedTranscripts.sort((a: Segment, b: Segment) => a.start - b.start);\n\n    return {\n        failures,\n        transcripts: combinedTranscripts,\n    };\n};\n","export const MAX_CHUNK_DURATION = 60 * 5; // wit.ai supports up to 5 mins\n\nexport const MIN_CHUNK_DURATION = 4; // minimum 4s duration\n\nexport const MIN_CONCURRENCY = 1;\n","import type { TranscribeOptions } from '@/types.js';\nimport { MAX_CHUNK_DURATION, MIN_CHUNK_DURATION, MIN_CONCURRENCY } from './constants.js';\n\nexport const validateTranscribeFileOptions = (options?: TranscribeOptions) => {\n    if (options?.splitOptions?.chunkDuration) {\n        const { chunkDuration } = options.splitOptions;\n\n        if (chunkDuration < MIN_CHUNK_DURATION) {\n            throw new Error(`chunkDuration=${chunkDuration} cannot be less than ${MIN_CHUNK_DURATION}s`);\n        }\n\n        if (chunkDuration > MAX_CHUNK_DURATION) {\n            throw new Error(`chunkDuration=${chunkDuration} cannot be greater than ${MAX_CHUNK_DURATION}s`);\n        }\n    }\n\n    if (options?.concurrency && options?.concurrency < MIN_CONCURRENCY) {\n        throw new Error(`concurrency=${options?.concurrency} must be a positive integer.`);\n    }\n};\n","import { promises as fs, type ReadStream } from 'node:fs';\nimport path from 'node:path';\nimport { formatMedia, splitFileOnSilences } from 'ffmpeg-simplified';\n\nimport { setApiKeys } from './apiKeys.js';\nimport { TranscriptionError } from './errors.js';\nimport { transcribeAudioChunks } from './transcriber.js';\nimport type { TranscribeOptions } from './types.js';\nimport logger from './utils/logger.js';\nimport { validateTranscribeFileOptions } from './utils/validation.js';\n\n/**\n * Initializes the tafrigh library with the provided Wit.ai API keys.\n *\n * @param {Object} options - Configuration options for initialization\n * @param {string[]} options.apiKeys - Array of Wit.ai API keys to use for transcription\n * @example\n * import { init } from 'tafrigh';\n * init({ apiKeys: ['your-wit-ai-key'] });\n */\nexport const init = (options: { apiKeys: string[] }) => {\n    setApiKeys(options.apiKeys);\n};\n\n/**\n * Transcribes audio content and returns an array of transcript segments.\n *\n * This function takes an audio file (or stream) and returns a structured transcript with\n * timestamps. It handles preprocessing the audio, splitting it into chunks, and\n * transcribing each chunk using Wit.ai's API.\n *\n * @param {string | ReadStream} content - Path to audio file, URL, or readable stream\n * @param {TranscribeOptions} [options] - Configuration options for transcription\n * @returns {Promise<Array>} - Promise resolving to an array of transcript segments\n * @throws {Error} - If transcription fails or if options validation fails\n * @example\n * import { transcribe } from 'tafrigh';\n *\n * const transcript = await transcribe('path/to/audio.mp3', {\n *   concurrency: 2,\n *   splitOptions: {\n *     chunkDuration: 60,\n *     silenceDetection: { silenceThreshold: -30 }\n *   }\n * });\n *\n * console.log(transcript);\n * // [{ text: \"Hello world\", start: 0, end: 2.5 }, ...]\n */\nexport const transcribe = async (content: ReadStream | string, options?: TranscribeOptions) => {\n    logger.info(options, `transcribe ${content} (${typeof content})`);\n\n    validateTranscribeFileOptions(options);\n\n    const preventCleanup = options?.preventCleanup ?? false;\n    let outputDir: string | undefined;\n    let shouldCleanup = !preventCleanup;\n\n    try {\n        outputDir = await fs.mkdtemp('tafrigh');\n        logger.debug(`Using ${outputDir}`);\n\n        const filePath = await formatMedia(\n            content,\n            path.format({\n                dir: outputDir,\n                ext: '.mp3',\n                name: Date.now().toString(),\n            }),\n            options?.preprocessOptions,\n            options?.callbacks,\n        );\n        const chunkFiles = await splitFileOnSilences(filePath, outputDir, options?.splitOptions, options?.callbacks);\n\n        logger.debug(chunkFiles, `Generated chunks`);\n\n        if (chunkFiles.length === 0) {\n            return [];\n        }\n\n        const { failures, transcripts } = await transcribeAudioChunks(chunkFiles, {\n            callbacks: options?.callbacks,\n            concurrency: options?.concurrency,\n            retries: options?.retries,\n        });\n\n        if (failures.length > 0) {\n            shouldCleanup = false;\n            throw new TranscriptionError(`Failed to transcribe ${failures.length} chunk(s)`, {\n                chunkFiles,\n                failures,\n                outputDir,\n                transcripts,\n            });\n        }\n\n        return transcripts;\n    } finally {\n        if (shouldCleanup && outputDir) {\n            logger.info(`Cleaning up ${outputDir}`);\n            await fs.rm(outputDir, { force: true, recursive: true });\n        }\n    }\n};\n\nexport * from './errors.js';\nexport { resumeFailedTranscriptions } from './transcriber.js';\nexport * from './types.js';\nexport * from './utils/constants.js';\n"],"mappings":"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"}