☀ RA
Deus do Sol · Portfolio de Mídia
Ra é um projeto de portfolio pessoal onde o usuário grava ou envia músicas (MP3) e vídeos (MP4), acompanha o processamento em tempo real e assiste ao próprio conteúdo no navegador. A identidade visual é egípcia — lapis, ouro e papiro — em homenagem ao deus do sol.
O diferencial arquitetural e um pipeline assincrono de midia: upload pela API Next.js, armazenamento em object storage, fila de conversao com RabbitMQ, worker .NET com FFmpeg gerando HLS para audio e video, cover automatico de video, entrega via Nginx e reproducao no frontend com hls.js. Progresso do job e publicado em Redis e exibido ao usuario em tempo real durante a conversao.
Projeto construído como demonstração de portfolio com foco em arquitetura distribuída, UX de processamento transparente e base web production-ready.
O usuário não apenas “sobe um arquivo”. Ele inicia um job de mídia cujo ciclo de vida é rastreável:
| Status | Significado |
|---|---|
processing |
Arquivo recebido; worker convertendo (FFmpeg) |
ready |
HLS gerado (audio/video), cover de video disponivel; pronto para playback |
error |
Falha na conversão, upload ou storage |
Enquanto o status é processing, o dashboard mostra progresso em tempo real (percentual, etapa atual, mensagem) — o mesmo padrão do worker de referência D:\globalleitorpdf\globalleitorpdf\WorkerServiceBuscaPrecoIA, que consome jobs do RabbitMQ e publica eventos de progresso via Redis Pub/Sub.
Usuário (browser)
│
├─ grava ou seleciona MP3 / MP4
│
▼
Next.js / API Routes
│ valida sessão, metadados (Zod)
│ cria registro MediaJob no PostgreSQL (status: processing)
│ grava original em Storage
│ publica mensagem na fila RabbitMQ
│
▼
Storage (MinIO / S3 / disco em dev)
│ original: uploads/{userId}/{jobId}/source.mp4
│
▼
RabbitMQ
│ fila: media-transcode-jobs
│ payload: jobId, userId, tipo (audio|video), path do original
│
▼
Worker .NET (FFmpeg) Redis Pub/Sub
│ consome mensagem ▲
│ baixa original do storage │ job.progress
│ executa FFmpeg │ job.completed
│ publica progresso a cada etapa ───────┘ job.failed
│ sobe artefatos convertidos
│
▼
Storage
│ vídeo: outputs/{userId}/{jobId}/index.m3u8 + *.ts
│ audio/video: outputs/{userId}/{jobId}/index.m3u8 + *.ts
│
▼
Worker atualiza API / PostgreSQL
│ status: ready | error
│ urls HLS, duracao, cover de video
│
▼
Nginx
│ serve .m3u8, segmentos .ts e covers com cache
│ (CORS e paths alinhados ao player)
│
▼
Frontend (dashboard)
│ biblioteca de mídia (TanStack Table)
│ player video: hls.js
│ player audio: hls.js anexado ao elemento audio
│ SSE ou WebSocket/Redis bridge para progresso live
Vídeo MP4 (upload)
↓
FFmpeg (worker .NET)
- probe metadata
- transcode múltiplas resoluções (opcional)
- gera playlist .m3u8 + segmentos .ts
↓
HLS (.m3u8 + .ts) no Storage
↓
Nginx (static / reverse proxy)
↓
Player web (hls.js / Video.js)
MP3/WebM (upload ou gravacao)
↓
FFmpeg gera HLS audio-only (AAC + .m3u8 + .ts)
↓
Storage (playlist + segmentos)
↓
Nginx
↓
Player audio HLS no dashboard
Videos e audios passam por conversao HLS para streaming no browser. Videos tambem geram cover.jpg automaticamente para listagens e detalhes.
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Next.js) │
│ Auth · Upload UI · Biblioteca · Players · Progresso realtime │
└───────────────┬───────────────────────────────┬─────────────────┘
│ REST / Server Actions │ SSE / WS (progresso)
▼ ▼
┌───────────────────────────┐ ┌────────────────────────────┐
│ Next.js API Routes │ │ Redis (pub/sub + cache) │
│ Prisma · Pino · Sentry │◄────►│ canais: job.progress, │
└───────┬───────────┬────────┘ │ job.completed, │
│ │ │ job.failed │
▼ ▼ └─────────────▲──────────────┘
┌─────────────┐ ┌─────────────┐ │
│ PostgreSQL │ │ RabbitMQ │───────────────────┤
│ users, jobs │ │ fila jobs │ │
│ media meta │ └──────┬──────┘ │
└─────────────┘ │ │
▼ │
┌────────────────────┐ │
│ Worker .NET 10 │──────────────┘
│ FFmpeg · Serilog │
│ (mesmo padrão do │
│ WorkerService │
│ BuscaPrecoIA) │
└─────────┬──────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MinIO / S3 │ │ Nginx │ │ Prometheus │
│ originals + │ │ serve HLS │ │ (opcional) │
│ HLS output │ │ + covers │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
O worker de transcodificação é um .NET 10 Worker Service, espelhando o padrão de arquitetura de D:\globalleitorpdf\globalleitorpdf\WorkerServiceBuscaPrecoIA:
BackgroundService+ consumer RabbitMQ (IRabbitMqConsumerService)- Processamento em
IMediaTranscodeWorkerService(equivalente aoIPriceSearchWorkerService) - Progresso via
IRedisService.PublishProgressAsyncnos canais Redis - Atualização de status final no PostgreSQL via API Next.js ou acesso direto
- Logs estruturados com Serilog; métricas opcionais com prometheus-net
| Pacote | Versão |
|---|---|
| TargetFramework | net10.0 |
| Microsoft.Extensions.Hosting | 10.0.9 |
| Microsoft.Extensions.Hosting.WindowsServices | 10.0.9 |
| Microsoft.Extensions.Http | 10.0.9 |
| RabbitMQ.Client | 6.8.1 |
| StackExchange.Redis | 2.8.16 |
| Newtonsoft.Json | 13.0.4 |
| Serilog | 4.3.1 |
| Serilog.Extensions.Hosting | 10.0.0 |
| Serilog.Sinks.Console / File | 6.1.1 / 6.0.0 |
| prometheus-net | 8.2.1 |
Estrutura prevista no repositório:
worker/
WorkerServiceRaMedia/ Worker .NET 10 (RabbitMQ + Redis + FFmpeg)
Worker.cs
Services/
RabbitMqConsumerService.cs
RedisService.cs PublishProgressAsync → canal job.progress
FfmpegTranscodeService.cs
MediaTranscodeWorkerService.cs
Models/
MediaTranscodeJob.cs
JobProgressEvent.cs
Inspirado no worker de referência (RedisChannel.Literal("job.progress")):
{
"jobId": "clx...",
"userId": "clx...",
"status": "processing",
"stage": "transcoding",
"progressPercentage": 42.5,
"message": "Gerando segmentos HLS (720p)",
"timestamp": "2026-06-20T12:00:00Z"
}Canais: job.progress, job.completed, job.failed. O frontend assina via SSE exposto pela API Next.js (bridge Redis → browser).
| Camada | Tecnologia |
|---|---|
| Framework | Next.js 16.2.9, App Router, React 19, TypeScript |
| Auth | Auth.js / NextAuth v5 (JWT + Credentials) |
| ORM | Prisma + PostgreSQL |
| UI | Tailwind v4, shadcn/ui, lucide-react, sonner |
| Forms | react-hook-form + Zod |
| Data | TanStack Query, axios, TanStack Table |
| Players | hls.js (audio e video HLS) |
| Realtime | SSE ou WebSocket (bridge Redis) |
| Estado UI | Zustand |
| Observabilidade | Sentry + Pino |
| Camada | Tecnologia |
|---|---|
| Runtime | .NET 10 Worker Service |
| Fila | RabbitMQ 6.8.x |
| Progresso / cache | Redis (StackExchange.Redis 2.8.x) |
| Transcoding | FFmpeg (CLI via Process) |
| Logs | Serilog |
| Métricas | prometheus-net (opcional) |
| Serviço | Porta host | Função |
|---|---|---|
| Next.js | 14001 | App + API |
| PostgreSQL | 14002 | Metadados, jobs, usuários |
| Prisma Studio | 14003 | Inspeção do banco (dev) |
| RabbitMQ AMQP | 14004 | Fila de conversão |
| RabbitMQ Management | 14005 | UI admin da fila (dev) |
| Redis | 14006 | Pub/sub de progresso + cache |
| MinIO API | 14007 | Object storage (S3-compatible) |
| MinIO Console | 14008 | UI do storage (dev) |
| Nginx | 14009 | Serve HLS (.m3u8/.ts) e covers |
| Worker metrics | 14010 | Prometheus do worker (opcional) |
| Mailhog SMTP | 14011 | E-mail em dev (esqueci senha) |
| Mailhog UI | 14012 | Inbox local de e-mails (dev) |
Todas as portas ficam no range 14001-15000. Constantes em src/config/ports.ts. No Compose hoje: Postgres, app, RabbitMQ, Redis, MinIO, Nginx, Mailhog e worker .NET.
Storage em produção pode ser S3, Cloudflare R2 ou MinIO — a API e o worker falam S3-compatible API; em dev, MinIO ou volume Docker.
A base web e o pipeline de midia end-to-end estao implementados: upload/gravacao -> MinIO -> fila RabbitMQ -> worker .NET/FFmpeg -> Redis/SSE -> Nginx/HLS -> player no navegador. Jobs saem de processing para ready/error pelo callback interno do worker.
- Autenticação completa (sign-in, sign-up, JWT, roles
USER/ADMIN) - Esqueci minha senha + reset + troca de senha no perfil (Mailhog em dev)
- Layout admin (sidebar, header, mobile nav) com tema egípcio
- Docker Compose: PostgreSQL, app Next.js, RabbitMQ, Redis, MinIO, Nginx, Mailhog e worker .NET
- Prisma migrations automáticas no startup
- Stack frontend configurada (Query, Table, Zustand, Lexical demo)
- Sentry, Pino, Vitest, Playwright, ESLint, Husky
- Modelos Prisma:
MediaAsset,TranscodeJob,Series,PlaylistePlaylistItem - API de upload (multipart → MinIO/S3)
- Publicação de job no RabbitMQ após upload
- Docker Compose: RabbitMQ, Redis, MinIO (+ Mailhog para e-mail dev)
- UI de mídia:
/resources,/resources/[id],/series,/series/[id],/playlists,/playlists/[id],/queue,/dashboard/upload - Séries como coleções (preview de itens na listagem + edição em drawer)
- Playlists pessoais com reproducao sequencial no mini-player persistente
- Playlist especial
Favoritascom botao de estrela nas rotas que exibem recursos - Ações em lote na biblioteca: organizar em serie, adicionar a playlist e excluir selecionados
- Fila de processamento (
/queue) com progresso live via Redis/SSE - Player audio HLS via hls.js quando status
ready - Docker Compose: Nginx (HLS/covers)
- Worker .NET
WorkerServiceRaMedia(FFmpeg, padrao WorkerServiceBuscaPrecoIA) - Redis pub/sub -> SSE no Next.js para progresso live
- Nginx servindo HLS e covers
- Player video HLS via hls.js
- TanStack Table na biblioteca
- Gravacao no browser (MediaRecorder -> upload WebM)
- Usuário cria conta e entra no dashboard.
- Cria séries (coleções) e playlists, envia MP3/MP4 em
/dashboard/uploade classifica por série. - A API valida, persiste metadados (
processing), salva o original no MinIO e enfileira o job no RabbitMQ. /resources,/series,/playlistse/queueexibem status e progresso em tempo real via SSE/Redis.- O worker .NET consome a fila, roda FFmpeg, publica progresso, grava HLS para audio/video e gera cover automatico para video.
- Ao concluir, o banco passa para
ready; Nginx/storage expõe as URLs de playback. - O usuário assiste/ouve no player integrado ou no mini-player persistente, incluindo sequencia de series/playlists. O portfolio é privado por padrão; vitrine pública é evolução futura.
D:\ra/
src/
app/(admin)/resources/ Biblioteca de mídia
app/(admin)/series/ Coleções (séries)
app/(admin)/playlists/ Playlists pessoais
app/(admin)/queue/ Fila de processamento
app/api/media/upload/ Upload → MinIO + RabbitMQ
app/api/series|playlists|resources|queue/
prisma/ Schema e migrations (MediaAsset, TranscodeJob, Series, Playlist)
docker/ Entrypoints
worker/ WorkerServiceRaMedia (.NET 10)
nginx/ conf para HLS/covers
docker-compose.yml Postgres, app, RabbitMQ, Redis, MinIO, Nginx, Mailhog, worker
- Node.js 22+
- Docker + Docker Compose
- .NET SDK 10 para desenvolver/testar o worker localmente
- FFmpeg na imagem Docker do worker
cp .env.example .envPORT=14001
DATABASE_URL=postgresql://postgres:postgres@localhost:14002/ra?schema=public
AUTH_SECRET=<openssl rand -base64 32>
AUTH_URL=http://localhost:14001
NEXT_PUBLIC_APP_URL=http://localhost:14001
Para codar, testar UI e usar Playwright MCP, rode a app localmente e só o Postgres no Docker:
npm run docker:db # Postgres na 14002
docker compose stop app # se a app Docker estiver rodando
npm run dev # http://localhost:14001| Serviço | URL |
|---|---|
| App (local) | http://localhost:14001 |
| PostgreSQL (Docker) | localhost:14002 |
npm run docker:upUse quando precisar testar o Compose/entrypoint — não para iterar frontend (sem hot reload eficiente).
npm run typecheck
npm run lint
npm run test
npm run buildcmd /c "cd /d D:\ra && npm run docker:up"PowerShell quebra em D:\ra por causa de \r — use cmd /c.
- Audio e video sempre em HLS apos upload — MP3/MP4/WebM originals sao entrada; playback no browser usa
.m3u8. - Fila RabbitMQ desacopla upload (rápido) de transcode (lento/CPU-bound).
- Redis Pub/Sub para progresso — mesmo padrão do
WorkerServiceBuscaPrecoIA; não bloquear o worker com polling do frontend. - Worker em .NET 10, não Node — FFmpeg em processo longo, Serilog maduro, reuso do template RabbitMQ+Redis já validado em produção.
- Nginx na frente dos segmentos HLS e covers — cache, range requests, MIME types corretos para
.m3u8/.ts/.jpg. - Object storage (MinIO/S3) — originals e outputs; banco guarda apenas metadados e URLs.
- Status no PostgreSQL é fonte de verdade; Redis é transporte efêmero de eventos.
- Portas 14001–15000 em todos os serviços expostos no host.
- UI pt-BR, tema egípcio preservado em novas telas.
AGENT.md— instruções operacionais para qualquer agente de IACLAUDE.md— guia detalhado para Claude Code / Cursor
Leia antes de implementar o pipeline de mídia, o worker .NET ou novos serviços no Compose.