lambda-audio icon indicating copy to clipboard operation
lambda-audio copied to clipboard

Error: spawn /tmp/soxi ENOENT

Open serverlesspolska opened this issue 6 years ago • 6 comments

Hello,

I think I will need your help with my issue. I'm using serverless framework and have lambda function that downloads mp3 file from a bucket, then saves it in /tmp/input.mp3

Next I'm executing code:

   import lambdaAudio from 'lambda-audio'



    const doAudioMagic = () => {
        console.log('executing soxi')
        lambdaAudio.soxi('/tmp/input.mp3')
            .then(response => {
                // Do something with the info
                console.log(response)
            })
            .catch(errorResponse => {
                console.log('Error from the soxi command:', errorResponse)
            })
    }

And I get following error:

Error: spawn /tmp/soxi ENOENT
    at exports._errnoException (util.js:1018:11)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:193:32)
    at onErrorNT (internal/child_process.js:367:16)
    at _combinedTickCallback (internal/process/next_tick.js:80:11)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

So the soxi file is not present the the path, but why? How should I "install" lambda-audio so the file is properly copied to the /tmp/ path?

serverlesspolska avatar Mar 24 '18 17:03 serverlesspolska

Hey, it's possible that input.mp3 is not present at that location at the moment you are executing soxi. Can you do following:

  1. Check if index.mp3 exists (ie. log fs.existsSync('/tmp/input.mp3')).
  2. Check if sox works, soxi is just a symlink to it, if sox works it's a symlink issue.
  3. If file exists, please provide a minimal project that I can run and reproduce the issue.

Thanks

stojanovic avatar Mar 25 '18 05:03 stojanovic

Hi @stojanovic

I changed soxi to lame, the effect is the same:

Here is my lambda function code

// eslint-disable-next-line import/prefer-default-export
import {getS3File, uploadToBucket} from "./Common"
// import lambdaAudio from 'lambda-audio'
import fs from 'fs'

export const main = (event, context, callback) => {
    const p = new Promise((resolve) => {
        resolve('success');
    });
    console.log('Extract info from mp3')
    console.log('Input data', event)


    const doAudioMagic = () => {
        const lambdaAudio = require('lambda-audio')
        console.log('executing lambda-audio')
        lambdaAudio.lame('-b 64 /tmp/input.mp3 /tmp/output.mp3')
            .then(response => {
                fs.readFile("/tmp/output2.mp3", (err, file) => {
                    if (err) throw err;
                    console.log(data);
                    uploadToBucket(event.bucket, 'output2.mp3', file, '', data.ContentType)
                        .then(console.log('Uploaded'))

                })
            })
            .catch(errorResponse => {
                console.log('Error from the lame command:', errorResponse)
            })
    }

    const processFile = data => {
        fs.writeFile("/tmp/input.mp3", data.Body, function (err) {
            if (err) {
                return console.log(err);
            }
            console.log("File saved successfully!");
            const mp3Exists = fs.existsSync('/tmp/input.mp3')
            console.log('result of fs.existsSync(\'/tmp/input.mp3\'): ', mp3Exists)
            console.log('Readdir /tmp')
            fs.readdirSync('/tmp/').forEach(console.log)
            if (mp3Exists) {
                doAudioMagic()
            }

        })
    }

    const data = getS3File(event.bucket, event.key, processFile)


    const results = {
        message: 'Go Serverless Step Function',
    }

    p
        .then(() => callback(null, JSON.stringify(results)))
        .catch(e => callback(e));
};

and the logs

Extract info from mp3
Input data { bucket: 'REDACTED', key: 'REDACTED.mp3' }
getS3File with those params: REDACTED.mp3 from REDACTED
File saved successfully!
result of fs.existsSync('/tmp/input.mp3'):  true
Readdir /tmp
input.mp3 0 [ 'input.mp3' ]
executing lambda-audio
Error: spawn /bin/lame ENOENT
    at exports._errnoException (util.js:1018:11)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:193:32)
    at onErrorNT (internal/child_process.js:367:16)
    at _combinedTickCallback (internal/process/next_tick.js:80:11)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

Looks like soxi, sox, and lame files are not copied over to /tmp/ directory. But why?

serverlesspolska avatar Mar 27 '18 11:03 serverlesspolska

Hm, are you using babel? If you are using "files" in package.json it's possible that you are not deploying sox at all. It shouldn't be in /tmp, sox is bundled with the module, but soxi is symlink, and lib creates it in /tmp because npm pack ignores symlinks and Claudia.js that I am using is using npm pack under the hood.

Can you try without babel or make sure sox is deployed? ie. code can look like this (not tested):

// eslint-disable-next-line import/prefer-default-export
cons {getS3File, uploadToBucket} = require('./Common')
const lambdaAudio = require('lambda-audio')
const fs = require('fs')

function doAudioMagic() {
  return lambdaAudio.lame('-b 64 /tmp/input.mp3 /tmp/output.mp3')
    .then(response => {
      // This is ok, because Lambda function is not doing anything else anyway
      const file = fs.readFile('/tmp/output2.mp3')

      return uploadToBucket(event.bucket, 'output2.mp3', file, '', data.ContentType)
}

function processFile(data) {
  return new Promise((resolve, reject) => {
    fs.writeFile('/tmp/input.mp3', data.Body, (err, file) => {
      if (err) {
        return reject(err)
      }

      return file
    })
  })
}

exports.handler = function(event, context, callback) {
  getS3File(event.bucket, event.key, processFile)
    .then(data => processFile(data))
    .then(() => doAudioMagic())
    .then(() => {
       const results = {  message: 'Go Serverless Step Function' }
       callback(null, JSON.stringify(results))
    })
    .catch(e => callback(e));
}

stojanovic avatar Mar 27 '18 11:03 stojanovic

Hi, I'm using serverless framework with aws-nodejs-ecma-script https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/aws-nodejs-ecma-script which automatically transpiles code using webpack

I'll try your code today

serverlesspolska avatar Mar 28 '18 12:03 serverlesspolska

Hm, I am not sure what that template sends to AWS Lambda, but you need to make sure that sox exists. You can probably download it from S3 as an alternative.

stojanovic avatar Mar 28 '18 12:03 stojanovic

Hi @stojanovic

Finally, I managed to include sox and lame binaries in the generated .zip that is uploaded into lambda. Unfortunately, I still have problems, though.

No matter if the contents of the zip file are like that:

unzip -l .serverless\musicquiz.zip
Archive:  .serverless\musicquiz.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   484896  1980-01-01 00:00   bin/lame
  2913860  1980-01-01 00:00   bin/sox
   763687  1980-01-01 00:00   ExtractInfo.js
     3316  1980-01-01 00:00   SaveIntoDynamo.js
  3062571  1980-01-01 00:00   TriggerOrchestrator.js
---------                     -------
  7228330                     5 files

or

Archive:  .serverless\musicquiz.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   763689  1980-01-01 00:00   ExtractInfo.js
   484896  1980-01-01 00:00   node_modules/lambda-audio/bin/lame
  2913860  1980-01-01 00:00   node_modules/lambda-audio/bin/sox
     3316  1980-01-01 00:00   SaveIntoDynamo.js
  3062571  1980-01-01 00:00   TriggerOrchestrator.js
---------                     -------
  7228332                     5 files

I still get the same error from lambda function:

Error: spawn /bin/lame ENOENT
    at exports._errnoException (util.js:1018:11)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:193:32)
    at onErrorNT (internal/child_process.js:367:16)
    at _combinedTickCallback (internal/process/next_tick.js:80:11)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

Files are available to the functions. Following code:

    const myPath = "bin/lame"
    console.log(`File ${myPath} exists ${fs.existsSync(myPath)}`)
    console.log(`File /${myPath} exists ${fs.existsSync('/'+myPath)}`)

generates:

File bin/lame exists true
File /bin/lame exists false

So I think this is just a matter of putting sox and lame files in proper path in the zip file, so they are where lambda-audio expectes them to be.

Could you please tell me where those files should be located? Where they are in the .zip file generated by ClaudiaJS?

serverlesspolska avatar Apr 10 '18 17:04 serverlesspolska