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.
| Script | Para quê |
|---|---|
importar_pecas.py | publica peça em /producoes e imprime o bloco cards(...) |
importar_downloads.py | põe arquivo em /downloads e mede o peso real |
importar_jogos.py | recorta e converte os sprites do Whac-A-Mole |
acervo_web.py | gera 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 casogerar_alta: Falsecorta 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:
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
origemde lib/downloads.ts para ninguém se perder depois.Tamanho de verdade. No fim ele imprime o byte count real de cada arquivo copiado. Esses números vão para o campo
bytesde 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
sharpdo 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:
- Reduz para uma altura de trabalho (1200px) ainda SEM alfa. Keyar em 2048px não melhora nada e custa caro.
- 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.
- 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.
- 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 emlib/navegacao.ts) ou morta (o contrário); - que nenhuma server action de escrita nasceu sem a trava de área;
- que o catálogo da
/bibliotecanão aponta para link morto; - que todo caminho de arquivo declarado em
lib/producoes.ts,lib/acervo.ts,lib/downloads.tselib/jogos.tsexiste mesmo empublic/, com a mesma caixa; - que não nasceu
<button>cru fora decomponents/bnv/; - que
lib/navegacao.tse 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.JPGquando 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.