@steipete/summarize-core 0.7.0 → 0.7.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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/content/index.js +14 -0
  3. package/dist/cjs/content/index.js.map +1 -0
  4. package/dist/cjs/content/link-preview/client.js +31 -0
  5. package/dist/cjs/content/link-preview/client.js.map +1 -0
  6. package/dist/cjs/content/link-preview/content/article.js +164 -0
  7. package/dist/cjs/content/link-preview/content/article.js.map +1 -0
  8. package/dist/cjs/content/link-preview/content/cleaner.js +63 -0
  9. package/dist/cjs/content/link-preview/content/cleaner.js.map +1 -0
  10. package/dist/cjs/content/link-preview/content/constants.js +10 -0
  11. package/dist/cjs/content/link-preview/content/constants.js.map +1 -0
  12. package/dist/cjs/content/link-preview/content/fetcher.js +128 -0
  13. package/dist/cjs/content/link-preview/content/fetcher.js.map +1 -0
  14. package/dist/cjs/content/link-preview/content/firecrawl.js +90 -0
  15. package/dist/cjs/content/link-preview/content/firecrawl.js.map +1 -0
  16. package/dist/cjs/content/link-preview/content/html.js +165 -0
  17. package/dist/cjs/content/link-preview/content/html.js.map +1 -0
  18. package/dist/cjs/content/link-preview/content/index.js +348 -0
  19. package/dist/cjs/content/link-preview/content/index.js.map +1 -0
  20. package/dist/cjs/content/link-preview/content/jsonld.js +80 -0
  21. package/dist/cjs/content/link-preview/content/jsonld.js.map +1 -0
  22. package/dist/cjs/content/link-preview/content/parsers.js +81 -0
  23. package/dist/cjs/content/link-preview/content/parsers.js.map +1 -0
  24. package/dist/cjs/content/link-preview/content/podcast-utils.js +85 -0
  25. package/dist/cjs/content/link-preview/content/podcast-utils.js.map +1 -0
  26. package/dist/cjs/content/link-preview/content/readability.js +90 -0
  27. package/dist/cjs/content/link-preview/content/readability.js.map +1 -0
  28. package/dist/cjs/content/link-preview/content/twitter-utils.js +74 -0
  29. package/dist/cjs/content/link-preview/content/twitter-utils.js.map +1 -0
  30. package/dist/cjs/content/link-preview/content/types.js +7 -0
  31. package/dist/cjs/content/link-preview/content/types.js.map +1 -0
  32. package/dist/cjs/content/link-preview/content/utils.js +177 -0
  33. package/dist/cjs/content/link-preview/content/utils.js.map +1 -0
  34. package/dist/cjs/content/link-preview/content/video.js +99 -0
  35. package/dist/cjs/content/link-preview/content/video.js.map +1 -0
  36. package/dist/cjs/content/link-preview/content/youtube.js +85 -0
  37. package/dist/cjs/content/link-preview/content/youtube.js.map +1 -0
  38. package/dist/cjs/content/link-preview/deps.js +23 -0
  39. package/dist/cjs/content/link-preview/deps.js.map +1 -0
  40. package/dist/cjs/content/link-preview/fetch-with-timeout.js +38 -0
  41. package/dist/cjs/content/link-preview/fetch-with-timeout.js.map +1 -0
  42. package/dist/cjs/content/link-preview/types.js +5 -0
  43. package/dist/cjs/content/link-preview/types.js.map +1 -0
  44. package/dist/cjs/content/transcript/cache.js +85 -0
  45. package/dist/cjs/content/transcript/cache.js.map +1 -0
  46. package/dist/cjs/content/transcript/index.js +134 -0
  47. package/dist/cjs/content/transcript/index.js.map +1 -0
  48. package/dist/cjs/content/transcript/normalize.js +49 -0
  49. package/dist/cjs/content/transcript/normalize.js.map +1 -0
  50. package/dist/cjs/content/transcript/providers/generic.js +16 -0
  51. package/dist/cjs/content/transcript/providers/generic.js.map +1 -0
  52. package/dist/cjs/content/transcript/providers/podcast/apple-flow.js +226 -0
  53. package/dist/cjs/content/transcript/providers/podcast/apple-flow.js.map +1 -0
  54. package/dist/cjs/content/transcript/providers/podcast/apple.js +43 -0
  55. package/dist/cjs/content/transcript/providers/podcast/apple.js.map +1 -0
  56. package/dist/cjs/content/transcript/providers/podcast/constants.js +11 -0
  57. package/dist/cjs/content/transcript/providers/podcast/constants.js.map +1 -0
  58. package/dist/cjs/content/transcript/providers/podcast/flow-context.js +3 -0
  59. package/dist/cjs/content/transcript/providers/podcast/flow-context.js.map +1 -0
  60. package/dist/cjs/content/transcript/providers/podcast/itunes.js +139 -0
  61. package/dist/cjs/content/transcript/providers/podcast/itunes.js.map +1 -0
  62. package/dist/cjs/content/transcript/providers/podcast/json.js +43 -0
  63. package/dist/cjs/content/transcript/providers/podcast/json.js.map +1 -0
  64. package/dist/cjs/content/transcript/providers/podcast/media.js +355 -0
  65. package/dist/cjs/content/transcript/providers/podcast/media.js.map +1 -0
  66. package/dist/cjs/content/transcript/providers/podcast/results.js +32 -0
  67. package/dist/cjs/content/transcript/providers/podcast/results.js.map +1 -0
  68. package/dist/cjs/content/transcript/providers/podcast/rss.js +262 -0
  69. package/dist/cjs/content/transcript/providers/podcast/rss.js.map +1 -0
  70. package/dist/cjs/content/transcript/providers/podcast/spotify-flow.js +221 -0
  71. package/dist/cjs/content/transcript/providers/podcast/spotify-flow.js.map +1 -0
  72. package/dist/cjs/content/transcript/providers/podcast/spotify.js +119 -0
  73. package/dist/cjs/content/transcript/providers/podcast/spotify.js.map +1 -0
  74. package/dist/cjs/content/transcript/providers/podcast.js +260 -0
  75. package/dist/cjs/content/transcript/providers/podcast.js.map +1 -0
  76. package/dist/cjs/content/transcript/providers/youtube/api.js +264 -0
  77. package/dist/cjs/content/transcript/providers/youtube/api.js.map +1 -0
  78. package/dist/cjs/content/transcript/providers/youtube/apify.js +59 -0
  79. package/dist/cjs/content/transcript/providers/youtube/apify.js.map +1 -0
  80. package/dist/cjs/content/transcript/providers/youtube/captions.js +413 -0
  81. package/dist/cjs/content/transcript/providers/youtube/captions.js.map +1 -0
  82. package/dist/cjs/content/transcript/providers/youtube/yt-dlp.js +170 -0
  83. package/dist/cjs/content/transcript/providers/youtube/yt-dlp.js.map +1 -0
  84. package/dist/cjs/content/transcript/providers/youtube.js +178 -0
  85. package/dist/cjs/content/transcript/providers/youtube.js.map +1 -0
  86. package/dist/cjs/content/transcript/types.js +3 -0
  87. package/dist/cjs/content/transcript/types.js.map +1 -0
  88. package/dist/cjs/content/transcript/utils.js +303 -0
  89. package/dist/cjs/content/transcript/utils.js.map +1 -0
  90. package/dist/cjs/index.js +22 -0
  91. package/dist/cjs/index.js.map +1 -0
  92. package/dist/cjs/language.js +132 -0
  93. package/dist/cjs/language.js.map +1 -0
  94. package/dist/cjs/package.json +3 -0
  95. package/dist/cjs/prompts/cli.js +23 -0
  96. package/dist/cjs/prompts/cli.js.map +1 -0
  97. package/dist/cjs/prompts/file.js +52 -0
  98. package/dist/cjs/prompts/file.js.map +1 -0
  99. package/dist/cjs/prompts/index.js +14 -0
  100. package/dist/cjs/prompts/index.js.map +1 -0
  101. package/dist/cjs/prompts/link-summary.js +122 -0
  102. package/dist/cjs/prompts/link-summary.js.map +1 -0
  103. package/dist/cjs/shared/contracts.js +5 -0
  104. package/dist/cjs/shared/contracts.js.map +1 -0
  105. package/dist/cjs/transcription/whisper/constants.js +11 -0
  106. package/dist/cjs/transcription/whisper/constants.js.map +1 -0
  107. package/dist/cjs/transcription/whisper/core.js +307 -0
  108. package/dist/cjs/transcription/whisper/core.js.map +1 -0
  109. package/dist/cjs/transcription/whisper/fal.js +44 -0
  110. package/dist/cjs/transcription/whisper/fal.js.map +1 -0
  111. package/dist/cjs/transcription/whisper/ffmpeg.js +187 -0
  112. package/dist/cjs/transcription/whisper/ffmpeg.js.map +1 -0
  113. package/dist/cjs/transcription/whisper/openai.js +51 -0
  114. package/dist/cjs/transcription/whisper/openai.js.map +1 -0
  115. package/dist/cjs/transcription/whisper/types.js +3 -0
  116. package/dist/cjs/transcription/whisper/types.js.map +1 -0
  117. package/dist/cjs/transcription/whisper/utils.js +70 -0
  118. package/dist/cjs/transcription/whisper/utils.js.map +1 -0
  119. package/dist/cjs/transcription/whisper/whisper-cpp.js +232 -0
  120. package/dist/cjs/transcription/whisper/whisper-cpp.js.map +1 -0
  121. package/dist/cjs/transcription/whisper.js +15 -0
  122. package/dist/cjs/transcription/whisper.js.map +1 -0
  123. package/package.json +15 -12
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transcribeMediaWithWhisper = transcribeMediaWithWhisper;
4
+ exports.transcribeMediaFileWithWhisper = transcribeMediaFileWithWhisper;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const node_fs_1 = require("node:fs");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = require("node:path");
9
+ const constants_js_1 = require("./constants.js");
10
+ const fal_js_1 = require("./fal.js");
11
+ const ffmpeg_js_1 = require("./ffmpeg.js");
12
+ const openai_js_1 = require("./openai.js");
13
+ const utils_js_1 = require("./utils.js");
14
+ const whisper_cpp_js_1 = require("./whisper-cpp.js");
15
+ async function transcribeMediaWithWhisper({ bytes, mediaType, filename, openaiApiKey, falApiKey, totalDurationSeconds = null, onProgress, }) {
16
+ const notes = [];
17
+ const localReady = await (0, whisper_cpp_js_1.isWhisperCppReady)();
18
+ let local = null;
19
+ if (localReady) {
20
+ const nameHint = filename?.trim() ? filename.trim() : 'media';
21
+ const tempFile = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `summarize-whisper-local-${(0, node_crypto_1.randomUUID)()}-${(0, utils_js_1.ensureWhisperFilenameExtension)(nameHint, mediaType)}`);
22
+ try {
23
+ // Prefer local whisper.cpp when installed + model available (no network, no upload limits).
24
+ await node_fs_1.promises.writeFile(tempFile, bytes);
25
+ try {
26
+ local = await (0, whisper_cpp_js_1.transcribeWithWhisperCppFile)({
27
+ filePath: tempFile,
28
+ mediaType,
29
+ totalDurationSeconds,
30
+ onProgress,
31
+ });
32
+ }
33
+ catch (error) {
34
+ local = {
35
+ text: null,
36
+ provider: 'whisper.cpp',
37
+ error: (0, utils_js_1.wrapError)('whisper.cpp failed', error),
38
+ notes: [],
39
+ };
40
+ }
41
+ if (local.text) {
42
+ if (local.notes.length > 0)
43
+ notes.push(...local.notes);
44
+ return { ...local, notes };
45
+ }
46
+ if (local.notes.length > 0)
47
+ notes.push(...local.notes);
48
+ if (local.error) {
49
+ notes.push(`whisper.cpp failed; falling back to remote Whisper: ${local.error.message}`);
50
+ }
51
+ }
52
+ finally {
53
+ await node_fs_1.promises.unlink(tempFile).catch(() => { });
54
+ }
55
+ }
56
+ if (!openaiApiKey && !falApiKey) {
57
+ return {
58
+ text: null,
59
+ provider: null,
60
+ error: new Error('No transcription providers available (install whisper-cpp or set OPENAI_API_KEY or FAL_KEY)'),
61
+ notes,
62
+ };
63
+ }
64
+ if (openaiApiKey && bytes.byteLength > constants_js_1.MAX_OPENAI_UPLOAD_BYTES) {
65
+ const canChunk = await (0, ffmpeg_js_1.isFfmpegAvailable)();
66
+ if (canChunk) {
67
+ const tempFile = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `summarize-whisper-${(0, node_crypto_1.randomUUID)()}`);
68
+ try {
69
+ await node_fs_1.promises.writeFile(tempFile, bytes);
70
+ const chunked = await transcribeMediaFileWithWhisper({
71
+ filePath: tempFile,
72
+ mediaType,
73
+ filename,
74
+ openaiApiKey,
75
+ falApiKey,
76
+ segmentSeconds: constants_js_1.DEFAULT_SEGMENT_SECONDS,
77
+ onProgress,
78
+ });
79
+ return chunked;
80
+ }
81
+ finally {
82
+ await node_fs_1.promises.unlink(tempFile).catch(() => { });
83
+ }
84
+ }
85
+ notes.push(`Media too large for Whisper upload (${(0, utils_js_1.formatBytes)(bytes.byteLength)}); transcribing first ${(0, utils_js_1.formatBytes)(constants_js_1.MAX_OPENAI_UPLOAD_BYTES)} only (install ffmpeg for full transcription)`);
86
+ bytes = bytes.slice(0, constants_js_1.MAX_OPENAI_UPLOAD_BYTES);
87
+ }
88
+ let openaiError = null;
89
+ if (openaiApiKey) {
90
+ try {
91
+ const text = await (0, openai_js_1.transcribeWithOpenAi)(bytes, mediaType, filename, openaiApiKey);
92
+ if (text) {
93
+ return { text, provider: 'openai', error: null, notes };
94
+ }
95
+ openaiError = new Error('OpenAI transcription returned empty text');
96
+ }
97
+ catch (error) {
98
+ openaiError = (0, utils_js_1.wrapError)('OpenAI transcription failed', error);
99
+ }
100
+ }
101
+ if (openaiApiKey && openaiError && (0, openai_js_1.shouldRetryOpenAiViaFfmpeg)(openaiError)) {
102
+ const canTranscode = await (0, ffmpeg_js_1.isFfmpegAvailable)();
103
+ if (canTranscode) {
104
+ try {
105
+ // Some providers hand out containers/codecs Whisper rejects. Transcoding to a small mono MP3
106
+ // is the most reliable cross-format fallback (and also reduces upload size).
107
+ notes.push('OpenAI could not decode media; transcoding via ffmpeg and retrying');
108
+ const mp3Bytes = await (0, ffmpeg_js_1.transcodeBytesToMp3)(bytes);
109
+ const retried = await (0, openai_js_1.transcribeWithOpenAi)(mp3Bytes, 'audio/mpeg', 'audio.mp3', openaiApiKey);
110
+ if (retried) {
111
+ return { text: retried, provider: 'openai', error: null, notes };
112
+ }
113
+ openaiError = new Error('OpenAI transcription returned empty text after ffmpeg transcode');
114
+ bytes = mp3Bytes;
115
+ mediaType = 'audio/mpeg';
116
+ filename = 'audio.mp3';
117
+ }
118
+ catch (error) {
119
+ notes.push(`ffmpeg transcode failed; cannot retry OpenAI decode error: ${error instanceof Error ? error.message : String(error)}`);
120
+ }
121
+ }
122
+ else {
123
+ notes.push('OpenAI could not decode media; install ffmpeg to enable transcoding retry');
124
+ }
125
+ }
126
+ const canUseFal = Boolean(falApiKey) && mediaType.toLowerCase().startsWith('audio/');
127
+ if (openaiError && canUseFal) {
128
+ notes.push(`OpenAI transcription failed; falling back to FAL: ${openaiError.message}`);
129
+ }
130
+ if (falApiKey && !canUseFal) {
131
+ notes.push(`Skipping FAL transcription: unsupported mediaType ${mediaType}`);
132
+ }
133
+ if (falApiKey && canUseFal) {
134
+ try {
135
+ const text = await (0, fal_js_1.transcribeWithFal)(bytes, mediaType, falApiKey);
136
+ if (text) {
137
+ return { text, provider: 'fal', error: null, notes };
138
+ }
139
+ return {
140
+ text: null,
141
+ provider: 'fal',
142
+ error: new Error('FAL transcription returned empty text'),
143
+ notes,
144
+ };
145
+ }
146
+ catch (error) {
147
+ return {
148
+ text: null,
149
+ provider: 'fal',
150
+ error: (0, utils_js_1.wrapError)('FAL transcription failed', error),
151
+ notes,
152
+ };
153
+ }
154
+ }
155
+ return {
156
+ text: null,
157
+ provider: openaiApiKey ? 'openai' : null,
158
+ error: openaiError ?? new Error('No transcription providers available'),
159
+ notes,
160
+ };
161
+ }
162
+ async function transcribeMediaFileWithWhisper({ filePath, mediaType, filename, openaiApiKey, falApiKey, segmentSeconds = constants_js_1.DEFAULT_SEGMENT_SECONDS, totalDurationSeconds = null, onProgress = null, }) {
163
+ const notes = [];
164
+ const localReady = await (0, whisper_cpp_js_1.isWhisperCppReady)();
165
+ let local = null;
166
+ if (localReady) {
167
+ onProgress?.({
168
+ partIndex: null,
169
+ parts: null,
170
+ processedDurationSeconds: null,
171
+ totalDurationSeconds,
172
+ });
173
+ try {
174
+ local = await (0, whisper_cpp_js_1.transcribeWithWhisperCppFile)({
175
+ filePath,
176
+ mediaType,
177
+ totalDurationSeconds,
178
+ onProgress,
179
+ });
180
+ }
181
+ catch (error) {
182
+ local = {
183
+ text: null,
184
+ provider: 'whisper.cpp',
185
+ error: (0, utils_js_1.wrapError)('whisper.cpp failed', error),
186
+ notes: [],
187
+ };
188
+ }
189
+ if (local.text) {
190
+ if (local.notes.length > 0)
191
+ notes.push(...local.notes);
192
+ return { ...local, notes };
193
+ }
194
+ if (local.notes.length > 0)
195
+ notes.push(...local.notes);
196
+ if (local.error) {
197
+ notes.push(`whisper.cpp failed; falling back to remote Whisper: ${local.error.message}`);
198
+ }
199
+ }
200
+ if (!openaiApiKey && !falApiKey) {
201
+ return {
202
+ text: null,
203
+ provider: null,
204
+ error: new Error('No transcription providers available (install whisper-cpp or set OPENAI_API_KEY or FAL_KEY)'),
205
+ notes,
206
+ };
207
+ }
208
+ const stat = await node_fs_1.promises.stat(filePath);
209
+ if (openaiApiKey && stat.size > constants_js_1.MAX_OPENAI_UPLOAD_BYTES) {
210
+ const canChunk = await (0, ffmpeg_js_1.isFfmpegAvailable)();
211
+ if (!canChunk) {
212
+ notes.push(`Media too large for Whisper upload (${(0, utils_js_1.formatBytes)(stat.size)}); install ffmpeg to enable chunked transcription`);
213
+ const head = await (0, utils_js_1.readFirstBytes)(filePath, constants_js_1.MAX_OPENAI_UPLOAD_BYTES);
214
+ const partial = await transcribeMediaWithWhisper({
215
+ bytes: head,
216
+ mediaType,
217
+ filename,
218
+ openaiApiKey,
219
+ falApiKey,
220
+ });
221
+ if (partial.notes.length > 0)
222
+ notes.push(...partial.notes);
223
+ return { ...partial, notes };
224
+ }
225
+ const dir = await node_fs_1.promises.mkdtemp((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'summarize-whisper-segments-'));
226
+ try {
227
+ const pattern = (0, node_path_1.join)(dir, 'part-%03d.mp3');
228
+ await (0, ffmpeg_js_1.runFfmpegSegment)({
229
+ inputPath: filePath,
230
+ outputPattern: pattern,
231
+ segmentSeconds,
232
+ });
233
+ const files = (await node_fs_1.promises.readdir(dir))
234
+ .filter((name) => name.startsWith('part-') && name.endsWith('.mp3'))
235
+ .sort((a, b) => a.localeCompare(b));
236
+ if (files.length === 0) {
237
+ return {
238
+ text: null,
239
+ provider: null,
240
+ error: new Error('ffmpeg produced no audio segments'),
241
+ notes,
242
+ };
243
+ }
244
+ notes.push(`ffmpeg chunked media into ${files.length} parts (${segmentSeconds}s each)`);
245
+ onProgress?.({
246
+ partIndex: null,
247
+ parts: files.length,
248
+ processedDurationSeconds: null,
249
+ totalDurationSeconds,
250
+ });
251
+ const parts = [];
252
+ let usedProvider = null;
253
+ for (const [index, name] of files.entries()) {
254
+ const segmentPath = (0, node_path_1.join)(dir, name);
255
+ const segmentBytes = new Uint8Array(await node_fs_1.promises.readFile(segmentPath));
256
+ const result = await transcribeMediaWithWhisper({
257
+ bytes: segmentBytes,
258
+ mediaType: 'audio/mpeg',
259
+ filename: name,
260
+ openaiApiKey,
261
+ falApiKey,
262
+ onProgress: null,
263
+ });
264
+ if (!usedProvider && result.provider)
265
+ usedProvider = result.provider;
266
+ if (result.error && !result.text) {
267
+ return { text: null, provider: usedProvider, error: result.error, notes };
268
+ }
269
+ if (result.text)
270
+ parts.push(result.text);
271
+ // Coarse but useful: update based on part boundaries. Duration is best-effort (RSS hints or
272
+ // ffprobe); the per-part time is stable enough to make the spinner feel alive.
273
+ const processedSeconds = Math.max(0, (index + 1) * segmentSeconds);
274
+ onProgress?.({
275
+ partIndex: index + 1,
276
+ parts: files.length,
277
+ processedDurationSeconds: typeof totalDurationSeconds === 'number' && totalDurationSeconds > 0
278
+ ? Math.min(processedSeconds, totalDurationSeconds)
279
+ : null,
280
+ totalDurationSeconds,
281
+ });
282
+ }
283
+ return { text: parts.join('\n\n'), provider: usedProvider, error: null, notes };
284
+ }
285
+ finally {
286
+ await node_fs_1.promises.rm(dir, { recursive: true, force: true }).catch(() => { });
287
+ }
288
+ }
289
+ const bytes = new Uint8Array(await node_fs_1.promises.readFile(filePath));
290
+ onProgress?.({
291
+ partIndex: null,
292
+ parts: null,
293
+ processedDurationSeconds: null,
294
+ totalDurationSeconds,
295
+ });
296
+ const result = await transcribeMediaWithWhisper({
297
+ bytes,
298
+ mediaType,
299
+ filename,
300
+ openaiApiKey,
301
+ falApiKey,
302
+ });
303
+ if (result.notes.length > 0)
304
+ notes.push(...result.notes);
305
+ return { ...result, notes };
306
+ }
307
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../src/transcription/whisper/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transcribeWithFal = transcribeWithFal;
4
+ const client_1 = require("@fal-ai/client");
5
+ const constants_js_1 = require("./constants.js");
6
+ const utils_js_1 = require("./utils.js");
7
+ async function transcribeWithFal(bytes, mediaType, apiKey) {
8
+ const fal = (0, client_1.createFalClient)({ credentials: apiKey });
9
+ const blob = new Blob([(0, utils_js_1.toArrayBuffer)(bytes)], { type: mediaType });
10
+ const audioUrl = await fal.storage.upload(blob);
11
+ const result = await Promise.race([
12
+ fal.subscribe('fal-ai/wizper', {
13
+ input: { audio_url: audioUrl, language: 'en' },
14
+ }),
15
+ new Promise((_, reject) => setTimeout(() => reject(new Error('FAL transcription timeout')), constants_js_1.TRANSCRIPTION_TIMEOUT_MS)),
16
+ ]);
17
+ return extractText(result);
18
+ }
19
+ function extractText(result) {
20
+ if (typeof result !== 'object' || result === null)
21
+ return null;
22
+ const data = 'data' in result ? result.data : result;
23
+ if (typeof data !== 'object' || data === null)
24
+ return null;
25
+ if ('text' in data && typeof data.text === 'string') {
26
+ const text = data.text.trim();
27
+ return text.length > 0 ? text : null;
28
+ }
29
+ if ('chunks' in data && Array.isArray(data.chunks)) {
30
+ const chunks = data.chunks;
31
+ const lines = [];
32
+ for (const chunk of chunks) {
33
+ if (typeof chunk === 'object' && chunk !== null && 'text' in chunk) {
34
+ const text = chunk.text;
35
+ if (typeof text === 'string' && text.trim()) {
36
+ lines.push(text.trim());
37
+ }
38
+ }
39
+ }
40
+ return lines.length > 0 ? lines.join(' ') : null;
41
+ }
42
+ return null;
43
+ }
44
+ //# sourceMappingURL=fal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fal.js","sourceRoot":"","sources":["../../../../src/transcription/whisper/fal.ts"],"names":[],"mappings":";;AAIA,8CAmBC;AAvBD,2CAAgD;AAChD,iDAAyD;AACzD,yCAA0C;AAEnC,KAAK,UAAU,iBAAiB,CACrC,KAAiB,EACjB,SAAiB,EACjB,MAAc;IAEd,MAAM,GAAG,GAAG,IAAA,wBAAe,EAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAA;IACpD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,IAAA,wBAAa,EAAC,KAAK,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;IAClE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAE/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;QAChC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE;YAC7B,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC/C,CAAC;QACF,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,EAAE,uCAAwB,CAAC,CAC3F;KACF,CAAC,CAAA;IAEF,OAAO,WAAW,CAAC,MAAM,CAAC,CAAA;AAC5B,CAAC;AAED,SAAS,WAAW,CAAC,MAAe;IAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC9D,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC,CAAC,CAAE,MAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;IAC3E,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC1D,IAAI,MAAM,IAAI,IAAI,IAAI,OAAQ,IAA0B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3E,MAAM,IAAI,GAAI,IAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QACnD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IACtC,CAAC;IACD,IAAI,QAAQ,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAE,IAA4B,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5E,MAAM,MAAM,GAAI,IAA8B,CAAC,MAAM,CAAA;QACrD,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;gBACnE,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI,CAAA;gBAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC5C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;gBACzB,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAClD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFfmpegAvailable = isFfmpegAvailable;
4
+ exports.probeMediaDurationSecondsWithFfprobe = probeMediaDurationSecondsWithFfprobe;
5
+ exports.runFfmpegSegment = runFfmpegSegment;
6
+ exports.runFfmpegTranscodeToMp3 = runFfmpegTranscodeToMp3;
7
+ exports.runFfmpegTranscodeToMp3Lenient = runFfmpegTranscodeToMp3Lenient;
8
+ exports.transcodeBytesToMp3 = transcodeBytesToMp3;
9
+ const node_child_process_1 = require("node:child_process");
10
+ const node_crypto_1 = require("node:crypto");
11
+ const node_fs_1 = require("node:fs");
12
+ const node_os_1 = require("node:os");
13
+ const node_path_1 = require("node:path");
14
+ async function isFfmpegAvailable() {
15
+ return new Promise((resolve) => {
16
+ const proc = (0, node_child_process_1.spawn)('ffmpeg', ['-version'], { stdio: ['ignore', 'ignore', 'ignore'] });
17
+ proc.on('error', () => resolve(false));
18
+ proc.on('close', (code) => resolve(code === 0));
19
+ });
20
+ }
21
+ async function probeMediaDurationSecondsWithFfprobe(filePath) {
22
+ // ffprobe is part of the ffmpeg suite. We keep this optional (best-effort) so environments
23
+ // without ffmpeg still work; it only powers nicer progress output.
24
+ return new Promise((resolve) => {
25
+ const args = [
26
+ '-v',
27
+ 'error',
28
+ '-show_entries',
29
+ 'format=duration',
30
+ '-of',
31
+ 'default=noprint_wrappers=1:nokey=1',
32
+ filePath,
33
+ ];
34
+ const proc = (0, node_child_process_1.spawn)('ffprobe', args, { stdio: ['ignore', 'pipe', 'ignore'] });
35
+ let stdout = '';
36
+ proc.stdout?.setEncoding('utf8');
37
+ proc.stdout?.on('data', (chunk) => {
38
+ if (stdout.length > 2048)
39
+ return;
40
+ stdout += chunk;
41
+ });
42
+ proc.on('error', () => resolve(null));
43
+ proc.on('close', (code) => {
44
+ if (code !== 0) {
45
+ resolve(null);
46
+ return;
47
+ }
48
+ const trimmed = stdout.trim();
49
+ const parsed = Number(trimmed);
50
+ resolve(Number.isFinite(parsed) && parsed > 0 ? parsed : null);
51
+ });
52
+ });
53
+ }
54
+ async function runFfmpegSegment({ inputPath, outputPattern, segmentSeconds, }) {
55
+ await new Promise((resolve, reject) => {
56
+ const args = [
57
+ '-hide_banner',
58
+ '-loglevel',
59
+ 'error',
60
+ '-i',
61
+ inputPath,
62
+ '-vn',
63
+ '-ac',
64
+ '1',
65
+ '-ar',
66
+ '16000',
67
+ '-b:a',
68
+ '32k',
69
+ '-f',
70
+ 'segment',
71
+ '-segment_time',
72
+ String(segmentSeconds),
73
+ '-reset_timestamps',
74
+ '1',
75
+ outputPattern,
76
+ ];
77
+ const proc = (0, node_child_process_1.spawn)('ffmpeg', args, { stdio: ['ignore', 'ignore', 'pipe'] });
78
+ let stderr = '';
79
+ proc.stderr?.setEncoding('utf8');
80
+ proc.stderr?.on('data', (chunk) => {
81
+ if (stderr.length > 8192)
82
+ return;
83
+ stderr += chunk;
84
+ });
85
+ proc.on('error', (error) => reject(error));
86
+ proc.on('close', (code) => {
87
+ if (code === 0) {
88
+ resolve();
89
+ return;
90
+ }
91
+ const detail = stderr.trim();
92
+ reject(new Error(`ffmpeg failed (${code ?? 'unknown'}): ${detail || 'unknown error'}`));
93
+ });
94
+ });
95
+ }
96
+ async function runFfmpegTranscodeToMp3({ inputPath, outputPath, }) {
97
+ await runFfmpegTranscode({
98
+ inputPath,
99
+ outputPath,
100
+ mode: 'strict',
101
+ args: [
102
+ '-hide_banner',
103
+ '-loglevel',
104
+ 'error',
105
+ '-i',
106
+ inputPath,
107
+ '-vn',
108
+ '-ac',
109
+ '1',
110
+ '-ar',
111
+ '16000',
112
+ '-b:a',
113
+ '64k',
114
+ outputPath,
115
+ ],
116
+ });
117
+ }
118
+ async function runFfmpegTranscodeToMp3Lenient({ inputPath, outputPath, }) {
119
+ await runFfmpegTranscode({
120
+ inputPath,
121
+ outputPath,
122
+ mode: 'lenient',
123
+ args: [
124
+ '-hide_banner',
125
+ '-loglevel',
126
+ 'error',
127
+ '-err_detect',
128
+ 'ignore_err',
129
+ '-fflags',
130
+ '+genpts',
131
+ '-i',
132
+ inputPath,
133
+ '-vn',
134
+ '-sn',
135
+ '-dn',
136
+ '-map',
137
+ '0:a:0?',
138
+ '-ac',
139
+ '1',
140
+ '-ar',
141
+ '16000',
142
+ '-b:a',
143
+ '64k',
144
+ outputPath,
145
+ ],
146
+ });
147
+ }
148
+ async function transcodeBytesToMp3(bytes) {
149
+ const inputPath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `summarize-whisper-input-${(0, node_crypto_1.randomUUID)()}.bin`);
150
+ const outputPath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `summarize-whisper-output-${(0, node_crypto_1.randomUUID)()}.mp3`);
151
+ try {
152
+ await node_fs_1.promises.writeFile(inputPath, bytes);
153
+ try {
154
+ await runFfmpegTranscodeToMp3({ inputPath, outputPath });
155
+ }
156
+ catch (_error) {
157
+ await runFfmpegTranscodeToMp3Lenient({ inputPath, outputPath });
158
+ }
159
+ return new Uint8Array(await node_fs_1.promises.readFile(outputPath));
160
+ }
161
+ finally {
162
+ await node_fs_1.promises.unlink(inputPath).catch(() => { });
163
+ await node_fs_1.promises.unlink(outputPath).catch(() => { });
164
+ }
165
+ }
166
+ async function runFfmpegTranscode({ inputPath, outputPath, mode, args, }) {
167
+ await new Promise((resolve, reject) => {
168
+ const proc = (0, node_child_process_1.spawn)('ffmpeg', args, { stdio: ['ignore', 'ignore', 'pipe'] });
169
+ let stderr = '';
170
+ proc.stderr?.setEncoding('utf8');
171
+ proc.stderr?.on('data', (chunk) => {
172
+ if (stderr.length > 8192)
173
+ return;
174
+ stderr += chunk;
175
+ });
176
+ proc.on('error', (error) => reject(error));
177
+ proc.on('close', (code) => {
178
+ if (code === 0) {
179
+ resolve();
180
+ return;
181
+ }
182
+ const detail = stderr.trim();
183
+ reject(new Error(`ffmpeg ${mode} transcode failed (${code ?? 'unknown'}) for ${inputPath} -> ${outputPath}: ${detail || 'unknown error'}`));
184
+ });
185
+ });
186
+ }
187
+ //# sourceMappingURL=ffmpeg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../../../src/transcription/whisper/ffmpeg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transcribeWithOpenAi = transcribeWithOpenAi;
4
+ exports.shouldRetryOpenAiViaFfmpeg = shouldRetryOpenAiViaFfmpeg;
5
+ const constants_js_1 = require("./constants.js");
6
+ const utils_js_1 = require("./utils.js");
7
+ async function transcribeWithOpenAi(bytes, mediaType, filename, apiKey) {
8
+ const form = new FormData();
9
+ const providedName = filename?.trim() ? filename.trim() : 'media';
10
+ // Whisper sometimes relies on the filename extension for format detection; ensure a reasonable one.
11
+ const safeName = (0, utils_js_1.ensureWhisperFilenameExtension)(providedName, mediaType);
12
+ form.append('file', new Blob([(0, utils_js_1.toArrayBuffer)(bytes)], { type: mediaType }), safeName);
13
+ form.append('model', 'whisper-1');
14
+ const response = await globalThis.fetch('https://api.openai.com/v1/audio/transcriptions', {
15
+ method: 'POST',
16
+ headers: { Authorization: `Bearer ${apiKey}` },
17
+ body: form,
18
+ signal: AbortSignal.timeout(constants_js_1.TRANSCRIPTION_TIMEOUT_MS),
19
+ });
20
+ if (!response.ok) {
21
+ const detail = await readErrorDetail(response);
22
+ const suffix = detail ? `: ${detail}` : '';
23
+ throw new Error(`OpenAI transcription failed (${response.status})${suffix}`);
24
+ }
25
+ const payload = (await response.json());
26
+ if (typeof payload?.text !== 'string')
27
+ return null;
28
+ const trimmed = payload.text.trim();
29
+ return trimmed.length > 0 ? trimmed : null;
30
+ }
31
+ function shouldRetryOpenAiViaFfmpeg(error) {
32
+ const msg = error.message.toLowerCase();
33
+ return (msg.includes('unrecognized file format') ||
34
+ msg.includes('could not be decoded') ||
35
+ msg.includes('format is not supported'));
36
+ }
37
+ async function readErrorDetail(response) {
38
+ try {
39
+ const text = await response.text();
40
+ const trimmed = text.trim();
41
+ if (!trimmed)
42
+ return null;
43
+ return trimmed.length > constants_js_1.MAX_ERROR_DETAIL_CHARS
44
+ ? `${trimmed.slice(0, constants_js_1.MAX_ERROR_DETAIL_CHARS)}…`
45
+ : trimmed;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../../src/transcription/whisper/openai.ts"],"names":[],"mappings":";;AAGA,oDA8BC;AAED,gEAOC;AA1CD,iDAAiF;AACjF,yCAA0E;AAEnE,KAAK,UAAU,oBAAoB,CACxC,KAAiB,EACjB,SAAiB,EACjB,QAAuB,EACvB,MAAc;IAEd,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC3B,MAAM,YAAY,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAA;IACjE,oGAAoG;IACpG,MAAM,QAAQ,GAAG,IAAA,yCAA8B,EAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACxE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,IAAA,wBAAa,EAAC,KAAK,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAA;IACpF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IAEjC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,gDAAgD,EAAE;QACxF,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;QAC9C,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,uCAAwB,CAAC;KACtD,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1C,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAA;IAC9E,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAA;IAC7D,IAAI,OAAO,OAAO,EAAE,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IACnC,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5C,CAAC;AAED,SAAgB,0BAA0B,CAAC,KAAY;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;IACvC,OAAO,CACL,GAAG,CAAC,QAAQ,CAAC,0BAA0B,CAAC;QACxC,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACpC,GAAG,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CACxC,CAAA;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,QAAkB;IAC/C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QACzB,OAAO,OAAO,CAAC,MAAM,GAAG,qCAAsB;YAC5C,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,qCAAsB,CAAC,GAAG;YAChD,CAAC,CAAC,OAAO,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/transcription/whisper/types.ts"],"names":[],"mappings":""}