52 KiB
TODO — Brasiliana → S3
Handoff em 2026-09-10 ~15h UTC: sessão
fix-image-pipeline-extrasperdeu conexão com a extensão Claude-in-Chrome e encerrou a pedido do usuário. Trabalho passado pra sessãochrome-extension-connection(tem o navegador conectado). Ver mensagem de handoff enviada via SendMessage pro contexto completo do estado no momento da troca — resumo abaixo já reflete isso.Atualização ~17h UTC (mesma sessão): aba antiga do coletor não é acessível por esta sessão (grupo de abas é por sessão) mas continuou rodando sozinha o tempo todo — confirmado por progresso do
bras_baixadosno localStorage (compartilhado por origem). Por isso o download principal (BAIXA) não foi reiniciado nesta aba (evitar duplicar ~8 mil downloads pesados em paralelo); só os 78 itens do achado 2.11 foram baixados cirurgicamente aqui.BAIXA_THUMBv2 foi iniciado numa aba nova (chavebras_thumbs_v2, começa do zero, ~13.059 itens). Durante a sessão houve uma queda de rede/extensão de alguns minutos (mesmo tipo de falha que derrubou a sessão anterior) — gerou ~1.280 erros transitórios no thumb v2 que precisam de uma segunda passada (BAIXA_THUMB.retomar()) depois que a primeira terminar.
Ver
tasks/PLANO.mdpro histórico/achados/runbook. Itens concluídos saem daqui e vão pratasks/done/(ex.:tasks/done/TODO-2026-09-10.md). Scripts e estado empipeline/(reorganização de 2026-09-10).
Em andamento (não mexer, só monitorar)
-
Upload pro S3 — CONCLUÍDO. A fila de
~/Downloadsestá em 0; tudo que foi baixado está no bucket. Total no ledger: 41.444 objetos. Opipeline/vigia-30min.shsegue de pé: a cada 30 min sobe o que aparecer e apaga do disco só depois do HEAD confirmar no bucket. Não editarvigia-30min.shnemrodar-paralelo.shenquanto rodam (o bash lê o script aos poucos) e não relançar osubidor.pyantigo (achado 2.22). Threads: 6, não 16 — ver achado 2.24. -
Download principal (1 imagem de preservação por item) — navegador. 2026-09-11 20:45 UTC: 12.986/13.059 (99,44%), faltam 73 — medido pela fonte autoritativa (bucket ∪ fila local), não pelo
bras_baixados. Parado há ~9h (último arquivo em~/Downloadsàs 13:19 local = 11:19 UTC). Lista exata dos 73 empipeline/faltantes-preservacao.jsonl({handle, url}). Scriptpipeline/baixar-faltantes-73.jscolado e rodado pelo usuário ~22:52 local (20:52 UTC) — lista embutida, chave própriabras_faltantes_73, imune ao achado 2.16. Resultado parcial: 2/73 (10993,10995, 31 MB). Os outros 71 estão falhando — ver achado 2.17. Recontagem de 12/09: faltam 211, não 73 — ver achado 2.23.
Achado 2.17 — os 71 restantes falham, e não é malformação de URL (2026-09-11 21:25 UTC)
bras_faltantes_73 = 2 e só 2 arquivos de preservação novos apareceram em
~/Downloads (22:52 local). O disco e a chave concordam — aqui o achado 2.16
não se aplica, o número é real.
Os 71 que falham têm URL estruturalmente idêntica às 2 que passaram
(mesmo host, mesmo /brasiliana/bitstream/PREFIX/ID/SEQ/arquivo, 21 dos 71 com
%20 no nome, igual às que funcionaram). Diferenças observadas, nenhuma
conclusiva: as 2 que passaram são SEQ=1; entre as 71 há SEQ 1 (58), 4 (11),
3 (1) e 5 (1).
Hipótese principal: as 2 que passaram estavam em cache de borda do Cloudflare
— exatamente o caso do item 6066 descrito no cabeçalho do
baixador-navegador.js (cf-cache-status: HIT), e não a regra geral. Se for
isso, o fetch de aba não basta para estes 71 e o problema é de origem, não de
script.
Próximo passo (precisa do console da aba): rodar FALTA.status() e
FALTA.erros() — o script guarda {handle, url, erro} de cada falha, e o
código HTTP decide o caminho:
- 404 → bitstream não existe nesse
SEQ; repararpreservation_urlno catálogo (mesma família dos achados 2.9/2.11/2.13) e regerar o script. - 403 → bloqueio de origem/Cloudflare; nem a aba resolve, precisa de outra saída.
- 500 → transitório;
FALTA.retomar()mais tarde deve pegar.
Enquanto isso o script pode ainda estar rodando: cada erro dorme 60 s de backoff, então 71 falhas levam ~71 min a partir das 22:52 local (fim previsto ~00:05).
Achado 2.18 — é recusa da origem, não URL errada; e a v1 do script ajudou a causar (2026-09-12)
FALTA.status() do usuário: atual: "43/73", erros: 101, feito: 0,
fase: "baixando".
Três leituras, em ordem de importância:
feito: 0no item 43 —10993e10995são os itens 1 e 2 da lista e baixaram com sucesso às 22:52; nesta passada falharam. Mesma URL, uma hora depois. Logo não é URL errada, é a origem recusando.erros: 101comatual: 43/73— mais erros que itens tentados só é possível com dois ou mais loops concorrentes somando no mesmoS. Cadainiciar()/retomar()sobre um script já rodando cria outro loop. Isso quebrou a pausa de 3 s combinada com a BN e provavelmente disparou rate-limit.total: 73(e não 71) — oFEITOSfoi lido vazio na largada dessa passada.
Hipóteses mortas (não perder tempo com elas de novo):
- Sequência errada no
preservation_url: os 2 que funcionaram têm exatamente a mesma forma dos 13 "divergentes" (catálogoseq=1, thumb emseq=3), e 58 dos 71 têmseqcoerente com o thumb e falham igual. - Bloqueio geral do Cloudflare: 71/71 desses itens têm thumb baixado com sucesso — o item responde; só o bitstream de preservação não.
pipeline/baixar-faltantes-v2.js corrige os defeitos da v1 (que eram meus):
guarda contra iniciar() duplo, aborta depois de 5 erros seguidos em vez de
moer os 71 com 60 s cada, pausa de 5 s, backoff de 30 s, e registra o código HTTP
separado (FALTA.resumo() devolve a tabela de códigos). Recarregar a aba antes
de colar — parar() não alcança closures de colagens anteriores.
- Thumbnails — ✅ concluído 100% (13.059/13.059).
retomar()já foi rodado uma vez (08h local); não re-rodar. - Fase 4 (legado objdigital) — ✅ concluído (2ª passada terminou): 2.137 ok / 565 erro (2.702/2.707 processados; os 565 são 404 permanentes confirmados via curl direto, atrito real da fonte legada, não bug). Confirmado em 2026-09-11 ~20h UTC:
select erro,count(*)devolve sóHTTP 404(565) — nenhumHTTP 0restante, então a 3ª passada não tem o que reprocessar; não reiniciar de novo. substituído pelosubidor.pysubidor-paralelo.pyem 2026-09-12 (achado 2.22); o texto a seguir é histórico. (pipeline/,--vigiar, PID 97295) — vivo há 1d8h, vigiando~/Downloads. 2026-09-11 ~20h UTC: fila local de 19.897 arquivosBRAS__/ ~77 GB (12.511 thumb, 1.660__imagem, 5.726 com nome original). Bucket: 33.388 objetos / 60,3 GB, 8.080 itens commetadata.json(61,9% dos 13.059). 2026-09-12 09:45 UTC: 16.207 enviados, fila de 18.286. Taxa nas horas acordadas ≈ 650–740 arq/h ≈ 2,0–2,4 GB/h.
⚠️ O relógio é de horas ACORDADAS, não de horas corridas. Os envios pararam às 22:30 UTC de 11/set e só voltaram às 09:00 UTC de 12/set — 10,5 h de silêncio, que é a máquina dormindo (00:30–11:00 local), não falha do subidor (o processo seguiu vivo e voltou sozinho). Por isso:
- fila de 18.286 ÷ ~700 arq/h = ~26 h de máquina acordada
- em dias normais (~14 h acordado), isso vira ~2 dias de calendário
- com
caffeinate -issegurando a máquina acordada, ~26 h corridas
Vale decidir isso conscientemente: é a diferença entre terminar em ~1 dia ou ~2.
Achado 2.15 — aba do coletor inalcançável, e storage da origem aparece vazio (2026-09-11 ~20h UTC)
Tentativa de retomar a trilha 1 sozinho falhou, mas rendeu dois fatos novos:
- Sobraram instâncias headless do Chrome de uma sessão anterior, uma delas
lançada com o perfil padrão (
--user-data-dirdefault). Mortas (PIDs 89329, 89327, 14818); o Chrome real do usuário (PID 57263, aberto desde 9/set) foi preservado. Nunca subir headless sem--user-data-dirpróprio — disputa o perfil onde mora todo o estado do coletor. - Numa aba nova em
https://brasilianafotografica.bn.gov.br(mesmo perfil, mesma origem),localStoragedevolve 0 chaves eindexedDB.databases()devolve [] — enquantoler-localstorage.js, lendo o leveldb do disco do mesmo perfil/origem, devolvebras_baixados: 12.974. Ou seja: o contexto vivo e o disco discordam. Não dá praBAIXA.retomar()numa aba nova: sem o IndexedDBbras,alvos()devolveria[].
Não colar baixador-navegador.js numa aba nesse estado: se o storage
estiver mesmo zerado, FEITOS começa vazio e a fila vira os ~13 mil itens
inteiros de novo (centenas de GB, e carga indevida na fonte). O caminho seguro
é o usuário voltar na aba original do coletor (a que já tem BAIXA no
window) e rodar BAIXA.status() / BAIXA.retomar() lá.
Achado 2.16 — bras_baixados anda PRA TRÁS; não usar como medida (2026-09-11 20:45 UTC)
O contador caiu de 12.974 → 12.111 entre 20:14 e 20:43 UTC (três leituras seguidas devolvem 12.111; o leveldb foi escrito pelo Chrome às 22:24 local, 19 min antes da leitura — é gravação real, não cache velho).
Causa provável: marcar() reescreve o array inteiro a partir do FEITOS
carregado quando o script foi colado. Qualquer aba com snapshot velho que ainda
marque um item sobrescreve a lista boa por uma mais curta. Com duas abas vivas
(v1 e v2 conviveram), a lista é sempre a do último escritor, não a união.
Consequência prática: bras_baixados não mede progresso — subestima. A
medida autoritativa é bucket ∪ fila local em ~/Downloads:
# handles com preservação no bucket (do aws s3 ls --recursive)
awk '... itens/<handle>/<arquivo!=metadata.json, sem "thumb">' → 8.080
# handles de preservação ainda na fila local
ls ~/Downloads | grep '^BRAS__' | grep -v thumb | sed -E 's/^BRAS__(.+)__.*/\1/' → 7.271
# união → 12.986 de 13.059 (faltam 73, listados em pipeline/faltantes-preservacao.jsonl)
Isso também refaz a conta do achado 2.15: o risco de recolar o script não é "85 itens", é re-baixar ~950+ que o contador esqueceu (ou tudo, se o storage estiver mesmo zerado).
Concluído agora
- Thumbnails contaminados com bitstream de LICENÇA (achado 2.8b): 2.653 arquivos locais apagados de
~/Downloads, 146 já subidos removidos do bucket,subidor.pyblindado com validação de magic bytes (rejeita e apaga automaticamente qualquer coisa que não comece com JPEG/PNG, mesmo que o nome diga o contrário).baixador-thumb.jsv2 escrito com validação + fallback — falta só reconectar o navegador pra trocar o script rodando. - WordPress → .md: 636/637 posts colhidos (1 erro), exportados e convertidos —
wordpress/{id}-{slug}.mdno repo (front-matter YAML: id/url/título/autor/data/categorias/handles referenciados) +wordpress-posts.jsonle a pastawordpress/subindo pro bucket agora. - Bug de corrida em arquivo temporário:
baixador.pyesubidor.pymoram na mesma pasta e usavam o mesmo nome fixo (.tmp/metadata.json) — rodando ao mesmo tempo, um apagava o arquivo que o outro tinha acabado de criar. Derrubou obaixador.pyde verdade (crash, não só log de erro). Corrigido: nome agora inclui o PID. 3 itens (1081,1082,1084) já tinham subido um HTML de visualizador como se fosse "legado" antes da correção — limpo do bucket, metadata regenerado. - PDFs de álbum perdidos no objdigital: 44 itens (achado 2.9 do PLANO.md) tinham PDF disponível mas a extração pegava um
.htmde landing page em vez do PDF de verdade. Corrigido emcatalogo.jsonle no IndexedDB antes dobaixador.py --somente-legadoalcançar esses handles (confirmado: nada contaminado no bucket).baixador.pyreiniciado (PID 37909) com o catálogo corrigido.
Concluído nesta sessão (chrome-extension-connection, ~17h UTC)
pipeline/reparo-hash-url.jscolado e rodado — 78/78 corrigidos no IndexedDB- Achado 2.13 (novo): mesmo bug do
#reaparecia no redirect do DSpace (forma antiga → canônica). Fix: usar/bitstream/handle/PREFIX/ID/arquivodireto.preservation_urldos 78 reescrito de novo com a forma canônica. - Os 78 itens baixados cirurgicamente — concluído 78/78, 0 erros (script à parte, não o
BAIXA.retomar()geral — aba antiga com o loop principal continua rodando sozinha, ver nota de handoff acima) BAIXA_THUMBv2 iniciado (aba nova, não foi possível parar a v1 na aba antiga — ver nota acima; sem risco de dado ruim no bucket graças à validação de magic bytes já nosubidor.py)- Thumbnails v2: CONCLUÍDO 100% —
BAIXA_THUMB.retomar()rodado (2026-09-11 ~08h local) recuperou todos os 1.281 erros da queda de rede (13.059/13.059 ok, 0 erro permanente) baixador.py --somente-legadoreiniciado uma vez (2026-09-11 ~08h local, PID 88387) depois que a passada anterior terminou (2.113 ok, 589 erro de 2.702 processados) — idempotente, só reprocessa não-'ok'- Checagem de status a cada 30 min retomada nesta sessão (ver
/loop)
Mural (web/wall.html) — sessão fix-mobile-wall-click-layout, 2026-09-12
No ar em https://xn--2dk.xyz/brasiliana/. Commits em rafapolo/xyz:
d8c3364 e a6085fa (empurrados pela sessão paralela brasiliano-ae, contêm
trabalho das duas) e f645008. O mural é publicado copiando
web/wall.html → xyz/brasiliana/index.html e web/atlas/atlas-map.json →
xyz/brasiliana/atlas/atlas-map.json.
Achado 3.1 — a metade descritiva do catálogo nunca chegou ao mural
Buscar "indigenista" não achava nada, mesmo com 42 peças classificadas
dc.subject = Política indigenista. A busca livre estava certa (varre todo o
meta recursivamente); quem descartava era o summarize() do
web/build-atlas.js, que projetava 8 campos e jogava fora assunto,
descrição, lugar, fonte, editor, relação e código de arquivo.
Havia um segundo bug atrás: pickDC() devolve só a primeira ocorrência do
campo. Como o catálogo repete dc.subject (78.835 linhas para 13.060
registros, ~6 por item), mesmo incluindo o campo sobreviveria um assunto por
peça. O pickAll() novo coleta todas, deduplicadas, na ordem.
Regras que ficaram (espelhadas em pipeline/reprojeta-meta-mapa.mjs):
- entram:
dc.subject,dc.description,dc.coverage,dc.source,dc.publisher,dc.relation,dc.identifier - ficam de fora:
dc.language(é sempre"por"— casaria ~2.000 itens por acidente, já que a busca é por substring) edc.format("JPG") dc.identifieredc.relationentram sem as URLs: era 1,1 MB de texto que ninguém busca, escondendo o que presta — os códigos de arquivo (013RJ012036) e os nomes de coleção (Gilberto Ferrez,Diários Associados)handleesource_urlsaíram dometa: o primeiro é a chave sob a qual o item já está arquivado no mapa, o segundo é o mesmo endereço para todos com o handle no meio. O mural remonta a URL numa linha (sourceUrl()) e injeta o handle na palha da busca, então buscar5546continua achando a peça.
Verificado no navegador: indigenista → 42, Gilberto Ferrez → 2.120,
013RJ012036 → 2, amazônia → 156, 5546 → 1. E o ruído sumiu: show=full
e brasilianafotografica saíram de 12.552 para 0.
Achado 3.2 — não vale pré-comprimir o atlas-map.json
Medido, não chutado:
- No fio já vem gzipado.
curl -sIem https://xn--2dk.xyz/brasiliana/atlas/atlas-map.json respondecontent-encoding: gzip. Pré-comprimir só moveria os mesmos bytes, ao custo de umDecompressionStreamno carregador e de um arquivo ilegível. (O host não oferece brotli, eDecompressionStreamnão decodifica brotli.) - No repositório também. O git comprime todo blob com zlib no pack, então
um JSON de 8 MB não ocupa 8 MB lá dentro — ocupa aproximadamente o que o
.gzocuparia, e ainda dá diff.
Encolher o conteúdo foi o que valeu, e só até certo ponto — o gzip come quase todo o ganho estrutural:
| variante | cru | gzip |
|---|---|---|
| com os campos descritivos | 9,61 MB | 1,11 MB |
sem source_url e meta.handle (feito) |
8,00 MB | 1,05 MB |
| + dicionário nos campos de vocabulário pequeno | 6,96 MB | 0,99 MB |
| + dicionário de assuntos/lugares/descrições | 5,14 MB | 0,93 MB |
As duas últimas linhas quase dobram a economia crua e rendem 30-60 KB no fio
cada, em troca de uma camada de dicionário no carregador — troca ruim. Onde o
tamanho cru ainda pesa é o JSON.parse no celular, não o download. Referência:
antes de tudo isso o mapa era 5,52 MB / 0,73 MB.
Consertos de interação (todos verificados por CDP num Chrome real)
- Toque numa peça abria e fechava a ficha no mesmo gesto (metade de cima da
tela). Um toque dispara
pointerupe depois umclickde compatibilidade, que caía no backdrop recém-aparecido; a guarda de 400 ms usavastopPropagation(), que não alcança listeners do mesmo nó — e quem fecha a ficha é um listener do próprio#card-wrap. Agora éstopImmediatePropagation(). - Tela inicial "sem estilo":
#title-screenera irmão flex do<p>e, invisível, ainda tomava metade da linha; e a regra#loading ppegava também o subtítulo, jogando-o em maiúsculas com tracking. Virouposition:absolutee#loading-msg. - Painel de filtros fugia do dedo no arrasto da década: o chip do filtro
aparecia e inseria uma linha inteira acima do corpo; o badge de contagem
crescia o header. Chips agora só com o painel fechado, badge reserva a caixa
com
visibility. - Movimento da parede: a câmera mirava o alvo enquanto estava atrás dele
(inclinava a vista a cada arrasto — era isso que fazia a parede "nadar"); o
easing era fração por frame (a 120 Hz convergia 2× mais rápido que a 60).
Agora constante de tempo,
lookAtremovido, e soltar em movimento joga a parede com desaceleração. Zoom do scroll virou multiplicativo. - Back/forward do navegador andam pelas peças abertas, com
#handlena URL (link direto reabre a peça). Mudar um filtro recolhe a ficha com um passo de histórico, mesmo comapply()rodando a cada frame do slider. - Filtros alcançáveis com a peça aberta: o painel estava em
z-index: 15debaixo do#card-wrap(20). Agora 25 (scrim 24), e no celular a ficha estaciona acima do peek da sheet para não cobrir ‹ anterior / próxima ›.
Achado 3.5 — o mural entrava em loop de carregamento no celular
Relato do usuário (14/09): no celular a tela inicial ficou em loop. Causa, lida no carregador: o celular decodificava cada página em 8192×8192 (~268 MB) e só então reduzia para 4096 num canvas, retendo os canvases além das texturas — pico de ~800 MB. O iOS não lança erro por falta de memória: mata a aba e a recarrega sozinho, e a página recomeçava o mesmo carregamento. Não havia tratamento de erro nenhum: fetch falho ou decodificação recusada deixavam a barra parada para sempre.
Correção (commit xyz e brasiliano de 14/09):
- Páginas leves
atlas-m-0..3.webp(3072², peça de 48 px), geradas pelobuild-atlas.js(--so-mobileregera só elas) e registradas ematlas-map.jsonno campomobile. Mesma posição de cada peça, então a conta de uv não muda. 6,8 MB no total contra 42 MB; 36 MB decodificados por página. - Modo leve em toque,
maxTextureSize< 8192 oudeviceMemory≤ 4, ou forçado comlocalStorage.mural_leve = '1'. - Guarda contra loop: marca
mural_carregandoemsessionStorageantes da parte pesada, apagada só depois de dois quadros renderizados. Se a página abre com a marca, a tentativa anterior morreu → vai direto para o modo leve e mostra "modo leve — a tentativa anterior não terminou". - Nunca recarrega sozinho: HTTP de erro, decodificação recusada e
webglcontextlostmostram aviso com botão "tentar de novo" (que recarrega uma vez, já no modo leve). Progresso parado por 30 s mostra "o carregamento parou de avançar" sem derrubar o que ainda pode estar vivo.
Verificado com Chrome real via puppeteer-core (perfil próprio, WebGL
swiftshader), emulando celular com toque — roteiros em
scratchpad/teste-mural/testa.mjs e testa2.mjs:
| cenário | resultado |
|---|---|
| A — celular carrega | só as 4 páginas leves, capa some em 8,3 s, marca apagada |
| C — marca de crash no desktop | aviso de modo leve, nenhuma página pesada, sem recarga |
| D — página falha no download | aviso com botão, nenhuma recarga em 10 s |
| E — botão tentar de novo | recarrega uma vez, termina no modo leve, sem 2ª recarga |
| F — perda de contexto WebGL | aviso com botão, sem recarga |
| G — download travado | aviso "parou de avançar" em 33 s, sem recarga |
Verificado em produção (14/09, após o deploy do GitHub Pages levar 62 s):
o cenário A contra https://xn--2dk.xyz/brasiliana/ baixou só as 4 páginas
leves, a capa sumiu em 9 s pela rede real, sem falha, e a marca foi apagada.
O card no modo leve recorta a peça certa: a mesma peça tirada do atlas normal e do leve difere 4/255 em média; a vizinha, 41,5/255.
Limite do teste: não há iPhone de verdade aqui — o limite de memória do iOS não se emula no Chrome. O que está provado é que o celular não decodifica mais 8192² e que nenhum caminho de falha recarrega sozinho.
Armadilha de teste: framenavigated do Puppeteer conta a about:blank
inicial e mudanças de histórico do mural, então contar navegações dá falso
positivo de recarga. Usar uma marca em window, que só some numa recarga real.
Miniaturas derivadas + reconciliação do ledger — 2026-09-12
Mural no ar com 13.059 peças (antes 12.552): commits bba32f6,
6a82a5e, 1ecded1 e e930158 em rafapolo/xyz. Desta vez o atlas/ inteiro foi junto, não só o
atlas-map.json — o formato do map mudou (ver achado 3.4).
Números fechados em 13/09 (medidos contra catálogo × ledger):
| 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 |
| no mural | 13.059 |
| fora do mural | 1 — só 7523 |
Os 8 que faltavam (7802, 7814, 7817, 7848, 7852, 7861, 7875,
8075) entraram: eles têm thumbnail/thumb_3.jpg de imagem real no
bucket (109–250 KB). A afirmação anterior de que o thumbnail deles era texto
de licença estava errada — o texto era o thumb_2 do material antigo do
finland, não o objeto do bucket.
Os 73 faltantes de preservação (achado 2.17/2.18) não ficam de fora do mural: têm thumbnail do DSpace, que basta para o tile de 128px. Faltar preservação e faltar no mural são coisas diferentes.
- Miniaturas derivadas — 13.051 itens (99,93%)
em
s3://brasiliana/derivados/thumb256/(256px WebP q80, ~8 KB cada, 100 MB no total). Prefixoderivados/deliberadamente fora deitens/:itens/é material de origem,derivados/é reconstruível do zero. Misturar os dois apagaria a procedência que o MANIFEST garante. Composição final (medida, igual à do MANIFEST): 12.849 derivadas da imagem de preservação + 202 derivadas dothumbnail/do DSpace (itens com miniatura mas sem preservação coletada). Caminho até lá: 6.338 no finland a partir do bucket, 6.483 locais a partir de~/Downloads, 164 do DSpace, e +66 do material do achado 2.21. - MANIFEST.md atualizado e subido — seção
derivados/, os achados 2.19 e 2.20 abaixo, e a nota de quethumbnail/do DSpace não é miniatura (achado 3.3). Re-subido em 12/09 com 13.051 / 99,93%, os 9 ausentes nomeados e a divergência 13.058 × 13.060 anotada. build-atlas.jsparou de descartar item sem título — a guardaif (!o.title) continueescondia do mural11749(Museu Aeroespacial) e6241(Instituto Moreira Salles), que têm imagem; o card já cai pra "(sem título)" sozinho. 12.983 → 12.985.- Wordmark "Acervo Brasiliana" ganhou fundo preto — só tinha
text-shadow, que some sobre peça clara, e peça clara é metade do acervo. Mesmo preto da dica do rodapé. - Rótulo de contagem deixou de dizer "no acervo" — dizia "N peças no acervo" como se o mural fosse o acervo inteiro; agora diz "peças com imagem". Foi essa frase que levantou a dúvida de que faltavam miniaturas.
web/build-atlas.jsganhou--thumbs=DIR— a fonte antiga (~/Downloads) é varrida pelosubidor.pyconforme sobe, então nunca tem o acervo inteiro ao mesmo tempo. Agora a fonte é reconstruível do bucket.- Ledger
subidor.sqlitereconciliado com o bucket: 28.553 linhas = 28.553 objetos, zero divergência nos dois sentidos. Backups em/private/tmp/claude-501/.../scratchpad/subidor.sqlite.bak-*. - 145 linhas dethumbnailsem objeto no bucket — são os bitstreams de LICENÇA do achado 2.8b, removidos do bucket mas deixados no ledger (426–815 bytes, só 5 sha256 distintos entre os 145). Apagadas. - 3 objetos no bucket sem linha nenhuma — inseridos, sha256 recalculado do próprio objeto. - 2.137 objetoslegado/que nenhum ledger de upload conhecia: obaixador.pysó anota nobaixador.sqlite. Importados, para quesubidor.sqliteseja ledger único. - 13.051 derivados registrados comotipo='derivado-thumb256'. - 63 "órfãos" de preservação/thumbnail eram falso positivo: gravados durante a própria listagem do bucket. Conferidos no S3, estão lá.
Achado 2.24 — 16 threads estouram o OpenSSL do macOS (SIGSEGV)
O subidor-paralelo.py morreu com segmentation fault no arranque da
passada das 10:56 de 13/09, e por isso nada subiu durante horas (o
vigia-30min só tentaria de novo meia hora depois). O relatório do macOS
aponta a pilha dentro do libcrypto:
libcrypto.3.dylib ossl_x509_store_ctx_get_by_subject
libcrypto.3.dylib X509_STORE_CTX_get1_issuer
libssl.3.dylib ssl_verify_internal / tls_post_process_server_certificate
É corrida de thread na verificação de certificado TLS quando muitas threads
fazem handshake ao mesmo tempo — bug do OpenSSL, não do nosso código. Com 4
threads subiu 50 arquivos a 19 GB/h sem problema. Default baixado para 6
threads no subidor-paralelo.py, no vigia-30min.sh e no
rodar-paralelo.sh. Não vale voltar a subir: acima de ~8 o ganho é nulo (o
link satura) e o risco de crash volta.
Achado 2.25 — 75 itens com HTTP 500 persistente na origem (não é só o 7523)
Passada de 13/09 com a aba conectada: 108 imagens novas baixaram, e 74
falharam com erro que não passa — 45 × HTTP 500, 26 de rede e 3 de blob
curto, com retry sem recuperar nenhuma. Somando o 7523, são 75 itens
cujos bitstreams de preservação a origem não serve. Lista em
pipeline/falhas-500-2026-09-13.txt.
Isso substitui o achado 2.23 (que falava em 211 faltantes): os outros 136 baixaram. Nenhum dos 75 fica fora do mural — todos têm thumbnail do DSpace.
Diagnóstico que vale registrar: os 500 são do item, não do ritmo. Com 3
requisições simultâneas, 6545 e 6555 davam 500 enquanto 1014, 8128,
10039, 14358 e 6520 davam 200 no mesmo instante, inclusive em série com
3 s de espaçamento. E os itens ruins são vizinhos de id, então em ordem
crescente eles davam erros seguidos e disparavam o aborto automático — por
isso o motor passou a embaralhar a lista.
Achado 2.26 — nova rodada sobre os 75 (13/09 noite): o que recupera e a armadilha da sequência
Recuperados (3): 6433 e 6520 por curl — a origem serve a ~11 KB/s, e o
"erro de rede" do navegador era estouro de tempo; 6656 no navegador na
sequência 3 (5,4 MB, 1604×2612) — o catálogo apontava a sequência 2, que é
o texto de licença.
Curl não escala: depois dos 2 primeiros o Cloudflare devolveu 403
(5.484 bytes) para 23 seguidos. Parado para não piorar o bloqueio.
Armadilha — não aceitar "qualquer imagem em qualquer sequência". A 1ª
versão do motor que varria as sequências 1–6 trouxe 8 "sucessos"; 7 eram o
thumbnail/thumb_3.jpg que já estava no bucket (sha256 idêntico, 27–170 KB,
~800 px). Retidos antes de subir e descartados. A estrutura típica de um item
DSpace deste acervo:
| sequência | conteúdo |
|---|---|
| 1 | imagem de preservação (nos itens com defeito: HTTP 500) |
| 2 | bitstream de licença (texto, 426–821 bytes) |
| 3 | thumbnail gerado pelo DSpace |
| 4–6 | inexistente |
A versão corrigida calcula SHA-256 no navegador e rejeita qualquer candidato
cujo hash já esteja no ledger como thumbnail ou legado, e grava com prefixo
HOLD__ (o subidor-paralelo.py ignora) para conferência antes de subir.
Achado 2.27 — os itens do IMS morrem num corte de 300 s; o 500 é o mesmo por sequência e por nome
Terceira rodada sobre os 75 (13/09, 21h), com um motor que registra o tempo de cada tentativa. Três comportamentos distintos, todos da origem:
- Corte em ~300 s sem resposta (
rede@301s), em 12 itens do Instituto Moreira Salles (8145–8316). A requisição não recebe nem cabeçalho: um teste comRange: bytes=0-1023estourou 18 s sem resposta, então não é transferência lenta que cai no meio — o servidor trava antes de responder e algo no caminho corta em 5 min. Retomar em pedaços não ajuda. - Corte em ~32 s em 3 itens (
8271,8275,8277): mesma família, limite diferente. 500imediato (0 s) em 11 itens (8739–9148). A forma canônica/bitstream/handle/20.500.12156.1/<id>/<arquivo>— que busca pelo nome em vez da sequência — também devolve500em 0,2 s (8739,6545). O defeito está no servidor localizar o bitstream, não no endereçamento.
Estrutura das sequências nos itens do IMS: 1 = preservação, 2 = thumbnail (sem bitstream de licença, ao contrário dos do Museu Aeroespacial/Arquivo Nacional/Fiocruz, onde 2 é licença e 3 é thumbnail). Varrer 1–6 nesses itens só gasta tempo: 3–6 não existem.
Recuperado nesta rodada: 8396 (Biblioteca Nacional, sequência 3, 13,5 MB,
3361×4453) — mesmo padrão de 6656 e 7939, onde o catálogo aponta a
sequência 2 (licença) e a imagem está na 3.
Achado 2.28 — a cópia legada: só existe para a Biblioteca Nacional, e 25 "cópias" são páginas HTML
Medido em 14/09. A cópia legada vem do objdigital.bn.br e só existe para
itens da Biblioteca Nacional: 2.702 dos 13.060 têm legacy_url, todos da BN.
Os 16% de cobertura não são falta — os outros 10.358 itens não têm cópia legada
na origem.
elegíveis (legacy_url no catálogo) |
2.702 |
no bucket como legado/ |
2.137 |
| — imagens JPG | 1.899 (859 MB) |
| — PDF de álbum | 213 (8,3 GB) |
| — páginas HTML | 25 (~2,4 KB cada) |
| HTTP 404 permanente | 565 (499 jpg, 41 html, 22 pdf, 2 htm, 1 sid) |
As 25 páginas HTML não são imagem. São o visualizador Seadragon do
objdigital (viewer.openDzi("GeneratedImages/dzc_output.xml")): a imagem mora
numa pirâmide de tiles Deep Zoom ao lado da página. Itens: 586, 587, 630,
802–806, 808, 809, 811, 812, 814, 816, 817, 870, 1080,
1083, 1085, 1086, 3198, 3545, 3546, 3866, 6655. Todos têm
imagem de preservação no bucket, então nenhum ficou sem imagem. Não foram
apagados — é decisão do usuário (ver pendência). Mesma família do bug de
1081/1082/1084 (HTML de visualizador subido como legado), que foi limpo
na época; estes 25 escaparam.
O objdigital não saiu do ar — mudou de endereço. Em 14/09 a porta 80
fechou nos dois IPs (200.9.175.46, 177.223.196.160), mas a 443 está aberta
e o site responde em https://objdigital.bn.br/objdigital2/acervo_digital/...
(o próprio visualizador Seadragon carrega o script desse prefixo). Prova de que
é a mesma fonte: 3 cópias legadas sorteadas do bucket, baixadas pelo endereço
novo, voltaram com sha256 idêntico. Correção de uma afirmação anterior
desta sessão: o icon573843.jpg usado como "controle" não tinha baixado
antes — ele é um dos 565 erros (item 10), e responde 200 no endereço novo.
Consequência: a maior parte dos 565 "404 permanentes" existe no endereço
novo. Sondagem completa por HEAD em 14/09 (pipeline/legado-caminho-novo.tsv):
| no endereço novo | itens |
|---|---|
200 com image/jpeg |
330 |
200 com application/pdf |
18 (o maior, 52 MB) |
200 com text/html (visualizador Deep Zoom, não arquivo) |
41 |
404 de verdade |
176 |
Recuperáveis como arquivo: 348 (~0,3 GB). As 41 páginas HTML somam-se às 25
já no bucket: 66 itens cuja imagem só existe como pirâmide de tiles. A pirâmide Deep Zoom das 25 páginas HTML
também responde (item 3545: dzc_output.xml, 5744×4245, tiles de 256 px).
**O baixador.py usa o endereço antigo (http://…/acervo_digital/) e precisa
ser atualizado antes de qualquer nova passada.
Achado 2.22 — o gargalo do upload era o subidor.py, não o link; troca pelo paralelo
Eu tinha escrito aqui que o gargalo era o link de casa. Errado. Medido em
12/09 (networkQuality): 69 Mbps de upload (~31 GB/h), contra ~1,3 GB/h
reais. O subidor.py sobe um arquivo por vez, espera 1,5 s de "estabilidade" e
dispara três aws s3 cp por arquivo (arquivo, metadata.json, manifest),
cada um pagando a partida do aws-cli: ~5 s por arquivo, seja miniatura de 74 KB
ou preservação de 40 MB.
Além de lento, ele estava estragando duas coisas:
- 873 manifests com linha errada,
derivado-thumb256/<handle>.webp. É consequência de eu ter registrado os derivados no ledger: ele lista toda linha do handle, e esse caminho não existe emitens/<handle>/. - Duplicatas
(N)voltando ao bucket depois da limpeza do achado 2.20 (ex.:5304,thumb_2.jpgethumb_2 (1).jpgcom o mesmo sha256).
Substituto: pipeline/subidor-paralelo.py (boto3, threads, HEAD antes de apagar
o local, manifest uma vez por item, sem linha de derivado, duplicata e rejeito
registrados) + pipeline/rodar-paralelo.sh. Teste com boto3 contra o bucket:
PUT de 20 MB a 27 Mbps num único stream.
Testes antes de soltar, com o que cada um mostrou:
- Lote de 30: todos eram rebaixa já presente no bucket e foram apagados do
disco sem HEAD e sem registro. Foi o que motivou a conferência por HEAD e a
tabela
descartados. Para esses 30, a garantia é só o ledger, reconciliado com o bucket às 12:50. - Lote de 150: 150 descartes, 150 conferidos no bucket.
- Lote de 800: 319 enviados (319 conferidos,
image/jpeg) + 481 descartes conferidos; manifests dos 319 sem linha de derivado, sha do metadata batendo. - Primeira execução real (18:4x UTC) interrompida: o log mostrava TIFF
indo para
rejeitados/(achado 2.19, correção). Assinaturas corrigidas; os 21 da quarentena (19 TIFF + 2 BMP) voltaram para a fila; relançado às 18:59.
Antes do relançamento, 180 arquivos locais (0,25 GB) já presentes no S3 foram
apagados do disco a pedido, cada um com MD5 local = ETag do objeto e registro em
descartados. Os outros 13.922 não estavam no bucket.
Achado 2.23 — faltam 211 preservações depois da fila, não 73
Cruzando catálogo × (ledger ∪ fila) em 12/09: 12.849 / 13.060 itens terão imagem de preservação quando a fila acabar (thumbnail: 13.059). Os 211:
- 71 da lista conhecida (
faltantes-preservacao.jsonl, recusa da origem); - 1 sem
preservation_urlno catálogo; - 139 fora de qualquer lista. Exemplo:
10039–10049, que no bucket têm sóthumbnail/emetadata.json. Desses: 8 rejeitados no upload antigo, 12 com sólegado/, ~119 sem rastro (nem bucket, nem fila, nem log).
Hipóteses, nenhuma confirmada: (a) o "12.986/13.059" do achado 2.16 contou
objetos legado/ e manifest como preservação, porque o awk só excluía
metadata.json e thumbnail; (b) parte dos ~119 pode ser TIFF apagado pelo
subidor.py antigo antes do trecho que o subidor.log cobre.
Achado 2.21 — a sessão de 11/09 já tinha as miniaturas no finland, e eu não olhei
/root/brasiliana-wall/ no finland tinha, desde 11/09 13:17–13:41:
downloads-flat/ (20.411 arquivos, todos THUMB__, 2,2 GB), s3-thumbs/
(5.168 handles do bucket, 851 MB) e o web/atlas/ cujos quatro .webp batem
byte a byte com o que estava publicado no xyz até 12/09. O mural de 12.552
peças foi construído lá, a partir das miniaturas do DSpace — não de
~/Downloads, como o comentário do build-atlas.js leva a crer.
A união desses dois diretórios cobre 13.041 handles, praticamente todos JPEG válido. Eu puxei ~63 GB de imagens de preservação do bucket para derivar 12.985 miniaturas sem ver que 3 GB de miniatura já estavam no disco da mesma máquina, cobrindo mais itens que o meu resultado. Aproveitados depois: +66 peças (74 no delta, 8 eram licença).
Lição de runbook: antes de gerar qualquer coisa no finland, ls /root e
cat /root/.bash_history. Sessões anteriores deixam material lá e nada no
projeto local registra isso.
Limpeza feita em 12/09: /root/brasiliana-wall e /root/thumbs apagados
(3,4 GB; disco 27 → 31 GB livres), depois de verificar que os 13.041 handles
estavam todos cobertos pelo bucket (7.038) ou pela fila do Mac (6.785) —
zero cópias exclusivas. Ficaram de pé, de propósito: /root/.s3env (600, de
08/09, anterior às nossas sessões — pode ser setup permanente da máquina) e
/root/brasiliana/ (44 MB, baixador.py + catálogo + sqlite com 13 linhas).
Achado 2.19 — a contaminação de LICENÇA também atinge a preservação, não só a miniatura
O achado 2.8b tratou isso como problema da trilha de thumbnail. Não é: os
handles 20.500.12156.1/6656 e 20.500.12156.1/7939 têm, em
~/Downloads, um "imagem.jpg" que é na verdade o bitstream de LICENÇA do
DSpace — 777 bytes de texto UTF-8 com CRLF e extensão .jpg. A validação de
magic bytes do subidor.py barra no upload, então nada disso chegou ao
bucket; o efeito é que esses dois itens não têm imagem coletada e ninguém
percebeu, porque a fila local os contava como prontos.
Implicação: qualquer contagem de "preservação pronta" que leia ~/Downloads
pelo nome do arquivo está 2 itens otimista. A contagem pelo bucket está certa.
Atualização 12/09: não são 2, são 10. Ao derivar as miniaturas do material
do finland (achado 2.21) apareceram mais oito com o mesmo defeito, todos
na trilha de thumbnail: 7802, 7814, 7817, 7848, 7852, 7861,
7875, 8075. São exatamente os itens que sobram fora do mural. O bloco
78xx sugere que a contaminação vem em lote, por faixa de handle — vale
varrer essa faixa inteira antes de dar o acervo por fechado.
Correção 12/09 (noite) — a regra de validação estava errada. O subidor.py
só aceitava JPEG/PNG e apagava o resto como "bitstream de licença". Pelo
próprio subidor.log, dos 40 arquivos que ele apagou assim, só 1 era
texto de licença: 36 eram TIFF (II*\0, ~8 MB, com extensão .jpg),
2 BMP e 1 ZIP (001AAN014011.jpg.zip, item 2384). Resultado, item a
item (lista com URL em pipeline/apagados-pelo-subidor-antigo.jsonl):
- 8 itens ficaram sem nenhuma imagem de preservação:
14358,14359,14360,14361,14362,14363,14364,14368(TIFF, série "Alb 0261"). - 18 itens perderam só bitstreams extras (
extra_seqN, TIFF), mas têm a imagem principal no bucket ou na fila.10993/10995(BMP) e2384(ZIP) voltaram por rebaixa e estão na fila. Osubidor-paralelo.pyaceita TIFF/BMP/GIF/JP2/WebP pela assinatura e manda qualquer outra coisa parapipeline/rejeitados/com linha na tabelarejeitados— nunca apaga. Os 8 casos78xxde thumbnail com texto de licença (acima) foram detectados pelovipsthumbnail, não por essa regra, e continuam valendo.
Achado 2.20 — 3.511 duplicatas (N) no bucket, do Chrome rebaixando por cima
Objetos arquivo (1).jpg convivendo com arquivo.jpg byte a byte idêntico —
sufixo que o Chrome põe ao rebaixar um arquivo que já existe em ~/Downloads,
e que o subidor.py subiu como se fosse nome legítimo. Confirmadas idênticas
por ETag + tamanho (não só tamanho) antes de apagar: 3.511 objetos,
0,39 GB, removidos do bucket e do ledger na mesma passada.
Não foram tocados, e precisam de decisão:
- 30 objetos cujo gêmeo tem tamanho diferente — não são duplicata, são versões distintas do mesmo nome. Precisam de conferência individual.
- 43 objetos cujo nome tem
(N)mas que são o único arquivo do item — candidatos a renomear, nunca a apagar.
aws s3api list-objects-v2 com --query "Contents[].[Key,Size,ETag]" resolve
isso sem baixar nada; o ETag é o MD5 pra upload simples (só os 2.612 objetos
multipart do bucket não servem pra comparação direta).
Achado 3.3 — o thumbnail/ do DSpace não é uma miniatura
Medido em amostra de 300: 721 a 3.709 px no maior lado, média 1.087; menor lado nunca abaixo de 462. São derivados de resolução média, não miniaturas de listagem. Duas consequências:
- Servem tão bem quanto a imagem de preservação pro tile de 128px do mural — por isso deu pra cobrir os 164 itens sem preservação sem perda visível.
- A linha "Miniatura | ~13.005 elegíveis" do MANIFEST descreve ~1 MB por arquivo, não ~8 KB. Quem ler esperando miniatura vai baixar 100x mais do que imagina.
Achado 3.4 — publicar o mural agora exige mandar o atlas/ junto
O wall.html de 12:32 passou a derivar handle (chave do map) e source_url
(mesmo prefixo pra todo item) em JS, em vez de lê-los de cada peça. O
summarize() do build-atlas.js acompanhou. Resultado: 8,2 MB de map com
12.983 peças contra 10 MB com 12.552 — menos peso com 431 peças a mais.
Mas isso quebra a nota antiga de que "o atlas/ já está commitado lá e não
muda a cada alteração do mural": map e wall.html agora são um par acoplado
por formato. Publicar um sem o outro deixa o mural sem link e sem busca por
handle.
Pendente
-
dc.rightsausente em 5.833 itens (44,7%) — confirmado como característica real da fonte, não bug. Só precisa ficar documentado no MANIFEST final, sem ação de reparo. -
web/build-atlas.jsnão está versionado em lugar nenhum —brasiliano/não é repo git e oxyzsó recebe oindex.htmle oatlas/. A correção dosummarize()/pickAll()(achado 3.1) vive só no disco desta máquina; umrmleva junto. Mesma exposição vale parapipeline/inteiro. -
Encolher mais o
atlas-map.jsonsó com dicionário (achado 3.2): 8,00 → 5,14 MB crus por ~120 KB no fio. Só vale se oJSON.parseno celular virar problema medido — não antes. -
30 objetos
(N)com tamanho diferente do gêmeo (achado 2.20) — conferência individual, não dá pra automatizar com segurança. -
43 objetos
(N)que são o único arquivo do item (achado 2.20) — renomear tirando o sufixo, pra o nome não mentir sobre haver um original ao lado. -
Handles
6656e7939sem imagem de preservação (achado 2.19) — rebaixar; o que há hoje na fila local é o bitstream de licença. -
Conferir os códigos HTTP que nunca foram tabulados — o runbook do achado 2.18 manda rodar
FALTA.erros()no console e rotear por código (404 → reparar URL, 403 → bloqueio de origem, 500 → transitório, retomar). Esse passo nunca foi executado: o achado 2.18 concluiu "é recusa da origem" a partir dostatus()agregado, não da tabela de erros por handle. Sem isso não dá pra saber quantos dos 71 são reparáveis. -
Reconferir o handle
7523(HTTP 500) — registrado uma vez e tratado como falha permanente da origem desde então, mas 500 costuma ser transitório e nunca houve segunda tentativa. Não dá pra testar porcurl, e agora está provado com controle (12/09): com User-Agent, Accept, Accept-Language e Referer de Chrome, o7523devolve403de 5.484 bytes (página do Cloudflare) — e o item10, que sabidamente funciona e está no bucket, devolve exatamente o mesmo403de 5.484 bytes. É bloqueio geral a requisição sem sessão real, nada específico do7523. Precisa da aba; a extensão Claude-in-Chrome seguia desconectada em duas tentativas no mesmo dia. -
Reconciliar 13.058 vs 13.060 — o MANIFEST fala em 13.058 itens reais, o
catalogo.jsonltem 13.060 handles distintos. Diferença de 2 nunca explicada; anotada no MANIFEST como pendência. -
Depois que os 212 faltantes baixarem e subirem (pedido do usuário, 13/09): 1. conferir se todos os itens têm miniatura em
derivados/thumb256/e gerar as que faltarem (as novas preservações não têm derivado ainda); 2. regerar o mural 3D com o conjunto completo — de preferência derivando no finland direto do bucket (itens/*/preservacao/*), que é tráfego intra-Hetzner, em vez de puxar pro Mac; 3. republicarwall.html+atlas/norafapolo/xyz(par acoplado, achado 3.4). -
Quando o
rodar-paralelo.shterminar (log termina em===== ... FIM): 1. conferir no log as três fases sem!!!, a linhaFIM da fila(enviados, duplicatas, rejeitados, falhas) e ospendente:que ficaram no disco; 2.ls pipeline/rejeitados/+select * from rejeitados— só deve ter texto de licença; imagem ali é bug de assinatura; 3. reconciliar ledger × bucket (mesmo diff do achado de 12/09: 0 divergências nos dois sentidos) e amostrar manifests (grep derivado= 0); 4. varrer~/Downloads: apagar o que tiver MD5 = ETag no bucket (mesmo método dos 180 de 12/09), manter o resto; 5. atualizar a tabela de contagens do MANIFEST com os números fechados. -
Rebaixar o que o
subidor.pyantigo apagou (achado 2.19, correção): 8 itens sem preservação (14358–14364,14368) + ~20extra_seqNem TIFF — lista com URL empipeline/apagados-pelo-subidor-antigo.jsonl. Precisa da aba do navegador. -
Recontar os faltantes de preservação (211)— feito em 13/09: 136 baixaram, sobraram 75 com defeito de origem (achado 2.25), listados empipeline/falhas-500-2026-09-13.txt. Script usado:pipeline/baixar-faltantes-v3.js(ou o motorF5colado direto na aba). -
subidor.pyantigo: aposentar ou corrigir a regra de assinatura antes de qualquer uso futuro — do jeito que está, apaga TIFF. -
Rebaixar os 69 que sobraram (achados 2.25–2.27; 6 recuperados em 13/09 à noite) quando a origem se recuperar — todos têm thumbnail, falta a preservação. Um
FALTA3.retomar()/F5.iniciar()na aba basta; o que já baixou fica marcado nolocalStorage(bras_faltantes_v3). -
Handle
8396sem imagem e sem diagnóstico — não está empipeline/faltantes-preservacao.jsonlnem nas exceções conhecidas do MANIFEST; apareceu ao cruzar catálogo × miniaturas. Precisa de uma tentativa avulsa pra saber se é recusa da origem (achado 2.18) ou outra coisa. -
.gitdorafapolo/xyzestá em 2,5 GB e cada publicação do atlas soma ~51 MB permanentes (binário não faz delta). Se o mural for republicar atlas com frequência, tirar os.webpdo git — LFS, ou servir do próprio bucket, que já tem os thumbs emderivados/thumb256/. -
Crawl da Fase 1 pode ter mais buracos (achado 2.10 do PLANO.md): 2 itens reais (
1799,4798) foram achados faltando no catálogo só porque o WordPress os citava — já corrigidos, mas não existe verificação exaustiva de que a enumeração porbrowse?type=titlepegou 100% de cada comunidade. Sem ideia barata de auditar isso por completo agora. -
Baixar os 348 legados recuperáveis pelo endereço novo (achado 2.28) — atualizar antes o
baixador.pyparahttps://objdigital.bn.br/objdigital2/acervo_digital/. -
Decidir o que fazer com as 66 páginas HTML de visualizador (25 já em
legado/+ 41 no endereço novo; achado 2.28): apagar do bucket e do ledger (não são imagem), ou montar a imagem a partir dos tiles Deep Zoom quando o objdigital voltar. Todos os 25 itens já têm preservação.
Ao final (não fazer antes do download terminar)
- Gerar
MANIFEST.md(o que é, como foi coletado, quando) — feito; atualizado em 2026-09-12 comderivados/e os achados 2.19/2.20/3.3 - Confirmar no bucket que a contagem de itens/checksums bate com os 13.058 itens reais esperados
Notas técnicas úteis pra retomar
- Downloads automáticos do Chrome exigem permissão manual em
chrome://settings/content/automaticDownloads(site adicionado:https://brasilianafotografica.bn.gov.br) — já concedida, vale pra novos scripts também, não só o original. BRAS,BAIXA,BAIXA_THUMB,BAIXA_EXTRA— todos nowindowda mesma aba, mesmo IndexedDB (bras). Todos sobrevivem a fechar/reabrir a aba (dado fica salvo), mas o loop para e precisa recolar o script certo pra retomar.- Regerar as miniaturas derivadas (tudo reconstruível, nada precisa ser
guardado): no finland,
apt install libvips-tools parallel+ s5cmd; pra cada chave deitens/*/preservacao/*,s5cmd catnum tmp evipsthumbnail "$TMP" --size 256 --output "$OUT[Q=80,strip]", comparallel -j 10. ~640 thumbs/min, tráfego intra-Hetzner (bucket éhel1, o finland também) — não passa pelo link de casa. O que ainda está só em~/Downloadssai mais barato derivado localmente commagick: mandar os 71 GB pro finland custaria os mesmos dias do upload pendente. - Cuidado ao parsear
s5cmd ls: nomes de arquivo do acervo têm espaço duplo (48094 Álbum 311 - ...jpg).awk '{$1="";$2="";$3=""; print}' recolapsa esses espaços e produz chaves que não existem — 31 downloads falharam assim antes de eu perceber. Usesed -E 's|^[0-9/]+ +[0-9:]+ +[0-9]+ ||'`, que preserva o resto literal. sqlite3desta máquina tem.sqlitercforçando modo box: qualquer script que leia a saída precisa desqlite3 -init /dev/null -noheader -separator $'\t', senão vem borda de tabela no lugar dos dados (me custou um diff inteiro de falso positivo).until ! pgrep -f X; do sleep; donenunca termina: a linha de comando do próprio loop contémX, então opgrep -fse acha e a condição nunca vira falsa. Um loop desses ficou girando horas depois do trabalho acabar (12/09, morto à mão). Esperar por PID (while kill -0 $PID) ou usarpgrep -f '[t]humb-local'para o padrão não casar consigo mesmo.- Parar processo lançado em background: SIGTERM, não SIGINT. Processo
iniciado com
&num shell não interativo herda o SIGINT ignorado (osubidor.pyficou 60 s vivo depois dokill -INT). Com SIGTERM, oaws s3 cpfilho termina órfão, o que é inofensivo: sem linha no ledger e sem apagar o local, o próximo uploader sobe de novo a mesma chave. - Upload de casa: 69 Mbps (
networkQuality -s, 12/09); download 312 Mbps. - Assinaturas aceitas como imagem (
subidor-paralelo.py): JPEGFFD8, PNG, TIFFII*\0/MM\0*, GIF, JP2, BMPBM, WebP. Content-Type sai do conteúdo, não da extensão (TIFF chega com nome.jpg). vipsthumbnail --outputé relativo à pasta do arquivo de entrada, não ao diretório corrente:vipsthumbnail tmp/x.img --output out/y.webptenta gravar emtmp/out/y.webpe falha. Usar caminho absoluto na saída. (Custou uma miniatura "refeita" que não foi refeita, em 13/09.)- Scripts-fonte em
pipeline/:coletor.js/coletor-resume.js(metadados),baixador-navegador.js(preservação, todos os itens),baixador-thumb.js(miniaturas),baixador-extras.js(2a+ imagem de itens multi-bitstream),reparo-multibitstream.js(enumera bitstreams reais de um item).