Características • Instalação • Docker • Logging • API • Exemplos • Testes • CORS & Headers • Changelog
YTune API é uma API REST robusta e moderna desenvolvida com Node.js e TypeScript para download e conversão de áudios do YouTube para formato M4A AAC de máxima qualidade (320kbps @ 48kHz). Com arquitetura em camadas, testes automatizados completos e 100% de cobertura de código.
- ✅ Download de Áudio do YouTube - Extração de áudio de vídeos em máxima qualidade
- ✅ Qualidade Premium - Áudio 320kbps com sample rate de 48kHz (qualidade profissional)
- ✅ Conversão M4A AAC - Conversão automática para formato M4A AAC de alta fidelidade
- ✅ Headers Customizados - Metadados do áudio (título, duração) via HTTP headers
- ✅ CORS Configurável - Headers expostos para acesso cross-origin
- ✅ Logging Profissional - Sistema de logs estruturado com Pino
- ✅ API RESTful - Endpoints bem definidos e versionados
- ✅ TypeScript - Código totalmente tipado e seguro
- ✅ 100% Cobertura de Testes - 68 testes automatizados
- ✅ Arquitetura em Camadas - Controller → Service → Routes
- ✅ Respostas Padronizadas - Formato consistente de resposta
- ✅ Validação Robusta - Validação de URLs e parâmetros
- ✅ Sanitização - Nomes de arquivo seguros
- ✅ Limpeza Automática - Gestão inteligente de arquivos temporários com UUID
- ✅ Processamento Concorrente - Suporte para múltiplas requisições simultâneas
- ✅ Docker Ready - Suporte completo para containers
| Tecnologia | Versão | Descrição |
|---|---|---|
| Node.js | 18+ | Runtime JavaScript |
| TypeScript | 5.9.3 | Superset com tipagem estática |
| Express | 5.1.0 | Framework web minimalista |
| Pino | 10.0.0 | Logger estruturado de alta performance |
| youtube-dl-exec | 3.0.25 | Download de vídeos do YouTube |
| FFmpeg | 1.1.0 | Processamento e conversão de áudio |
| Jest | 30.2.0 | Framework de testes |
| Supertest | 7.1.4 | Testes HTTP |
| ts-jest | 29.4.4 | Suporte TypeScript para Jest |
A YTune API foi otimizada para fornecer áudio na máxima qualidade possível, garantindo uma experiência auditiva excepcional:
| Parâmetro | Valor | Descrição |
|---|---|---|
| Bitrate | 320 kbps | Máxima qualidade para AAC/MP3 |
| Sample Rate | 48 kHz | Qualidade profissional de estúdio |
| Formato | M4A AAC | Codec de alta eficiência e qualidade |
| Seletor de Formato | bestaudio* |
Melhor stream disponível sem restrições |
- Download Inteligente: O yt-dlp seleciona automaticamente o melhor stream de áudio disponível (geralmente OPUS ou AAC em alta qualidade)
- Conversão Otimizada: FFmpeg converte o áudio para M4A AAC com parâmetros otimizados:
-b:a 320k→ Bitrate fixo de 320kbps-ar 48000→ Sample rate de 48kHz
- Metadados Preservados: Thumbnail e metadados do vídeo são incorporados ao arquivo
| Aspecto | Qualidade Padrão | YTune API (v1.4.0+) |
|---|---|---|
| Bitrate | ~128 kbps | 320 kbps (2.5x melhor) |
| Sample Rate | 44.1 kHz | 48 kHz |
| Tamanho (música 3min) | ~3 MB | ~7-8 MB |
| Qualidade Percebida | Boa | Excelente (Indistinguível do original) |
💡 Nota: Arquivos terão tamanho aproximadamente 2.5x maior, mas com qualidade significativamente superior, ideal para audiófilos e uso profissional.
Antes de começar, certifique-se de ter instalado:
- Node.js 18 ou superior (Download)
- npm ou yarn
- FFmpeg (necessário para conversão de áudio)
sudo apt-get update
sudo apt-get install ffmpegbrew install ffmpeg- Baixe o FFmpeg de ffmpeg.org
- Extraia o arquivo
- Adicione o diretório
binao PATH do sistema
git clone https://github.com/GabrielFinotti/youtube-music-download-api.git
cd youtube-music-download-apinpm install
# ou
yarn installCrie um arquivo .env na raiz do projeto:
cp .env.example .envEdite o arquivo .env conforme necessário:
NODE_ENV=development
PORT=3000
CORS=*
VERSION=v1
LOG_LEVEL=info
SECRET_KEY=your-secret-key-hereModo Desenvolvimento:
npm run devModo Produção:
npm run build
npm startO servidor estará rodando em http://localhost:3000 🚀
A aplicação possui suporte completo para Docker, incluindo multi-stage builds, otimizações de segurança e health checks.
A forma mais simples de executar a aplicação é usando Docker Compose:
# Construir a imagem
npm run docker:build
# Iniciar o container
npm run docker:up
# Ver logs
npm run docker:logs
# Parar o container
npm run docker:down
# Reconstruir do zero (sem cache)
npm run docker:rebuildConstruir a imagem:
docker build -t ytune-api:latest .Executar o container:
docker run -d \
--name ytune-api \
-p 3000:3000 \
-e NODE_ENV=production \
-e PORT=3000 \
-e CORS=* \
-e VERSION=v1 \
-e LOG_LEVEL=info \
-e SECRET_KEY=your-secret-key \
ytune-api:latestVerificar logs:
docker logs -f ytune-apiParar e remover:
docker stop ytune-api
docker rm ytune-api- ✅ Multi-stage builds - Imagem final otimizada e menor
- ✅ Usuário não-root - Execução com usuário
nodejs(UID 1001) - ✅ Capabilities mínimas - Apenas permissões essenciais
- ✅ Health checks - Monitoramento automático de saúde
- ✅ Security options -
no-new-privileges:true - ✅ Imagem Alpine - Base mínima e segura
| Característica | Valor |
|---|---|
| Imagem Base | node:20-alpine |
| Tamanho Final | ~200MB |
| Porta Exposta | 3000 |
| Health Check | A cada 30s |
| Usuário | nodejs (non-root) |
O container possui um health check integrado que verifica o endpoint /api/v1/health:
healthcheck:
interval: 30s
timeout: 10s
retries: 3
start_period: 40sVerificar status:
docker inspect --format='{{.State.Health.Status}}' ytune-apiO docker-compose.yml está configurado para usar uma rede externa chamada proxy_net. Isso permite integração com reverse proxies como Traefik ou Nginx.
Criar a rede (se ainda não existir):
docker network create proxy_netCrie um arquivo .env na raiz do projeto:
NODE_ENV=production
PORT=3000
CORS=*
VERSION=v1
LOG_LEVEL=info
SECRET_KEY=your-secret-key-hereO Docker Compose lerá automaticamente essas variáveis.
A YTune API possui um sistema de logging profissional baseado em Pino, otimizado para alta performance e ambientes com recursos limitados como Raspberry Pi 4.
- ✅ Alta Performance - ~10x mais rápido que Winston
- ✅ Logs Estruturados - Formato JSON para produção
- ✅ Logs Formatados - Coloridos e legíveis em desenvolvimento
- ✅ Logging Condicional - Formato adaptado automaticamente ao ambiente
- ✅ Níveis Configuráveis - TRACE, DEBUG, INFO, WARN, ERROR, FATAL
- ✅ Middleware HTTP - Logging automático de requisições
- ✅ Child Loggers - Contexto específico por módulo
- ✅ Otimizado para RPi - Mínimo uso de CPU e memória
- ✅ Docker Ready - Funciona perfeitamente em containers
Configure o nível de log através da variável de ambiente LOG_LEVEL:
# .env
LOG_LEVEL=info # trace, debug, info, warn, error, fatalO sistema de logging adapta automaticamente o formato baseado no ambiente (NODE_ENV):
Desenvolvimento (legível e colorido com pino-pretty):
[15:30:45.123] INFO (DownloadService): Iniciando processo de download
url: "https://www.youtube.com/watch?v=..."
Produção/Docker (JSON estruturado - alta performance):
{
"level": 30,
"time": "14/10/2025 15:30:45",
"context": "DownloadService",
"msg": "Iniciando processo de download",
"url": "https://www.youtube.com/watch?v=..."
}💡 Nota: Em produção, o logger usa JSON nativo do Pino (sem
pino-pretty) para máxima performance e compatibilidade com ferramentas de agregação de logs como ELK Stack, Grafana Loki, Datadog, etc.
Para guias detalhados, exemplos práticos e melhores práticas, consulte a documentação completa de logging:
- Overview - Documentação técnica completa
- Quickstart - Guia rápido de início
- Examples - Exemplos práticos de uso
Logs são gerados automaticamente para:
- ✅ Requisições HTTP (método, URL, status, tempo de resposta)
- ✅ Processos de download (início, progresso, conclusão)
- ✅ Erros e exceções (com stack traces completos)
- ✅ Validações e sanitizações
- ✅ Limpeza de arquivos temporários
# 🔨 Desenvolvimento
npm run dev # Inicia servidor em modo desenvolvimento (hot-reload)
# 🏗️ Build
npm run build # Compila TypeScript para JavaScript
# 🚀 Produção
npm start # Inicia servidor em produção
# 🧪 Testes
npm test # Executa todos os testes
npm run test:watch # Executa testes em modo watch
npm run test:coverage # Executa testes com relatório de cobertura
# ✨ Formatação
npm run format # Formata código com Prettier
npm run format:check # Verifica formatação do código
# 🐳 Docker
npm run docker:build # Constrói a imagem Docker
npm run docker:up # Inicia o container em background
npm run docker:down # Para e remove o container
npm run docker:logs # Exibe logs do container
npm run docker:rebuild # Reconstrói do zero (sem cache)http://localhost:3000
A API usa versionamento por URL. A versão atual é v1.
Formato: /api/{version}/{resource}
Todas as respostas seguem um formato padronizado:
{
"success": true,
"status": 200,
"message": "Mensagem descritiva",
"data": {
// Dados da resposta
}
}{
"success": false,
"status": 400 | 404 | 500,
"message": "Mensagem de erro descritiva",
"error": {
"details": "Detalhes adicionais (opcional)"
}
}| Código | Descrição | Uso |
|---|---|---|
200 |
OK | Requisição bem-sucedida |
400 |
Bad Request | Parâmetros inválidos ou ausentes |
404 |
Not Found | Recurso não encontrado |
500 |
Internal Server Error | Erro interno do servidor |
Verifica o status de saúde da API.
Resposta:
{
"status": "ok",
"timestamp": "2025-10-09T12:00:00.000Z"
}Exemplo:
curl http://localhost:3000/healthFaz o download de áudio de um vídeo do YouTube e retorna o buffer do arquivo MP3.
Query Parameters:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
url |
string |
✅ Sim | URL válida do YouTube |
Validações:
- ✅ URL deve ser fornecida
- ✅ URL deve ser uma string
- ✅ URL deve ser do YouTube (
youtube.comouyoutu.be) - ✅ URL não pode ser de playlist
Headers de Resposta:
Content-Type: audio/mpeg
Content-Disposition: attachment; filename*=UTF-8''<nome-do-arquivo>.mp3
Content-Length: <tamanho-do-arquivo>
X-Track-Title: <titulo-original-do-video>
X-Track-Filename: <nome-do-arquivo-formatado>.mp3
X-Track-Duration: <duracao-em-segundos>
📝 Nota sobre os Headers:
X-Track-Title: Contém o título original do vídeo (pode ter caracteres especiais)X-Track-Filename: Contém o nome do arquivo sanitizado e formatado, pronto para ser usado como nome de arquivo no frontendX-Track-Duration: Duração do áudio em segundos- Todos os valores são codificados com
encodeURIComponentpara garantir compatibilidade
Exemplo - cURL:
curl -X GET \
"http://localhost:3000/api/v1/download?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
--output musica.mp3Respostas:
✅ 200 - Sucesso
Headers:
Content-Type: audio/mpeg
Content-Disposition: attachment; filename*=UTF-8''Rick%20Astley%20-%20Never%20Gonna%20Give%20You%20Up.mp3
Body: Buffer binário do arquivo MP3
❌ 400 - URL não fornecida
{
"success": false,
"status": 400,
"message": "URL do YouTube é obrigatória"
}❌ 400 - URL inválida
{
"success": false,
"status": 400,
"message": "URL do YouTube inválida"
}❌ 400 - URL de playlist
{
"success": false,
"status": 400,
"message": "URLs de playlist não são suportadas. Por favor, forneça uma URL de vídeo individual"
}❌ 500 - Erro no servidor
{
"success": false,
"status": 500,
"message": "Erro ao processar o download",
"error": {
"details": "Detalhes do erro"
}
}# Download simples
curl -X GET \
"http://localhost:3000/api/v1/download?url=https://youtu.be/dQw4w9WgXcQ" \
--output musica.mp3
# Com verbose para debug
curl -v -X GET \
"http://localhost:3000/api/v1/download?url=https://youtu.be/dQw4w9WgXcQ" \
--output musica.mp3async function downloadYouTubeAudio(url) {
const apiUrl = `http://localhost:3000/api/v1/download?url=${encodeURIComponent(url)}`;
try {
const response = await fetch(apiUrl);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
// Extrair informações dos headers customizados
const filename = decodeURIComponent(response.headers.get('X-Track-Filename') || 'audio.mp3');
const title = decodeURIComponent(response.headers.get('X-Track-Title') || 'Sem título');
const duration = response.headers.get('X-Track-Duration');
console.log(`📝 Título: ${title}`);
console.log(`⏱️ Duração: ${duration}s`);
console.log(`📄 Nome do arquivo: ${filename}`);
// Download do arquivo
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = filename; // Usa o nome formatado do servidor
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(downloadUrl);
console.log('✅ Download concluído!');
} catch (error) {
console.error('❌ Erro:', error.message);
}
}
// Uso
downloadYouTubeAudio('https://www.youtube.com/watch?v=dQw4w9WgXcQ');import axios from 'axios';
import fs from 'fs';
async function downloadAudio(youtubeUrl) {
try {
const response = await axios.get('http://localhost:3000/api/v1/download', {
params: { url: youtubeUrl },
responseType: 'arraybuffer'
});
// Extrair informações dos headers customizados
const filename = decodeURIComponent(response.headers['x-track-filename'] || 'audio.mp3');
const title = decodeURIComponent(response.headers['x-track-title'] || 'Sem título');
const duration = response.headers['x-track-duration'];
console.log(`📝 Título: ${title}`);
console.log(`⏱️ Duração: ${duration}s`);
console.log(`📄 Nome do arquivo: ${filename}`);
// Salvar o arquivo com o nome formatado
fs.writeFileSync(filename, response.data);
console.log(`✅ Download concluído: ${filename}`);
} catch (error) {
console.error('❌ Erro:', error.response?.data || error.message);
}
}
downloadAudio('https://www.youtube.com/watch?v=dQw4w9WgXcQ');import axios, { AxiosResponse } from 'axios';
import * as fs from 'fs';
interface DownloadOptions {
url: string;
outputPath?: string;
}
async function downloadAudio({ url, outputPath = 'output.mp3' }: DownloadOptions): Promise<void> {
try {
const response: AxiosResponse<ArrayBuffer> = await axios.get(
'http://localhost:3000/api/v1/download',
{
params: { url },
responseType: 'arraybuffer',
headers: {
'Accept': 'audio/mpeg'
}
}
);
fs.writeFileSync(outputPath, Buffer.from(response.data));
console.log(`✅ Download concluído: ${outputPath}`);
} catch (error: any) {
if (error.response) {
console.error(`❌ Erro: ${error.response.data}`);
} else {
console.error('❌ Erro:', error.message);
}
}
}
// Uso
downloadAudio({
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
outputPath: 'minha-musica.mp3'
});import requests
def download_audio(youtube_url, output_path='musica.mp3'):
"""
Faz download de áudio do YouTube
Args:
youtube_url: URL do vídeo do YouTube
output_path: Caminho para salvar o arquivo
"""
url = "http://localhost:3000/api/v1/download"
params = {"url": youtube_url}
try:
response = requests.get(url, params=params)
response.raise_for_status()
with open(output_path, "wb") as f:
f.write(response.content)
print(f"✅ Download concluído: {output_path}")
except requests.exceptions.HTTPError as e:
error_data = response.json()
print(f"❌ Erro {error_data['status']}: {error_data['message']}")
except Exception as e:
print(f"❌ Erro: {str(e)}")
# Uso
download_audio("https://www.youtube.com/watch?v=dQw4w9WgXcQ")O projeto possui 100% de cobertura de testes com 67 testes automatizados.
# Executar todos os testes
npm test
# Executar em modo watch
npm run test:watch
# Executar com cobertura
npm run test:coveragetests/
├── unit/ # Testes unitários
│ ├── controllers/
│ │ └── download.controller.test.ts
│ ├── services/
│ │ ├── download.service.test.ts
│ │ └── download.service.integration.test.ts
│ └── utils/
│ ├── apiResponse.test.ts
│ └── envConfig.test.ts
├── integration/ # Testes de integração
│ ├── routes/
│ │ └── download.route.test.ts
│ └── server.test.ts
├── e2e/ # Testes end-to-end
│ └── download.e2e.test.ts
└── setup.ts # Configuração global
-------------------------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
-------------------------|---------|----------|---------|---------|
All files | 100 | 100 | 100 | 100 |
controllers | 100 | 100 | 100 | 100 |
routes | 100 | 100 | 100 | 100 |
services | 100 | 100 | 100 | 100 |
utils | 100 | 100 | 100 | 100 |
-------------------------|---------|----------|---------|---------|
youtube-music-download-api/
├── src/
│ ├── server.ts # Arquivo principal da aplicação
│ ├── controllers/
│ │ └── download.controller.ts # Controlador de download
│ ├── routes/
│ │ └── download.route.ts # Rotas da API
│ ├── services/
│ │ ├── download.service.ts # Lógica de negócio
│ │ └── temp-download.ts # Gerenciamento de temp
│ ├── types/
│ │ └── apiResponse.ts # Tipos TypeScript
│ └── utils/
│ ├── api/
│ │ └── apiResponse.ts # Utilitário de resposta
│ ├── config/
│ │ └── envConfig.ts # Configurações de ambiente
│ └── downloads/
├── tests/ # Testes automatizados
│ ├── unit/ # Testes unitários
│ ├── integration/ # Testes de integração
│ ├── e2e/ # Testes end-to-end
│ └── setup.ts # Configuração de testes
├── dist/ # Código compilado (gerado)
├── coverage/ # Relatórios de cobertura
├── docs/ # Documentação adicional
├── .env # Variáveis de ambiente (não versionado)
├── .env.example # Exemplo de variáveis
├── .prettierrc # Configuração Prettier
├── jest.config.js # Configuração Jest
├── tsconfig.json # Configuração TypeScript
├── CHANGELOG.md # Histórico de mudanças
└── package.json # Dependências e scripts
O projeto segue uma arquitetura em camadas clara e bem definida:
┌─────────────────────────────────────────┐
│ Controllers │ ← Camada de Apresentação
│ (Validação de entrada e formatação) │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ Services │ ← Camada de Negócio
│ (Lógica de negócio e processamento) │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ External APIs │ ← Camada de Dados
│ (youtube-dl-exec, FFmpeg, File System) │
└─────────────────────────────────────────┘
- Singleton Pattern -
DownloadServiceeEnvConfig - Repository Pattern - Acesso a dados externos
- Dependency Injection - Injeção de dependências
- Error Handling - Tratamento centralizado de erros
- Response Standardization - Respostas padronizadas com
ApiResponse
- ✅ Validação de Entrada - Todas as URLs são validadas
- ✅ Sanitização - Nomes de arquivo são sanitizados
- ✅ Limpeza Automática - Arquivos temporários são removidos
- ✅ Validação de Variáveis de Ambiente - Verifica variáveis obrigatórias
- ✅ Tratamento de Erros - Erros não expõem detalhes internos
- ✅ TypeScript - Tipagem estática previne erros
// ✅ Validação de URL
if (!url || typeof url !== 'string') {
return ApiResponse.error(res, 400, 'URL do YouTube é obrigatória');
}
// ✅ Sanitização de nome de arquivo
const sanitized = filename.replace(/[<>:"/\\|?*]/g, '').trim();
// ✅ Limpeza de recursos
finally {
await this.cleanupTempDir(tempDir);
}- Streaming - Download e conversão em streaming
- Limpeza Assíncrona - Remoção de arquivos não bloqueia
- Gestão de Memória - Buffers otimizados
- Cache de Singleton - Instâncias reutilizadas
| Operação | Tempo Médio | Memória |
|---|---|---|
| Download 3min MP3 | ~15-30s | ~50MB |
| Conversão FFmpeg | ~5-10s | ~30MB |
| Limpeza Temp | ~100ms | ~1MB |
- Suporte a playlists do YouTube
- Múltiplos formatos de áudio (WAV, FLAC, AAC)
- Sistema de fila para downloads
- WebSockets para progresso em tempo real
- Autenticação JWT
- Rate limiting por IP
- Cache de downloads recentes
- Compressão de respostas (gzip)
- GraphQL API
- CI/CD com GitHub Actions
- Documentação OpenAPI/Swagger
- Métricas e monitoramento (Prometheus)
Veja o arquivo CHANGELOG.md para detalhes sobre as mudanças em cada versão.
Versão Atual: 1.3.0 (11 de outubro de 2025)
- 📝 Sistema de Logging Profissional com Pino:
- Logger estruturado de alta performance (~10x mais rápido que Winston)
- Logs em formato JSON para produção
- Logs formatados e coloridos para desenvolvimento (pino-pretty)
- Níveis de log configuráveis (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
- Middleware HTTP automático para logging de requisições
- Child loggers com contexto específico por módulo
- Otimizado para Raspberry Pi 4 e ambientes com recursos limitados
- Serialização automática de erros com stack traces
- 📚 Documentação Completa de Logging:
- Overview técnico detalhado
- Guia rápido (Quickstart)
- Exemplos práticos de uso
- Boas práticas e integração
- ⚙️ Nova Variável de Ambiente:
LOG_LEVELpara configurar nível de log
- 🐳 Suporte Docker Completo: Dockerfile multi-stage otimizado
- Build em duas etapas para imagem final menor
- Imagem baseada em Alpine Linux (~200MB)
- Usuário não-root para segurança
- Health checks integrados
- 🔧 Docker Compose: Orquestração simplificada
- Configuração pronta para produção
- Integração com rede externa (proxy_net)
- Suporte a variáveis de ambiente
- Security options otimizadas
- 📦 Scripts Docker: Novos comandos npm para gerenciamento
- 📝 Documentação Docker: Guia completo de uso do Docker
- 🔒 Segurança Aprimorada: Capabilities mínimas e boas práticas
- ✨ Headers HTTP Customizados: Acesso a metadados do áudio via headers
X-Track-Title: Título original do vídeoX-Track-Duration: Duração em segundos
- 🔧 CORS Configurável: Headers expostos para acesso cross-origin
- 📚 Documentação CORS: Guia completo em
docs/CORS_HEADERS.md - 🧪 68 Testes: Mantida 100% de cobertura
- ✨ Primeira versão estável
- ✨ 100% de cobertura de testes
- ✨ API REST completa e documentada
- ✨ Arquitetura em camadas
- ✨ TypeScript com tipagem completa
Contribuições são bem-vindas! Siga os passos abaixo:
git clone https://github.com/SEU-USUARIO/youtube-music-download-api.gitgit checkout -b feature/nova-funcionalidadegit commit -m "feat: adiciona nova funcionalidade"Padrão de Commits:
feat:- Nova funcionalidadefix:- Correção de bugdocs:- Documentaçãostyle:- Formataçãorefactor:- Refatoraçãotest:- Testeschore:- Manutenção
git push origin feature/nova-funcionalidadeDescreva suas mudanças e aguarde a revisão!
Este projeto está licenciado sob a Licença MIT.
Veja o arquivo LICENSE para mais detalhes.
MIT License
Copyright (c) 2025 Gabriel H. Finotti
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Gabriel H. Finotti
- GitHub: @GabrielFinotti
- Email: gabriel.finotti@example.com
- youtube-dl-exec - Download de vídeos
- FFmpeg - Processamento de áudio
- Express.js - Framework web
- TypeScript - Superset JavaScript
- Jest - Framework de testes
Se encontrar problemas ou tiver dúvidas:
- Consulte a Documentação
- Verifique o Changelog
- Veja os Exemplos de Uso
- Abra uma Issue