@pipedream/openai 0.1.4 → 0.1.5
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.
|
@@ -1,18 +1,27 @@
|
|
|
1
|
+
import axios from "axios";
|
|
1
2
|
import fs from "fs";
|
|
2
|
-
import
|
|
3
|
-
|
|
3
|
+
import {
|
|
4
|
+
join, extname,
|
|
5
|
+
} from "path";
|
|
4
6
|
import FormData from "form-data";
|
|
5
7
|
import { ConfigurationError } from "@pipedream/platform";
|
|
6
8
|
import common from "../common/common.mjs";
|
|
7
9
|
import constants from "../common/constants.mjs";
|
|
8
10
|
import lang from "../common/lang.mjs";
|
|
9
11
|
import openai from "../../app/openai.app.mjs";
|
|
12
|
+
import { promisify } from "util";
|
|
13
|
+
import stream from "stream";
|
|
14
|
+
import { exec } from "child_process";
|
|
15
|
+
import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
|
|
10
16
|
|
|
11
17
|
const COMMON_AUDIO_FORMATS_TEXT = "Your audio file must be in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm.";
|
|
12
18
|
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
const pipelineAsync = promisify(stream.pipeline);
|
|
21
|
+
|
|
13
22
|
export default {
|
|
14
23
|
name: "Create Transcription",
|
|
15
|
-
version: "0.0.
|
|
24
|
+
version: "0.0.3",
|
|
16
25
|
key: "openai-create-transcription",
|
|
17
26
|
description: "Transcribes audio into the input language. [See docs here](https://platform.openai.com/docs/api-reference/audio/create).",
|
|
18
27
|
type: "action",
|
|
@@ -80,6 +89,95 @@ export default {
|
|
|
80
89
|
|
|
81
90
|
return props;
|
|
82
91
|
},
|
|
92
|
+
methods: {
|
|
93
|
+
createForm({
|
|
94
|
+
file, outputDir,
|
|
95
|
+
}) {
|
|
96
|
+
const form = new FormData();
|
|
97
|
+
form.append("model", "whisper-1");
|
|
98
|
+
if (this.prompt) form.append("prompt", this.prompt);
|
|
99
|
+
if (this.temperature) form.append("temperature", this.temperature);
|
|
100
|
+
if (this.language) form.append("language", this.language);
|
|
101
|
+
if (this.responseFormat) form.append("response_format", this.responseFormat);
|
|
102
|
+
const readStream = fs.createReadStream(join(outputDir, file));
|
|
103
|
+
form.append("file", readStream);
|
|
104
|
+
return form;
|
|
105
|
+
},
|
|
106
|
+
async chunkFileAndTranscribe({
|
|
107
|
+
file, $,
|
|
108
|
+
}) {
|
|
109
|
+
const outputDir = join("/tmp", "chunks");
|
|
110
|
+
await execAsync(`mkdir -p ${outputDir}`);
|
|
111
|
+
await execAsync(`rm -f ${outputDir}/*`);
|
|
112
|
+
|
|
113
|
+
await this.chunkFile({
|
|
114
|
+
file,
|
|
115
|
+
outputDir,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const files = await fs.promises.readdir(outputDir);
|
|
119
|
+
const transcription = await this.transcribeFiles({
|
|
120
|
+
files,
|
|
121
|
+
outputDir,
|
|
122
|
+
$,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
transcription,
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
async chunkFile({
|
|
130
|
+
file, outputDir,
|
|
131
|
+
}) {
|
|
132
|
+
const ffmpegPath = ffmpegInstaller.path;
|
|
133
|
+
const ext = extname(file);
|
|
134
|
+
|
|
135
|
+
const fileSizeInMB = fs.statSync(file).size / (1024 * 1024);
|
|
136
|
+
const numberOfChunks = Math.ceil(fileSizeInMB / 24);
|
|
137
|
+
|
|
138
|
+
if (numberOfChunks === 1) {
|
|
139
|
+
await execAsync(`cp ${file} ${outputDir}/chunk-000${ext}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const { stdout } = await execAsync(`${ffmpegPath} -i ${file} 2>&1 | grep "Duration"`);
|
|
144
|
+
const duration = stdout.match(/\d{2}:\d{2}:\d{2}\.\d{2}/s)[0];
|
|
145
|
+
const [
|
|
146
|
+
hours,
|
|
147
|
+
minutes,
|
|
148
|
+
seconds,
|
|
149
|
+
] = duration.split(":").map(parseFloat);
|
|
150
|
+
|
|
151
|
+
const totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds;
|
|
152
|
+
const segmentTime = Math.ceil(totalSeconds / numberOfChunks);
|
|
153
|
+
|
|
154
|
+
const command = `${ffmpegPath} -i ${file} -f segment -segment_time ${segmentTime} -c copy ${outputDir}/chunk-%03d${ext}`;
|
|
155
|
+
await execAsync(command);
|
|
156
|
+
},
|
|
157
|
+
async transcribeFiles({
|
|
158
|
+
files, outputDir, $,
|
|
159
|
+
}) {
|
|
160
|
+
const transcriptions = await Promise.all(files.map((file) => this.transcribe({
|
|
161
|
+
file,
|
|
162
|
+
outputDir,
|
|
163
|
+
$,
|
|
164
|
+
})));
|
|
165
|
+
return transcriptions.join(" ");
|
|
166
|
+
},
|
|
167
|
+
async transcribe({
|
|
168
|
+
file, outputDir, $,
|
|
169
|
+
}) {
|
|
170
|
+
const form = this.createForm({
|
|
171
|
+
file,
|
|
172
|
+
outputDir,
|
|
173
|
+
});
|
|
174
|
+
const response = await this.openai.createTranscription({
|
|
175
|
+
$,
|
|
176
|
+
form,
|
|
177
|
+
});
|
|
178
|
+
return response.text;
|
|
179
|
+
},
|
|
180
|
+
},
|
|
83
181
|
async run({ $ }) {
|
|
84
182
|
const {
|
|
85
183
|
url,
|
|
@@ -90,38 +188,37 @@ export default {
|
|
|
90
188
|
throw new Error("Must specify either File URL or File Path");
|
|
91
189
|
}
|
|
92
190
|
|
|
93
|
-
|
|
94
|
-
form.append("model", "whisper-1");
|
|
95
|
-
if (this.prompt) form.append("prompt", this.prompt);
|
|
96
|
-
if (this.temperature) form.append("temperature", this.temperature);
|
|
97
|
-
if (this.language) form.append("language", this.language);
|
|
98
|
-
if (this.responseFormat) form.append("response_format", this.responseFormat);
|
|
191
|
+
let file;
|
|
99
192
|
|
|
100
193
|
if (path) {
|
|
101
194
|
if (!fs.existsSync(path)) {
|
|
102
195
|
throw new Error(`${path} does not exist`);
|
|
103
196
|
}
|
|
104
|
-
|
|
105
|
-
form.append("file", readStream);
|
|
197
|
+
file = path;
|
|
106
198
|
} else if (url) {
|
|
107
199
|
const ext = extname(url);
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await new Promise((resolve, reject) => {
|
|
115
|
-
writeStream.on("finish", resolve);
|
|
116
|
-
writeStream.on("error", reject);
|
|
117
|
-
responseStream.on("error", reject);
|
|
200
|
+
|
|
201
|
+
const response = await axios({
|
|
202
|
+
method: "GET",
|
|
203
|
+
url,
|
|
204
|
+
responseType: "stream",
|
|
205
|
+
timeout: 250000,
|
|
118
206
|
});
|
|
119
|
-
|
|
120
|
-
|
|
207
|
+
|
|
208
|
+
const bufferStream = new stream.PassThrough();
|
|
209
|
+
response.data.pipe(bufferStream);
|
|
210
|
+
|
|
211
|
+
const downloadPath = join("/tmp", `audio${ext}`);
|
|
212
|
+
const writeStream = fs.createWriteStream(downloadPath);
|
|
213
|
+
|
|
214
|
+
await pipelineAsync(bufferStream, writeStream);
|
|
215
|
+
|
|
216
|
+
file = downloadPath;
|
|
121
217
|
}
|
|
122
|
-
|
|
218
|
+
|
|
219
|
+
const response = await this.chunkFileAndTranscribe({
|
|
220
|
+
file,
|
|
123
221
|
$,
|
|
124
|
-
form,
|
|
125
222
|
});
|
|
126
223
|
|
|
127
224
|
if (response) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/openai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Pipedream OpenAI Components",
|
|
5
5
|
"main": "app/openai.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
17
18
|
"@pipedream/platform": "^1.2.1",
|
|
18
19
|
"@pipedream/types": "^0.1.4",
|
|
19
20
|
"got": "^12.6.0",
|