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:
@@ -0,0 +1,86 @@
|
||||
/* ===================================================================
|
||||
Baixa as imagens EXTRAS de itens com mais de um bitstream real
|
||||
(achado do reparo-multibitstream.js: 114 itens, a maioria com 2,
|
||||
alguns com 3). A maior de cada item ja e o preservation_url, coberta
|
||||
pelo baixador-navegador.js -- este script pega as demais.
|
||||
|
||||
PRE-REQUISITO: reparo-multibitstream.js ja rodado (precisa dos
|
||||
campos all_preservation_urls / all_preservation_sizes no IndexedDB).
|
||||
|
||||
Nomeia como extra_seqN.jpg -- sobe pra preservacao/extra_seqN.jpg
|
||||
junto com a imagem principal do item (subidor.py nao precisa de
|
||||
ajuste, ja trata qualquer nome que nao comece com THUMB__).
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const PAUSA = 3000;
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 1000);
|
||||
|
||||
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 getAll = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAll();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error);
|
||||
});
|
||||
|
||||
const S = {fase: 'ocioso', feito: 0, erros: 0, total: 0, atual: '', parar: false};
|
||||
const FEITOS = new Set(JSON.parse(localStorage.getItem('bras_extras') || '[]'));
|
||||
const marcar = k => { FEITOS.add(k); localStorage.setItem('bras_extras', JSON.stringify([...FEITOS])); };
|
||||
|
||||
async function alvos() {
|
||||
const itens = await getAll('itens');
|
||||
const tarefas = [];
|
||||
for (const o of itens) {
|
||||
if (!o.all_preservation_urls || o.all_preservation_urls.length < 2) continue;
|
||||
o.all_preservation_urls.forEach((u, idx) => {
|
||||
if (u === o.preservation_url) return; // a maior ja e coberta pelo BAIXA principal
|
||||
tarefas.push({handle: o.handle, url: u, idx});
|
||||
});
|
||||
}
|
||||
return tarefas;
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
S.parar = false; S.fase = 'baixando';
|
||||
const lista = (await alvos()).filter(t => !FEITOS.has(`${t.handle}#${t.idx}`));
|
||||
S.total = lista.length;
|
||||
console.log(`${lista.length} imagens extras a baixar (itens com multiplos bitstreams)`);
|
||||
for (let i = 0; i < lista.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const o = lista[i];
|
||||
S.atual = `${i + 1}/${lista.length}`;
|
||||
const chave = `${o.handle}#${o.idx}`;
|
||||
try {
|
||||
const r = await fetch(o.url, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const b = await r.blob();
|
||||
const seq = o.url.match(/\/(\d+)\/[^/]+$/)?.[1] || o.idx;
|
||||
const nome = `extra_seq${seq}.jpg`;
|
||||
const arqNome = `BRAS__${o.handle.replace(/\//g, '_')}__${nome}`;
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(b); a.download = arqNome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
marcar(chave); S.feito++;
|
||||
} catch (e) {
|
||||
S.erros++; console.warn(` ${chave}: ${e.message}`);
|
||||
}
|
||||
if (i % 20 === 0) console.log(` ${i}/${lista.length} ok=${S.feito} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nExtras concluido. ok=${S.feito} erro=${S.erros}`);
|
||||
}
|
||||
|
||||
window.BAIXA_EXTRA = {
|
||||
quantos: async () => { const a = await alvos();
|
||||
return {total: a.length, jaFeitos: FEITOS.size}; },
|
||||
iniciar, status: () => ({...S}),
|
||||
parar: () => { S.parar = true; },
|
||||
};
|
||||
|
||||
(async () => { db = await abrir(); })();
|
||||
})();
|
||||
Reference in New Issue
Block a user