0

I have uploaded a file using my google app engine backend to my storage bucket but now I cannot access the file to pass in into ffmpeg. I am getting this error message from the try-catch: "The input file does not exist". I can see that the file was uploaded because I checked my developer console under the storage bucket. I am using the boilerplate code provided by google but added the ffmpeg for testing. I am trying to access the path to the uploaded file using, but it is incorrect, though I am getting the bucket.name value and the blob.name value. I am using the "flex" environment for this.

const originalFilePath = `gs://${bucket.name}/${blob.name}`; 

here is the full code:

const process = require('process'); // Required to mock environment variables
const express = require('express');
const helpers = require('./helpers/index');
const Multer = require('multer');
const bodyParser = require('body-parser');
const ffmpeg = require("ffmpeg"); //https://www.npmjs.com/package/ffmpeg
const {Storage} = require('@google-cloud/storage');

// Instantiate a storage client
const storage = new Storage();

const app = express();
app.set('view engine', 'pug');
app.use(bodyParser.json());

// Multer is required to process file uploads and make them available via
// req.files.
const multer = Multer({
storage: Multer.memoryStorage(),
 limits: {
  fileSize: 5 * 1024 * 1024, // no larger than 5mb, you can change as needed.
 },
});

// A bucket is a container for objects (files).
const bucket = storage.bucket(process.env.GCLOUD_STORAGE_BUCKET);

// Display a form for uploading files.
app.get('/', (req, res) => {
 res.render('form.pug');
});

// Process the file upload and upload to Google Cloud Storage.
app.post('/upload', multer.single('file'), (req, res, next) => {

if (!req.file) {
 res.status(400).send('No file uploaded.');
 return;
}

// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
 resumable: false,
});

blobStream.on('error', err => {
 next(err);
});

blobStream.on('finish', () => {

const audioFile = helpers.replaceAllExceptNumbersAndLetters(new Date());

// this path is incorrect but I cannot find correct way to do it
const originalFilePath = `gs://${bucket.name}/${blob.name}`; 

const filePathOutput = `gs://${bucket.name}/${audioFile}.mp3`;

try {
 const process = new ffmpeg(originalFilePath);
 process.then(function (video) {
 // Callback mode
 video.fnExtractSoundToMP3(filePathOutput, (error, file) => {
 if (!error)
  res.send(`audio file: ${file}`);
 });
}, (error) => {
 res.send(`process error: ${error}`);

});
} catch (e) {
 res.send(`try catch error: ${JSON.stringify(e)} | bucket: ${JSON.stringify(bucket)} | 
 blob: : ${JSON.stringify(blob)}`);
}  


});

blobStream.end(req.file.buffer);

});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
 console.log(`App listening on port ${PORT}`);
 console.log('Press Ctrl+C to quit.');
});


module.exports = app;
jessiPP
  • 436
  • 1
  • 6
  • 21
  • 1
    I don't think ```gs://....``` is available on dev env. On local environment (at least for images), I usually get something like ```http://localhost:8080/_ah/img/encoded_gs_file:....```. the key here is the url has ```encoded_gs_file:```. If this is production, then you should use the url ```https://storage.googleapis.com/${bucket.name}/${blob.name}``` – NoCommandLine Apr 11 '21 at 03:58
  • Thank you @NoCommandLine. I have tried that already but I am getting an error when I try to pass it the ffmpeg function. This is the error from try-catch: {"code":103,"msg":"The input file does not exist"}. Do you know of any special permissions that need to be set in the google developer console before the file can be accessed this way? – jessiPP Apr 11 '21 at 05:19
  • After more checks I now belive its not an issue with google apis but with the node-ffmpeg library. It does not accept a URL only local files with a path. – jessiPP Apr 11 '21 at 21:38
  • This repo showed me how to work out the paths and what npm packages to use: https://github.com/firebase/functions-samples/blob/master/ffmpeg-convert-audio/functions/index.js, also, I used this post https://stackoverflow.com/questions/62652721/nodejs-fluent-ffmpeg-cannot-find-ffmpeg-for-firebase-cloud-functions to figure out an alternative to ffmpeg-static. everything now works like a charm. – jessiPP Apr 12 '21 at 22:31

1 Answers1

2

I have created this community wiki using the information in the comments.

This repository shows the required npm packages

const functions = require('firebase-functions');
const { Storage } = require('@google-cloud/storage');
const path = require('path');
const os = require('os');
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');

and how to correctly build the path of the file in the bucket to pass it into ffmpeg.

// Get the file name.
  const fileName = path.basename(filePath);
  // Exit if the audio is already converted.
  if (fileName.endsWith('_output.flac')) {
    functions.logger.log('Already a converted audio.');
    return null;
  }

  // Download file from bucket.
  const bucket = gcs.bucket(fileBucket);
  const tempFilePath = path.join(os.tmpdir(), fileName);
  // We add a '_output.flac' suffix to target audio file name. That's where we'll upload the converted audio.
  const targetTempFileName = fileName.replace(/\.[^/.]+$/, '') + '_output.flac';
  const targetTempFilePath = path.join(os.tmpdir(), targetTempFileName);
  const targetStorageFilePath = path.join(path.dirname(filePath), targetTempFileName);

  await bucket.file(filePath).download({destination: tempFilePath});
  functions.logger.log('Audio downloaded locally to', tempFilePath);
  // Convert the audio to mono channel using FFMPEG.

  let command = ffmpeg(tempFilePath)
      .setFfmpegPath(ffmpeg_static)
      .audioChannels(1)
      .audioFrequency(16000)
      .format('flac')
      .output(targetTempFilePath);

  await promisifyCommand(command);
  functions.logger.log('Output audio created at', targetTempFilePath);

This another post shows how to replace ffmpeg_static.path installing "ffmpeg-installer/ffmpeg" instead and how to set the correct path

const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');

let command = ffmpeg(tempFilePath)
      .setFfmpegPath(ffmpegPath)
      .audioChannels(1)
      .audioFrequency(16000)
      .format('flac')
      .output(targetTempFilePath);
SSoulMiles
  • 88
  • 9