whispermix 1.2.2 → 1.3.4

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 (3) hide show
  1. package/README.md +7 -1
  2. package/index.js +60 -1
  3. package/package.json +7 -4
package/README.md CHANGED
@@ -53,6 +53,12 @@ whisperGroq.fromStream(audioStream)
53
53
  .catch(error => console.error(error));
54
54
  ```
55
55
 
56
+ ### ⏱️ Long Audio Processing
57
+
58
+ WhisperMix automatically handles long audio files by splitting them into smaller segments if they exceed 15 minutes in duration. This process is transparent to the user:
59
+
60
+ The segmented transcriptions are automatically merged into a single result, ensuring a smooth experience when working with content of any length.
61
+
56
62
  ### 🚦 Bottleneck Configuration
57
63
 
58
64
  WhisperMix uses Bottleneck for rate limiting. You can configure the Bottleneck settings when initializing WhisperMix:
@@ -88,7 +94,7 @@ Creates a new WhisperMix instance.
88
94
 
89
95
  - `options.model`: The model to use for transcription. Can be 'whisper-1' (OpenAI) or 'whisper-large-v3' (Groq).
90
96
  - `options.bottleneck`: (Optional) Configuration for Bottleneck rate limiting.
91
-
97
+ - `options.chunkSize`: (Optional) The size in seconds of the chunks to split the audio into. Default is 890 seconds.
92
98
  ### `whisper.fromFile(filePath)`
93
99
 
94
100
  Transcribes audio from a file.
package/index.js CHANGED
@@ -2,6 +2,10 @@ const axios = require('axios');
2
2
  const FormData = require('form-data');
3
3
  const fs = require('fs');
4
4
  const Bottleneck = require('bottleneck');
5
+ const path = require('path');
6
+ const ffmpeg = require('fluent-ffmpeg');
7
+ const { getAudioDurationInSeconds } = require('get-audio-duration');
8
+ const os = require('os'); // For temporary directory
5
9
 
6
10
  class WhisperMix {
7
11
  constructor(setup = {}) {
@@ -13,6 +17,7 @@ class WhisperMix {
13
17
  reservoirRefreshAmount: 18,
14
18
  reservoirRefreshInterval: 60000
15
19
  };
20
+ this.chunkSize = 15 * 60 - 10; // 14 minutes 50 seconds
16
21
 
17
22
  const config = {
18
23
  'whisper-1': {
@@ -34,7 +39,61 @@ class WhisperMix {
34
39
  }
35
40
 
36
41
  async fromFile(filePath) {
37
- return this.fromStream(fs.createReadStream(filePath));
42
+ const absolutePath = path.resolve(filePath);
43
+ const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'whispermix-chunks-'));
44
+
45
+ try {
46
+ const duration = await getAudioDurationInSeconds(absolutePath);
47
+
48
+ if (duration <= this.chunkSize) {
49
+ // Process as a single file
50
+ return this.fromStream(fs.createReadStream(absolutePath));
51
+ } else {
52
+ // Split audio and process chunks
53
+ let accumulatedTranscription = "";
54
+ const numChunks = Math.ceil(duration / this.chunkSize);
55
+
56
+ for (let i = 0; i < numChunks; i++) {
57
+ const chunkPath = path.join(tempDir, `chunk-${i}.mp3`);
58
+ const startTime = i * this.chunkSize;
59
+
60
+ await new Promise((resolve, reject) => {
61
+ ffmpeg(absolutePath)
62
+ .setStartTime(startTime)
63
+ .setDuration(this.chunkSize)
64
+ .output(chunkPath)
65
+ .on('end', () => {
66
+ console.log(`Chunk ${i + 1}/${numChunks} created: ${chunkPath}`);
67
+ resolve();
68
+ })
69
+ .on('error', (err) => {
70
+ console.error(`Error creating chunk ${i + 1}:`, err);
71
+ reject(err);
72
+ })
73
+ .run();
74
+ });
75
+
76
+ const chunkStream = fs.createReadStream(chunkPath);
77
+ const transcription = await this.fromStream(chunkStream);
78
+ accumulatedTranscription += (transcription + " ").trimStart();
79
+
80
+ // Clean up chunk immediately after processing
81
+ try {
82
+ await fs.promises.unlink(chunkPath);
83
+ } catch (unlinkErr) {
84
+ console.warn(`Could not delete chunk ${chunkPath}:`, unlinkErr);
85
+ }
86
+ }
87
+ return accumulatedTranscription.trim();
88
+ }
89
+ } finally {
90
+ // Clean up the temporary directory
91
+ try {
92
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
93
+ } catch (rmErr) {
94
+ console.warn(`Could not delete temporary directory ${tempDir}:`, rmErr);
95
+ }
96
+ }
38
97
  }
39
98
 
40
99
  async fromStream(audioStream) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whispermix",
3
3
  "description": "🎙️ WhisperMix is a versatile module for transcribing audio using OpenAI’s Whisper or Groq’s Whisper v3 model.",
4
- "version": "1.2.2",
4
+ "version": "1.3.4",
5
5
  "keywords": [
6
6
  "whisper",
7
7
  "openai",
@@ -23,7 +23,8 @@
23
23
  "streaming",
24
24
  "file-processing",
25
25
  "multilingual",
26
- "bottleneck"
26
+ "bottleneck",
27
+ "clasen"
27
28
  ],
28
29
  "repository": {
29
30
  "type": "git",
@@ -39,8 +40,10 @@
39
40
  "url": "https://github.com/clasen/WhisperMix/issues"
40
41
  },
41
42
  "dependencies": {
42
- "axios": "^1.7.4",
43
+ "axios": "^1.9.0",
43
44
  "bottleneck": "^2.19.5",
44
- "form-data": "^4.0.0"
45
+ "fluent-ffmpeg": "^2.1.3",
46
+ "form-data": "^4.0.0",
47
+ "get-audio-duration": "^4.0.1"
45
48
  }
46
49
  }