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
+93
View File
@@ -0,0 +1,93 @@
/* ===================================================================
v2 -- a v1 usava .file-wrapper a[href*="sequence="], que nao extrai
nada (testado, retorna vazio). O que funciona: contar quantos
.file-wrapper existem (bate com o numero real de bitstreams) e
tentar as sequencias 1..N direto -- ja confirmado que o nome do
arquivo na URL nao importa nessa rota, so a sequencia.
Reparo: itens sem preservation_url que na verdade tem bitstream(s)
real(is) -- so nao geram citation_pdf_url (provavelmente por terem
mais de um bitstream no bundle ORIGINAL, o que confunde o gerador
de citacao do DSpace).
Grava all_preservation_urls / all_preservation_sizes (ordenado do
maior pro menor) e preservation_url = o maior, pra compatibilidade
com o pipeline existente. Rodar depois baixador-extras.js pra pegar
as demais imagens dos itens com mais de uma.
=================================================================== */
(async () => {
const BASE = 'https://brasilianafotografica.bn.gov.br/brasiliana';
const PREFIX = '20.500.12156.1';
const PAUSA = 2000;
const sleep = ms => new Promise(r => setTimeout(r, ms));
const jit = () => PAUSA + Math.floor(Math.random() * 600);
// preencha com os handles a reparar (ex.: os que tem preservation_url nulo)
const HANDLES = __HANDLES__;
let db;
const abrir = () => new Promise((res, rej) => {
const q = indexedDB.open('bras', 1);
q.onsuccess = () => res(q.result); q.onerror = () => rej(q.error);
});
const get = (st, k) => new Promise(res => {
const r = db.transaction(st).objectStore(st).get(k);
r.onsuccess = () => res(r.result); r.onerror = () => res(undefined);
});
const put = (st, k, v) => new Promise((res, rej) => {
const t = db.transaction(st, 'readwrite'); t.objectStore(st).put(v, k);
t.oncomplete = res; t.onerror = () => rej(t.error);
});
async function contaBitstreams(h) {
const r = await fetch(`${BASE}/handle/${PREFIX}/${h}`, {credentials: 'same-origin'});
if (!r.ok) throw new Error('HTTP ' + r.status);
const html = await r.text();
const d = new DOMParser().parseFromString(html, 'text/html');
return d.querySelectorAll('.file-wrapper').length;
}
async function tamanho(url) {
const r = await fetch(url, {credentials: 'same-origin'});
if (!r.ok) return -1;
const b = await r.blob();
return b.size;
}
const RESULT = {ok: 0, erro: 0, multiplos: 0, total: HANDLES.length, atual: 0};
window.REPARO_MULTI = RESULT;
db = await abrir();
console.log(`reparo-multibitstream v2: ${HANDLES.length} itens a checar`);
for (let i = 0; i < HANDLES.length; i++) {
const h = HANDLES[i];
RESULT.atual = i + 1;
try {
const n = await contaBitstreams(h);
if (!n) { RESULT.erro++; console.warn(` ${h}: 0 file-wrapper (sem bitstream mesmo)`); await sleep(jit()); continue; }
const comTamanho = [];
for (let seq = 1; seq <= n; seq++) {
const u = `${BASE}/bitstream/${PREFIX}/${h}/${seq}/imagem.jpg`;
const bytes = await tamanho(u);
if (bytes > 0) comTamanho.push({url: u, bytes});
await sleep(300);
}
if (!comTamanho.length) { RESULT.erro++; console.warn(` ${h}: ${n} wrapper(s) mas nenhuma sequencia respondeu`); await sleep(jit()); continue; }
comTamanho.sort((a, b) => b.bytes - a.bytes);
const item = await get('itens', h) || {};
item.preservation_url = comTamanho[0].url;
item.all_preservation_urls = comTamanho.map(c => c.url);
item.all_preservation_sizes = comTamanho.map(c => c.bytes);
await put('itens', h, item);
RESULT.ok++;
if (comTamanho.length > 1) RESULT.multiplos++;
console.log(` ${h}: ${comTamanho.length}/${n} bitstream(s), maior=${(comTamanho[0].bytes/1048576).toFixed(1)}MB`);
} catch (e) {
RESULT.erro++;
console.warn(` ${h}: ${e.message}`);
}
await sleep(jit());
}
console.log(`\nreparo-multibitstream v2 concluido. ok=${RESULT.ok} erro=${RESULT.erro} itens-com-multiplos-bitstreams=${RESULT.multiplos}`);
})();