{"id":51183306,"url":"https://github.com/psielta/ra","last_synced_at":"2026-06-27T08:30:33.633Z","repository":{"id":366143565,"uuid":"1275213772","full_name":"psielta/ra","owner":"psielta","description":"Portfolio personal media app for music (MP3) and video (MP4/HLS) with Egyptian-themed UI, built with Next.js 15, Auth.js, Prisma, PostgreSQL, Docker Compose, and an async transcode pipeline (RabbitMQ, Redis, FFmpeg, Nginx).","archived":false,"fork":false,"pushed_at":"2026-06-20T13:49:18.000Z","size":353,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-20T14:08:24.990Z","etag":null,"topics":["authjs","docker-compose","ffmpeg","full-stack","hls","media-streaming","minio","nextjs","nextjs15","playwright","portfolio","postgresql","prisma","rabbitmq","redis","shadcn-ui","tailwindcss","typescript","vitest"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/psielta.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-20T11:59:22.000Z","updated_at":"2026-06-20T13:49:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/psielta/ra","commit_stats":null,"previous_names":["psielta/ra"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/psielta/ra","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psielta%2Fra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psielta%2Fra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psielta%2Fra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psielta%2Fra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/psielta","download_url":"https://codeload.github.com/psielta/ra/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psielta%2Fra/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34847287,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authjs","docker-compose","ffmpeg","full-stack","hls","media-streaming","minio","nextjs","nextjs15","playwright","portfolio","postgresql","prisma","rabbitmq","redis","shadcn-ui","tailwindcss","typescript","vitest"],"created_at":"2026-06-27T08:30:32.665Z","updated_at":"2026-06-27T08:30:33.621Z","avatar_url":"https://github.com/psielta.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cstrong style=\"font-size: 2.5rem; letter-spacing: 0.3em; font-family: serif;\"\u003e☀ RA\u003c/strong\u003e\n  \u003cbr /\u003e\n  \u003cem\u003eDeus do Sol · Portfolio de Mídia\u003c/em\u003e\n\u003c/p\u003e\n\n**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.\n\nO 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.\n\nProjeto construído como demonstração de portfolio com foco em arquitetura distribuída, UX de processamento transparente e base web production-ready.\n\n---\n\n## Ideia Central\n\nO usuário não apenas “sobe um arquivo”. Ele inicia um **job de mídia** cujo ciclo de vida é rastreável:\n\n| Status       | Significado                                                               |\n| ------------ | ------------------------------------------------------------------------- |\n| `processing` | Arquivo recebido; worker convertendo (FFmpeg)                             |\n| `ready`      | HLS gerado (audio/video), cover de video disponivel; pronto para playback |\n| `error`      | Falha na conversão, upload ou storage                                     |\n\nEnquanto 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.\n\n---\n\n## Pipeline de Mídia (visão alvo)\n\n### Fluxo completo\n\n```text\nUsuário (browser)\n    │\n    ├─ grava ou seleciona MP3 / MP4\n    │\n    ▼\nNext.js / API Routes\n    │  valida sessão, metadados (Zod)\n    │  cria registro MediaJob no PostgreSQL (status: processing)\n    │  grava original em Storage\n    │  publica mensagem na fila RabbitMQ\n    │\n    ▼\nStorage (MinIO / S3 / disco em dev)\n    │  original: uploads/{userId}/{jobId}/source.mp4\n    │\n    ▼\nRabbitMQ\n    │  fila: media-transcode-jobs\n    │  payload: jobId, userId, tipo (audio|video), path do original\n    │\n    ▼\nWorker .NET (FFmpeg)                    Redis Pub/Sub\n    │  consome mensagem                      ▲\n    │  baixa original do storage             │  job.progress\n    │  executa FFmpeg                        │  job.completed\n    │  publica progresso a cada etapa ───────┘  job.failed\n    │  sobe artefatos convertidos\n    │\n    ▼\nStorage\n    │  vídeo: outputs/{userId}/{jobId}/index.m3u8 + *.ts\n    │  audio/video: outputs/{userId}/{jobId}/index.m3u8 + *.ts\n    │\n    ▼\nWorker atualiza API / PostgreSQL\n    │  status: ready | error\n    │  urls HLS, duracao, cover de video\n    │\n    ▼\nNginx\n    │  serve .m3u8, segmentos .ts e covers com cache\n    │  (CORS e paths alinhados ao player)\n    │\n    ▼\nFrontend (dashboard)\n    │  biblioteca de mídia (TanStack Table)\n    │  player video: hls.js\n    │  player audio: hls.js anexado ao elemento audio\n    │  SSE ou WebSocket/Redis bridge para progresso live\n```\n\n### Fluxo de vídeo (MP4 → HLS)\n\n```text\nVídeo MP4 (upload)\n        ↓\n   FFmpeg (worker .NET)\n   - probe metadata\n   - transcode múltiplas resoluções (opcional)\n   - gera playlist .m3u8 + segmentos .ts\n        ↓\n   HLS (.m3u8 + .ts) no Storage\n        ↓\n   Nginx (static / reverse proxy)\n        ↓\n   Player web (hls.js / Video.js)\n```\n\n### Fluxo de audio (MP3 -\u003e HLS)\n\n```text\nMP3/WebM (upload ou gravacao)\n        ↓\n   FFmpeg gera HLS audio-only (AAC + .m3u8 + .ts)\n        ↓\n   Storage (playlist + segmentos)\n        ↓\n   Nginx\n        ↓\n   Player audio HLS no dashboard\n```\n\nVideos e audios passam por conversao HLS para streaming no browser. Videos tambem geram `cover.jpg` automaticamente para listagens e detalhes.\n\n---\n\n## Arquitetura de Sistemas\n\n```text\n┌─────────────────────────────────────────────────────────────────┐\n│                         Frontend (Next.js)                       │\n│  Auth · Upload UI · Biblioteca · Players · Progresso realtime   │\n└───────────────┬───────────────────────────────┬─────────────────┘\n                │ REST / Server Actions          │ SSE / WS (progresso)\n                ▼                                ▼\n┌───────────────────────────┐      ┌────────────────────────────┐\n│   Next.js API Routes       │      │   Redis (pub/sub + cache)   │\n│   Prisma · Pino · Sentry   │◄────►│   canais: job.progress,     │\n└───────┬───────────┬────────┘      │           job.completed,     │\n        │           │               │           job.failed         │\n        ▼           ▼               └─────────────▲──────────────┘\n┌─────────────┐ ┌─────────────┐                   │\n│ PostgreSQL  │ │  RabbitMQ   │───────────────────┤\n│ users, jobs │ │  fila jobs  │                   │\n│ media meta  │ └──────┬──────┘                   │\n└─────────────┘        │                          │\n                       ▼                          │\n              ┌────────────────────┐              │\n              │ Worker .NET 10      │──────────────┘\n              │ FFmpeg · Serilog   │\n              │ (mesmo padrão do   │\n              │  WorkerService     │\n              │  BuscaPrecoIA)     │\n              └─────────┬──────────┘\n                        │\n        ┌───────────────┼───────────────┐\n        ▼               ▼               ▼\n┌─────────────┐ ┌─────────────┐ ┌─────────────┐\n│ MinIO / S3  │ │   Nginx     │ │  Prometheus │\n│ originals + │ │ serve HLS   │ │  (opcional) │\n│ HLS output  │ │ + covers    │ │             │\n└─────────────┘ └─────────────┘ └─────────────┘\n```\n\n---\n\n## Worker .NET (referência e versões)\n\nO worker de transcodificação é um **.NET 10 Worker Service**, espelhando o padrão de arquitetura de `D:\\globalleitorpdf\\globalleitorpdf\\WorkerServiceBuscaPrecoIA`:\n\n- `BackgroundService` + consumer RabbitMQ (`IRabbitMqConsumerService`)\n- Processamento em `IMediaTranscodeWorkerService` (equivalente ao `IPriceSearchWorkerService`)\n- Progresso via `IRedisService.PublishProgressAsync` nos canais Redis\n- Atualização de status final no PostgreSQL via API Next.js ou acesso direto\n- Logs estruturados com **Serilog**; métricas opcionais com **prometheus-net**\n\n### Pacotes principais do worker\n\n| Pacote                                       | Versão        |\n| -------------------------------------------- | ------------- |\n| TargetFramework                              | `net10.0`     |\n| Microsoft.Extensions.Hosting                 | 10.0.9        |\n| Microsoft.Extensions.Hosting.WindowsServices | 10.0.9        |\n| Microsoft.Extensions.Http                    | 10.0.9        |\n| RabbitMQ.Client                              | 6.8.1         |\n| StackExchange.Redis                          | 2.8.16        |\n| Newtonsoft.Json                              | 13.0.4        |\n| Serilog                                      | 4.3.1         |\n| Serilog.Extensions.Hosting                   | 10.0.0        |\n| Serilog.Sinks.Console / File                 | 6.1.1 / 6.0.0 |\n| prometheus-net                               | 8.2.1         |\n\nEstrutura prevista no repositório:\n\n```text\nworker/\n  WorkerServiceRaMedia/          Worker .NET 10 (RabbitMQ + Redis + FFmpeg)\n    Worker.cs\n    Services/\n      RabbitMqConsumerService.cs\n      RedisService.cs              PublishProgressAsync → canal job.progress\n      FfmpegTranscodeService.cs\n      MediaTranscodeWorkerService.cs\n    Models/\n      MediaTranscodeJob.cs\n      JobProgressEvent.cs\n```\n\n### Eventos Redis (contrato previsto)\n\nInspirado no worker de referência (`RedisChannel.Literal(\"job.progress\")`):\n\n```json\n{\n  \"jobId\": \"clx...\",\n  \"userId\": \"clx...\",\n  \"status\": \"processing\",\n  \"stage\": \"transcoding\",\n  \"progressPercentage\": 42.5,\n  \"message\": \"Gerando segmentos HLS (720p)\",\n  \"timestamp\": \"2026-06-20T12:00:00Z\"\n}\n```\n\nCanais: `job.progress`, `job.completed`, `job.failed`. O frontend assina via SSE exposto pela API Next.js (bridge Redis → browser).\n\n---\n\n## Stack por camada\n\n### Frontend / API (este repositório — `D:\\ra`)\n\n| Camada          | Tecnologia                                       |\n| --------------- | ------------------------------------------------ |\n| Framework       | Next.js 16.2.9, App Router, React 19, TypeScript |\n| Auth            | Auth.js / NextAuth v5 (JWT + Credentials)        |\n| ORM             | Prisma + PostgreSQL                              |\n| UI              | Tailwind v4, shadcn/ui, lucide-react, sonner     |\n| Forms           | react-hook-form + Zod                            |\n| Data            | TanStack Query, axios, TanStack Table            |\n| Players         | hls.js (audio e video HLS)                       |\n| Realtime        | SSE ou WebSocket (bridge Redis)                  |\n| Estado UI       | Zustand                                          |\n| Observabilidade | Sentry + Pino                                    |\n\n### Worker de midia (`worker/`)\n\n| Camada            | Tecnologia                        |\n| ----------------- | --------------------------------- |\n| Runtime           | .NET 10 Worker Service            |\n| Fila              | RabbitMQ 6.8.x                    |\n| Progresso / cache | Redis (StackExchange.Redis 2.8.x) |\n| Transcoding       | FFmpeg (CLI via Process)          |\n| Logs              | Serilog                           |\n| Métricas          | prometheus-net (opcional)         |\n\n### Infraestrutura (Docker Compose — visão alvo)\n\n| Serviço             | Porta host | Função                          |\n| ------------------- | ---------- | ------------------------------- |\n| Next.js             | **14001**  | App + API                       |\n| PostgreSQL          | **14002**  | Metadados, jobs, usuários       |\n| Prisma Studio       | **14003**  | Inspeção do banco (dev)         |\n| RabbitMQ AMQP       | **14004**  | Fila de conversão               |\n| RabbitMQ Management | **14005**  | UI admin da fila (dev)          |\n| Redis               | **14006**  | Pub/sub de progresso + cache    |\n| MinIO API           | **14007**  | Object storage (S3-compatible)  |\n| MinIO Console       | **14008**  | UI do storage (dev)             |\n| Nginx               | **14009**  | Serve HLS (.m3u8/.ts) e covers  |\n| Worker metrics      | **14010**  | Prometheus do worker (opcional) |\n| Mailhog SMTP        | **14011**  | E-mail em dev (esqueci senha)   |\n| Mailhog UI          | **14012**  | Inbox local de e-mails (dev)    |\n\nTodas 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.\n\nStorage 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.\n\n---\n\n## Estado Atual (v1.0 - pipeline completo)\n\nA base web e o **pipeline de midia end-to-end** estao implementados: upload/gravacao -\u003e MinIO -\u003e fila RabbitMQ -\u003e worker .NET/FFmpeg -\u003e Redis/SSE -\u003e Nginx/HLS -\u003e player no navegador. Jobs saem de `processing` para `ready`/`error` pelo callback interno do worker.\n\n### Já implementado\n\n- Autenticação completa (sign-in, sign-up, JWT, roles `USER`/`ADMIN`)\n- Esqueci minha senha + reset + troca de senha no perfil (Mailhog em dev)\n- Layout admin (sidebar, header, mobile nav) com tema egípcio\n- Docker Compose: PostgreSQL, app Next.js, RabbitMQ, Redis, MinIO, Nginx, Mailhog e worker .NET\n- Prisma migrations automáticas no startup\n- Stack frontend configurada (Query, Table, Zustand, Lexical demo)\n- Sentry, Pino, Vitest, Playwright, ESLint, Husky\n\n### Pipeline de mídia — checklist 100% concluido\n\n- [x] Modelos Prisma: `MediaAsset`, `TranscodeJob`, `Series`, `Playlist` e `PlaylistItem`\n- [x] API de upload (multipart → MinIO/S3)\n- [x] Publicação de job no RabbitMQ após upload\n- [x] Docker Compose: RabbitMQ, Redis, MinIO (+ Mailhog para e-mail dev)\n- [x] UI de mídia: `/resources`, `/resources/[id]`, `/series`, `/series/[id]`, `/playlists`, `/playlists/[id]`, `/queue`, `/dashboard/upload`\n- [x] Séries como coleções (preview de itens na listagem + edição em drawer)\n- [x] Playlists pessoais com reproducao sequencial no mini-player persistente\n- [x] Playlist especial `Favoritas` com botao de estrela nas rotas que exibem recursos\n- [x] Ações em lote na biblioteca: organizar em serie, adicionar a playlist e excluir selecionados\n- [x] Fila de processamento (`/queue`) com progresso live via Redis/SSE\n- [x] Player audio HLS via hls.js quando status `ready`\n- [x] Docker Compose: Nginx (HLS/covers)\n- [x] Worker .NET `WorkerServiceRaMedia` (FFmpeg, padrao WorkerServiceBuscaPrecoIA)\n- [x] Redis pub/sub -\u003e SSE no Next.js para progresso live\n- [x] Nginx servindo HLS e covers\n- [x] Player video HLS via hls.js\n- [x] TanStack Table na biblioteca\n- [x] Gravacao no browser (MediaRecorder -\u003e upload WebM)\n\n---\n\n## Fluxo de Produto\n\n1. Usuário cria conta e entra no dashboard.\n2. Cria séries (coleções) e playlists, envia MP3/MP4 em `/dashboard/upload` e classifica por série.\n3. A API valida, persiste metadados (`processing`), salva o original no MinIO e enfileira o job no RabbitMQ.\n4. `/resources`, `/series`, `/playlists` e `/queue` exibem status e progresso em tempo real via SSE/Redis.\n5. O worker .NET consome a fila, roda FFmpeg, publica progresso, grava HLS para audio/video e gera cover automatico para video.\n6. Ao concluir, o banco passa para `ready`; Nginx/storage expõe as URLs de playback.\n7. 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.\n\n---\n\n## Estrutura do Repositório (atual + prevista)\n\n```text\nD:\\ra/\n  src/\n    app/(admin)/resources/      Biblioteca de mídia\n    app/(admin)/series/         Coleções (séries)\n    app/(admin)/playlists/      Playlists pessoais\n    app/(admin)/queue/          Fila de processamento\n    app/api/media/upload/       Upload → MinIO + RabbitMQ\n    app/api/series|playlists|resources|queue/\n  prisma/                       Schema e migrations (MediaAsset, TranscodeJob, Series, Playlist)\n  docker/                       Entrypoints\n  worker/                       WorkerServiceRaMedia (.NET 10)\n  nginx/                        conf para HLS/covers\n  docker-compose.yml            Postgres, app, RabbitMQ, Redis, MinIO, Nginx, Mailhog, worker\n```\n\n---\n\n## Como Executar (base atual)\n\n### Pré-requisitos\n\n- Node.js 22+\n- Docker + Docker Compose\n- .NET SDK 10 para desenvolver/testar o worker localmente\n- FFmpeg na imagem Docker do worker\n\n### 1. Variáveis de ambiente\n\n```bash\ncp .env.example .env\n```\n\n```text\nPORT=14001\nDATABASE_URL=postgresql://postgres:postgres@localhost:14002/ra?schema=public\nAUTH_SECRET=\u003copenssl rand -base64 32\u003e\nAUTH_URL=http://localhost:14001\nNEXT_PUBLIC_APP_URL=http://localhost:14001\n```\n\n### 2. Desenvolvimento (recomendado — hot reload)\n\nPara codar, testar UI e usar Playwright MCP, rode a app **localmente** e só o Postgres no Docker:\n\n```bash\nnpm run docker:db    # Postgres na 14002\ndocker compose stop app   # se a app Docker estiver rodando\nnpm run dev          # http://localhost:14001\n```\n\n| Serviço             | URL                    |\n| ------------------- | ---------------------- |\n| App (local)         | http://localhost:14001 |\n| PostgreSQL (Docker) | localhost:14002        |\n\n### 3. Docker completo (validar infra)\n\n```bash\nnpm run docker:up\n```\n\nUse quando precisar testar o Compose/entrypoint — não para iterar frontend (sem hot reload eficiente).\n\n### 4. Validação\n\n```bash\nnpm run typecheck\nnpm run lint\nnpm run test\nnpm run build\n```\n\n### Windows\n\n```bat\ncmd /c \"cd /d D:\\ra \u0026\u0026 npm run docker:up\"\n```\n\nPowerShell quebra em `D:\\ra` por causa de `\\r` — use `cmd /c`.\n\n---\n\n## Decisões Arquiteturais\n\n- **Audio e video sempre em HLS** apos upload — MP3/MP4/WebM originals sao entrada; playback no browser usa `.m3u8`.\n- **Fila RabbitMQ** desacopla upload (rápido) de transcode (lento/CPU-bound).\n- **Redis Pub/Sub** para progresso — mesmo padrão do `WorkerServiceBuscaPrecoIA`; não bloquear o worker com polling do frontend.\n- **Worker em .NET 10**, não Node — FFmpeg em processo longo, Serilog maduro, reuso do template RabbitMQ+Redis já validado em produção.\n- **Nginx** na frente dos segmentos HLS e covers — cache, range requests, MIME types corretos para `.m3u8`/`.ts`/`.jpg`.\n- **Object storage** (MinIO/S3) — originals e outputs; banco guarda apenas metadados e URLs.\n- **Status no PostgreSQL** é fonte de verdade; Redis é transporte efêmero de eventos.\n- **Portas 14001–15000** em todos os serviços expostos no host.\n- **UI pt-BR**, tema egípcio preservado em novas telas.\n\n---\n\n## Documentação para Agentes\n\n- [`AGENT.md`](./AGENT.md) — instruções operacionais para qualquer agente de IA\n- [`CLAUDE.md`](./CLAUDE.md) — guia detalhado para Claude Code / Cursor\n\nLeia antes de implementar o pipeline de mídia, o worker .NET ou novos serviços no Compose.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsielta%2Fra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpsielta%2Fra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsielta%2Fra/lists"}