diff --git a/CHANGELOG.md b/CHANGELOG.md index db747b4..59e3f9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [0.2.3] (2026-03-18) + +### Changed + +- Synced channel config with Python SDK: removed `transcript` field, added `isPrimary` to `RecordingChannelConfig`, `ChannelConfig`, and `Channel` +- Removed `record` from `Channel.toDict()` (always sent as `true` server-side) +- Updated capture binary to v0.2.10 + +### Fixed + +- Clip literal types for `contentType` parameter +- Shot URLs from search results +- Caption style warning on missing fields +- `generateTranscript` language code parameter + +--- + ## [0.2.2] (2026-03-10) ### Added diff --git a/package-lock.json b/package-lock.json index e5d9457..a35a17d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "videodb", - "version": "0.2.0", + "version": "0.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "videodb", - "version": "0.2.0", + "version": "0.2.3", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index c4f1392..de29d26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "videodb", - "version": "0.2.2", + "version": "0.2.3", "description": "A NodeJS wrapper for VideoDB's API written in TypeScript", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -40,11 +40,11 @@ }, "binaryConfig": { "baseUrl": "https://artifacts.videodb.io/capture", - "version": "0.2.8", + "version": "0.2.10", "checksums": { - "darwin-arm64": "4aab67e524c2541bebbae24b8dd845da5d7f74fba006ce860a4914844e185c5d", - "darwin-x64": "ac67dc1a9edd2094d36e6961ed1dabab3e8b3e3e2a61655a49195b7e518901ca", - "win32-x64": "f19110d9b632c0149088abc09f4c86e0f43f64ce4b52a73bca6eb20789e156d0" + "darwin-arm64": "fc4be7de94153aa9f492b014db7b4f7378e45c3c6f1b5f3f838c2c007bde832f", + "darwin-x64": "bdfc3aa33a961ff532a99639ea95c181d51baee74a1eda555598ce45c30908ac", + "win32-x64": "3f9b9a355edc54dd06cef051b0ec7ed55df6beef6eb9e299fa6ba5f02ba3a50a" } }, "repository": { diff --git a/src/capture/captureClient.ts b/src/capture/captureClient.ts index e8160a5..7f72675 100644 --- a/src/capture/captureClient.ts +++ b/src/capture/captureClient.ts @@ -39,8 +39,8 @@ import { * await client.startSession({ * sessionId: 'ss-xxx', // Required: from CaptureSession.id * channels: [ - * { channelId: 'mic:default', type: 'audio', record: true, transcript: true }, - * { channelId: 'display:1', type: 'video', record: true }, + * { channelId: 'mic:default', type: 'audio', store: true }, + * { channelId: 'display:1', type: 'video', store: true }, * ], * }); * @@ -236,16 +236,16 @@ export class CaptureClient extends EventEmitter implements ChannelClient { channel_id: (channel as { channel_id?: string }).channel_id ?? channel.channelId, type: channel.type, - record: channel.record ?? true, - transcript: channel.transcript ?? false, store: channel.store ?? true, + is_primary: channel.isPrimary ?? false, })); if (channels.some(ch => !ch.channel_id)) { throw new Error('channels must include channelId for each channel'); } - const primaryVideo = channels.find(ch => ch.type === 'video'); + const primaryVideo = channels.find(ch => ch.is_primary && ch.type === 'video') + || channels.find(ch => ch.type === 'video'); await this.binaryManager.sendCommand('startRecording', { uploadToken: this.sessionToken, diff --git a/src/capture/channel.ts b/src/capture/channel.ts index 88ffa70..d97831c 100644 --- a/src/capture/channel.ts +++ b/src/capture/channel.ts @@ -12,6 +12,7 @@ export class Channel { public readonly name: string; public readonly type: 'audio' | 'video'; public store: boolean = false; + public isPrimary: boolean = false; /** Reference to the CaptureClient for pause/resume operations */ #client: ChannelClient | null = null; @@ -85,15 +86,15 @@ export class Channel { channel_id: string; type: string; name: string; - record: boolean; store: boolean; + is_primary: boolean; } { return { channel_id: this.id, type: this.type, name: this.name, - record: true, store: this.store, + is_primary: this.isPrimary, }; } @@ -150,19 +151,23 @@ export class Channels { public displays: ChannelList; /** System audio channels */ public systemAudio: ChannelList; + /** Camera channels (not yet supported for capture) */ + public cameras: ChannelList; constructor( mics: AudioChannel[] = [], displays: VideoChannel[] = [], - systemAudio: AudioChannel[] = [] + systemAudio: AudioChannel[] = [], + cameras: VideoChannel[] = [] ) { this.mics = new ChannelList(...mics); this.displays = new ChannelList(...displays); this.systemAudio = new ChannelList(...systemAudio); + this.cameras = new ChannelList(...cameras); } /** - * Return a flat list of all channels + * Return a flat list of all capturable channels (excludes cameras) */ public all(): Channel[] { return [ @@ -173,7 +178,7 @@ export class Channels { } toString(): string { - return `Channels(mics=${this.mics.length}, displays=${this.displays.length}, systemAudio=${this.systemAudio.length})`; + return `Channels(mics=${this.mics.length}, displays=${this.displays.length}, systemAudio=${this.systemAudio.length}, cameras=${this.cameras.length})`; } } @@ -209,6 +214,7 @@ export function groupChannels( const mics: AudioChannel[] = []; const displays: VideoChannel[] = []; const systemAudio: AudioChannel[] = []; + const cameras: VideoChannel[] = []; for (const ch of channels) { const channelId = ch.channelId; @@ -222,6 +228,8 @@ export function groupChannels( displays.push(new VideoChannel(ch, client)); } else if (channelId.startsWith('system_audio:')) { systemAudio.push(new AudioChannel(ch, client)); + } else if (channelId.startsWith('camera:')) { + cameras.push(new VideoChannel(ch, client)); } else if (ch.type === 'audio') { // Fallback for unknown audio channels mics.push(new AudioChannel(ch, client)); @@ -231,5 +239,5 @@ export function groupChannels( } } - return new Channels(mics, displays, systemAudio); + return new Channels(mics, displays, systemAudio, cameras); } diff --git a/src/capture/index.ts b/src/capture/index.ts index 2010c5f..0b4a93f 100644 --- a/src/capture/index.ts +++ b/src/capture/index.ts @@ -27,8 +27,8 @@ * await client.startSession({ * sessionId: 'ss-xxx', // Required: from CaptureSession.id * channels: [ - * { channelId: 'mic:default', type: 'audio', record: true, transcript: true }, - * { channelId: 'display:1', type: 'video', record: true }, + * { channelId: 'mic:default', type: 'audio', store: true }, + * { channelId: 'display:1', type: 'video', store: true }, * ], * }); * diff --git a/src/capture/installer.ts b/src/capture/installer.ts index 2169321..367d406 100644 --- a/src/capture/installer.ts +++ b/src/capture/installer.ts @@ -37,11 +37,11 @@ export class RecorderInstaller { // Default binary config - can be overridden or loaded from package.json this.binaryConfig = binaryConfig || { baseUrl: 'https://artifacts.videodb.io/capture', - version: '0.2.8', + version: '0.2.10', checksums: { - 'darwin-x64': 'ac67dc1a9edd2094d36e6961ed1dabab3e8b3e3e2a61655a49195b7e518901ca', - 'darwin-arm64': '4aab67e524c2541bebbae24b8dd845da5d7f74fba006ce860a4914844e185c5d', - 'win32-x64': 'f19110d9b632c0149088abc09f4c86e0f43f64ce4b52a73bca6eb20789e156d0', + 'darwin-x64': 'bdfc3aa33a961ff532a99639ea95c181d51baee74a1eda555598ce45c30908ac', + 'darwin-arm64': 'fc4be7de94153aa9f492b014db7b4f7378e45c3c6f1b5f3f838c2c007bde832f', + 'win32-x64': '3f9b9a355edc54dd06cef051b0ec7ed55df6beef6eb9e299fa6ba5f02ba3a50a', }, }; diff --git a/src/capture/types.ts b/src/capture/types.ts index fc62dd9..8a2c458 100644 --- a/src/capture/types.ts +++ b/src/capture/types.ts @@ -57,12 +57,10 @@ export interface RecordingChannelConfig { channelId: string; /** Channel type */ type: 'audio' | 'video'; - /** Whether to record this channel */ - record?: boolean; - /** Whether to enable transcription */ - transcript?: boolean; /** Whether to store the recording */ store?: boolean; + /** Whether this is the primary video channel */ + isPrimary?: boolean; } /** diff --git a/src/core/editor.ts b/src/core/editor.ts index c059115..b128f61 100644 --- a/src/core/editor.ts +++ b/src/core/editor.ts @@ -643,6 +643,12 @@ export class CaptionAsset { constructor(config: CaptionAssetConfig = {}) { this.src = config.src ?? 'auto'; + if (this.src === 'auto') { + console.warn( + "CaptionAsset(src='auto'): the video must be indexed " + + '(e.g. video.indexSpokenWords()) for captions to be generated.' + ); + } this.font = config.font ?? new FontStyling(); this.primaryColor = config.primaryColor ?? '&H00FFFFFF'; this.secondaryColor = config.secondaryColor ?? '&H000000FF'; diff --git a/src/core/search/searchResult.ts b/src/core/search/searchResult.ts index f49af89..804dbee 100644 --- a/src/core/search/searchResult.ts +++ b/src/core/search/searchResult.ts @@ -39,6 +39,8 @@ export class SearchResult { sceneIndexId: doc.sceneIndexId, sceneIndexName: doc.sceneIndexName, metadata: doc.metadata, + streamUrl: doc.streamLink, + playerUrl: doc.playerUrl, }) ); } diff --git a/src/core/video.ts b/src/core/video.ts index 3213555..5dd87ee 100644 --- a/src/core/video.ts +++ b/src/core/video.ts @@ -236,11 +236,12 @@ export class Video implements IVideo { * @returns Success status or transcript data */ public generateTranscript = async ( - force: boolean = false + force: boolean = false, + languageCode?: string ): Promise<{ success: boolean; message: string } | Transcript> => { const res = await this.#vhttp.post( [video, this.id, transcription], - { force } + { force, language_code: languageCode } ); const transcript = res.data?.wordTimestamps; @@ -818,8 +819,8 @@ export class Video implements IVideo { */ public clip = async ( prompt: string, - contentType: string, - modelName: string + contentType: 'spoken' | 'visual' | 'multimodal', + modelName: 'basic' | 'pro' | 'ultra' ): Promise => { type ClipResponse = { results: Array<{ diff --git a/src/types/capture.ts b/src/types/capture.ts index 30d39f3..e34f50a 100644 --- a/src/types/capture.ts +++ b/src/types/capture.ts @@ -64,12 +64,10 @@ export interface ChannelConfig { channelId: string; /** Type of the channel */ type: ChannelTypeValue; - /** Whether to record this channel */ - record?: boolean; - /** Whether to enable transcription for this channel (audio only) */ - transcript?: boolean; /** Whether to store the recorded content */ store?: boolean; + /** Whether this is the primary video channel */ + isPrimary?: boolean; } /** diff --git a/src/types/response.ts b/src/types/response.ts index 68f81cd..3a11eb1 100644 --- a/src/types/response.ts +++ b/src/types/response.ts @@ -143,7 +143,8 @@ export type SearchResponse = { end: number; score: number; start: number; - stream_url: string; + stream_link?: string; + player_url?: string; text: string; scene_index_id?: string; scene_index_name?: string;