Pular para o conteúdo
Beneverso
Equipe

Guia

Os scripts do site — como publicar peça, download, sprite e foto

Os 5 scripts vivos do repositório. Publicar peça em /producoes, arquivo em /downloads, sprite no jogo ou foto no acervo passa OBRIGATORIAMENTE por um deles — e nenhuma página do site dizia que eles existem.

Sistema

Original no vault: beneverso-dashboard/scripts/

Esta página também reúne

  • beneverso-dashboard/MAPA.mdonde os cinco scripts estavam documentados, e o alvo dos seis pontos de divergência que a ressalva desta página lista
Ressalva: POR QUE ESTA PÁGINA EXISTE: os cinco scripts só estavam descritos no `MAPA.md`, que é um arquivo do repositório e não uma rota do site. Quem trabalha pelo navegador não tinha como descobrir que `importar_pecas.py` existe — e ele é o caminho obrigatório para publicar peça. Ferramenta que não se descobre é ferramenta invisível. SEIS PONTOS EM QUE O MAPA.md NÃO BATE COM O QUE O SCRIPT FAZ. Conferi lendo o código, e registro aqui em vez de consertar — o MAPA tem dono próprio. 1) `importar_pecas.py`: o MAPA diz “ele copia, redimensiona e gera o .zip”. Ele não copia — re-codifica tudo (a pasta `alta/` é JPEG q92 gerado, não o original). O .zip é CONDICIONAL: só sai se a peça declarar a chave `zip`, e duas das peças cadastradas não geram nenhum. E há uma regra SEM UPSCALE que o MAPA não menciona: original abaixo de 1080px de largura vai para `web/` sem reamostrar. 2) `importar_pecas.py`: o MAPA diz “PNG só quando a imagem tiver transparência de verdade”. Na prática o teste de alfa só imprime um aviso — não muda o formato — e a peça `captcha-benedita` está cadastrada como PNG por outro motivo (já está publicada assim, e trocar quebraria link no ar). 3) `importar_downloads.py`: o MAPA diz que o script “renomeia para kebab-case ASCII”. Não existe rotina de renomeação no arquivo: o nome de destino é DIGITADO À MÃO na lista `ITENS`, em par com o caminho de origem. Quem seguir o MAPA ao pé da letra e acrescentar só o caminho quebra a execução. O campo `previa` é obrigatório e o MAPA não o menciona — item sem ele levanta erro. 4) `importar_jogos.py`: o MAPA diz que o destino é `public/jogos/`. É `public/jogos/whac-a-mole/` — o nível a mais existe no código e em `lib/jogos.ts`. O MAPA também omite dois passos do pipeline (a redução intermediária para 1200px antes de tirar o verde, e o corte pelo bbox do alfa, que muda o enquadramento) e as dependências `numpy` e `scipy`. 5) `testar-navegacao.mts`: a lista de sete checagens do MAPA está certa, mas incompleta — o teste roda mais cinco blocos que ele não lista (host decide a área; higiene do cadastro de rotas; botões de CRUD checando área; corte por campo no servidor; slugs únicos da /biblioteca com checagem em disco). 6) Nenhum dos quatro scripts em Python tem a flag `--force` documentada no MAPA, e `importar_pecas.py` aceita ainda um filtro posicional por slug que o MAPA não cita — é justamente o que permite trazer peça nova sem regerar as que já estão no ar. MAIS UM NÚMERO ENVELHECIDO: o MAPA diz que `public/` está em 224,1 MB. Medido em 18/08/2026, está em ~247 MB (a pasta `producoes` sozinha subiu de 170 para ~193 MB). O alerta do MAPA — `public/` entra inteira na imagem Docker e no git, para sempre — continua valendo, e mais ainda.

Em scripts/ ficam só as ferramentas vivas, as que a equipe roda de verdade. Cada uma resolve um caminho de publicação, e nenhuma delas aceita varredura de pasta: a lista do que entra é sempre explícita, digitada à mão. Isso é de propósito — arquivo só entra no site depois de alguém decidir que aquele é o final.

ScriptPara quê
importar_pecas.pypublica peça em /producoes e imprime o bloco cards(...)
importar_downloads.pypõe arquivo em /downloads e mede o peso real
importar_jogos.pyrecorta e converte os sprites do Whac-A-Mole
acervo_web.pygera web/ e alta/ das fotos do acervo
testar-navegacao.mtsé o npm test

1. Publicar uma peça em /producoes

Receita (do MAPA.md): cadastre a peça em scripts/importar_pecas.py e rode o script: ele copia, redimensiona e gera o .zip. Formato da casa, desde 16/08/2026: web/ em WebP q82 (é o que este site exibe) e alta/ em JPEG q92 (é o que a pessoa baixa para subir no Instagram, e JPEG passa em qualquer app). PNG só quando a imagem tiver transparência de verdade — o script testa o canal alfa e avisa. As peças anteriores a essa data continuam em .png de propósito: trocar a extensão quebraria link que já está no ar.

(Leia a nota no topo desta página antes de seguir esta receita ao pé da letra: há quatro pontos em que ela não descreve o que o script faz.)

O que o próprio script diz de si

Traz peças já renderizadas do Beneverso_Vault para public/producoes/.

Origem : Beneverso_Vault/10_🎬_Conteudo/Carrosseis/... (render final da peça) — e também Posts_Estaticos/ e 80_📤_Outputs/, ver as constantes abaixo. Destino : public/producoes/<slug>/web/<prefixo>-<NN>-<nome>.png (1080 de largura) public/producoes/<slug>/alta/<prefixo>-<NN>-<nome>.png (o original) public/producoes/<slug>/<zip> (só se a peça pedir)

Mesma convenção que public/producoes/crivella-senador/ já usa — os dois caminhos são montados pelo helper cards() de lib/producoes.ts a partir de slug, prefixo, ordem e nome do card.

VOCÊ NÃO PRECISA REDIGITAR ESSA CONVENÇÃO. No fim da execução o script imprime o bloco cards(...) de cada peça, pronto para colar em lib/producoes.ts — do mesmo jeito que importar_downloads.py imprime o JSON com os bytes reais. Redigitar o padrão à mão foi o que sempre deu 404 silencioso: cards é string solta, nada valida esse caminho em build, e uma letra errada só aparece quando alguém abre a página. Copie o bloco impresso; a única coisa que sobra para escrever é o título legível de cada card.

Idempotente: pula o card cujo destino já é mais novo que a origem. Requer Pillow.

REGRA: a lista PECAS é explícita. Nada entra por varredura de pasta — peça só entra aqui depois de alguém decidir que aquele render é o final. Pastas com _render, _render_hq, v2, v3… são ITERAÇÕES da mesma peça: escolha uma.

SEM UPSCALE: quando o original já nasce menor que 1080 de largura (é o caso das imagens geradas por modelo, 928 ou 1024 de largura), web/ recebe o original no tamanho em que ele veio. Ninguém interpola pixel que não existe — a peça vale o que ela é. Nesse caso gerar_alta: False corta a pasta alta/, que sairia igual à web/, e a peça fica sem .zip (seria uma terceira cópia do mesmo arquivo).

FORMATO DE SAÍDA: PNG é o formato de quem RENDERIZA a peça, não de quem a entrega. Estes renders são fotografia e arte chapada em rgb24, sem canal alfa — em PNG cada card custa megabytes que o public/ carrega para sempre (ele não está no .gitignore e o Dockerfile o copia duas vezes). web/ -> WebP q82. É o que o próprio site exibe, e o browser que abre /producoes suporta WebP desde 2020. alta/ -> JPEG q92. Esta pasta e o .zip existem para alguém BAIXAR e subir no Instagram: JPEG passa em qualquer fluxo de upload, WebP ainda tropeça em app de terceiro. A perda de q92 fica abaixo do que o próprio Instagram recomprime no upload. Peça com transparência de verdade deve declarar "fmt_web"/"fmt_alta" como "png"; o script avisa quando encontra alfa e a peça não pediu PNG.

Rodar

python scripts/importar_pecas.py                    # processa o que falta / o que mudou
python scripts/importar_pecas.py --force            # regera tudo
python scripts/importar_pecas.py acho-chique        # só esta peça (filtro por slug)

O que editar antes: a lista PECAS. Cada entrada tem slug, prefixo, origem e cards (a lista ordenada de arquivo → nome do card); e os opcionais zip, fmt_web, fmt_alta, gerar_alta.

O que sai: o bloco cards(...) para colar em lib/producoes.ts. Os títulos impressos são rascunho — o script avisa. Requer Pillow.


2. Adicionar um arquivo para baixar

Receita (do MAPA.md): acrescente o arquivo à lista ITENS de scripts/importar_downloads.py e rode o script: ele copia para public/downloads/, renomeia para kebab-case ASCII, gera a prévia e imprime o tamanho real em bytes. Cole o resultado em lib/downloads.ts. Peso é campo obrigatório — quem vai baixar 6 MB no celular merece saber antes. E /downloads é rota pública: material de terceiro sem licença apurada não entra.

O que o próprio script diz de si

Traz o material de download da campanha para public/downloads/.

Origem : _Assets_Producao/Botons/ e _Assets_Producao/_Downloads_2026-05/ Destino : public/downloads/<slug>/<nome-web>

Duas coisas que este script existe para resolver:

  1. Nome de arquivo. A origem tem espaço, acento, vírgula e até ponto-e-vírgula ("lula com bene 4por2 alta res;.svg"). Nada disso vai para a web. Aqui cada arquivo ganha um nome kebab-case ASCII, e o nome ORIGINAL fica registrado no campo origem de lib/downloads.ts para ninguém se perder depois.

  2. Tamanho de verdade. No fim ele imprime o byte count real de cada arquivo copiado. Esses números vão para o campo bytes de lib/downloads.ts — a página mostra o peso antes do clique. Não estime: cole o que sair aqui.

Idempotente: pula o arquivo cujo destino já é mais novo que a origem.

Dependências (só para rodar o script; o site não depende de nada disso): pip install Pillow pypdfium2 # rasteriza PNG/TIFF e a capa do PDF o sharp do próprio node_modules do repo rasteriza os SVG (já é dependência)

REGRA: a lista ITENS é explícita. Nada entra por varredura de pasta. As pastas de origem têm material de terceiro no meio (foto de agência, vídeo de dançarino, briefing em .docx) e /downloads é rota PÚBLICA — o que entra aqui fica visível em beneverso.com.br para qualquer pessoa. Ver a seção NAO_ENTRA no fim.

Rodar

python scripts/importar_downloads.py            # copia o que falta / mudou
python scripts/importar_downloads.py --force    # refaz tudo

O que editar antes: a lista ITENS. Cada entrada tem slug, arquivos (pares de caminho de origem → nome de destino digitado à mão), o campo obrigatório previa e, opcionalmente, derivados.

O que sai: um JSON com os bytes reais de cada arquivo, para colar em lib/downloads.ts, mais a lista NAO_ENTRA — o registro escrito do que fica de fora e por quê.

Requer: Pillow, pypdfium2, e node com o sharp do node_modules do repo.


3. Acrescentar personagem ao jogo

Receita (do MAPA.md): ponha os três PNGs (normal, instalada, pancada) na pasta de origem, acrescente as três linhas à lista ARQUIVOS de scripts/importar_jogos.py e rode o script: ele recorta o fundo verde, reduz para 600px de altura e salva em WebP. Depois declare o personagem em lib/jogos.ts. Os três estados são obrigatórios — sprite que falta vira buraco vazio na tela, sem fallback.

O que o próprio script diz de si

Publica os sprites do Whac-A-Mole da Bené em public/jogos/whac-a-mole/.

Segue o mesmo formato de scripts/acervo_web.py: lista explícita, idempotente, e imprime o tamanho real antes -> depois.

── Por que este script existe ─────────────────────────────────────────────── Os originais são PNG de 5 a 8 MB, 2048px, e — a pegadinha — não têm transparência: cada boneco é um recorte com contorno branco de adesivo colado sobre um fundo VERDE de chroma key. Jogar isso na web do jeito que está põe um retângulo verde de 8 MB em cima de cada buraco.

Então o script faz quatro coisas, nesta ordem:

  1. Reduz para uma altura de trabalho (1200px) ainda SEM alfa. Keyar em 2048px não melhora nada e custa caro.
  2. Tira o verde. O fundo não é um verde só: vai do verde vivo (23,150,77) ao verde escuro e dessaturado (42,67,54), com vinheta forte em alguns arquivos. Por isso o teste não é por cor nem por limiar de brilho, e sim por MATIZ (o canal verde é o mais forte, com saturação mínima) somado a conectividade com a borda da imagem: só sai o verde que encosta na moldura. Verde que estiver dentro do sujeito — o maço de dinheiro do Flávio, por exemplo — fica onde está. Depois vem o despill, numa faixa estreita ao redor do recorte: o halo esverdeado do contorno vira cinza neutro.
  3. Corta a moldura vazia (bbox do alfa): o boneco passa a ocupar o quadro inteiro, que é o que faz ele encher o buraco na tela.
  4. Reduz para 600px de altura e salva em WebP com alfa. Este segundo resize é feito com o alfa PREMULTIPLICADO — sem isso a cor dos pixels transparentes sangra na borda e volta o halo que acabamos de tirar.

── Para acrescentar um personagem / um estado ─────────────────────────────── Acrescente uma linha em ARQUIVOS: ("nome-do-arquivo.png", "destino.webp"). O destino é kebab-case ASCII e é o que vai literalmente para lib/jogos.ts. Rode o script e cole os caminhos lá. Nada entra por varredura de pasta.

Requer Pillow (12.2.0), numpy (2.4.4) e scipy (1.17.1) — versões testadas.

Rodar

python scripts/importar_jogos.py            # processa o que falta / mudou
python scripts/importar_jogos.py --force    # regera tudo

Este script não imprime bloco para colar. Ele imprime o relatório (origem → destino, com tamanhos) e o operador leva os caminhos para lib/jogos.ts à mão. Se faltar arquivo, ele termina com erro.


4. Adicionar fotos ao acervo

Receita (do MAPA.md): ponha os arquivos em _Assets_Producao/Fotos Bené/baixadas_2026-08/, registre a licença e o crédito no ACERVO.csv, acrescente à lista em scripts/acervo_web.py, rode o script (gera web/ e alta/ em public/) e declare a foto em lib/acervo.ts. Crédito é campo obrigatório — as fotos são CC BY, creditar é exigência da licença, não capricho. Foto sem crédito não compila.

(Este é o único dos cinco em que a receita do MAPA bate item a item com o código.)

O que o próprio script diz de si

Gera as versões web + alta do acervo histórico licenciado do Beneverso.

Origem : _Assets_Producao/Fotos Bené/baixadas_2026-08/ Destino : public/acervo/web/<nome>.jpg (máx. 1600px no maior lado, qualidade 82) public/acervo/alta/<nome>.jpg (cópia fiel do original, para download)

Segue a mesma convenção web/ + alta/ que public/producoes/ já usa.

SÓ entram aqui as 10 fotos com licença VERIFICADA (CC BY / CC BY 2.0 / CC BY 3.0 BR). As 40 fotos de "Fotos Bené/antigas/" NÃO são processadas: estão sem auditoria de licença. Não acrescente arquivo à lista sem antes registrar fonte, licença e crédito no ACERVO.csv e no lib/acervo.ts.

Idempotente: pula o arquivo cujo destino já é mais novo que a origem. Requer Pillow (testado com 12.2.0).

Rodar

python scripts/acervo_web.py            # processa o que falta / o que mudou
python scripts/acervo_web.py --force    # regera tudo

O que editar antes: a lista ARQUIVOS — aqui são nomes simples, porque o nome de origem e o de destino são o mesmo. Diferente dos outros scripts, alta/ é cópia byte a byte do original, não um arquivo gerado.

Quem pode e quem não pode publicar cada foto está em Acervo de imagens e direitos.


5. npm test — a vedação virando verificação

O que o próprio arquivo diz de si

Teste da vedação do site público e da coerência da navegação.

Rode antes de publicar: npm test

Por que isso existe: a página de roteiros já vazou uma vez para o site público. A vedação virou allowlist (lib/navegacao.ts), mas allowlist sem teste é promessa. Este arquivo é a promessa virando verificação.

Para adicionar um caso: acrescente uma linha em ATAQUES (caminho que NÃO pode passar no site público) ou um verificar(...) numa das seções.

O que ele confere

Do MAPA.md:

  • que nenhuma rota da equipe vaza no site público, e que a arte de peça não publicada também não;
  • que nenhuma página está escondida (existe em app/ mas não em lib/navegacao.ts) ou morta (o contrário);
  • que nenhuma server action de escrita nasceu sem a trava de área;
  • que o catálogo da /biblioteca não aponta para link morto;
  • que todo caminho de arquivo declarado em lib/producoes.ts, lib/acervo.ts, lib/downloads.ts e lib/jogos.ts existe mesmo em public/, com a mesma caixa;
  • que não nasceu <button> cru fora de components/bnv/;
  • que lib/navegacao.ts e a cadeia que ele importa continuam puros.

E mais cinco blocos que o MAPA não lista: que é o host que decide a área (incluindo host nulo e host parecido com o da equipe); a higiene do cadastro de rotas (href duplicado, descrição vazia, rota de equipe sem grupo, link externo sem http); que os botões de CRUD checam a área; que o corte por campo continua no servidor em /pautas e /timeline; e que os slugs da /biblioteca são únicos e apontam para caminho que existe no disco.

Ele também é mais rigoroso do que o MAPA descreve num ponto: não basta a action chamar podeEscrever() — a chamada tem que vir antes de abrir o cliente administrativo, e comentário não conta como chamada.

As três exceções de <button> cru

Existem três, cada uma declarada com o porquê na lista BOTAO_CRU_PERMITIDO. A exceção vale por contagem: um <button> a mais num arquivo já liberado também reprova. Se você precisa de exceção nova, escreva o motivo lá; se não der para escrever, era caso de <Botao>.

⚠️ Maiúscula importa, e só quebra na VPS

O Windows acha /Acervo/Foto.JPG quando o arquivo é /acervo/foto.jpg; o Linux da VPS não acha — e é lá que o site roda. Por isso a checagem de caminhos compara nome a nome contra o que o disco lista, em vez de perguntar "existe?". Um teste que só passa no Windows não serve para nada.


Os seeds antigos, que não se roda

Os seeds one-shot que carregaram o Supabase em junho/agosto de 2026 (seed-*.mjs, insert-lote6.mjs) e os dumps .json que eles leram foram para scripts/historico/, com um LEIA-ME.md explicando o que é. Nada dali roda no build nem no teste; rodar um seed hoje sobrescreve com dado de junho o que a equipe já editou pela tela. Atenção especial: scripts/historico/personagens.json não é fonte de personagem — é a terceira cópia da ficha, congelada. A fonte é lib/universo.ts.


Original completo

Os arquivos estão em beneverso-dashboard/scripts/. A descrição de origem é a seção "## Os scripts" e as receitas da seção "## Receitas" do MAPA.md, na raiz do repositório. Os blocos citados acima em recuo são o cabeçalho de cada script, como está escrito no topo do arquivo.

Transcrição do vault. Mudou o arquivo lá? Atualize lib/guias.ts — o vault é a fonte, esta página é o espelho.