O objdigital fechou a porta 80 e passou a servir em https://objdigital.bn.br/objdigital2/acervo_digital/. Três cópias legadas do bucket baixadas pelo endereço novo voltaram com sha256 idêntico. Dos 565 legados marcados como 404 permanente, 348 existem no endereço novo (330 JPEG, 18 PDF), 41 são páginas de visualizador Deep Zoom e 176 seguem 404. Lista em pipeline/legado-caminho-novo.tsv. MANIFEST corrigido: 2.112 cópias legadas são imagem; as outras 25 no bucket são páginas HTML do visualizador.
Brasiliana Fotográfica → acervo arquivístico em S3
Retomando o trabalho? Comece por
tasks/PLANO.md— estado atual, achados que não devem ser redescobertos e runbook.
Captura integral do acervo da Brasiliana Fotográfica
— 13.060 itens de 16 instituições — para o bucket s3://brasiliana
(Hetzner Object Storage, hel1.your-objectstorage.com).
Mural 3D do acervo no ar: https://xn--2dk.xyz/brasiliana/
A Biblioteca Nacional foi consultada: confirmou que o material é público e que não tem equipe técnica para fornecer um dump. A coleta por scraping é, portanto, o caminho combinado. Todo o desenho abaixo assume ritmo cortês e identificação.
Estado atual
Coleta concluída em 2026-09-13. Medido contra catálogo × ledger de upload:
| Itens no catálogo | 13.060 |
| Com imagem de preservação | 12.991 |
| Com thumbnail do DSpace | 13.059 |
Com miniatura derivada (derivados/thumb256/) |
13.059 |
| Cópia legada (objdigital) | 2.137 |
| Posts do blog WordPress | 636 |
| Objetos no bucket (ledger) | 41.450 |
| Peças no mural 3D | 13.059 |
Os 69 itens sem imagem de preservação estão em errors.md:
a página do item responde 200, mas a origem serve o bitstream com HTTP 500.
Nenhum fica fora do mural — todos têm thumbnail. A fila local está zerada:
tudo que foi baixado está no bucket.
O acervo
DSpace 4.2, handles com prefixo 20.500.12156.1. 13.036 itens, conferidos
comunidade a comunidade — a soma das 15 fecha exatamente com o total da raiz,
sem sobra nem duplicata.
| Instituição | Itens | Instituição | Itens | |
|---|---|---|---|---|
| Instituto Moreira Salles | 5.133 | Fiocruz | 548 | |
| Biblioteca Nacional | 2.726 | Marinha (DPHDM) | 456 | |
| Museu Aeroespacial | 1.143 | Arquivo Nacional | 383 | |
| Museu da República | 906 | Arq. Geral da Cidade do RJ | 248 | |
| Museu Histórico Nacional | 649 | Leibniz-Institut, Leipzig | 216 | |
| IHGB | 206 | Fund. Joaquim Nabuco | 190 | |
| Museu Goeldi | 95 | FGV CPDOC | 79 | |
| IBGE | 58 |
Espaço projetado: ~116 GB (média de 9,08 MB em 6 arquivos medidos ×
13.036). Mediana 3,99 MB dá ~51 GB; a distribuição tem cauda pesada, então a
média é o estimador certo para o total. Amostra pequena — confirmar com sizes.
Cinco descobertas que definem o desenho
1. O sitemap está sete anos desatualizado
htmlmap/sitemap foram gerados em abril de 2019 e listam ~5.300 itens.
Quem enumerar por eles — o caminho óbvio, e o que ferramentas de mirror usam —
perde 59% do acervo sem erro aparente: o crawl termina limpo, pela metade.
Fonte correta: o índice de navegação, construído ao vivo.
browse?type=title&rpp=100&offset=N, percorrido por comunidade (o que de quebra
etiqueta a instituição de origem, que a página do item não expõe de forma
confiável).
2. O arquivo de preservação está no DSpace, não no objdigital
O campo DC.identifier aponta para objdigital.bn.br, e é natural supor que
ali esteja o master. Medido, é o contrário:
| Item 6066 | Resolução | Peso |
|---|---|---|
| Bitstream do DSpace | 5533×4065 (22 MP) | 7,39 MB |
| Cópia no objdigital | ~2600×2100 (5,5 MP) | 147 KB |
O maior bitstream medido chega a 8679×6484 (34,7 MB). O EXIF mostra digitalização em Hasselblad H3D II-39MS a 7216×5412 — preservação de verdade.
O PDF de 71,9 MB que aparece no objdigital do item 14582 não é a mesma imagem em melhor qualidade: é outro objeto, um álbum de várias páginas.
Regra de captura: o bitstream do DSpace é o arquivo de preservação e vai sempre. A cópia do objdigital entra como procedência, em
legado/, nunca como substituta. Inverter isso arquiva o acervo a um quarto da resolução, e nada no site avisa.
3. O Cloudflare bloqueia automação de navegador, não IP nem geografia
Medido na mesma máquina, mesmo IP, mesmo instante:
| Cliente | Páginas HTML |
|---|---|
| Chrome do usuário | ✅ 200 |
fetch() de dentro da página |
✅ 200 (>100 requisições seguidas) |
| Playwright headed | ❌ 200 na primeira, 403 depois |
| Playwright headless | ❌ 403 |
| curl / httpx | ❌ 403 |
| Servidor na Finlândia | ❌ 403 |
Não é reputação de IP: o Chrome comum carregava normalmente enquanto o Playwright levava 403 na mesma rede. É detecção de controle remoto (CDP).
Consequência: proxies não ajudam — o bloqueio dispara na segunda requisição da mesma sessão, não por origem. Testado e descartado.
4. Bitstreams passam por curl; páginas HTML não
Curioso e muito útil: bitstream/... responde 200 a curl -L simples, mesmo
com UA de curl. Só as páginas HTML são desafiadas. Isso permite baixar os
116 GB fora do navegador — inclusive de um servidor.
5. Nomes de arquivo com percent-encoding levam 403
Aqui mora a pendência. Testado com cinco user-agents (Chrome, Safari, Firefox,
Googlebot, curl): todos 403 numa URL com %28/%20, enquanto curl/8.7.1
puro dá 200 numa URL de nome simples. O gatilho é o caminho, não o cliente
— variar user-agent não resolve.
| Arquivo | curl | navegador |
|---|---|---|
imagem.jpg |
✅ 200 | ✅ 200 |
LB.001%288%29.dvft.JPG |
❌ 403 | ✅ 200 |
GV%20dvft%20...Get%c3%balio...JPG |
❌ 403 | ✅ 200 |
Itens da Biblioteca Nacional usam imagem.jpg; os do IMS usam nomes
descritivos com espaços e acentos. Como o IMS sozinho são 5.133 itens, a
fração afetada é grande — mas o número exato sai do catálogo, sem nenhuma
requisição extra: basta contar quantos preservation_url precisam de
encoding.
Arquitetura: duas trilhas
páginas HTML (13.036) arquivos (116 GB)
│ │
coletor.js ──────────────┘ │
(Chrome real, in-page fetch) │
→ catalogo.jsonl │
┌────────────────┴────────────────┐
│ │
nome simples │ nome com encoding
│ │
TRILHA A ▼ TRILHA B ▼
baixador.py na Finlândia navegador na máquina local
curl → sha256 → S3 → apaga fetch → download → S3 → apaga
4,7 MB/s, mesmo datacenter (uploader por construir)
A cópia legada do objdigital não é alcançável da Finlândia (conexão
morta) — essas rodam na máquina local, com --somente-legado. São ~328 KB em
média, ~1,5 GB no total.
Nada acumula em disco. Cada arquivo é baixado, somado, enviado e apagado;
pico de uso local é um arquivo. O baixador.py aborta se o disco livre cair
abaixo de 2 GB (a Finlândia tem só 31 GB).
Runbook
Todos os comandos abaixo assumem cd pipeline/ primeiro — é onde vivem os
scripts, o catálogo e o estado (.sqlite, .log, .tmp/).
Fase 1 — metadados (navegador, ~7 h)
- Abra https://brasilianafotografica.bn.gov.br/brasiliana/
- DevTools → Console → cole
pipeline/coletor.jsinteiro BRAS.descobrir()— ~5 min, enumera os 13.036 handlesBRAS.coletar()— o passo longo;BRAS.status()acompanha,BRAS.parar()interrompeBRAS.exportar()— baixabrasiliana-catalogo-AAAA-MM-DD.jsonl
O estado fica em localStorage: pode fechar e retomar colando o arquivo de novo.
Fase 2 — arquivos (trilha A, servidor)
scp pipeline/baixador.py pipeline/catalogo.jsonl finland:/root/brasiliana/
ssh finland
set -a; . /root/.s3env; set +a
cd /root/brasiliana
python3 baixador.py --catalogo catalogo.jsonl --pular-legado
python3 baixador.py --status
Fase 3 — trilha B, arquivos com encoding (máquina local)
Na mesma aba do coletor, cole pipeline/baixador-navegador.js, depois:
BAIXA.quantos() // quantos precisam desta trilha
BAIXA.iniciar() // os arquivos caem em ~/Downloads
Em outro terminal, em paralelo — sobe cada arquivo e apaga na hora:
cd pipeline
set -a; . .s3env; set +a
python3 subidor.py --vigiar --catalogo catalogo.jsonl
Fase 4 — cópias legadas (máquina local)
cd pipeline
set -a; . .s3env; set +a
python3 baixador.py --catalogo catalogo.jsonl --somente-legado
Estrutura no bucket
s3://brasiliana/
MANIFEST.md o que é, como foi coletado, quando
catalogo.jsonl uma linha por item
wordpress-posts.jsonl uma linha por post do blog
falhas-500-2026-09-13.txt itens que a origem não serve
itens/20.500.12156.1_6066/
metadata.json Dublin Core completo + procedência
preservacao/imagem.jpg 5533×4065, 7,4 MB
thumbnail/thumb_2.jpg derivado do DSpace (721–3.709 px)
legado/icon843660.jpg 147 KB, referência de origem
manifest-sha256.txt checksum de cada arquivo do item
derivados/thumb256/
20.500.12156.1_6066.webp miniatura gerada por nós, 256 px
itens/ é material de origem; derivados/ é o que geramos e pode ser
reconstruído do zero. A separação é proposital — misturar apagaria a
procedência que o resto do manifesto garante.
Dois pontos não negociáveis: SHA-256 de tudo (prova de integridade) e
URL de origem + data/hora UTC de captura em cada metadata.json
(procedência). Sem os dois é uma pasta de imagens, não um arquivo. O formato
é compatível com BagIt se um dia for depositado em outro repositório.
Estrutura do repositório
brasiliano/
README.md este arquivo
tasks/ retomada de sessão e plano de trabalho
PLANO.md estado atual, achados, runbook (comece por aqui)
TODO.md lista de trabalho da sessão corrente
PROMPT_TERMINAL.md runbook original (parcialmente superado, ver PLANO.md)
pipeline/ scripts ativos + estado em disco (catálogo, .sqlite, .log, .tmp/, credenciais)
coletor.js Fase 1 — metadados, console do navegador
coletor-resume.js idem, com resume + reparo juntos
coletor-reparo.js reprocessa itens marcados com erro
coletor-chrome.js trilha paralela (2ª aba/navegador)
baixador-navegador.js Fase 3 — trilha B, baixa no navegador p/ ~/Downloads
baixador-thumb.js baixa miniaturas
baixador-extras.js baixa bitstreams extras de itens com múltiplas imagens
reparo-multibitstream.js detecta itens com mais de um bitstream real
baixador.py Fases 2 e 4 — curl → sha256 → S3, um arquivo por vez
subidor.py Fase 3 — Downloads → S3, um por vez (APOSENTADO, ver TODO)
subidor-paralelo.py substituto: boto3 + threads, ~19 GB/h; valida por
assinatura (JPEG/PNG/TIFF/BMP/GIF/JP2/WebP) e
nunca apaga o que recusa — manda pra rejeitados/
rodar-paralelo.sh drena a fila, limpa duplicatas, regrava manifests
vigia-30min.sh a cada 30 min sobe o que chegar e apaga o que subiu
baixar-faltantes-v3.js console do navegador: baixa os itens que faltam
falhas-500-2026-09-13.txt os 75 que a origem não serve (ver errors.md)
catalogo.jsonl catálogo corrente, uma linha por item
catalogo-teste.jsonl dois itens, teste de fumaça
.env, .s3env, .s3env.exemplo credenciais AWS (reais e modelo)
*.sqlite, *.log, .tmp/ estado e logs dos scripts acima — não versionar
web/ visualizações do acervo
wall.html mural 3D
wall2.html linha do tempo
build-atlas.js gera atlas/; use --thumbs=DIR com derivados/thumb256/
atlas/ atlas-0.webp + atlas-map.json (saída do build)
archive/
censo.py versão anterior (Playwright) — não funciona, mantida pelo parsing
Arquivos deste projeto
| Arquivo | O quê |
|---|---|
pipeline/coletor.js |
Coletor de metadados; roda no console do Chrome |
pipeline/baixador.py |
Baixa arquivos e deposita no S3, um por vez |
pipeline/baixador-navegador.js |
Trilha B: baixa no navegador os arquivos que o curl não alcança |
pipeline/subidor.py |
Trilha B: leva da pasta Downloads para o S3 e apaga |
archive/censo.py |
Versão anterior, transporte Playwright — não funciona, mantida pelo parsing e pelo modelo de dados |
pipeline/.s3env.exemplo |
Modelo de credenciais; preencher as chaves AWS |
pipeline/catalogo-teste.jsonl |
Dois itens para teste de fumaça |
Pendências
Lista completa e com contexto em tasks/TODO.md. As principais:
- 69 itens sem preservação (
errors.md) — depende da origem voltar a servir esses bitstreams; retomar comFALTA3.retomar()na aba. - 30 bitstreams
extra_seqNapagados pelosubidor.pyantigo (que confundia TIFF com texto de licença) precisam ser enumerados item a item comreparo-multibitstream.jsantes de rebaixar. - 30 objetos
(N)com tamanho diferente do gêmeo — não são duplicata, são versões distintas; precisam de conferência individual. - Reconciliar 13.058 × 13.060 — o MANIFEST fala em 13.058 itens reais e o catálogo tem 13.060 handles distintos; a diferença nunca foi explicada.
- PDFs de álbum no objdigital são objetos distintos e precisam de decisão própria — o de 71,9 MB do item 14582 provavelmente não é caso isolado.
Direitos
Acervo majoritariamente em domínio público por antiguidade (fotografia do séc.
XIX e início do XX), mas as 16 instituições têm políticas distintas. O campo
dc.rights e a instituição de origem são preservados por item — a decisão
de redistribuir é item a item, não do acervo em bloco. Arquivar para si é uma
coisa; republicar é outra.