assemblyai
Version:
The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription, as well as the latest LeMUR models.
286 lines (265 loc) • 9.56 kB
text/typescript
import { BaseService } from "../base";
import {
ParagraphsResponse,
SentencesResponse,
Transcript,
TranscriptList,
TranscriptParams,
CreateTranscriptOptions,
SubtitleFormat,
RedactedAudioResponse,
ListTranscriptParams,
WordSearchResponse,
BaseServiceParams,
PollingOptions,
TranscribeParams,
TranscribeOptions,
SubmitParams,
RedactedAudioFile,
} from "../..";
import { FileService } from "../files";
import { getPath } from "../../utils/path";
export class TranscriptService extends BaseService {
constructor(
params: BaseServiceParams,
private files: FileService,
) {
super(params);
}
/**
* Transcribe an audio file. This will create a transcript and wait until the transcript status is "completed" or "error".
* @param params - The parameters to transcribe an audio file.
* @param options - The options to transcribe an audio file.
* @returns A promise that resolves to the transcript. The transcript status is "completed" or "error".
*/
async transcribe(
params: TranscribeParams,
options?: TranscribeOptions,
): Promise<Transcript> {
const transcript = await this.submit(params);
return await this.waitUntilReady(transcript.id, options);
}
/**
* Submits a transcription job for an audio file. This will not wait until the transcript status is "completed" or "error".
* @param params - The parameters to start the transcription of an audio file.
* @returns A promise that resolves to the queued transcript.
*/
async submit(params: SubmitParams): Promise<Transcript> {
let audioUrl;
let transcriptParams: TranscriptParams | undefined = undefined;
if ("audio" in params) {
const { audio, ...audioTranscriptParams } = params;
if (typeof audio === "string") {
const path = getPath(audio);
if (path !== null) {
// audio is local path, upload local file
audioUrl = await this.files.upload(path);
} else {
if (audio.startsWith("data:")) {
audioUrl = await this.files.upload(audio);
} else {
// audio is not a local path, and not a data-URI, assume it's a normal URL
audioUrl = audio;
}
}
} else {
// audio is of uploadable type
audioUrl = await this.files.upload(audio);
}
transcriptParams = { ...audioTranscriptParams, audio_url: audioUrl };
} else {
transcriptParams = params;
}
const data = await this.fetchJson<Transcript>("/v2/transcript", {
method: "POST",
body: JSON.stringify(transcriptParams),
});
return data;
}
/**
* Create a transcript.
* @param params - The parameters to create a transcript.
* @param options - The options used for creating the new transcript.
* @returns A promise that resolves to the transcript.
* @deprecated Use `transcribe` instead to transcribe a audio file that includes polling, or `submit` to transcribe a audio file without polling.
*/
async create(
params: TranscriptParams,
options?: CreateTranscriptOptions,
): Promise<Transcript> {
const path = getPath(params.audio_url);
if (path !== null) {
const uploadUrl = await this.files.upload(path);
params.audio_url = uploadUrl;
}
const data = await this.fetchJson<Transcript>("/v2/transcript", {
method: "POST",
body: JSON.stringify(params),
});
if (options?.poll ?? true) {
return await this.waitUntilReady(data.id, options);
}
return data;
}
/**
* Wait until the transcript ready, either the status is "completed" or "error".
* @param transcriptId - The ID of the transcript.
* @param options - The options to wait until the transcript is ready.
* @returns A promise that resolves to the transcript. The transcript status is "completed" or "error".
*/
async waitUntilReady(
transcriptId: string,
options?: PollingOptions,
): Promise<Transcript> {
const pollingInterval = options?.pollingInterval ?? 3_000;
const pollingTimeout = options?.pollingTimeout ?? -1;
const startTime = Date.now();
// eslint-disable-next-line no-constant-condition
while (true) {
const transcript = await this.get(transcriptId);
if (transcript.status === "completed" || transcript.status === "error") {
return transcript;
} else if (
pollingTimeout > 0 &&
Date.now() - startTime > pollingTimeout
) {
throw new Error("Polling timeout");
} else {
await new Promise((resolve) => setTimeout(resolve, pollingInterval));
}
}
}
/**
* Retrieve a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the transcript.
*/
get(id: string): Promise<Transcript> {
return this.fetchJson<Transcript>(`/v2/transcript/${id}`);
}
/**
* Retrieves a page of transcript listings.
* @param params - The parameters to filter the transcript list by, or the URL to retrieve the transcript list from.
*/
async list(params?: ListTranscriptParams | string): Promise<TranscriptList> {
let url = "/v2/transcript";
if (typeof params === "string") {
url = params;
} else if (params) {
url = `${url}?${new URLSearchParams(
Object.keys(params).map((key) => [
key,
params[key as keyof ListTranscriptParams]?.toString() || "",
]),
)}`;
}
const data = await this.fetchJson<TranscriptList>(url);
for (const transcriptListItem of data.transcripts) {
transcriptListItem.created = new Date(transcriptListItem.created);
if (transcriptListItem.completed) {
transcriptListItem.completed = new Date(transcriptListItem.completed);
}
}
return data;
}
/**
* Delete a transcript
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the transcript.
*/
delete(id: string): Promise<Transcript> {
return this.fetchJson(`/v2/transcript/${id}`, { method: "DELETE" });
}
/**
* Search through the transcript for a specific set of keywords.
* You can search for individual words, numbers, or phrases containing up to five words or numbers.
* @param id - The identifier of the transcript.
* @param words - Keywords to search for.
* @returns A promise that resolves to the sentences.
*/
wordSearch(id: string, words: string[]): Promise<WordSearchResponse> {
const params = new URLSearchParams({ words: words.join(",") });
return this.fetchJson<WordSearchResponse>(
`/v2/transcript/${id}/word-search?${params.toString()}`,
);
}
/**
* Retrieve all sentences of a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the sentences.
*/
sentences(id: string): Promise<SentencesResponse> {
return this.fetchJson<SentencesResponse>(`/v2/transcript/${id}/sentences`);
}
/**
* Retrieve all paragraphs of a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the paragraphs.
*/
paragraphs(id: string): Promise<ParagraphsResponse> {
return this.fetchJson<ParagraphsResponse>(
`/v2/transcript/${id}/paragraphs`,
);
}
/**
* Retrieve subtitles of a transcript.
* @param id - The identifier of the transcript.
* @param format - The format of the subtitles.
* @param chars_per_caption - The maximum number of characters per caption.
* @returns A promise that resolves to the subtitles text.
*/
async subtitles(
id: string,
format: SubtitleFormat = "srt",
chars_per_caption?: number,
): Promise<string> {
let url = `/v2/transcript/${id}/${format}`;
if (chars_per_caption) {
const params = new URLSearchParams();
params.set("chars_per_caption", chars_per_caption.toString());
url += `?${params.toString()}`;
}
const response = await this.fetch(url);
return await response.text();
}
/**
* Retrieve the redacted audio URL of a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the details of the redacted audio.
* @deprecated Use `redactedAudio` instead.
*/
redactions(id: string): Promise<RedactedAudioResponse> {
return this.redactedAudio(id);
}
/**
* Retrieve the redacted audio URL of a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the details of the redacted audio.
*/
redactedAudio(id: string): Promise<RedactedAudioResponse> {
return this.fetchJson<RedactedAudioResponse>(
`/v2/transcript/${id}/redacted-audio`,
);
}
/**
* Retrieve the redacted audio file of a transcript.
* @param id - The identifier of the transcript.
* @returns A promise that resolves to the fetch HTTP response of the redacted audio file.
*/
async redactedAudioFile(id: string): Promise<RedactedAudioFile> {
const { redacted_audio_url, status } = await this.redactedAudio(id);
if (status !== "redacted_audio_ready") {
throw new Error(`Redacted audio status is ${status}`);
}
const response = await fetch(redacted_audio_url);
if (!response.ok) {
throw new Error(`Failed to fetch redacted audio: ${response.statusText}`);
}
return {
arrayBuffer: response.arrayBuffer.bind(response),
blob: response.blob.bind(response),
body: response.body,
bodyUsed: response.bodyUsed,
};
}
}