Nodejs - Extraindo Áudio de Vídeos

Olá meus Unicórnios! 🦄✨

Em muitos momentos, precisamos extrair o áudio de um vídeo para permitir transcrever este áudio, ou tratá-lo de forma diferenciada.

Isto pode ser feito com o FFmpeg, uma ferramenta excelente para processar áudio/vídeo.

Extraindo Áudio (MP3) de arquivo de Vídeo (MP4)

Inicialmente, instale o FFmpeg em seu Servidor:

Download FFmpeg

💡
Caso você esteja utilizando Windows, não esqueça de adicionar a pasta "bin" do FFmpeg ao PATH de seu computador, para que esteja disponível, no CDM, o "ffmpeg.exe"

Para extrair o Áudio de um Vídeo, iremos utilizar o comando abaixo:

ffmpeg -i [VIDEO] -vn -acodec libmp3lame [AUDIO]

Exemplo:

ffmpeg -i teste1.mp4 -vn -acodec libmp3lame teste1.mp3

Quando o comando é executado, o FFmpeg faz a extração e nos mostra os detalhes:

Porém, simplesmente executar um comando no CMD, qualquer um faz.

Vamos melhorar isto, vamos criar uma API em Nodejs, que receba o link de um vídeo, e retorne o MP3 extraído.

Criando nossa API com NodeJs

A função que iremos criar ira se chamar "executar", ira rodar na porta "8019", e ira esperar um POST de um Json:

const fs = require("fs");
const { v4: uuidv4 } = require("uuid");
const path = require('path');
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);

const express = require("express");
const bodyParser = require("body-parser");
const axios = require('axios');
const { pipeline } = require('stream/promises');

var app = express();
app.use(bodyParser.json());

app.post("/executar", async function(req, res){

  //

});

app.listen(8019, function(){ 
    
    console.log("API Iniciada");

});

Observe que já inclui as dependências que iremos utilizar:

  • fs: Para manipular arquivos
  • uuid: Para gerar uma string aleatória que iremos utilizar no nome dos arquivos
  • path: Para criar os caminhos independente do sistema operacional
  • promisify: Para converter funções que retornam um callback em funções que retornam promessas (Sendo controladas pelo async/await)
  • exec: Para executar o "ffmpeg" que é uma aplicação instalada no servidor
  • express: Para criar a API em si
  • bodyParser: Para permitir configurar o Body das chamadas (Iremos utilizar para definir que esperamos receber um Json)
  • axios: Para as requisições (Iremos utilizar para carregar o arquivo de vídeo)
  • pipeline: Para escrevermos o Vídeo retornado em um arquivo físico

O Json que será enviado possui apenas um elemento "url":

{
  "url": "https://download.samplelib.com/mp4/sample-5s.mp4"
}

Antes de começarmos a aplicar qualquer ação, precisamos garantir que a Url foi informada e que o arquivo é um arquivo de extensão "mp4":

if(!req.body.url)
{

    return res.status(400).json({ Message: "Preencha o Campo 'URL'" });

}

if(!req.body.url.endsWith('.mp4')) 
{

    return res.status(400).json({ Message: "Preeencha a 'URL' com um arquivo MP4" });

}

Neste momento ainda não podemos validar que o arquivo é realmente um MP4, apenas podemos validar a extensão (Afinal, ainda não temos o arquivo em si apenas a Url dele).

Mas iremos fazer esta validação posteriormente, para garantir que o "ffmpeg" não falhe.

Antes de seguirmos para as partes mais interessantes, iremos criar uma constante com um nome aleatório, utilizando o "uuidv4" e já popular uma constante com o caminho completo do arquivo MP4 e o do arquivo MP3:

const InfFilename = uuidv4();
const InfArquivoVideo = path.join("tmp", InfFilename + ".mp4");
const InfArquivoAudio = path.join("tmp", InfFilename + ".mp3");

Sempre prefiro criar um nome novo, do que utilizar o nome original do arquivo.

Isto evita problemas com caracteres especiais presentes no nome do arquivo, que acaba gerando problemas de compatibilidade.

Observe que iremos utilizar a pasta "tmp", então iremos configurar o "express" para tornar público o conteúdo desta pasta:

app.use('/tmp', express.static(path.join(__dirname, 'tmp')));

Já temos o que precisamos, vamos fazer um Axios para carregar o Vídeo:

const response = await axios({
url: req.body.url,
method: 'GET',
responseType: 'stream'
});

Não podemos deixar de validar que tivemos um Sucesso

if(response.status !== 200) 
{

    return res.status(400).json({ Message: "Falha em Obter Arquivo da URL Informada: " + response.status });

}

E que o tipo de arquivo realmente é um MP4:

const contentType = response.headers['content-type'];

if(!contentType || !contentType.includes('video/mp4')) 
{

    return res.status(400).json({ Message: "A URL informada não contém um arquivo MP4" });

}

Pronto! Temos um arquivo retornado com sucesso.

Vamos salvar o conteúdo do arquivo, retornado pelo Axios, no arquivo MP4 que preparamos na constante anterior:

await pipeline(response.data, fs.createWriteStream(InfArquivoVideo));

Neste momento ficou fácil, já podemos acionar o "ffmpeg", indicando o arquivo MP4 que acabamos de baixar, e o arquivo MP3 que queremos criar:

await exec("ffmpeg -i " + InfArquivoVideo + " -vn -acodec libmp3lame " + InfArquivoAudio);

Embora podemos utilizar o try/catch para validar possíveis problemas no comando executado, sou muito a favor de validar se o arquivo MP3 foi realmente carregado:

if( fs.existsSync(InfArquivoAudio) )
{

    // Sucesso

}else{

    // Falha

}

PRONTO! Temos uma API que ira nos retornar o MP3:

GitHub - cmacetko/ExtrairAudioDeVideo
Contribute to cmacetko/ExtrairAudioDeVideo development by creating an account on GitHub.

Vamos Testar?

Iremos fazer um POST para a API "http://127.0.0.1:8019/executar" passando a Url do vídeo no Json:

{
  "url": "https://download.samplelib.com/mp4/sample-5s.mp4"
}

O retorno será um elemento "arquivo" indicando o caminho do arquivo gerado:

{
  "arquivo": "/tmp/83331471-3e57-41cf-917d-a06a3b9d94fa.mp3"
}

Depois disto, podemos utilizar o arquivo MP3 diretamente em:

http://127.0.0.1:8019/tmp/83331471-3e57-41cf-917d-a06a3b9d94fa.mp3

O que fazer com isto?

Este recurso é muito útil quando precisamos tratar as conversas de um vídeo.

Então, primeiro extraímos o áudio, depois enviamos para a OpenAI transcrever:

ChatGPT — Transcrever arquivo de áudio
Aprenda como extrair transcrições de áudio facilmente com a API da OpenAI

E com esta transcrição, podemos enviar para a OpenAI e fazer perguntas sobre o contexto da conversa:

ChatGPT - Questionando o contexto de uma conversa
Aprenda a usar o modelo GPT-4o da OpenAI para fazer perguntas sobre transcrições de reuniões e gerar respostas precisas com base em conversas.

E não apenas isto, podemos obter um Json, com elementos da Conversa:

ChatGPT - Retornando um Json com elementos relacionados a conversa
Descubra como solicitar respostas no formato JSON com a OpenAI para extrair informações específicas de conversas

Por hoje é só, meus unicórnios! 🦄✨

Que a magia do arco-íris continue brilhando em suas vidas! Até mais! 🌈🌟