feat: acervo Brasiliana Fotográfica coletado e mural 3D no ar

Captura integral do acervo da Brasiliana Fotográfica (Biblioteca Nacional)
para s3://brasiliana: 13.060 itens, 12.985 com imagem de preservação, 13.059
com miniatura derivada, 2.137 cópias legadas e 636 posts do blog. 41.444
objetos no ledger.

Os 75 itens sem 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,
porque todos têm thumbnail.

Não versionado, por viver no bucket ou ser reconstruível: catálogo,
posts .jsonl, atlas .webp, sqlite de estado e logs. Credenciais ficam de
fora pelo .gitignore.
This commit is contained in:
rodado
2026-09-13 20:17:02 +02:00
commit 290c4cc21d
690 changed files with 81187 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env node
/*
* ler-localstorage.js — lê o localStorage do Chrome direto do disco, sem
* depender da extensão Claude-in-Chrome estar conectada.
*
* Achado 2026-09-11: o progresso de BAIXA/BAIXA_THUMB (arrays de handles em
* localStorage.bras_baixados / bras_thumbs_v2) sobrevive em
* "~/Library/Application Support/Google/Chrome/Default/Local Storage/leveldb"
* mesmo com a extensão desconectada — é só um arquivo leveldb no disco.
* Copiamos antes de ler pra não brigar com o LOCK do Chrome (que está com o
* diretório aberto o tempo todo).
*
* Uso: node ler-localstorage.js [chave1] [chave2] ...
* sem argumentos, lê as chaves conhecidas do projeto.
*/
const { ClassicLevel } = require('classic-level');
const { execSync } = require('child_process');
const os = require('os');
const path = require('path');
const ORIGIN = 'https://brasilianafotografica.bn.gov.br';
const SRC = path.join(os.homedir(),
'Library/Application Support/Google/Chrome/Default/Local Storage/leveldb');
const COPY = '/tmp/ls-copy-' + Date.now();
const KEYS_PADRAO = ['bras_baixados', 'bras_thumbs', 'bras_thumbs_v2'];
function mk(name) {
return Buffer.concat([
Buffer.from('_' + ORIGIN, 'latin1'),
Buffer.from([0x00, 0x01]),
Buffer.from(name, 'latin1'),
]);
}
async function main() {
const keys = process.argv.slice(2);
const alvo = keys.length ? keys : KEYS_PADRAO;
execSync(`cp -r "${SRC}" "${COPY}"`);
const db = new ClassicLevel(COPY, { valueEncoding: 'buffer', keyEncoding: 'buffer' });
await db.open({ errorIfExists: false, createIfMissing: false });
const resultado = {};
for (const label of alvo) {
try {
const v = await db.get(mk(label));
const flag = v[0];
const str = flag === 0 ? v.slice(1).toString('utf16le') : v.slice(1).toString('utf8');
const arr = JSON.parse(str);
resultado[label] = Array.isArray(arr) ? arr.length : str.length;
} catch (e) {
resultado[label] = null; // chave não existe ou não é JSON — ok, só ausente
}
}
await db.close();
execSync(`rm -rf "${COPY}"`);
console.log(JSON.stringify(resultado, null, 2));
}
main().catch(e => { console.error('ERRO', e.message); process.exit(1); });