MAX_CONCURRENT_JOBS.Uma API REST completa para processamento de vídeo e áudio usando FFmpeg em containers Docker.
Esta API fornece uma interface HTTP para executar comandos FFmpeg, gerenciar arquivos de entrada e saída, e monitorar jobs de processamento. É ideal para automação de processamento de mídia em ambientes containerizados.
MAX_CONCURRENT_JOBSgit clone <repository>
cd gabriel-store-ffmpeg
docker-compose up -d
A API estará disponível em http://localhost:5135
GET /queue/statusRetorna o status atual da fila de jobs assíncronos.
Exemplo:
curl http://localhost:5135/queue/status
Resposta:
{
"queue": {
"maxConcurrentJobs": 3,
"currentlyProcessing": {
"count": 2,
"jobs": [
{
"id": "jobId1",
"status": "running",
"command": "ffmpeg ...",
"startTime": "2025-11-12T10:00:00.000Z",
"progress": null
},
{
"id": "jobId2",
"status": "running",
"command": "ffmpeg ...",
"startTime": "2025-11-12T10:01:00.000Z",
"progress": null
}
]
},
"waiting": {
"count": 2,
"jobs": [
{
"id": "jobId3",
"status": "queued",
"command": "ffmpeg ...",
"startTime": "2025-11-12T10:02:00.000Z",
"positionInQueue": 1
},
{
"id": "jobId4",
"status": "queued",
"command": "ffmpeg ...",
"startTime": "2025-11-12T10:03:00.000Z",
"positionInQueue": 2
}
]
}
},
"statistics": {
"totalJobs": 10,
"completedJobs": 5,
"failedJobs": 2,
"runningJobs": 2,
"queuedJobs": 2
},
"timestamp": "2025-11-12T10:05:00.000Z"
}
Notas:
maxConcurrentJobs pode ser alterado via variável de ambiente MAX_CONCURRENT_JOBS.currentlyProcessing.jobs estão sendo executados; jobs em waiting.jobs aguardam vaga na fila.GET /statusVerifica o status dos diretórios compartilhados e containers.
Parâmetros:
Exemplo:
curl http://localhost:5135/status
Resposta de Sucesso:
{
"status": "ok",
"directories": "total 8\ndrwxr-xr-x 2 abc abc 4096 Jan 15 10:30 input\ndrwxr-xr-x 2 abc abc 4096 Jan 15 10:30 output"
}
Resposta de Erro (container FFmpeg não encontrado):
{
"status": "error",
"error": "Error: No such container: ffmpeg"
}
Resposta de Erro (diretórios não acessíveis):
{
"status": "error",
"error": "docker: Error response from daemon: container ffmpeg is not running"
}
POST /initCria os diretórios necessários se não existirem.
Parâmetros:
Exemplo:
curl -X POST http://localhost:5135/init
Resposta de Sucesso:
{
"success": true,
"message": "Diretórios criados/verificados"
}
Resposta de Erro (container não acessível):
{
"error": "Error: No such container: ffmpeg"
}
Resposta de Erro (permissão negada):
{
"error": "docker: Error response from daemon: container ffmpeg is not running"
}
GET /files/:typeLista arquivos em um diretório específico.
Parâmetros:
type: input ou outputExemplo:
curl http://localhost:5135/files/input
Resposta de Sucesso:
{
"type": "input",
"count": 2,
"files": [
{
"name": "1642254000000-video.mp4",
"size": 15728640,
"sizeFormatted": "15.00 MB",
"date": "Jan 15 10:30",
"permissions": "-rw-r--r--",
"isMedia": true,
"downloadUrl": null,
"directUrl": null
},
{
"name": "1642254000001-audio.mp3",
"size": 5242880,
"sizeFormatted": "5.00 MB",
"date": "Jan 15 10:32",
"permissions": "-rw-r--r--",
"isMedia": true,
"downloadUrl": null,
"directUrl": null
}
]
}
Resposta (diretório vazio):
{
"type": "input",
"count": 0,
"files": []
}
Resposta de Erro:
{
"error": "Tipo de diretório inválido. Use 'input' ou 'output'"
}
GET /info/:type/:filenameObtém informações detalhadas de um arquivo de mídia usando ffprobe.
Parâmetros:
type: input ou outputfilename: nome do arquivoExemplo:
curl http://localhost:5135/info/input/video.mp4
Resposta de Sucesso:
{
"filename": "1642254000000-video.mp4",
"type": "input",
"format": {
"formatName": "mov,mp4,m4a,3gp,3g2,mj2",
"formatLongName": "QuickTime / MOV",
"duration": 120.5,
"durationFormatted": "2:00",
"size": 15728640,
"sizeFormatted": "15.00 MB",
"bitRate": 1045000
},
"video": {
"codec": "h264",
"codecLongName": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"width": 1920,
"height": 1080,
"resolution": "1920x1080",
"frameRate": 30,
"bitRate": 1000000,
"pixelFormat": "yuv420p"
},
"audio": {
"codec": "aac",
"codecLongName": "AAC (Advanced Audio Coding)",
"sampleRate": 48000,
"channels": 2,
"bitRate": 128000
},
"downloadUrl": null,
"directUrl": null
}
Resposta de Erro (arquivo não encontrado):
{
"error": "Arquivo não encontrado",
"filename": "inexistente.mp4",
"type": "input"
}
Resposta de Erro (não é arquivo de mídia):
{
"error": "Não foi possível obter informações do arquivo. Certifique-se de que é um arquivo de mídia válido",
"filename": "documento.txt",
"type": "input"
}
POST /uploadUpload via multipart/form-data (recomendado para arquivos grandes).
Parâmetros:
file - arquivo a ser enviado (obrigatório)Content-Type: multipart/form-data (automático)Exemplo:
curl -X POST http://localhost:5135/upload \
-F "[email protected]"
Resposta de Sucesso:
{
"success": true,
"message": "Arquivo enviado com sucesso",
"file": {
"originalName": "video.mp4",
"savedName": "1642254000000-video.mp4",
"size": 15728640,
"sizeFormatted": "15.00 MB",
"path": "/shared/input/1642254000000-video.mp4",
"mimetype": "video/mp4"
}
}
Resposta de Erro (nenhum arquivo):
{
"error": "Nenhum arquivo enviado"
}
Resposta de Erro (erro do sistema):
{
"error": "Erro ao fazer upload do arquivo",
"details": "ENOSPC: no space left on device, write '/shared/input/temp'"
}
POST /upload-jsonUpload via base64 (até 500MB).
Parâmetros:
data (string, obrigatório): arquivo codificado em base64filename (string, obrigatório): nome do arquivo com extensãoHeaders necessários:
Content-Type: application/jsonExemplo:
curl -X POST http://localhost:5135/upload-json \
-H "Content-Type: application/json" \
-d '{
"data": "data:video/mp4;base64,AAAAHGZ0eXBpc29...",
"filename": "video.mp4"
}'
Resposta de Sucesso:
{
"success": true,
"message": "Arquivo enviado com sucesso",
"file": {
"originalName": "video.mp4",
"savedName": "1642254000000-video.mp4",
"size": 15728640,
"sizeFormatted": "15.00 MB",
"path": "/shared/input/1642254000000-video.mp4"
}
}
Resposta de Erro (dados faltando):
{
"error": "Dados ou nome do arquivo não fornecidos"
}
Resposta de Erro (base64 inválido):
{
"error": "Erro ao processar dados base64",
"details": "Invalid character in base64 string"
}
Resposta de Erro (arquivo muito grande):
{
"error": "Payload too large",
"details": "Arquivo excede o limite de 500MB para upload JSON"
}
```{
{
"originalName": "video.mp4",
"savedName": "1642254000000-video.mp4",
"size": 15728640,
"sizeFormatted": "15.00 MB",
"path": "/shared/input/1642254000000-video.mp4",
"mimetype": "video/mp4"
}
}
POST /ffmpegExecuta comandos FFmpeg síncronos (timeout: 5 minutos).
Parâmetros:
command (string, obrigatório): comando FFmpeg completoHeaders necessários:
Content-Type: application/jsonObservações:
-y é adicionado automaticamenteExemplo:
curl -X POST http://localhost:5135/ffmpeg \
-H "Content-Type: application/json" \
-d '{
"command": "ffmpeg -i /shared/input/video.mp4 -c:v libx264 -crf 23 /shared/output/compressed.mp4"
}'
Resposta de Sucesso:
{
"success": true,
"stdout": "ffmpeg version 4.4.2-0ubuntu0.20.04.4 Copyright (c) 2000-2021 the FFmpeg developers\nbuilt with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)\n...\nframe= 3600 fps= 45 q=23.0 size= 15360kB time=00:02:00.00 bitrate=1024.0kbits/s speed=1.5x\nvideo:14080kB audio:1280kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%",
"stderr": "",
"outputFile": "compressed.mp4",
"downloadUrl": "/download/compressed.mp4",
"directUrl": "/files/compressed.mp4"
}
Resposta de Erro (comando vazio):
{
"success": false,
"error": "Comando não fornecido"
}
Resposta de Erro (arquivo não encontrado):
{
"success": false,
"stdout": "",
"stderr": "/shared/input/inexistente.mp4: No such file or directory",
"error": "Erro na execução do FFmpeg"
}
Resposta de Erro (timeout):
{
"success": false,
"error": "Comando cancelado por timeout (5 minutos)"
}
POST /ffmpeg-asyncExecuta comandos FFmpeg assíncronos com sistema de jobs avançado.
Novidade: Agora o endpoint controla a fila de jobs. Se o limite de concorrência for atingido, o job é adicionado à fila de espera e processado assim que possível.
Como executar por filas:
Para que o job seja processado usando o sistema de filas, envie o parâmetro useQueue como true no corpo da requisição JSON:
curl -X POST http://localhost:5135/ffmpeg-async \
-H "Content-Type: application/json" \
-d '{
"command": "ffmpeg -i /shared/input/video.mp4 -c:v libx264 -crf 23 /shared/output/compressed.mp4",
"useQueue": true
}'
Se useQueue não for enviado ou for false, o job será executado diretamente (sem controle de fila).
Parâmetros:
command (string, obrigatório): comando FFmpeg completoHeaders necessários:
Content-Type: application/jsonObservações:
-y é adicionado automaticamenteMAX_CONCURRENT_JOBS), o job será adicionado à fila de espera e processado assim que possível.GET /download/:filenameDownload direto de arquivos processados com headers apropriados para download.
Parâmetros:
filename (path, obrigatório): nome do arquivo no diretório /shared/output/Observações:
Exemplo:
curl -O http://localhost:5135/download/compressed.mp4
Headers de Resposta:
Content-Type: video/mp4
Content-Disposition: attachment; filename="compressed.mp4"
Content-Length: 15728640
Resposta de Erro (404):
{
"error": "Arquivo não encontrado",
"filename": "inexistente.mp4"
}
GET /files/:filenameAcesso direto a arquivos para visualização/streaming (servidos estaticamente).
Parâmetros:
filename (path, obrigatório): nome do arquivo no diretório /shared/output/Observações:
Exemplo:
curl http://localhost:5135/files/compressed.mp4
Diferenças do /download:
GET /files/:type/:filenameAcesso direto a arquivos por tipo (input/output) para visualização/streaming ou download.
Parâmetros:
type (path, obrigatório): input ou outputfilename (path, obrigatório): nome do arquivoObservações:
Exemplo:
# Acessar arquivo do diretório input
curl http://localhost:5135/files/input/video.mp4
# Acessar arquivo do diretório output
curl http://localhost:5135/files/output/processed.mp4
Resposta de Sucesso:
Resposta de Erro (404):
{
"error": "Arquivo não encontrado",
"type": "input",
"filename": "inexistente.mp4"
}
Resposta de Erro (tipo inválido):
{
"error": "Tipo de diretório inválido. Use 'input' ou 'output'"
}
GET /Serve esta documentação como HTML estilizado.
Parâmetros:
Exemplo:
curl http://localhost:5135/
GET /uiInterface web moderna para gerenciamento visual.
Parâmetros:
Exemplo:
curl http://localhost:5135/ui
# 1. Upload de arquivos
curl -X POST http://localhost:5135/upload -F "[email protected]"
curl -X POST http://localhost:5135/upload -F "[email protected]"
# 2. Verificar arquivos
curl http://localhost:5135/files/input
# 3. Obter informações do vídeo
curl http://localhost:5135/info/input/1642254000000-video.mp4
# 4. Processar (assíncrono)
RESPONSE=$(curl -X POST http://localhost:5135/ffmpeg-async \
-H "Content-Type: application/json" \
-d '{
"command": "ffmpeg -i /shared/input/1642254000000-video.mp4 -i /shared/input/1642254000001-audio.mp3 -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 /shared/output/resultado.mp4"
}')
# 5. Extrair jobId da resposta
JOB_ID=$(echo $RESPONSE | jq -r '.jobId')
# 6. Monitorar progresso
curl http://localhost:5135/job/$JOB_ID
# 7. Listar jobs
curl http://localhost:5135/jobs
# 8. Download do resultado
curl -O http://localhost:5135/download/resultado.mp4
ffmpeg: Container LinuxServer FFmpeg para processamentoffmpeg-api: API Node.js/TypeScript que controla o FFmpegO sistema de jobs assíncronos inclui:
PORT=3001 # Porta da API (padrão: 3001)
MAX_CONCURRENT_JOBS=3 # Máximo de jobs FFmpeg assíncronos em processamento simultâneo (padrão: 3)
MAX_CONCURRENT_JOBS (default: 3)# Verificar saúde da API
curl http://localhost:5135/status
# Verificar se containers estão rodando
docker ps | grep ffmpeg
/shared/input/ e /shared/output/ffmpegcd code
npm install
npm run dev
export MAX_CONCURRENT_JOBS=5 # Exemplo para rodar com 5 jobs simultâneos
ffmpeg api typescript docker video audio conversion multimedia rest-api node.js jobs heartbeat async media-processing file-management
📖 Documentação • 📊 Status • 📁 Arquivos Input • 📁 Arquivos Output • 👷 Jobs
Desenvolvido com ❤️ usando TypeScript, Express e Docker
Content type
Image
Digest
sha256:26d272426…
Size
53.1 MB
Last updated
11 months ago
docker pull gabrielsv01/ffmpeg-api