Skip to content

Instantly share code, notes, and snippets.

@bramses
Last active October 16, 2024 03:35
Show Gist options
  • Save bramses/dc314b25e6d9125b06feff7a6aecb00a to your computer and use it in GitHub Desktop.
Save bramses/dc314b25e6d9125b06feff7a6aecb00a to your computer and use it in GitHub Desktop.
<audio controls src="your-cfURL-from-server">
<track kind="captions" />
Your browser does not support the
<code>audio</code> element.
</audio>
import { R2 } from 'node-cloudflare-r2';
import ffmpeg from 'fluent-ffmpeg';
import ffmpegPath from 'ffmpeg-static';
import multer from "multer";
import express from "express";
const r2AccountId = process.env.R2_ACCOUNT_ID;
const r2AccessKeyId = process.env.R2_ACCESS_KEY_ID;
const r2SecretAccessKey = process.env.R2_SECRET_ACCESS_KEY;
const r2BucketName = process.env.R2_BUCKET_NAME;
const r2Endpoint = `https://${r2AccountId}.r2.cloudflarestorage.com`;
ffmpeg.setFfmpegPath(ffmpegPath);
const audioStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`);
},
});
const fileFilter = (req, file, cb) => {
const allowedTypes = [
"audio/m4a",
"audio/mp4",
"audio/mpeg",
"audio/mp3",
"audio/wav",
"audio/ogg",
"audio/webm",
"image/png",
"image/jpeg",
];
const mimeType = mime.getType(file.originalname);
if (allowedTypes.includes(mimeType)) {
file.mimetype = mimeType; // Manually set the correct mimetype
cb(null, true);
} else {
cb(
new Error(
`Unsupported file format. Supported formats: ${allowedTypes.join(", ")}`
),
false
);
}
};
const audioUpload = multer({ storage: audioStorage, fileFilter });
// ...
const r2 = new R2({
accountId: r2AccountId,
accessKeyId: r2AccessKeyId,
secretAccessKey: r2SecretAccessKey,
});
const bucket = r2.bucket(r2BucketName);
bucket.provideBucketPublicUrl('your-pub-bucket');
console.log(await bucket.exists()); // true
/**
* Converts an m4a file to mp3.
*
* @param {string} inputFilePath - The path to the input m4a file.
* @param {string} outputFilePath - The path to save the converted mp3 file.
* @returns {Promise<void>} - A promise that resolves when the conversion is complete.
*/
function convertM4aToMp3(inputFilePath, outputFilePath) {
return new Promise((resolve, reject) => {
ffmpeg(inputFilePath)
.toFormat('mp3')
.on('end', () => {
console.log('Conversion finished!');
resolve();
})
.on('error', (err) => {
console.error('Error during conversion:', err.message);
reject(err);
})
.save(outputFilePath);
});
}
server.post("/transcribe", audioUpload.single("audio"), async (req, res) => {
try {
const file = req.file;
if (!file) {
return res.status(400).json({ error: "Audio file is required" });
}
let filePath = file.path;
// if file is m4a, convert to mp3
if (file.originalname.endsWith('.m4a')) {
const outputFilePath = file.path.replace('.m4a', '.mp3');
await convertM4aToMp3(file.path, outputFilePath);
filePath = outputFilePath;
}
const content = fs.readFileSync(filePath);
const uploadResult = await bucket.upload(content, `${Date.now()}_${filePath.replace('uploads/', '')}`);
const cfURL = uploadResult.publicUrl;
const transcription = await transcribe(file.path); // im using openai whisper and gpt 4o to transcribe my audio to text
// delete the file after processing
fs.unlinkSync(filePath);
if (file.path !== filePath) {
fs.unlinkSync(file.path);
}
res.json({ transcription, cfURL });
} catch (err) {
console.error(err);
// unlink file if it exists
fs.unlinkSync(filePath);
if (file.path !== filePath) {
fs.unlinkSync(file.path);
}
res.status(500).json({ error: err.message });
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment