Search code examples
javascriptfirebasegoogle-cloud-platformffmpeggoogle-cloud-functions

Firebase function to convert YouTube to mp3


I want to deploy to Firebase cloud functions.

However, I get a vague error: “Cannot analyze code” after it goes through the initial pre deploy checks successfully.

But I cannot figure out the problem given the vagueness of the error.

It looks right with these requirements:

  • receive a POST with JSON body of YouTube videoID as a string
  • Download locally using the YouTube download package
  • Pipe to the ffmpeg package and save mp3 to the local temp
  • Store in default bucket on firestore storage
  • Apply make public method to make public
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const ytdl = require('ytdl-core');
const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');
const path = require('path');
const os = require('os');

admin.initializeApp();

// Set the path to the FFmpeg binary
const ffmpegPath = path.join(__dirname, 'bin', 'ffmpeg');
ffmpeg.setFfmpegPath(ffmpegPath);

exports.audioUrl = functions.https.onRequest(async (req, res) => {
    if (req.method !== 'POST') {
        res.status(405).send('Method Not Allowed');
        return;
    }

    const videoId = req.body.videoID;
    const videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
    const audioPath = path.join(os.tmpdir(), `${videoId}.mp3`);

    try {
        await new Promise((resolve, reject) => {
            ytdl(videoUrl, { filter: format => format.container === 'mp4' })
                .pipe(ffmpeg())
                .audioCodec('libmp3lame')
                .save(audioPath)
                .on('end', resolve)
                .on('error', reject);
        });

        const bucket = admin.storage().bucket();
        const file = bucket.file(`${videoId}.mp3`);
        await bucket.upload(audioPath, {
            destination: file.name,
            metadata: {
                contentType: 'audio/mp3',
            },
        });

        // Make the file publicly accessible
        await file.makePublic();

        const publicUrl = file.publicUrl();
        res.status(200).send({ url: publicUrl });
    } catch (error) {
        console.error('Error processing video:', error);
        res.status(500).send('Internal Server Error');
    }
});

The following is the package.json file which is used to reference the dependencies for the function, as well as the entry point, which I believe just needs to be the name of the filename with the code:

{
  "name": "firebase-functions",
  "description": "Firebase Cloud Functions",
  "main": "audioUrl.js", 
  "dependencies": {
    "firebase-admin": "^10.0.0",
    "firebase-functions": "^4.0.0",
    "ytdl-core": "^4.9.1",
    "fluent-ffmpeg": "^2.1.2"
  },
  "engines": {
    "node": "18"
  },
  "private": true
}

(Edit) Here is the error:

 deploying functions
✔  functions: Finished running predeploy script.
i  functions: preparing codebase default for deployment
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
i  artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled...
✔  functions: required API cloudbuild.googleapis.com is enabled
✔  artifactregistry: required API artifactregistry.googleapis.com is enabled
✔  functions: required API cloudfunctions.googleapis.com is enabled
i  functions: Loading and analyzing source code for codebase default to determine what to deploy
Serving at port 8171

shutdown requested via /__/quitquitquit


Error: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error
Failed to load function definition from source: FirebaseError: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error

I get the same error when running the following:

firebase deploy --only functions:audioUrl

And I thought I might get more detailed errors using the emulator:

firebase emulators:start

Under the emulator I had this additional error initially:

Your requested "node" version "18" doesn't match your global version "16". Using node@16 from host.

Solution

  • You declare using node.js version 18 in the package.json:

    engines": {
      "node": "18"
      }
    

    but the installed version on your computer is 16.15.1.

    You should adapt the package.json file as follows:

    engines": {
      "node": "16"
      }