From 957df0c1607250142a0e07444cd4c5b8277b9bc4 Mon Sep 17 00:00:00 2001 From: AssemblyAI Date: Tue, 24 Oct 2023 12:12:17 -0400 Subject: [PATCH] Project import generated by Copybara. GitOrigin-RevId: 7cc3eb832cc3ddb3235562a6aa8790d171ef53d1 --- .eslintrc.json | 2 +- README.md | 4 ++-- jest.config.rollup.ts | 1 + package.json | 2 +- rollup.config.js | 26 +++++++++++++------------- scripts/kitchensink.ts | 18 ++++++++++++++++-- src/index.ts | 7 +------ src/services/files/index.ts | 28 ++++++++++++++++++---------- src/services/transcripts/index.ts | 24 ++++++++++++++++-------- src/types/files/index.ts | 10 ++++++++++ src/types/index.ts | 1 + src/types/openapi.generated.ts | 22 ++++++++++++++++++++++ src/utils/axios.ts | 2 +- tests/file.test.ts | 25 +++++++++++++++++-------- tests/lemur.test.ts | 2 +- tests/realtime.test.ts | 2 +- tests/transcript.test.ts | 8 ++++++-- tsconfig.json | 2 -- 18 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 src/types/files/index.ts diff --git a/.eslintrc.json b/.eslintrc.json index 15217c6..2a9c116 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,5 +11,5 @@ ], "rules": { }, - "ignorePatterns": ["test/**/*", "dist/**/*", "node_modules/**/*"] + "ignorePatterns": ["test/**/*", "dist/**/*", "node_modules/**/*", "scripts/**/*"] } diff --git a/README.md b/README.md index 3c3a5f3..b4a644a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ bun add assemblyai Import the AssemblyAI package and create an AssemblyAI object with your API key: ```javascript -import AssemblyAI from "assemblyai"; +import { AssemblyAI } from "assemblyai"; const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY, @@ -94,7 +94,7 @@ const transcript = await client.transcripts.get(transcript.id) ## List transcripts -This will return a paged list of transcripts that you have transcript. +This will return a page of transcripts that you have transcript. ```javascript const page = await client.transcripts.list() diff --git a/jest.config.rollup.ts b/jest.config.rollup.ts index c5b7901..3f98e37 100644 --- a/jest.config.rollup.ts +++ b/jest.config.rollup.ts @@ -7,6 +7,7 @@ const jestConfig: JestConfigWithTsJest = { moduleNameMapper: { "^@/(.*)$": "/src/$1", }, + modulePathIgnorePatterns: ['/dist'], }; process.env.TESTDATA_DIR = "tests/static"; diff --git a/package.json b/package.json index 3a5f7d7..8fdd05e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assemblyai", - "version": "2.0.2", + "version": "3.0.0", "description": "The AssemblyAI Node.js 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.", "main": "dist/index.js", "module": "dist/index.esm.js", diff --git a/rollup.config.js b/rollup.config.js index c03b6b9..4f1f801 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,20 +1,20 @@ const pkg = require('./package.json') const ts = require('rollup-plugin-typescript2') -const plugins = [ - ts({ - tsconfigOverride: { exclude: ['**/*.test.ts'] }, - }), -] - module.exports = [ { - input: 'src/index.ts', - output: [ - { file: pkg.main, format: 'cjs' }, - { file: pkg.module, format: 'es' } + plugins: [ + ts({ + tsconfigOverride: { exclude: ['**/*.test.ts'] }, + }) ], - plugins, - external: ['axios', 'fs/promises', 'stream', 'ws'] - }, + external: ['axios', 'fs', 'stream', 'ws'], + input: 'src/index.ts', + output: + [{ + file: pkg.main, format: 'cjs', exports: 'named' + }, { + file: pkg.module, format: 'es', exports: 'named' + }] + } ] diff --git a/scripts/kitchensink.ts b/scripts/kitchensink.ts index b96d787..c881434 100644 --- a/scripts/kitchensink.ts +++ b/scripts/kitchensink.ts @@ -1,7 +1,16 @@ import { createReadStream } from 'fs' import 'dotenv/config' -import AssemblyAI, { Transcript, CreateTranscriptParameters } from '../src/index'; -import { FinalTranscript, LemurBaseResponse, PartialTranscript, RealtimeTranscript } from '../src/types' +import +{ + AssemblyAI, + Transcript, + CreateTranscriptParameters, + FinalTranscript, + LemurBaseResponse, + PartialTranscript, + RealtimeTranscript +} from '../src'; + const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY || '', @@ -64,6 +73,11 @@ const createTranscriptParams: CreateTranscriptParameters = { speech_threshold: 0.5, }; +(async function uploadFileFromPath() { + const uploadUrl = await client.files.upload('./tests/static/gore.wav'); + console.log('Upload URL:', uploadUrl); +})(); + (async function createStandardTranscript() { const transcript = await client.transcripts.create(createTranscriptParams); console.log(transcript); diff --git a/src/index.ts b/src/index.ts index a5668ca..5aba930 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,2 @@ -import * as services from "./services"; -import { AssemblyAI } from "./services"; -export * from "./services"; export type * from "./types"; -export default AssemblyAI; -class AssemblyAIExports extends AssemblyAI {} -module.exports = Object.assign(AssemblyAIExports, services); +export * from "./services"; diff --git a/src/services/files/index.ts b/src/services/files/index.ts index 260e384..879c9f3 100644 --- a/src/services/files/index.ts +++ b/src/services/files/index.ts @@ -1,21 +1,29 @@ -import { readFile } from "fs/promises"; +// import the fs module instead if specific named exports +// to keep the assemblyai module more compatible. Some fs polyfills don't include `createReadStream`. +import fs from "fs"; import { BaseService } from "@/services/base"; -import { UploadedFile } from "@/types"; +import { UploadedFile, FileUploadParameters, FileUploadData } from "@/types"; export class FileService extends BaseService { /** * Upload a local file to AssemblyAI. - * @param path The local file to upload. + * @param input The local file path to upload, or a stream or buffer of the file to upload. * @return A promise that resolves to the uploaded file URL. */ - async upload(path: string): Promise { - const file = await readFile(path); + async upload(input: FileUploadParameters): Promise { + let fileData: FileUploadData; + if (typeof input === "string") fileData = fs.createReadStream(input); + else fileData = input; - const { data } = await this.client.post("/v2/upload", file, { - headers: { - "Content-Type": "application/octet-stream", - }, - }); + const { data } = await this.client.post( + "/v2/upload", + fileData, + { + headers: { + "Content-Type": "application/octet-stream", + }, + } + ); return data.upload_url; } diff --git a/src/services/transcripts/index.ts b/src/services/transcripts/index.ts index a7c4ef0..243e236 100644 --- a/src/services/transcripts/index.ts +++ b/src/services/transcripts/index.ts @@ -12,6 +12,7 @@ import { Retrieveable, SubtitleFormat, RedactedAudioResponse, + TranscriptListParameters, WordSearchResponse, } from "@/types"; import { AxiosInstance } from "axios"; @@ -87,16 +88,23 @@ export class TranscriptService return res.data; } - // TODO: add options overload to support list querystring parameters /** - * Retrieves a paged list of transcript listings. - * @param nextUrl The URL to retrieve the transcript list from. If not provided, the first page will be retrieved. - * @returns + * Retrieves a page of transcript listings. + * @param parameters The parameters to filter the transcript list by, or the URL to retrieve the transcript list from. */ - async list(nextUrl?: string | null): Promise { - const { data } = await this.client.get( - nextUrl ?? "/v2/transcript" - ); + async list( + parameters?: TranscriptListParameters | string + ): Promise { + let url = "/v2/transcript"; + let query: TranscriptListParameters | undefined; + if (typeof parameters === "string") { + url = parameters; + } else if (parameters) { + query = parameters; + } + const { data } = await this.client.get(url, { + params: query, + }); for (const transcriptListItem of data.transcripts) { transcriptListItem.created = new Date(transcriptListItem.created); if (transcriptListItem.completed) { diff --git a/src/types/files/index.ts b/src/types/files/index.ts new file mode 100644 index 0000000..bf3d4c3 --- /dev/null +++ b/src/types/files/index.ts @@ -0,0 +1,10 @@ +type FileUploadParameters = string | FileUploadData; +type FileUploadData = + | NodeJS.ReadableStream + | ReadableStream + | Buffer + | ArrayBufferView + | ArrayBufferLike + | Uint8Array; + +export type { FileUploadParameters, FileUploadData }; diff --git a/src/types/index.ts b/src/types/index.ts index 7aca1e9..855dc39 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,4 @@ +export type * from "./files"; export type * from "./transcripts"; export type * from "./realtime"; export type * from "./services"; diff --git a/src/types/openapi.generated.ts b/src/types/openapi.generated.ts index 3814cdb..3142dfd 100644 --- a/src/types/openapi.generated.ts +++ b/src/types/openapi.generated.ts @@ -762,6 +762,28 @@ export type TranscriptListItem = { status: TranscriptStatus; }; +export type TranscriptListParameters = { + /** @description Get transcripts that were created after this transcript ID */ + after_id?: string; + /** @description Get transcripts that were created before this transcript ID */ + before_id?: string; + /** + * Format: date + * @description Only get transcripts created on this date + */ + created_on?: string; + /** + * Format: int64 + * @description Maximum amount of transcripts to retrieve + * @default 10 + */ + limit?: number; + /** @description Filter by transcript status */ + status?: TranscriptStatus; + /** @description Only get throttled transcripts, overrides the status filter */ + throttled_only?: boolean; +}; + export type TranscriptParagraph = { /** Format: double */ confidence: number; diff --git a/src/utils/axios.ts b/src/utils/axios.ts index efb7a70..ddda0b0 100644 --- a/src/utils/axios.ts +++ b/src/utils/axios.ts @@ -1,5 +1,5 @@ import axios, { isAxiosError } from "axios"; -import { BaseServiceParams } from "../."; +import { BaseServiceParams } from "@/types"; export function createAxiosClient(params: BaseServiceParams) { const client = axios.create({ diff --git a/tests/file.test.ts b/tests/file.test.ts index 4417bd4..29a3206 100644 --- a/tests/file.test.ts +++ b/tests/file.test.ts @@ -1,4 +1,6 @@ -import AssemblyAI from '../src' +import { AssemblyAI } from '../src' +import { createReadStream } from "fs"; +import { readFile } from "fs/promises"; import path from "path" const testDir = process.env["TESTDATA_DIR"] ?? '.' @@ -8,16 +10,23 @@ const assembly = new AssemblyAI({ }) describe('files', () => { - it('should upload a file', async () => { + it('should upload a file from path', async () => { const uploadUrl = await assembly.files.upload(path.join(testDir, 'gore.wav')) expect(uploadUrl).toBeTruthy() - }, 10_000) + }) + + it('should upload a file from stream', async () => { + const stream = createReadStream(path.join(testDir, 'gore.wav')) + const uploadUrl = await assembly.files.upload(stream) - it('should not find file', async () => { - const promise = assembly.files.upload(path.join(testDir, 'bad-path.wav')) - await expect(promise).rejects.toThrowError( - "ENOENT: no such file or directory, open '" + testDir + "/bad-path.wav'", - ) + expect(uploadUrl).toBeTruthy() + }) + + it('should upload a file from buffer', async () => { + const data = await readFile(path.join(testDir, 'gore.wav')) + const uploadUrl = await assembly.files.upload(data) + + expect(uploadUrl).toBeTruthy() }) }) diff --git a/tests/lemur.test.ts b/tests/lemur.test.ts index b70566b..23bf9db 100644 --- a/tests/lemur.test.ts +++ b/tests/lemur.test.ts @@ -1,5 +1,5 @@ import { knownTranscriptIds, knownLemurRequestId, purgeRequestId } from './__mocks__/api' -import AssemblyAI from '../src' +import { AssemblyAI } from '../src' const assembly = new AssemblyAI({ apiKey: '', diff --git a/tests/realtime.test.ts b/tests/realtime.test.ts index e470902..4e24ec8 100644 --- a/tests/realtime.test.ts +++ b/tests/realtime.test.ts @@ -1,5 +1,5 @@ import WS from "jest-websocket-mock"; -import AssemblyAI, { RealtimeService } from '../src' +import { AssemblyAI, RealtimeService } from '../src' import { RealtimeError, RealtimeErrorType, diff --git a/tests/transcript.test.ts b/tests/transcript.test.ts index f65feb2..d522a26 100644 --- a/tests/transcript.test.ts +++ b/tests/transcript.test.ts @@ -1,6 +1,6 @@ import { knownTranscriptIds } from './__mocks__/api' import axios, { withData } from './__mocks__/axios' -import AssemblyAI from '../src' +import { AssemblyAI } from '../src' import path from "path" const testDir = process.env["TESTDATA_DIR"] ?? '.' @@ -15,6 +15,10 @@ const remoteAudioURL = const badRemoteAudioURL = 'https://storage.googleapis.com/aai-web-samples/does-not-exist.m4a' +beforeEach(() => { + jest.clearAllMocks(); +}); + describe('core', () => { it('should create the transcript object with a remote url', async () => { const transcript = await assembly.transcripts.create( @@ -67,7 +71,7 @@ describe('core', () => { expect(transcript.status).toBe('completed') }, 6000) - it('should list the transcript objects', async () => { + it('should retrieve a page of transcripts', async () => { const page = await assembly.transcripts.list() expect(page.transcripts).toBeInstanceOf(Array) expect(page.page_details).not.toBeNull() diff --git a/tsconfig.json b/tsconfig.json index 485ba67..58f09cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "outDir": "./dist", "rootDir": "./src", - "module": "esnext", "moduleResolution": "node", "target": "es6", @@ -11,7 +10,6 @@ "removeComments": false, "strict": true, "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, "paths": { "@/*": ["./src/*"]