vidgen 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +781 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/db/init.d.ts +6 -0
  7. package/dist/commands/db/init.js +18 -0
  8. package/dist/commands/generate/stt.d.ts +13 -0
  9. package/dist/commands/generate/stt.js +73 -0
  10. package/dist/commands/generate/tts.d.ts +11 -0
  11. package/dist/commands/generate/tts.js +30 -0
  12. package/dist/commands/index/chunk.d.ts +11 -0
  13. package/dist/commands/index/chunk.js +55 -0
  14. package/dist/commands/index/describe.d.ts +11 -0
  15. package/dist/commands/index/describe.js +29 -0
  16. package/dist/commands/index/embed.d.ts +11 -0
  17. package/dist/commands/index/embed.js +29 -0
  18. package/dist/commands/index/save.d.ts +10 -0
  19. package/dist/commands/index/save.js +24 -0
  20. package/dist/commands/project/create.d.ts +11 -0
  21. package/dist/commands/project/create.js +63 -0
  22. package/dist/commands/project/delete.d.ts +9 -0
  23. package/dist/commands/project/delete.js +31 -0
  24. package/dist/commands/project/get.d.ts +9 -0
  25. package/dist/commands/project/get.js +39 -0
  26. package/dist/commands/project/list.d.ts +6 -0
  27. package/dist/commands/project/list.js +26 -0
  28. package/dist/commands/project/validate.d.ts +10 -0
  29. package/dist/commands/project/validate.js +29 -0
  30. package/dist/commands/search/asset.d.ts +12 -0
  31. package/dist/commands/search/asset.js +35 -0
  32. package/dist/commands/segment/add.d.ts +11 -0
  33. package/dist/commands/segment/add.js +49 -0
  34. package/dist/commands/segment/delete.d.ts +9 -0
  35. package/dist/commands/segment/delete.js +29 -0
  36. package/dist/commands/segment/list.d.ts +9 -0
  37. package/dist/commands/segment/list.js +33 -0
  38. package/dist/commands/segment/update.d.ts +13 -0
  39. package/dist/commands/segment/update.js +50 -0
  40. package/dist/commands/video/caption.d.ts +12 -0
  41. package/dist/commands/video/caption.js +43 -0
  42. package/dist/commands/video/trim.d.ts +13 -0
  43. package/dist/commands/video/trim.js +52 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.js +1 -0
  46. package/dist/lib/db.d.ts +7 -0
  47. package/dist/lib/db.js +39 -0
  48. package/dist/lib/stt/deepgram-to-combo.d.ts +2 -0
  49. package/dist/lib/stt/deepgram-to-combo.js +49 -0
  50. package/dist/lib/stt/deepgram.d.ts +18 -0
  51. package/dist/lib/stt/deepgram.js +71 -0
  52. package/dist/lib/stt/detect-language.d.ts +2 -0
  53. package/dist/lib/stt/detect-language.js +31 -0
  54. package/dist/lib/stt/index.d.ts +50 -0
  55. package/dist/lib/stt/index.js +50 -0
  56. package/dist/lib/stt/types.d.ts +65 -0
  57. package/dist/lib/stt/types.js +1 -0
  58. package/dist/lib/types.d.ts +131 -0
  59. package/dist/lib/types.js +1 -0
  60. package/oclif.manifest.json +874 -0
  61. package/package.json +79 -0
@@ -0,0 +1,35 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ export default class SearchAsset extends Command {
3
+ static description = 'Semantically search the video RAG database for the best matching clip for a given query. Returns matching VisualBroll / Clip metadata.';
4
+ static examples = [
5
+ '<%= config.bin %> <%= command.id %> --query "bitcoin coin spinning on blue background" --db postgresql://user:pass@localhost/db --limit 3',
6
+ ];
7
+ static flags = {
8
+ db: Flags.string({
9
+ char: 'd',
10
+ description: 'Database connection string',
11
+ required: true,
12
+ }),
13
+ limit: Flags.integer({
14
+ char: 'l',
15
+ default: 1,
16
+ description: 'Maximum number of results to return',
17
+ }),
18
+ query: Flags.string({
19
+ char: 'q',
20
+ description: 'The natural language search query (e.g. comes from a Segment\'s searchQuery field)',
21
+ required: true,
22
+ }),
23
+ type: Flags.string({
24
+ char: 't',
25
+ description: 'Filter results by asset type',
26
+ options: ['video', 'image'],
27
+ }),
28
+ };
29
+ async run() {
30
+ const { flags } = await this.parse(SearchAsset);
31
+ this.log(`[search:asset] Searching for "${flags.query}" (limit: ${flags.limit}) in database`);
32
+ // TODO: embed query, run vector similarity search, return top results as JSON
33
+ // Output shape: Array of VisualBroll { url, duration, type, time, ... }
34
+ }
35
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SegmentAdd extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ data: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ order: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ projectId: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,49 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getDb } from '../../lib/db.js';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ export default class SegmentAdd extends Command {
5
+ static description = 'Add a new segment to a project. Data should be a JSON string matching the Segment interface.';
6
+ static examples = [
7
+ `<%= config.bin %> <%= command.id %> --projectId <project-id> --data '{"title":"Scene 1","text":"Hello"}' --order 0`,
8
+ ];
9
+ static flags = {
10
+ data: Flags.string({
11
+ char: 'd',
12
+ description: 'JSON string of the segment data',
13
+ required: true,
14
+ }),
15
+ order: Flags.integer({
16
+ char: 'n',
17
+ default: 0,
18
+ description: 'Order index for the segment',
19
+ }),
20
+ projectId: Flags.string({
21
+ char: 'p',
22
+ description: 'Project ID to attach the segment to',
23
+ required: true,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(SegmentAdd);
28
+ const id = uuidv4();
29
+ try {
30
+ // Validate JSON data can be parsed
31
+ const segmentObject = JSON.parse(flags.data);
32
+ // Ensure ID is set in the data too if not provided
33
+ if (!segmentObject.id) {
34
+ segmentObject.id = id;
35
+ }
36
+ await getDb().execute({
37
+ args: [segmentObject.id, flags.projectId, flags.order, JSON.stringify(segmentObject)],
38
+ sql: 'INSERT INTO segments (id, projectId, orderIndex, data) VALUES (?, ?, ?, ?)',
39
+ });
40
+ this.log(`Segment added successfully!`);
41
+ this.log(`ID: ${segmentObject.id}`);
42
+ // Output JSON for agent
43
+ this.log(JSON.stringify({ id: segmentObject.id, projectId: flags.projectId }));
44
+ }
45
+ catch (error) {
46
+ this.error(`Failed to add segment: ${error instanceof Error ? error.message : error}`);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SegmentDelete extends Command {
3
+ static args: {
4
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,29 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { getDb } from '../../lib/db.js';
3
+ export default class SegmentDelete extends Command {
4
+ static args = {
5
+ id: Args.string({ description: 'Segment ID to delete', required: true }),
6
+ };
7
+ static description = 'Delete a specific segment from the Turso database.';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> <segment-id>',
10
+ ];
11
+ async run() {
12
+ const { args } = await this.parse(SegmentDelete);
13
+ try {
14
+ const rs = await getDb().execute({
15
+ args: [args.id],
16
+ sql: 'DELETE FROM segments WHERE id = ?',
17
+ });
18
+ if (rs.rowsAffected === 0) {
19
+ this.log(`Segment ${args.id} not found or already deleted.`);
20
+ }
21
+ else {
22
+ this.log(`Segment ${args.id} deleted successfully.`);
23
+ }
24
+ }
25
+ catch (error) {
26
+ this.error(`Failed to delete segment: ${error instanceof Error ? error.message : error}`);
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SegmentList extends Command {
3
+ static args: {
4
+ projectId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,33 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { getDb } from '../../lib/db.js';
3
+ export default class SegmentList extends Command {
4
+ static args = {
5
+ projectId: Args.string({ description: 'Project ID to list segments for', required: true }),
6
+ };
7
+ static description = 'List all segments associated with a specific project.';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> <project-id>',
10
+ ];
11
+ async run() {
12
+ const { args } = await this.parse(SegmentList);
13
+ try {
14
+ const rs = await getDb().execute({
15
+ args: [args.projectId],
16
+ sql: 'SELECT id, orderIndex, data FROM segments WHERE projectId = ? ORDER BY orderIndex ASC',
17
+ });
18
+ if (rs.rows.length === 0) {
19
+ this.log('No segments found for this project.');
20
+ return;
21
+ }
22
+ this.log(`Listing segments for project ${args.projectId}:`);
23
+ for (const row of rs.rows) {
24
+ const data = JSON.parse(row.data);
25
+ this.log(`[${row.orderIndex}] ${data.title || 'Untitled'} (${row.id})`);
26
+ }
27
+ this.log(JSON.stringify(rs.rows.map((r) => ({ id: r.id, orderIndex: r.orderIndex, ...JSON.parse(r.data) }))));
28
+ }
29
+ catch (error) {
30
+ this.error(`Failed to list segments: ${error instanceof Error ? error.message : error}`);
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SegmentUpdate extends Command {
3
+ static args: {
4
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ data: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ order: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,50 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { getDb } from '../../lib/db.js';
3
+ export default class SegmentUpdate extends Command {
4
+ static args = {
5
+ id: Args.string({ description: 'Segment ID to update', required: true }),
6
+ };
7
+ static description = 'Update an existing segment\'s data.';
8
+ static examples = [
9
+ `<%= config.bin %> <%= command.id %> <segment-id> --data '{"title":"New Title"}'`,
10
+ ];
11
+ static flags = {
12
+ data: Flags.string({
13
+ char: 'd',
14
+ description: 'JSON string of the segment data to merge/update',
15
+ required: true,
16
+ }),
17
+ order: Flags.integer({
18
+ char: 'n',
19
+ description: 'New order index for the segment',
20
+ }),
21
+ };
22
+ async run() {
23
+ const { args, flags } = await this.parse(SegmentUpdate);
24
+ try {
25
+ // Get existing data
26
+ const rs = await getDb().execute({
27
+ args: [args.id],
28
+ sql: 'SELECT data, orderIndex FROM segments WHERE id = ?',
29
+ });
30
+ if (rs.rows.length === 0) {
31
+ this.error(`Segment with ID ${args.id} not found.`);
32
+ }
33
+ const existingData = JSON.parse(rs.rows[0].data);
34
+ const updateData = JSON.parse(flags.data);
35
+ const newData = {
36
+ ...existingData,
37
+ ...updateData,
38
+ };
39
+ const orderIndex = flags.order !== undefined ? flags.order : rs.rows[0].orderIndex;
40
+ await getDb().execute({
41
+ args: [orderIndex, JSON.stringify(newData), args.id],
42
+ sql: 'UPDATE segments SET orderIndex = ?, data = ? WHERE id = ?',
43
+ });
44
+ this.log(`Segment ${args.id} updated successfully.`);
45
+ }
46
+ catch (error) {
47
+ this.error(`Failed to update segment: ${error instanceof Error ? error.message : error}`);
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class VideoSubtitle extends Command {
3
+ static description: string;
4
+ static flags: {
5
+ burnIn: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ transcript: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ video: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ private formatTime;
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,43 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { exec } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ import * as fs from 'node:fs/promises';
5
+ const execAsync = promisify(exec);
6
+ export default class VideoSubtitle extends Command {
7
+ static description = 'Generate SRT captions from a JSON transcript and optionally embed them into a video.';
8
+ static flags = {
9
+ burnIn: Flags.boolean({ char: 'b' }),
10
+ output: Flags.string({ char: 'o', required: true }),
11
+ transcript: Flags.string({ char: 't', required: true }),
12
+ video: Flags.string({ char: 'v' }),
13
+ };
14
+ formatTime(seconds) {
15
+ const date = new Date(seconds * 1000);
16
+ return date.toISOString().substr(11, 12).replace('.', ',');
17
+ }
18
+ async run() {
19
+ const { flags } = await this.parse(VideoSubtitle);
20
+ const transcript = JSON.parse(await fs.readFile(flags.transcript, 'utf8'));
21
+ const words = transcript.results?.channels?.[0]?.alternatives?.[0]?.words || [];
22
+ let srtContent = '';
23
+ let group = [];
24
+ let index = 1;
25
+ for (let i = 0; i < words.length; i++) {
26
+ group.push(words[i]);
27
+ if (group.length >= 7 || i === words.length - 1) {
28
+ srtContent += `${index}\n${this.formatTime(group[0].start)} --> ${this.formatTime(group[group.length - 1].end)}\n${group.map(w => w.punctuated_word || w.word).join(' ')}\n\n`;
29
+ index++;
30
+ group = [];
31
+ }
32
+ }
33
+ const srtPath = flags.video ? `${flags.transcript}.srt` : flags.output;
34
+ await fs.writeFile(srtPath, srtContent);
35
+ if (flags.video) {
36
+ let cmd = flags.burnIn
37
+ ? `ffmpeg -i "${flags.video}" -vf "subtitles='${srtPath.replace(/:/g, '\\:')}'" "${flags.output}" -y`
38
+ : `ffmpeg -i "${flags.video}" -i "${srtPath}" -c copy -c:s mov_text "${flags.output}" -y`;
39
+ await execAsync(cmd);
40
+ }
41
+ this.log(JSON.stringify({ path: flags.video ? flags.output : srtPath }));
42
+ }
43
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class VideoTrim extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ duration: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ end: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ input: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ start: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,52 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { exec } from 'node:child_process';
3
+ import { promisify } from 'node:util';
4
+ const execAsync = promisify(exec);
5
+ export default class VideoTrim extends Command {
6
+ static description = 'Precisely trim a video file using start and end timestamps.';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> --input input.mp4 --start 00:00:10 --end 00:00:20 --output trimmed.mp4',
9
+ ];
10
+ static flags = {
11
+ duration: Flags.string({
12
+ char: 'd',
13
+ description: 'Duration to trim',
14
+ }),
15
+ end: Flags.string({
16
+ char: 'e',
17
+ description: 'End timestamp',
18
+ }),
19
+ input: Flags.string({
20
+ char: 'i',
21
+ description: 'Input video file path',
22
+ required: true,
23
+ }),
24
+ output: Flags.string({
25
+ char: 'o',
26
+ description: 'Output video file path',
27
+ required: true,
28
+ }),
29
+ start: Flags.string({
30
+ char: 's',
31
+ description: 'Start timestamp',
32
+ required: true,
33
+ }),
34
+ };
35
+ async run() {
36
+ const { flags } = await this.parse(VideoTrim);
37
+ let cmd = `ffmpeg -i "${flags.input}" -ss ${flags.start}`;
38
+ if (flags.end)
39
+ cmd += ` -to ${flags.end}`;
40
+ else if (flags.duration)
41
+ cmd += ` -t ${flags.duration}`;
42
+ cmd += ` -c copy "${flags.output}" -y`;
43
+ try {
44
+ await execAsync(cmd);
45
+ this.log(`[video:trim] Done.`);
46
+ this.log(JSON.stringify({ path: flags.output }));
47
+ }
48
+ catch (error) {
49
+ this.error(`FFmpeg failed: ${error instanceof Error ? error.message : error}`);
50
+ }
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,7 @@
1
+ import { type Client } from '@libsql/client';
2
+ declare function getDb(): Client;
3
+ /**
4
+ * Initialize the database tables if they don't exist.
5
+ */
6
+ export declare function initDb(): Promise<void>;
7
+ export { getDb };
package/dist/lib/db.js ADDED
@@ -0,0 +1,39 @@
1
+ import { createClient } from '@libsql/client';
2
+ let internalDb = null;
3
+ function getDb() {
4
+ if (internalDb)
5
+ return internalDb;
6
+ const url = process.env.TURSO_DATABASE_URL;
7
+ const authToken = process.env.TURSO_AUTH_TOKEN;
8
+ if (!url || !authToken) {
9
+ throw new Error('TURSO_DATABASE_URL and TURSO_AUTH_TOKEN must be set');
10
+ }
11
+ internalDb = createClient({
12
+ url,
13
+ authToken,
14
+ });
15
+ return internalDb;
16
+ }
17
+ /**
18
+ * Initialize the database tables if they don't exist.
19
+ */
20
+ export async function initDb() {
21
+ const db = getDb();
22
+ await db.batch([
23
+ `CREATE TABLE IF NOT EXISTS projects (
24
+ id TEXT PRIMARY KEY,
25
+ title TEXT NOT NULL,
26
+ description TEXT,
27
+ data TEXT, -- JSON blob
28
+ createdAt DATETIME DEFAULT CURRENT_TIMESTAMP
29
+ );`,
30
+ `CREATE TABLE IF NOT EXISTS segments (
31
+ id TEXT PRIMARY KEY,
32
+ projectId TEXT NOT NULL,
33
+ orderIndex INTEGER NOT NULL,
34
+ data TEXT, -- JSON blob
35
+ FOREIGN KEY (projectId) REFERENCES projects(id) ON DELETE CASCADE
36
+ );`,
37
+ ], "write");
38
+ }
39
+ export { getDb };
@@ -0,0 +1,2 @@
1
+ import { TranscriptObject } from './types.js';
2
+ export declare function deepgramToCombo(deepgramResult: any): Promise<Partial<TranscriptObject>>;
@@ -0,0 +1,49 @@
1
+ import { detectLanguage } from './detect-language.js';
2
+ const getWords = (deepgramResult) => {
3
+ const words = deepgramResult.results.channels[0].alternatives[0].words.map((w) => {
4
+ return {
5
+ word: w.punctuated_word,
6
+ start: w.start,
7
+ end: w.end,
8
+ confidence: w.confidence,
9
+ };
10
+ });
11
+ return words;
12
+ };
13
+ const getParagraphs = (deepgramResult) => {
14
+ const paragraphs = deepgramResult.results.channels[0].alternatives[0].paragraphs.paragraphs
15
+ .map((p) => {
16
+ return {
17
+ sentences: p.sentences.map((s) => {
18
+ return {
19
+ text: s.text,
20
+ start: s.start,
21
+ end: s.end,
22
+ };
23
+ }),
24
+ numWords: p.num_words,
25
+ start: p.start,
26
+ end: p.end,
27
+ };
28
+ })
29
+ .filter((p) => p.sentences.length > 0);
30
+ return paragraphs;
31
+ };
32
+ export async function deepgramToCombo(deepgramResult) {
33
+ const text = deepgramResult.results.channels[0].alternatives[0].transcript;
34
+ const language = await detectLanguage(text);
35
+ const words = getWords(deepgramResult);
36
+ const duration = deepgramResult.metadata.duration;
37
+ const paragraphs = getParagraphs(deepgramResult);
38
+ return {
39
+ duration,
40
+ results: {
41
+ main: {
42
+ language,
43
+ paragraphs,
44
+ text,
45
+ words,
46
+ },
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,18 @@
1
+ import { TranscriptObject } from './types.js';
2
+ export declare class SttService {
3
+ private apiKey;
4
+ private params;
5
+ private url;
6
+ constructor(url: string, apiKey: string, model: string);
7
+ transcribe(audioUrl: string): Promise<{
8
+ transcript: Partial<TranscriptObject>;
9
+ duration: number;
10
+ }>;
11
+ transcribeV2(audioUrl: string): Promise<{
12
+ success: boolean;
13
+ data: {
14
+ transcript: Partial<TranscriptObject>;
15
+ duration: number;
16
+ };
17
+ }>;
18
+ }
@@ -0,0 +1,71 @@
1
+ import { deepgramToCombo } from './deepgram-to-combo.js';
2
+ export class SttService {
3
+ apiKey;
4
+ params;
5
+ url;
6
+ constructor(url, apiKey, model) {
7
+ this.url = url;
8
+ this.apiKey = apiKey;
9
+ this.params = new URLSearchParams({
10
+ model,
11
+ smart_format: 'true',
12
+ filler_words: 'false',
13
+ punctuate: 'true',
14
+ detect_language: 'true',
15
+ }).toString();
16
+ }
17
+ async transcribe(audioUrl) {
18
+ try {
19
+ const response = await fetch(`${this.url}/listen?${this.params}`, {
20
+ method: 'POST',
21
+ headers: {
22
+ Authorization: `Token ${this.apiKey}`,
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ body: JSON.stringify({ url: audioUrl }),
26
+ });
27
+ const data = await response.json();
28
+ if (!response.ok) {
29
+ console.error(data);
30
+ throw new Error(`Transcription failed - ${response.status}`);
31
+ }
32
+ const caption_combo = await deepgramToCombo(data);
33
+ return { transcript: caption_combo, duration: data.metadata.duration };
34
+ }
35
+ catch (err) {
36
+ console.error(err);
37
+ throw new Error('An unknown error occurred.');
38
+ }
39
+ }
40
+ async transcribeV2(audioUrl) {
41
+ try {
42
+ const response = await fetch(`${this.url}/listen?${this.params}`, {
43
+ method: 'POST',
44
+ headers: {
45
+ Authorization: `Token ${this.apiKey}`,
46
+ 'Content-Type': 'application/json',
47
+ },
48
+ body: JSON.stringify({ url: audioUrl }),
49
+ });
50
+ const data = await response.json();
51
+ if (!response.ok) {
52
+ console.error(data);
53
+ return { success: false, data: { transcript: {}, duration: 0 } };
54
+ }
55
+ const caption_combo = await deepgramToCombo(data);
56
+ const durationNum = typeof data.metadata.duration === 'number'
57
+ ? data.metadata.duration
58
+ : parseFloat(data.metadata.duration);
59
+ if (isNaN(durationNum))
60
+ throw new Error(`Invalid duration for ${audioUrl}`);
61
+ return {
62
+ success: true,
63
+ data: { transcript: caption_combo, duration: durationNum },
64
+ };
65
+ }
66
+ catch (err) {
67
+ console.error(err);
68
+ return { success: false, data: { transcript: {}, duration: 0 } };
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,2 @@
1
+ export declare function detectLanguage(text: string): Promise<import("./types.js").LanguageDetectionResultsEntry>;
2
+ export declare function languageCodeToName(languageCode: string): string;
@@ -0,0 +1,31 @@
1
+ import { detectAll } from 'tinyld';
2
+ export async function detectLanguage(text) {
3
+ const tinyldResults = detectAll(text);
4
+ const results = tinyldResults.map((result) => ({
5
+ language: result.lang,
6
+ languageName: languageCodeToName(result.lang),
7
+ confidence: result.accuracy,
8
+ }));
9
+ const [mainLanguage] = results;
10
+ if (!mainLanguage) {
11
+ return {
12
+ language: 'en',
13
+ languageName: 'English',
14
+ confidence: 1,
15
+ };
16
+ }
17
+ if (mainLanguage.language === 'und') {
18
+ mainLanguage.language = 'en';
19
+ mainLanguage.languageName = 'English';
20
+ }
21
+ return mainLanguage;
22
+ }
23
+ export function languageCodeToName(languageCode) {
24
+ const languageNames = new Intl.DisplayNames(['en'], { type: 'language' });
25
+ let translatedLanguageName;
26
+ try {
27
+ translatedLanguageName = languageNames.of(languageCode);
28
+ }
29
+ catch (e) { }
30
+ return translatedLanguageName || 'Unknown';
31
+ }
@@ -0,0 +1,50 @@
1
+ import type { TranscriptObject } from './types.js';
2
+ export interface TranscribeOptions {
3
+ /**
4
+ * Audio URL to transcribe
5
+ */
6
+ url: string;
7
+ /**
8
+ * API key for Deepgram (optional, defaults to env variable)
9
+ */
10
+ apiKey?: string;
11
+ /**
12
+ * Target language for transcription (optional)
13
+ * If not provided, will auto-detect
14
+ */
15
+ language?: string;
16
+ /**
17
+ * Deepgram model to use (optional, defaults to "nova-3")
18
+ */
19
+ model?: string;
20
+ /**
21
+ * Whether to enable smart formatting (optional, defaults to true)
22
+ */
23
+ smartFormat?: boolean;
24
+ /**
25
+ * Whether to include paragraphs in the result (optional, defaults to true)
26
+ */
27
+ paragraphs?: boolean;
28
+ /**
29
+ * Whether to include word-level timestamps (optional, defaults to true)
30
+ */
31
+ words?: boolean;
32
+ }
33
+ /**
34
+ * Transcribe audio from a URL using Deepgram
35
+ *
36
+ * @param options - Transcription options
37
+ * @returns Parsed transcription result in Combo format
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const result = await transcribe({
42
+ * url: "https://example.com/audio.mp3",
43
+ * language: "en"
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function transcribe(options: TranscribeOptions): Promise<Partial<TranscriptObject>>;
48
+ export * from './types.js';
49
+ export { deepgramToCombo } from './deepgram-to-combo.js';
50
+ export { detectLanguage } from './detect-language.js';