@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 got from "got";
3
- import { extname } from "path";
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.2",
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
- const form = new FormData();
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
- const readStream = fs.createReadStream(path);
105
- form.append("file", readStream);
197
+ file = path;
106
198
  } else if (url) {
107
199
  const ext = extname(url);
108
- // OpenAI only supports a few audio formats and uses the extension to determine the format
109
- const tempFilePath = `/tmp/audioFile${ext}`;
110
-
111
- const writeStream = fs.createWriteStream(tempFilePath);
112
- const responseStream = got.stream(url);
113
- responseStream.pipe(writeStream);
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
- const readStream = fs.createReadStream(tempFilePath);
120
- form.append("file", readStream);
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
- const response = await this.openai.createTranscription({
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.4",
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",