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,5 @@
|
||||
AWS_ACCESS_KEY_ID=<preencher>
|
||||
AWS_SECRET_ACCESS_KEY=<preencher>
|
||||
AWS_DEFAULT_REGION=<preencher>
|
||||
S3_ENDPOINT=https://hel1.your-objectstorage.com
|
||||
ARCHIVE_BUCKET=brasiliana
|
||||
@@ -0,0 +1,40 @@
|
||||
{"handle": "20.500.12156.1/10993", "tipo": "preservacao", "nome": "Alb 0279 113 CT.jpg", "formato": "bmp", "ja_no_bucket": false, "na_fila": true, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10993/1/Alb%200279%20113%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/10995", "tipo": "preservacao", "nome": "Alb 0279 106 CT.jpg", "formato": "bmp", "ja_no_bucket": false, "na_fila": true, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10995/1/Alb%200279%20106%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/14293", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14293/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14295", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14295/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14357", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14357/3/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14357", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14357/3/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14365", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14365/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14365", "tipo": "preservacao", "nome": "extra_seq3.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14365/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14365", "tipo": "preservacao", "nome": "extra_seq4.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14365/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14365", "tipo": "preservacao", "nome": "extra_seq5.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14365/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14366", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14366/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14367", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14367/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14408", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "texto/licenca", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14408/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14434", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14434/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14434", "tipo": "preservacao", "nome": "extra_seq3.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14434/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14434", "tipo": "preservacao", "nome": "extra_seq4.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14434/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14434", "tipo": "preservacao", "nome": "extra_seq5.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14434/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14437", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14437/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14437", "tipo": "preservacao", "nome": "extra_seq3.jpg", "formato": "tiff", "ja_no_bucket": true, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14437/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14514", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14514/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14515", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14515/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14515", "tipo": "preservacao", "nome": "extra_seq3.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14515/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14516", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14516/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14518", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14518/3/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14518", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14518/3/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14518", "tipo": "preservacao", "nome": "extra_seq4.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14518/3/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14519", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14519/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14520", "tipo": "preservacao", "nome": "extra_seq1.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14520/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14520", "tipo": "preservacao", "nome": "extra_seq3.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14520/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14520", "tipo": "preservacao", "nome": "extra_seq4.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14520/2/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14521", "tipo": "preservacao", "nome": "extra_seq2.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14521/1/imagem.jpg"}
|
||||
{"handle": "20.500.12156.1/14358", "tipo": "preservacao", "nome": "Alb 0261 139.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14358/1/Alb%200261%20139.jpg"}
|
||||
{"handle": "20.500.12156.1/14359", "tipo": "preservacao", "nome": "Alb 0261 136.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14359/1/Alb%200261%20136.jpg"}
|
||||
{"handle": "20.500.12156.1/14360", "tipo": "preservacao", "nome": "Alb 0261 141.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14360/1/Alb%200261%20141.jpg"}
|
||||
{"handle": "20.500.12156.1/14361", "tipo": "preservacao", "nome": "Alb 0261 142.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14361/1/Alb%200261%20142.jpg"}
|
||||
{"handle": "20.500.12156.1/14362", "tipo": "preservacao", "nome": "Alb 0261 143.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14362/1/Alb%200261%20143.jpg"}
|
||||
{"handle": "20.500.12156.1/14363", "tipo": "preservacao", "nome": "Alb 0261 144.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14363/1/Alb%200261%20144.jpg"}
|
||||
{"handle": "20.500.12156.1/14364", "tipo": "preservacao", "nome": "Alb 0261 146.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14364/1/Alb%200261%20146.jpg"}
|
||||
{"handle": "20.500.12156.1/14368", "tipo": "preservacao", "nome": "Alb 0261 163.jpg", "formato": "tiff", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14368/1/Alb%200261%20163.jpg"}
|
||||
{"handle": "20.500.12156.1/2384", "tipo": "preservacao", "nome": "001AAN014011.jpg.zip", "formato": "zip", "ja_no_bucket": false, "na_fila": false, "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/2384/1/001AAN014011.jpg"}
|
||||
@@ -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(); })();
|
||||
})();
|
||||
@@ -0,0 +1,103 @@
|
||||
/* ===================================================================
|
||||
TODAS as trilhas -- baixa pelo navegador, ponto.
|
||||
|
||||
MUDANCA DE PLANO (2026-09-10): o achado 2.4 do PLANO.md ("bitstreams
|
||||
passam por curl") nao se sustentou. Testado ao vivo: item nunca antes
|
||||
acessado, buscado pelo navegador (200 OK) e IMEDIATAMENTE depois via
|
||||
curl na Finlandia -- 403, pagina real do Cloudflare ("Attention
|
||||
Required"). O unico item que passa por curl (6066) so passa porque
|
||||
ja tinha sido baixado com sucesso antes e ficou em cache de borda
|
||||
(cf-cache-status: HIT) -- nao e regra geral, e sobra de um caso.
|
||||
Ou seja: SEM navegador vivo, sem imagem, pra praticamente tudo --
|
||||
nao so os nomes com encoding (trilha B). baixador.py na Finlandia
|
||||
fica pausado ate se achar outra saida.
|
||||
|
||||
Por isso esta versao pega TODO item com preservation_url, nao so os
|
||||
de nome com encoding. A logica de baixar/nomear/salvar e a mesma que
|
||||
ja validava a trilha B -- so mudou o filtro de quem entra na fila.
|
||||
|
||||
COMO USAR (depois que o coletor.js terminar)
|
||||
1. Na mesma aba do coletor, cole este arquivo.
|
||||
2. BAIXA.quantos() quantos itens precisam desta trilha
|
||||
3. BAIXA.iniciar() comeca; os arquivos caem na pasta Downloads
|
||||
4. Em outro terminal, rode python3 subidor.py --catalogo catalogo.jsonl
|
||||
Ele sobe cada arquivo pro S3 e APAGA na hora, para o disco nao encher.
|
||||
|
||||
BAIXA.status() / BAIXA.parar() / BAIXA.retomar()
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const PAUSA = 3000; // arquivos sao grandes; va devagar
|
||||
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.onupgradeneeded = e => { const d = e.target.result;
|
||||
if (!d.objectStoreNames.contains('fila')) d.createObjectStore('fila');
|
||||
if (!d.objectStoreNames.contains('itens')) d.createObjectStore('itens'); };
|
||||
q.onsuccess = e => res(e.target.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); });
|
||||
|
||||
/* curl nao e mais confiavel pra quase nada (ver nota no topo) --
|
||||
qualquer item com preservation_url entra na fila, nome simples
|
||||
ou com encoding, tanto faz. */
|
||||
const precisaNavegador = u => !!u;
|
||||
|
||||
const S = {fase: 'ocioso', feito: 0, erros: 0, total: 0, atual: '', parar: false};
|
||||
const FEITOS = new Set(JSON.parse(localStorage.getItem('bras_baixados') || '[]'));
|
||||
const marcar = h => { FEITOS.add(h);
|
||||
localStorage.setItem('bras_baixados', JSON.stringify([...FEITOS])); };
|
||||
|
||||
async function alvos() {
|
||||
const itens = await getAll('itens');
|
||||
return itens.filter(o => !o.erro && precisaNavegador(o.preservation_url));
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
S.parar = false; S.fase = 'baixando';
|
||||
const lista = (await alvos()).filter(o => !FEITOS.has(o.handle));
|
||||
S.total = lista.length;
|
||||
console.log(`${lista.length} arquivos por esta trilha (~${(lista.length*PAUSA/3600000).toFixed(1)} h)`);
|
||||
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}`;
|
||||
try {
|
||||
const r = await fetch(o.preservation_url, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const b = await r.blob();
|
||||
const orig = decodeURIComponent(o.preservation_url.split('/').pop());
|
||||
// o prefixo carrega o handle: e assim que o subidor sabe onde guardar
|
||||
const nome = `BRAS__${o.handle.replace(/\//g, '_')}__${orig}`.replace(/[\\/:*?"<>|]/g, '_');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(b); a.download = nome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
marcar(o.handle); S.feito++;
|
||||
} catch (e) {
|
||||
S.erros++; console.warn(` ${o.handle}: ${e.message}`);
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) await sleep(60000);
|
||||
}
|
||||
if (i % 25 === 0) console.log(` ${i}/${lista.length} ok=${S.feito} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nConcluido. ok=${S.feito} erro=${S.erros}`);
|
||||
}
|
||||
|
||||
window.BAIXA = {
|
||||
quantos: async () => { const a = await alvos();
|
||||
return {precisamDoNavegador: a.length, jaBaixados: FEITOS.size, restam: a.length - FEITOS.size}; },
|
||||
iniciar, status: () => ({...S}),
|
||||
parar: () => { S.parar = true; }, retomar: () => iniciar(),
|
||||
limparMarcas: () => localStorage.removeItem('bras_baixados'),
|
||||
};
|
||||
|
||||
(async () => { db = await abrir();
|
||||
const q = await window.BAIXA.quantos();
|
||||
console.log('BAIXA pronto:', q, '\nRode BAIXA.iniciar() para comecar.');
|
||||
})();
|
||||
})();
|
||||
@@ -0,0 +1,137 @@
|
||||
/* ===================================================================
|
||||
v2 -- a v1 confiava cegamente que seq_master+1 e a miniatura, so
|
||||
descartando se viesse GRANDE demais. Bug real (2026-09-10): pra
|
||||
muitos itens (varias instituicoes -- visto em Marinha e IMS), essa
|
||||
sequencia cai no bitstream de LICENCA do DSpace (texto puro, poucas
|
||||
centenas de bytes) em vez da miniatura. 62% dos thumbs de um lote
|
||||
sairam errados assim -- pequenos o bastante pra passar no filtro de
|
||||
tamanho, mas nao sao imagem nenhuma.
|
||||
|
||||
FIX: valida os magic bytes (JPEG/PNG) antes de aceitar. Se a
|
||||
primeira tentativa (seq+1) nao for imagem, tenta seq+2 e seq+3 (caso
|
||||
a licenca esteja no meio). Se nenhuma bater, cai pro metodo confiavel
|
||||
do achado 2.7 (extrai o <img src> da pagina do item).
|
||||
|
||||
O subidor.py TAMBEM valida antes de subir agora (defesa em camadas)
|
||||
-- mas o ideal e nao gerar o lixo em primeiro lugar.
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const BASE = 'https://brasilianafotografica.bn.gov.br/brasiliana';
|
||||
const PREFIX = '20.500.12156.1';
|
||||
const PAUSA = 1500;
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 500);
|
||||
|
||||
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_thumbs_v2') || '[]'));
|
||||
const marcar = h => { FEITOS.add(h); localStorage.setItem('bras_thumbs_v2', JSON.stringify([...FEITOS])); };
|
||||
|
||||
function seqDoUrl(u) {
|
||||
const m = u.match(/\/bitstream\/[^/]+\/[^/]+\/(\d+)\//);
|
||||
return m ? +m[1] : null;
|
||||
}
|
||||
|
||||
async function ehImagem(blob) {
|
||||
const buf = new Uint8Array(await blob.slice(0, 8).arrayBuffer());
|
||||
if (buf[0] === 0xFF && buf[1] === 0xD8) return true; // JPEG
|
||||
if (buf[0]===0x89 && buf[1]===0x50 && buf[2]===0x4E && buf[3]===0x47) return true; // PNG
|
||||
return false;
|
||||
}
|
||||
|
||||
async function tentaSequencia(handle, seq) {
|
||||
const url = `${BASE}/bitstream/${PREFIX}/${handle}/${seq}/thumb.jpg`;
|
||||
const r = await fetch(url, {credentials: 'same-origin'});
|
||||
if (!r.ok) return null;
|
||||
const b = await r.blob();
|
||||
if (b.size > 2 * 1024 * 1024) return null; // grande demais pra thumb
|
||||
if (!(await ehImagem(b))) return null; // achado 2026-09-10: pode ser bitstream de licenca
|
||||
return {blob: b, nomeSeq: seq};
|
||||
}
|
||||
|
||||
async function viaImgDaPagina(handle) {
|
||||
const r = await fetch(`${BASE}/handle/${PREFIX}/${handle}`, {credentials: 'same-origin'});
|
||||
if (!r.ok) return null;
|
||||
const html = await r.text();
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const img = d.querySelector('.file-wrapper img');
|
||||
if (!img) return null;
|
||||
let u;
|
||||
try { u = new URL(img.getAttribute('src'), location.href); } catch(e) { return null; }
|
||||
const rr = await fetch(u.href, {credentials: 'same-origin'});
|
||||
if (!rr.ok) return null;
|
||||
const b = await rr.blob();
|
||||
if (!(await ehImagem(b))) return null;
|
||||
return {blob: b, nomeSeq: 'pagina'};
|
||||
}
|
||||
|
||||
async function alvos() {
|
||||
const itens = await getAll('itens');
|
||||
return itens.filter(o => {
|
||||
if (o.erro || !o.preservation_url) return false;
|
||||
const seqs = (o.all_preservation_urls || [o.preservation_url]).map(seqDoUrl).filter(Boolean);
|
||||
return seqs.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
S.parar = false; S.fase = 'baixando';
|
||||
const lista = (await alvos()).filter(o => !FEITOS.has(o.handle));
|
||||
S.total = lista.length;
|
||||
console.log(`${lista.length} miniaturas a checar/baixar (v2, com validacao)`);
|
||||
for (let i = 0; i < lista.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const o = lista[i];
|
||||
const h = o.handle.split('/').pop();
|
||||
S.atual = `${i + 1}/${lista.length}`;
|
||||
try {
|
||||
const seqs = (o.all_preservation_urls || [o.preservation_url]).map(seqDoUrl).filter(Boolean);
|
||||
const maxSeq = Math.max(...seqs);
|
||||
let achado = null;
|
||||
for (const seq of [maxSeq + 1, maxSeq + 2, maxSeq + 3]) {
|
||||
achado = await tentaSequencia(h, seq);
|
||||
if (achado) break;
|
||||
await sleep(300);
|
||||
}
|
||||
if (!achado) achado = await viaImgDaPagina(h);
|
||||
if (!achado) throw new Error('nenhuma miniatura valida encontrada');
|
||||
|
||||
const nome = `THUMB__thumb_${achado.nomeSeq}.jpg`;
|
||||
const arqNome = `BRAS__${o.handle.replace(/\//g, '_')}__${nome}`;
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(achado.blob); a.download = arqNome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
marcar(o.handle); S.feito++;
|
||||
} catch (e) {
|
||||
S.erros++; console.warn(` ${o.handle}: ${e.message}`);
|
||||
}
|
||||
if (i % 50 === 0) console.log(` ${i}/${lista.length} ok=${S.feito} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nThumbs v2 concluido. ok=${S.feito} erro=${S.erros}`);
|
||||
}
|
||||
|
||||
window.BAIXA_THUMB = {
|
||||
quantos: async () => { const a = await alvos();
|
||||
return {total: a.length, jaFeitos: FEITOS.size, restam: a.length - FEITOS.size}; },
|
||||
iniciar, status: () => ({...S}),
|
||||
parar: () => { S.parar = true; }, retomar: () => iniciar(),
|
||||
limparMarcas: () => localStorage.removeItem('bras_thumbs_v2'),
|
||||
};
|
||||
|
||||
(async () => { db = await abrir();
|
||||
const q = await window.BAIXA_THUMB.quantos();
|
||||
console.log('BAIXA_THUMB v2 pronto:', q, '\nRode BAIXA_THUMB.iniciar() para comecar.');
|
||||
})();
|
||||
})();
|
||||
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Baixa os arquivos da Brasiliana e deposita no S3, um por vez, sem acumular.
|
||||
|
||||
Roda no servidor (Finlandia): mede 4,7 MB/s no bitstream e o bucket e Hetzner
|
||||
tambem, entao a transferencia nao sai do datacenter. NUNCA guarda mais de um
|
||||
arquivo em disco: baixa -> sha256 -> sobe -> apaga. Pico de uso = 1 arquivo.
|
||||
|
||||
Os bitstreams do DSpace passam pelo Cloudflare com curl simples (as paginas
|
||||
HTML e que sao desafiadas -- essas vem do coletor.js, no navegador).
|
||||
A copia legada do objdigital NAO e alcancavel da Finlandia: rode --somente-legado
|
||||
na maquina local para essas.
|
||||
|
||||
python3 baixador.py --catalogo catalogo.jsonl
|
||||
python3 baixador.py --catalogo catalogo.jsonl --somente-legado # na sua maquina
|
||||
python3 baixador.py --status
|
||||
"""
|
||||
import argparse, hashlib, json, os, shutil, sqlite3, subprocess, sys, time
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote, urlparse
|
||||
|
||||
UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
|
||||
"(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36")
|
||||
CONTACT = os.environ.get("ARCHIVE_CONTACT", "")
|
||||
S3_ENDPOINT = os.environ.get("S3_ENDPOINT", "https://hel1.your-objectstorage.com")
|
||||
BUCKET = os.environ.get("ARCHIVE_BUCKET", "brasiliana")
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
DB = ROOT / "baixador.sqlite"
|
||||
TMP = ROOT / ".tmp"
|
||||
PAUSA = 2.0
|
||||
MIN_LIVRE = 2 * 1024**3 # 2 GB de folga; abaixo disso, para
|
||||
|
||||
|
||||
def db():
|
||||
con = sqlite3.connect(DB)
|
||||
con.execute("PRAGMA journal_mode=WAL")
|
||||
con.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS arquivos (
|
||||
handle TEXT, tipo TEXT, url TEXT, nome TEXT,
|
||||
bytes INTEGER, sha256 TEXT, status TEXT, erro TEXT,
|
||||
PRIMARY KEY (handle, tipo));
|
||||
""")
|
||||
return con
|
||||
|
||||
|
||||
def livre():
|
||||
return shutil.disk_usage(ROOT).free
|
||||
|
||||
|
||||
def baixar(url, destino):
|
||||
"""curl -L para arquivo. Retorna (http, bytes) sem manter nada em memoria."""
|
||||
r = subprocess.run(
|
||||
["curl", "-sL", "-o", str(destino), "-w", "%{http_code} %{size_download}",
|
||||
"-A", UA, *(["-H", f"From: {CONTACT}"] if CONTACT else []),
|
||||
"--max-time", "600", "--retry", "2", "--retry-delay", "5", url],
|
||||
capture_output=True, text=True)
|
||||
try:
|
||||
http, size = r.stdout.strip().split()
|
||||
return int(http), int(size)
|
||||
except Exception:
|
||||
return 0, 0
|
||||
|
||||
|
||||
def s3_put(local, key):
|
||||
return subprocess.run(
|
||||
["aws", "--endpoint-url", S3_ENDPOINT, "s3", "cp", str(local),
|
||||
f"s3://{BUCKET}/{key}", "--only-show-errors"],
|
||||
capture_output=True, text=True).returncode == 0
|
||||
|
||||
|
||||
def nome_de(url):
|
||||
n = unquote(urlparse(url).path.rsplit("/", 1)[-1]) or "arquivo.bin"
|
||||
return n.replace("/", "_")[:150]
|
||||
|
||||
|
||||
def processa(con, rec, tipo, url, args):
|
||||
handle = rec["handle"]
|
||||
ja = con.execute("SELECT status FROM arquivos WHERE handle=? AND tipo=?",
|
||||
(handle, tipo)).fetchone()
|
||||
if ja and ja[0] == "ok":
|
||||
return "pulado"
|
||||
if livre() < MIN_LIVRE:
|
||||
sys.exit(f"\nESPACO BAIXO ({livre()/1024**3:.1f} GB livres). Parando por seguranca.")
|
||||
|
||||
TMP.mkdir(exist_ok=True)
|
||||
nome = nome_de(url)
|
||||
tmp = TMP / nome
|
||||
http, size = baixar(url, tmp)
|
||||
if http != 200 or size <= 0:
|
||||
tmp.unlink(missing_ok=True)
|
||||
con.execute("INSERT OR REPLACE INTO arquivos VALUES (?,?,?,?,NULL,NULL,'erro',?)",
|
||||
(handle, tipo, url, nome, f"HTTP {http}"))
|
||||
con.commit()
|
||||
return f"erro HTTP {http}"
|
||||
|
||||
sha = hashlib.sha256()
|
||||
with tmp.open("rb") as fh:
|
||||
for bloco in iter(lambda: fh.read(1 << 20), b""):
|
||||
sha.update(bloco)
|
||||
dig = sha.hexdigest()
|
||||
|
||||
pasta = handle.replace("/", "_")
|
||||
sub = "preservacao" if tipo == "preservacao" else "legado"
|
||||
ok = s3_put(tmp, f"itens/{pasta}/{sub}/{nome}")
|
||||
tmp.unlink(missing_ok=True) # apaga SEMPRE, deu certo ou nao
|
||||
|
||||
if not ok:
|
||||
con.execute("INSERT OR REPLACE INTO arquivos VALUES (?,?,?,?,?,?,'erro','falha no upload')",
|
||||
(handle, tipo, url, nome, size, dig))
|
||||
con.commit()
|
||||
return "erro no S3"
|
||||
|
||||
con.execute("INSERT OR REPLACE INTO arquivos VALUES (?,?,?,?,?,?,'ok',NULL)",
|
||||
(handle, tipo, url, nome, size, dig))
|
||||
con.commit()
|
||||
return f"{size/1048576:.1f} MB"
|
||||
|
||||
|
||||
def sobe_metadados(con, rec):
|
||||
"""metadata.json + manifest-sha256.txt do item, a partir do catalogo.
|
||||
|
||||
Nome de arquivo temporario inclui o PID: baixador.py e subidor.py
|
||||
moram na mesma pasta (mesmo TMP) e podem rodar ao mesmo tempo -- um
|
||||
nome fixo faz um apagar o arquivo que o outro acabou de criar
|
||||
(achado 2026-09-10, derrubou o baixador.py com FileNotFoundError)."""
|
||||
handle = rec["handle"]; pasta = handle.replace("/", "_")
|
||||
TMP.mkdir(exist_ok=True)
|
||||
mj = TMP / f"metadata.{os.getpid()}.json"
|
||||
mj.write_text(json.dumps(rec, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
s3_put(mj, f"itens/{pasta}/metadata.json")
|
||||
linhas = [f"{sha} {'preservacao' if t=='preservacao' else 'legado'}/{n}"
|
||||
for t, n, sha in con.execute(
|
||||
"SELECT tipo,nome,sha256 FROM arquivos WHERE handle=? AND status='ok'",
|
||||
(handle,))]
|
||||
linhas.append(f"{hashlib.sha256(mj.read_bytes()).hexdigest()} metadata.json")
|
||||
mf = TMP / f"manifest-sha256.{os.getpid()}.txt"
|
||||
mf.write_text("\n".join(linhas) + "\n", encoding="utf-8")
|
||||
s3_put(mf, f"itens/{pasta}/manifest-sha256.txt")
|
||||
mj.unlink(missing_ok=True); mf.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--catalogo")
|
||||
ap.add_argument("--limit", type=int, default=0)
|
||||
ap.add_argument("--somente-legado", action="store_true")
|
||||
ap.add_argument("--pular-legado", action="store_true")
|
||||
ap.add_argument("--status", action="store_true")
|
||||
args = ap.parse_args()
|
||||
con = db()
|
||||
|
||||
if args.status:
|
||||
for tipo, st, n, b in con.execute(
|
||||
"SELECT tipo,status,COUNT(*),COALESCE(SUM(bytes),0) FROM arquivos GROUP BY 1,2"):
|
||||
print(f" {tipo:<12} {st:<6} {n:>6} {b/1024**3:.2f} GB")
|
||||
print(f" disco livre: {livre()/1024**3:.1f} GB")
|
||||
return
|
||||
|
||||
if not args.catalogo:
|
||||
sys.exit("informe --catalogo catalogo.jsonl")
|
||||
if not os.environ.get("AWS_ACCESS_KEY_ID"):
|
||||
sys.exit("credenciais S3 ausentes no ambiente")
|
||||
|
||||
recs = [json.loads(l) for l in open(args.catalogo, encoding="utf-8") if l.strip()]
|
||||
recs = [r for r in recs if not r.get("erro")]
|
||||
if args.limit:
|
||||
recs = recs[: args.limit]
|
||||
print(f"{len(recs)} itens | destino s3://{BUCKET}/ | livre {livre()/1024**3:.1f} GB\n")
|
||||
|
||||
for i, rec in enumerate(recs, 1):
|
||||
partes = []
|
||||
if not args.somente_legado and rec.get("preservation_url"):
|
||||
partes.append(("preservacao", rec["preservation_url"]))
|
||||
if not args.pular_legado and rec.get("legacy_url"):
|
||||
# objdigital.bn.br reseta conexao na porta 80 (http simples) --
|
||||
# so responde em https. O DC.identifier na fonte traz http://.
|
||||
legado_url = rec["legacy_url"].replace("http://", "https://", 1)
|
||||
partes.append(("legado", legado_url))
|
||||
if not partes:
|
||||
continue
|
||||
res = []
|
||||
for tipo, url in partes:
|
||||
res.append(f"{tipo}:{processa(con, rec, tipo, url, args)}")
|
||||
time.sleep(PAUSA)
|
||||
sobe_metadados(con, rec)
|
||||
print(f"[{i}/{len(recs)}] {rec['handle'].split('/')[-1]:<6} {' '.join(res)}", flush=True)
|
||||
|
||||
print("\nconcluido")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrompido. Estado salvo -- rode de novo para continuar.")
|
||||
@@ -0,0 +1,176 @@
|
||||
/* ===================================================================
|
||||
FALTA -- fecha a trilha 1: baixa SO os 73 itens de preservacao que
|
||||
ainda nao existem nem no bucket nem na fila local de ~/Downloads.
|
||||
|
||||
POR QUE ESTE SCRIPT EXISTE (achado 2.16, 2026-09-11)
|
||||
O contador `bras_baixados` anda pra tras: `marcar()` do
|
||||
baixador-navegador.js reescreve o array inteiro a partir do FEITOS
|
||||
carregado quando o script foi colado, entao qualquer aba com snapshot
|
||||
velho sobrescreve a lista boa por uma mais curta (12.974 -> 12.111 em
|
||||
meia hora). Recolar o baixador geral re-baixaria ~950+ arquivos que o
|
||||
contador esqueceu -- ou os 13 mil, se o storage estiver zerado.
|
||||
|
||||
Por isso aqui a lista dos faltantes vem EMBUTIDA no script (apurada
|
||||
por bucket + fila local, nao pelo localStorage) e o progresso vai pra
|
||||
uma chave propria, `bras_faltantes_73`. Este script NUNCA escreve em
|
||||
`bras_baixados` -- nao piora a lista que ja esta corrompida.
|
||||
|
||||
COMO USAR
|
||||
1. Cole na aba da Brasiliana (a do coletor, ou qualquer uma da origem).
|
||||
2. FALTA.iniciar()
|
||||
3. Os arquivos caem em ~/Downloads com o prefixo BRAS__<handle>__,
|
||||
que e como o subidor.py (ja rodando, PID 97295) sabe onde guardar.
|
||||
Nao precisa mexer no subidor: ele vigia a pasta sozinho.
|
||||
|
||||
FALTA.status() / FALTA.parar() / FALTA.retomar() / FALTA.pendentes()
|
||||
FALTA.erros() lista o que falhou, pra uma segunda passada
|
||||
=================================================================== */
|
||||
(() => {
|
||||
|
||||
/* [handle, preservation_url] -- apurados em 2026-09-11 20:45 UTC */
|
||||
const FALTANTES = [
|
||||
["20.500.12156.1/10993", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10993/1/Alb%200279%20113%20CT.jpg"],
|
||||
["20.500.12156.1/10995", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10995/1/Alb%200279%20106%20CT.jpg"],
|
||||
["20.500.12156.1/6433", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6433/1/0072430cx006-05t.jpg"],
|
||||
["20.500.12156.1/6520", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6520/1/007_IMG_1008t.jpg"],
|
||||
["20.500.12156.1/6545", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6545/1/CC-05-01-015-3.jpg"],
|
||||
["20.500.12156.1/6555", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6555/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0009_m0003de0003.jpg"],
|
||||
["20.500.12156.1/6568", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6568/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0019_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6570", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6570/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0024_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6669", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6669/1/BP-06-TP-01-012.jpg"],
|
||||
["20.500.12156.1/6670", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6670/1/BP-06-TP-01-015.jpg"],
|
||||
["20.500.12156.1/6674", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6674/1/BP-06-TP-01-022.jpg"],
|
||||
["20.500.12156.1/7034", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7034/1/007A6P3F08-004.jpg"],
|
||||
["20.500.12156.1/7317", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7317/4/Alb%200294%20069ct.jpg"],
|
||||
["20.500.12156.1/7326", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7326/4/Alb%200294%20037ct.jpg"],
|
||||
["20.500.12156.1/7344", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7344/4/Alb%200294%20093ct.jpg"],
|
||||
["20.500.12156.1/7353", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7353/4/Alb%200294%20097ct.jpg"],
|
||||
["20.500.12156.1/7355", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7355/4/Alb%200259%20073%20ct.jpg"],
|
||||
["20.500.12156.1/7358", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7358/1/BR_RJANRIO_OG_0_FOT_0001_021.jpg"],
|
||||
["20.500.12156.1/7379", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7379/1/BR_RJANRIO_OG_0_FOT_0001_057.jpg"],
|
||||
["20.500.12156.1/7386", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7386/1/BR_RJANRIO_OG_0_FOT_0001_067.jpg"],
|
||||
["20.500.12156.1/7393", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7393/1/BR_RJANRIO_OG_0_FOT_0001_089.jpg"],
|
||||
["20.500.12156.1/7438", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7438/1/BR_RJANRIO_OG_0_FOT_0001_085.jpg"],
|
||||
["20.500.12156.1/7440", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7440/1/BR_RJANRIO_OG_0_FOT_0001_087.jpg"],
|
||||
["20.500.12156.1/7457", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7457/4/Alb%200294%20127ct.jpg"],
|
||||
["20.500.12156.1/7461", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7461/4/Alb%200259%20081ct.jpg"],
|
||||
["20.500.12156.1/7463", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7463/4/Alb%200294%20106ct.jpg"],
|
||||
["20.500.12156.1/7475", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7475/4/Alb%200259%20096ct.jpg"],
|
||||
["20.500.12156.1/7515", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7515/4/Alb%200259%20145ct.jpg"],
|
||||
["20.500.12156.1/7516", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7516/4/Alb%200259%20139ct.jpg"],
|
||||
["20.500.12156.1/7517", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7517/3/Album%200035%20297%20CT.jpg"],
|
||||
["20.500.12156.1/7529", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7529/5/Alb%200259%20130ct.jpg"],
|
||||
["20.500.12156.1/7743", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7743/1/001ALA011011.jpg"],
|
||||
["20.500.12156.1/7744", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7744/1/001ALA011012.jpg"],
|
||||
["20.500.12156.1/7766", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7766/1/001ATK012010.jpg"],
|
||||
["20.500.12156.1/7802", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7802/1/Alb%200036%20037%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7814", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7814/1/Alb%200036%20081%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7817", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7817/1/Alb%200036%20087%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7848", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7848/1/Alb%200036%20013%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7852", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7852/1/Album%200035%20105%20CT.jpg"],
|
||||
["20.500.12156.1/7861", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7861/1/Album%200035%20088%20CT.jpg"],
|
||||
["20.500.12156.1/7875", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7875/1/Album%200035%20078%20CT.jpg"],
|
||||
["20.500.12156.1/8075", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8075/1/Alb%200038%20009%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/8128", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8128/1/014AM006009.jpg"],
|
||||
["20.500.12156.1/8133", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8133/1/014AM010003.jpg"],
|
||||
["20.500.12156.1/8141", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8141/1/014AM010014.jpg"],
|
||||
["20.500.12156.1/8145", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8145/1/014AM012006.jpg"],
|
||||
["20.500.12156.1/8161", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8161/1/014AM012058.jpg"],
|
||||
["20.500.12156.1/8182", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8182/1/014AM019007.jpg"],
|
||||
["20.500.12156.1/8187", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8187/1/014AM019013.jpg"],
|
||||
["20.500.12156.1/8249", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8249/1/014AM002012.jpg"],
|
||||
["20.500.12156.1/8254", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8254/1/014AM002019.jpg"],
|
||||
["20.500.12156.1/8262", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8262/1/014AM003015.jpg"],
|
||||
["20.500.12156.1/8267", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8267/1/014AM004003.jpg"],
|
||||
["20.500.12156.1/8268", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8268/1/014AM005003.jpg"],
|
||||
["20.500.12156.1/8271", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8271/1/014AM005005.jpg"],
|
||||
["20.500.12156.1/8275", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8275/1/014AM005014.jpg"],
|
||||
["20.500.12156.1/8277", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8277/1/014AM005020.jpg"],
|
||||
["20.500.12156.1/8286", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8286/1/014AM006002.jpg"],
|
||||
["20.500.12156.1/8313", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8313/1/001AAN005123.jpg"],
|
||||
["20.500.12156.1/8316", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8316/1/001ACL019004.jpg"],
|
||||
["20.500.12156.1/8374", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8374/1/002080NUS0309.jpg"],
|
||||
["20.500.12156.1/8739", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8739/1/007ALA102.jpg"],
|
||||
["20.500.12156.1/8761", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8761/1/007ALA015.jpg"],
|
||||
["20.500.12156.1/8762", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8762/1/007ALA016.jpg"],
|
||||
["20.500.12156.1/8774", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8774/1/007ALA027.jpg"],
|
||||
["20.500.12156.1/8781", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8781/1/007ALA033.jpg"],
|
||||
["20.500.12156.1/8785", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8785/1/007ALA037.jpg"],
|
||||
["20.500.12156.1/8796", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8796/1/007ALA047.jpg"],
|
||||
["20.500.12156.1/8804", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8804/1/007ALA054.jpg"],
|
||||
["20.500.12156.1/8825", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8825/1/007ALA073.jpg"],
|
||||
["20.500.12156.1/8872", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8872/1/A05F03P02.jpg"],
|
||||
["20.500.12156.1/8912", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8912/1/pixinguinha04.jpg"],
|
||||
["20.500.12156.1/9148", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/9148/1/007ALA091.jpg"]
|
||||
];
|
||||
|
||||
const PAUSA = 3000; // mesmo ritmo cortes do baixador geral
|
||||
const CHAVE = 'bras_faltantes_73'; // chave propria: nao toca em bras_baixados
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 1000);
|
||||
|
||||
const FEITOS = new Set(JSON.parse(localStorage.getItem(CHAVE) || '[]'));
|
||||
const marcar = h => { FEITOS.add(h);
|
||||
localStorage.setItem(CHAVE, JSON.stringify([...FEITOS])); };
|
||||
|
||||
const S = {fase: 'ocioso', feito: 0, erros: 0, total: 0, atual: '', parar: false};
|
||||
const FALHAS = [];
|
||||
|
||||
async function baixarUm(handle, url) {
|
||||
const r = await fetch(url, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const b = await r.blob();
|
||||
if (b.size < 1024) throw new Error('blob suspeito: ' + b.size + ' bytes');
|
||||
const orig = decodeURIComponent(url.split('/').pop());
|
||||
// o prefixo carrega o handle: e assim que o subidor sabe onde guardar
|
||||
const nome = `BRAS__${handle.replace(/\//g, '_')}__${orig}`.replace(/[\\/:*?"<>|]/g, '_');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(b); a.download = nome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
return b.size;
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
S.parar = false; S.fase = 'baixando'; FALHAS.length = 0;
|
||||
const lista = FALTANTES.filter(([h]) => !FEITOS.has(h));
|
||||
S.total = lista.length; S.feito = 0; S.erros = 0;
|
||||
console.log(`FALTA: ${lista.length} arquivos (~${Math.ceil(lista.length * PAUSA / 60000)} min)`);
|
||||
|
||||
for (let i = 0; i < lista.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; console.log('parado em ' + i); return; }
|
||||
const [handle, url] = lista[i];
|
||||
S.atual = `${i + 1}/${lista.length} ${handle}`;
|
||||
try {
|
||||
const bytes = await baixarUm(handle, url);
|
||||
marcar(handle); S.feito++;
|
||||
console.log(` ok ${handle} ${(bytes / 1048576).toFixed(1)} MB (${i + 1}/${lista.length})`);
|
||||
} catch (e) {
|
||||
S.erros++; FALHAS.push({handle, url, erro: e.message});
|
||||
console.warn(` ERRO ${handle}: ${e.message}`);
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) await sleep(60000); // mesmo backoff do geral
|
||||
}
|
||||
await sleep(jit());
|
||||
}
|
||||
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nFALTA concluido. ok=${S.feito} erro=${S.erros}`);
|
||||
if (FALHAS.length) {
|
||||
console.log('Rode FALTA.retomar() pra tentar de novo so os que falharam:');
|
||||
console.table(FALHAS);
|
||||
} else {
|
||||
console.log('Trilha 1 fechada: 13.059/13.059. O subidor sobe o resto sozinho.');
|
||||
}
|
||||
}
|
||||
|
||||
window.FALTA = {
|
||||
iniciar, status: () => ({...S}), retomar: () => iniciar(),
|
||||
parar: () => { S.parar = true; },
|
||||
pendentes: () => FALTANTES.filter(([h]) => !FEITOS.has(h)).map(([h]) => h),
|
||||
erros: () => [...FALHAS],
|
||||
limparMarcas: () => localStorage.removeItem(CHAVE),
|
||||
};
|
||||
|
||||
console.log(`FALTA pronto: ${FALTANTES.length} no total, ${window.FALTA.pendentes().length} pendentes.`);
|
||||
console.log('Rode FALTA.iniciar() para comecar.');
|
||||
})();
|
||||
@@ -0,0 +1,195 @@
|
||||
/* ===================================================================
|
||||
FALTA v2 -- fecha a trilha 1 (73 itens de preservacao restantes).
|
||||
|
||||
POR QUE V2 (achado 2.18, 2026-09-12)
|
||||
A v1 tinha tres defeitos que juntos viraram rate-limit na origem:
|
||||
1. `iniciar()` nao se protegia contra chamada dupla -- cada
|
||||
FALTA.iniciar()/retomar() num script ja rodando somava OUTRO loop
|
||||
sobre o mesmo S. Deu `erros: 101` com `atual: 43/73`, e a pausa
|
||||
de 3s combinada com a BN virou 1,5s ou menos.
|
||||
2. Erro nao interrompia nada: o loop moia os 71 com 60s de backoff
|
||||
cada, martelando a origem por mais de uma hora sem chance de
|
||||
alguem olhar o resultado.
|
||||
3. Nao registrava o codigo HTTP separado da mensagem, entao o
|
||||
diagnostico dependia de ler FALHAS na mao.
|
||||
|
||||
Evidencia de que o problema e de origem, nao de URL: 10993 e 10995
|
||||
baixaram as 22:52 e FALHARAM na passada seguinte, mesma URL. E os 71
|
||||
tem 71/71 thumbs baixados com sucesso -- o item responde, o bitstream
|
||||
de preservacao e que nao.
|
||||
|
||||
COMO USAR
|
||||
1. Recarregue a aba antes de colar (mata loops velhos da v1).
|
||||
2. Cole. Rode FALTA.iniciar().
|
||||
3. Ele PARA SOZINHO depois de 5 erros seguidos e mostra a tabela
|
||||
de codigos -- e o sinal de que a origem esta recusando, e insistir
|
||||
so piora. Espere e rode FALTA.retomar() mais tarde.
|
||||
|
||||
FALTA.status() / .parar() / .retomar() / .erros() / .pendentes()
|
||||
=================================================================== */
|
||||
(() => {
|
||||
|
||||
/* [handle, preservation_url] -- apurados em 2026-09-11 20:45 UTC */
|
||||
const FALTANTES = [
|
||||
["20.500.12156.1/10993", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10993/1/Alb%200279%20113%20CT.jpg"],
|
||||
["20.500.12156.1/10995", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10995/1/Alb%200279%20106%20CT.jpg"],
|
||||
["20.500.12156.1/6433", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6433/1/0072430cx006-05t.jpg"],
|
||||
["20.500.12156.1/6520", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6520/1/007_IMG_1008t.jpg"],
|
||||
["20.500.12156.1/6545", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6545/1/CC-05-01-015-3.jpg"],
|
||||
["20.500.12156.1/6555", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6555/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0009_m0003de0003.jpg"],
|
||||
["20.500.12156.1/6568", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6568/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0019_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6570", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6570/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0024_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6669", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6669/1/BP-06-TP-01-012.jpg"],
|
||||
["20.500.12156.1/6670", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6670/1/BP-06-TP-01-015.jpg"],
|
||||
["20.500.12156.1/6674", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6674/1/BP-06-TP-01-022.jpg"],
|
||||
["20.500.12156.1/7034", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7034/1/007A6P3F08-004.jpg"],
|
||||
["20.500.12156.1/7317", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7317/4/Alb%200294%20069ct.jpg"],
|
||||
["20.500.12156.1/7326", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7326/4/Alb%200294%20037ct.jpg"],
|
||||
["20.500.12156.1/7344", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7344/4/Alb%200294%20093ct.jpg"],
|
||||
["20.500.12156.1/7353", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7353/4/Alb%200294%20097ct.jpg"],
|
||||
["20.500.12156.1/7355", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7355/4/Alb%200259%20073%20ct.jpg"],
|
||||
["20.500.12156.1/7358", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7358/1/BR_RJANRIO_OG_0_FOT_0001_021.jpg"],
|
||||
["20.500.12156.1/7379", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7379/1/BR_RJANRIO_OG_0_FOT_0001_057.jpg"],
|
||||
["20.500.12156.1/7386", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7386/1/BR_RJANRIO_OG_0_FOT_0001_067.jpg"],
|
||||
["20.500.12156.1/7393", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7393/1/BR_RJANRIO_OG_0_FOT_0001_089.jpg"],
|
||||
["20.500.12156.1/7438", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7438/1/BR_RJANRIO_OG_0_FOT_0001_085.jpg"],
|
||||
["20.500.12156.1/7440", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7440/1/BR_RJANRIO_OG_0_FOT_0001_087.jpg"],
|
||||
["20.500.12156.1/7457", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7457/4/Alb%200294%20127ct.jpg"],
|
||||
["20.500.12156.1/7461", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7461/4/Alb%200259%20081ct.jpg"],
|
||||
["20.500.12156.1/7463", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7463/4/Alb%200294%20106ct.jpg"],
|
||||
["20.500.12156.1/7475", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7475/4/Alb%200259%20096ct.jpg"],
|
||||
["20.500.12156.1/7515", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7515/4/Alb%200259%20145ct.jpg"],
|
||||
["20.500.12156.1/7516", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7516/4/Alb%200259%20139ct.jpg"],
|
||||
["20.500.12156.1/7517", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7517/3/Album%200035%20297%20CT.jpg"],
|
||||
["20.500.12156.1/7529", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7529/5/Alb%200259%20130ct.jpg"],
|
||||
["20.500.12156.1/7743", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7743/1/001ALA011011.jpg"],
|
||||
["20.500.12156.1/7744", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7744/1/001ALA011012.jpg"],
|
||||
["20.500.12156.1/7766", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7766/1/001ATK012010.jpg"],
|
||||
["20.500.12156.1/7802", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7802/1/Alb%200036%20037%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7814", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7814/1/Alb%200036%20081%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7817", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7817/1/Alb%200036%20087%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7848", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7848/1/Alb%200036%20013%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7852", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7852/1/Album%200035%20105%20CT.jpg"],
|
||||
["20.500.12156.1/7861", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7861/1/Album%200035%20088%20CT.jpg"],
|
||||
["20.500.12156.1/7875", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7875/1/Album%200035%20078%20CT.jpg"],
|
||||
["20.500.12156.1/8075", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8075/1/Alb%200038%20009%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/8128", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8128/1/014AM006009.jpg"],
|
||||
["20.500.12156.1/8133", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8133/1/014AM010003.jpg"],
|
||||
["20.500.12156.1/8141", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8141/1/014AM010014.jpg"],
|
||||
["20.500.12156.1/8145", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8145/1/014AM012006.jpg"],
|
||||
["20.500.12156.1/8161", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8161/1/014AM012058.jpg"],
|
||||
["20.500.12156.1/8182", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8182/1/014AM019007.jpg"],
|
||||
["20.500.12156.1/8187", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8187/1/014AM019013.jpg"],
|
||||
["20.500.12156.1/8249", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8249/1/014AM002012.jpg"],
|
||||
["20.500.12156.1/8254", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8254/1/014AM002019.jpg"],
|
||||
["20.500.12156.1/8262", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8262/1/014AM003015.jpg"],
|
||||
["20.500.12156.1/8267", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8267/1/014AM004003.jpg"],
|
||||
["20.500.12156.1/8268", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8268/1/014AM005003.jpg"],
|
||||
["20.500.12156.1/8271", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8271/1/014AM005005.jpg"],
|
||||
["20.500.12156.1/8275", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8275/1/014AM005014.jpg"],
|
||||
["20.500.12156.1/8277", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8277/1/014AM005020.jpg"],
|
||||
["20.500.12156.1/8286", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8286/1/014AM006002.jpg"],
|
||||
["20.500.12156.1/8313", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8313/1/001AAN005123.jpg"],
|
||||
["20.500.12156.1/8316", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8316/1/001ACL019004.jpg"],
|
||||
["20.500.12156.1/8374", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8374/1/002080NUS0309.jpg"],
|
||||
["20.500.12156.1/8739", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8739/1/007ALA102.jpg"],
|
||||
["20.500.12156.1/8761", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8761/1/007ALA015.jpg"],
|
||||
["20.500.12156.1/8762", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8762/1/007ALA016.jpg"],
|
||||
["20.500.12156.1/8774", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8774/1/007ALA027.jpg"],
|
||||
["20.500.12156.1/8781", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8781/1/007ALA033.jpg"],
|
||||
["20.500.12156.1/8785", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8785/1/007ALA037.jpg"],
|
||||
["20.500.12156.1/8796", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8796/1/007ALA047.jpg"],
|
||||
["20.500.12156.1/8804", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8804/1/007ALA054.jpg"],
|
||||
["20.500.12156.1/8825", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8825/1/007ALA073.jpg"],
|
||||
["20.500.12156.1/8872", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8872/1/A05F03P02.jpg"],
|
||||
["20.500.12156.1/8912", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8912/1/pixinguinha04.jpg"],
|
||||
["20.500.12156.1/9148", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/9148/1/007ALA091.jpg"]
|
||||
];
|
||||
|
||||
const PAUSA = 5000; // v1 usava 3000; mais folga depois do rate-limit
|
||||
const BACKOFF = 30000; // v1 dormia 60s por erro e moia os 71: 71 min
|
||||
const MAX_SEGUIDOS = 5; // desiste depois disso -- nao martela a origem
|
||||
const CHAVE = 'bras_faltantes_73';
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 1500);
|
||||
|
||||
const FEITOS = new Set(JSON.parse(localStorage.getItem(CHAVE) || '[]'));
|
||||
const marcar = h => { FEITOS.add(h);
|
||||
localStorage.setItem(CHAVE, JSON.stringify([...FEITOS])); };
|
||||
|
||||
const S = {fase: 'ocioso', feito: 0, erros: 0, total: 0, atual: '', parar: false};
|
||||
const FALHAS = [];
|
||||
|
||||
function resumo() {
|
||||
const c = {};
|
||||
FALHAS.forEach(f => { c[f.codigo] = (c[f.codigo] || 0) + 1; });
|
||||
return c;
|
||||
}
|
||||
|
||||
async function baixarUm(handle, url) {
|
||||
const r = await fetch(url, {credentials: 'same-origin'});
|
||||
if (!r.ok) { const e = new Error('HTTP ' + r.status); e.codigo = r.status; throw e; }
|
||||
const b = await r.blob();
|
||||
if (b.size < 1024) { const e = new Error('blob ' + b.size + 'B'); e.codigo = 'blob-curto'; throw e; }
|
||||
const orig = decodeURIComponent(url.split('/').pop());
|
||||
const nome = `BRAS__${handle.replace(/\//g, '_')}__${orig}`.replace(/[\\/:*?"<>|]/g, '_');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(b); a.download = nome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
return b.size;
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
if (S.fase === 'baixando') { // <-- o defeito nº1 da v1
|
||||
console.warn('FALTA ja esta rodando (' + S.atual + '). Use FALTA.parar() antes.');
|
||||
return;
|
||||
}
|
||||
S.parar = false; S.fase = 'baixando'; FALHAS.length = 0;
|
||||
const lista = FALTANTES.filter(([h]) => !FEITOS.has(h));
|
||||
S.total = lista.length; S.feito = 0; S.erros = 0;
|
||||
console.log(`FALTA v2: ${lista.length} pendentes, ~${Math.ceil(lista.length * PAUSA / 60000)} min se tudo der certo`);
|
||||
|
||||
let seguidos = 0;
|
||||
for (let i = 0; i < lista.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; console.log('parado em ' + (i + 1)); return; }
|
||||
const [handle, url] = lista[i];
|
||||
S.atual = `${i + 1}/${lista.length} ${handle}`;
|
||||
try {
|
||||
const bytes = await baixarUm(handle, url);
|
||||
marcar(handle); S.feito++; seguidos = 0;
|
||||
console.log(` ok ${handle} ${(bytes / 1048576).toFixed(1)} MB (${i + 1}/${lista.length})`);
|
||||
} catch (e) {
|
||||
S.erros++; seguidos++;
|
||||
FALHAS.push({handle, url, codigo: e.codigo ?? 'rede', erro: e.message});
|
||||
console.warn(` ERRO ${handle}: ${e.message}`);
|
||||
if (seguidos >= MAX_SEGUIDOS) { // <-- o defeito nº2 da v1
|
||||
S.fase = 'abortado';
|
||||
console.error(`\nFALTA abortou: ${seguidos} erros seguidos. A origem esta recusando -- insistir so piora.`);
|
||||
console.table(resumo());
|
||||
console.log('Espere (30+ min) e rode FALTA.retomar(). O que ja baixou esta marcado.');
|
||||
return;
|
||||
}
|
||||
await sleep(BACKOFF);
|
||||
}
|
||||
await sleep(jit());
|
||||
}
|
||||
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nFALTA v2 concluido. ok=${S.feito} erro=${S.erros}`);
|
||||
if (FALHAS.length) { console.table(resumo()); console.table(FALHAS); }
|
||||
else console.log('Trilha 1 fechada: 13.059/13.059. O subidor sobe o resto sozinho.');
|
||||
}
|
||||
|
||||
window.FALTA = {
|
||||
iniciar, status: () => ({...S}), retomar: () => iniciar(),
|
||||
parar: () => { S.parar = true; },
|
||||
pendentes: () => FALTANTES.filter(([h]) => !FEITOS.has(h)).map(([h]) => h),
|
||||
erros: () => [...FALHAS], resumo,
|
||||
limparMarcas: () => localStorage.removeItem(CHAVE),
|
||||
};
|
||||
|
||||
console.log(`FALTA v2 pronto: ${window.FALTA.pendentes().length} pendentes de ${FALTANTES.length}.`);
|
||||
console.log('Rode FALTA.iniciar()');
|
||||
})();
|
||||
@@ -0,0 +1,344 @@
|
||||
/* ===================================================================
|
||||
FALTA v3 -- fecha a trilha 1 (212 itens de preservacao restantes).
|
||||
|
||||
POR QUE V3 (2026-09-13): a lista da v2 (73) estava subestimada. Recontagem
|
||||
direta contra o bucket, com a fila de ~/Downloads ja esvaziada, da 212
|
||||
itens sem NENHUMA imagem de preservacao -- ver achado 2.23. Inclui os 8 que
|
||||
o subidor.py antigo apagou por confundir TIFF com licenca (achado 2.19).
|
||||
Motor identico ao da v2, so a lista muda.
|
||||
|
||||
NAO cobre os 30 bitstreams "extra_seqN" apagados junto: o nome deles e
|
||||
nosso, nao da origem, entao a URL precisa ser enumerada item a item com
|
||||
pipeline/reparo-multibitstream.js antes.
|
||||
|
||||
POR QUE V2 (achado 2.18, 2026-09-12)
|
||||
A v1 tinha tres defeitos que juntos viraram rate-limit na origem:
|
||||
1. `iniciar()` nao se protegia contra chamada dupla -- cada
|
||||
FALTA.iniciar()/retomar() num script ja rodando somava OUTRO loop
|
||||
sobre o mesmo S. Deu `erros: 101` com `atual: 43/73`, e a pausa
|
||||
de 3s combinada com a BN virou 1,5s ou menos.
|
||||
2. Erro nao interrompia nada: o loop moia os 71 com 60s de backoff
|
||||
cada, martelando a origem por mais de uma hora sem chance de
|
||||
alguem olhar o resultado.
|
||||
3. Nao registrava o codigo HTTP separado da mensagem, entao o
|
||||
diagnostico dependia de ler FALHAS na mao.
|
||||
|
||||
Evidencia de que o problema e de origem, nao de URL: 10993 e 10995
|
||||
baixaram as 22:52 e FALHARAM na passada seguinte, mesma URL. E os 71
|
||||
tem 71/71 thumbs baixados com sucesso -- o item responde, o bitstream
|
||||
de preservacao e que nao.
|
||||
|
||||
COMO USAR
|
||||
1. Recarregue a aba antes de colar (mata loops velhos da v1).
|
||||
2. Cole. Rode FALTA.iniciar().
|
||||
3. Ele PARA SOZINHO depois de 5 erros seguidos e mostra a tabela
|
||||
de codigos -- e o sinal de que a origem esta recusando, e insistir
|
||||
so piora. Espere e rode FALTA.retomar() mais tarde.
|
||||
|
||||
FALTA.status() / .parar() / .retomar() / .erros() / .pendentes()
|
||||
=================================================================== */
|
||||
(() => {
|
||||
|
||||
/* [handle, preservation_url] -- apurados em 2026-09-13 (bucket, depois da fila esvaziar) */
|
||||
const FALTANTES = [
|
||||
["20.500.12156.1/101", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/101/1/imagem.jpg"],
|
||||
["20.500.12156.1/1004", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1004/1/imagem.jpg"],
|
||||
["20.500.12156.1/1005", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1005/1/imagem.jpg"],
|
||||
["20.500.12156.1/1006", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1006/1/imagem.jpg"],
|
||||
["20.500.12156.1/1007", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1007/1/imagem.jpg"],
|
||||
["20.500.12156.1/1008", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1008/1/imagem.jpg"],
|
||||
["20.500.12156.1/1009", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1009/1/imagem.jpg"],
|
||||
["20.500.12156.1/1010", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1010/1/imagem.jpg"],
|
||||
["20.500.12156.1/1011", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1011/1/imagem.jpg"],
|
||||
["20.500.12156.1/1012", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1012/1/imagem.jpg"],
|
||||
["20.500.12156.1/1013", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1013/1/imagem.jpg"],
|
||||
["20.500.12156.1/1014", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1014/1/imagem.jpg"],
|
||||
["20.500.12156.1/1015", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1015/1/imagem.jpg"],
|
||||
["20.500.12156.1/6433", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6433/1/0072430cx006-05t.jpg"],
|
||||
["20.500.12156.1/6520", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6520/1/007_IMG_1008t.jpg"],
|
||||
["20.500.12156.1/6545", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6545/1/CC-05-01-015-3.jpg"],
|
||||
["20.500.12156.1/6555", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6555/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0009_m0003de0003.jpg"],
|
||||
["20.500.12156.1/6568", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6568/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0019_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6570", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6570/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0024_m0001de0001.jpg"],
|
||||
["20.500.12156.1/6656", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6656/2/imagem.jpg"],
|
||||
["20.500.12156.1/6669", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6669/1/BP-06-TP-01-012.jpg"],
|
||||
["20.500.12156.1/6670", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6670/1/BP-06-TP-01-015.jpg"],
|
||||
["20.500.12156.1/6674", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6674/1/BP-06-TP-01-022.jpg"],
|
||||
["20.500.12156.1/7034", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7034/1/007A6P3F08-004.jpg"],
|
||||
["20.500.12156.1/7317", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7317/4/Alb%200294%20069ct.jpg"],
|
||||
["20.500.12156.1/7326", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7326/4/Alb%200294%20037ct.jpg"],
|
||||
["20.500.12156.1/7344", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7344/4/Alb%200294%20093ct.jpg"],
|
||||
["20.500.12156.1/7353", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7353/4/Alb%200294%20097ct.jpg"],
|
||||
["20.500.12156.1/7355", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7355/4/Alb%200259%20073%20ct.jpg"],
|
||||
["20.500.12156.1/7358", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7358/1/BR_RJANRIO_OG_0_FOT_0001_021.jpg"],
|
||||
["20.500.12156.1/7379", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7379/1/BR_RJANRIO_OG_0_FOT_0001_057.jpg"],
|
||||
["20.500.12156.1/7386", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7386/1/BR_RJANRIO_OG_0_FOT_0001_067.jpg"],
|
||||
["20.500.12156.1/7393", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7393/1/BR_RJANRIO_OG_0_FOT_0001_089.jpg"],
|
||||
["20.500.12156.1/7438", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7438/1/BR_RJANRIO_OG_0_FOT_0001_085.jpg"],
|
||||
["20.500.12156.1/7440", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7440/1/BR_RJANRIO_OG_0_FOT_0001_087.jpg"],
|
||||
["20.500.12156.1/7457", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7457/4/Alb%200294%20127ct.jpg"],
|
||||
["20.500.12156.1/7461", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7461/4/Alb%200259%20081ct.jpg"],
|
||||
["20.500.12156.1/7463", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7463/4/Alb%200294%20106ct.jpg"],
|
||||
["20.500.12156.1/7475", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7475/4/Alb%200259%20096ct.jpg"],
|
||||
["20.500.12156.1/7515", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7515/4/Alb%200259%20145ct.jpg"],
|
||||
["20.500.12156.1/7516", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7516/4/Alb%200259%20139ct.jpg"],
|
||||
["20.500.12156.1/7517", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7517/3/Album%200035%20297%20CT.jpg"],
|
||||
["20.500.12156.1/7529", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7529/5/Alb%200259%20130ct.jpg"],
|
||||
["20.500.12156.1/7743", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7743/1/001ALA011011.jpg"],
|
||||
["20.500.12156.1/7744", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7744/1/001ALA011012.jpg"],
|
||||
["20.500.12156.1/7766", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7766/1/001ATK012010.jpg"],
|
||||
["20.500.12156.1/7802", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7802/1/Alb%200036%20037%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7814", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7814/1/Alb%200036%20081%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7817", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7817/1/Alb%200036%20087%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7848", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7848/1/Alb%200036%20013%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/7852", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7852/1/Album%200035%20105%20CT.jpg"],
|
||||
["20.500.12156.1/7861", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7861/1/Album%200035%20088%20CT.jpg"],
|
||||
["20.500.12156.1/7875", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7875/1/Album%200035%20078%20CT.jpg"],
|
||||
["20.500.12156.1/7939", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7939/2/imagem.jpg"],
|
||||
["20.500.12156.1/8075", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8075/1/Alb%200038%20009%20CT%20ok.jpg"],
|
||||
["20.500.12156.1/8128", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8128/1/014AM006009.jpg"],
|
||||
["20.500.12156.1/8133", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8133/1/014AM010003.jpg"],
|
||||
["20.500.12156.1/8141", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8141/1/014AM010014.jpg"],
|
||||
["20.500.12156.1/8145", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8145/1/014AM012006.jpg"],
|
||||
["20.500.12156.1/8161", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8161/1/014AM012058.jpg"],
|
||||
["20.500.12156.1/8182", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8182/1/014AM019007.jpg"],
|
||||
["20.500.12156.1/8187", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8187/1/014AM019013.jpg"],
|
||||
["20.500.12156.1/8249", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8249/1/014AM002012.jpg"],
|
||||
["20.500.12156.1/8254", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8254/1/014AM002019.jpg"],
|
||||
["20.500.12156.1/8262", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8262/1/014AM003015.jpg"],
|
||||
["20.500.12156.1/8267", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8267/1/014AM004003.jpg"],
|
||||
["20.500.12156.1/8268", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8268/1/014AM005003.jpg"],
|
||||
["20.500.12156.1/8271", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8271/1/014AM005005.jpg"],
|
||||
["20.500.12156.1/8275", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8275/1/014AM005014.jpg"],
|
||||
["20.500.12156.1/8277", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8277/1/014AM005020.jpg"],
|
||||
["20.500.12156.1/8286", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8286/1/014AM006002.jpg"],
|
||||
["20.500.12156.1/8313", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8313/1/001AAN005123.jpg"],
|
||||
["20.500.12156.1/8316", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8316/1/001ACL019004.jpg"],
|
||||
["20.500.12156.1/8374", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8374/1/002080NUS0309.jpg"],
|
||||
["20.500.12156.1/8396", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8396/2/imagem.jpg"],
|
||||
["20.500.12156.1/8739", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8739/1/007ALA102.jpg"],
|
||||
["20.500.12156.1/8761", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8761/1/007ALA015.jpg"],
|
||||
["20.500.12156.1/8762", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8762/1/007ALA016.jpg"],
|
||||
["20.500.12156.1/8774", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8774/1/007ALA027.jpg"],
|
||||
["20.500.12156.1/8781", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8781/1/007ALA033.jpg"],
|
||||
["20.500.12156.1/8785", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8785/1/007ALA037.jpg"],
|
||||
["20.500.12156.1/8796", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8796/1/007ALA047.jpg"],
|
||||
["20.500.12156.1/8804", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8804/1/007ALA054.jpg"],
|
||||
["20.500.12156.1/8825", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8825/1/007ALA073.jpg"],
|
||||
["20.500.12156.1/8872", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8872/1/A05F03P02.jpg"],
|
||||
["20.500.12156.1/8912", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8912/1/pixinguinha04.jpg"],
|
||||
["20.500.12156.1/9148", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/9148/1/007ALA091.jpg"],
|
||||
["20.500.12156.1/10039", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10039/1/007CX190-01.jpg"],
|
||||
["20.500.12156.1/10040", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10040/1/007CX190-05.jpg"],
|
||||
["20.500.12156.1/10041", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10041/1/007CX190-11.jpg"],
|
||||
["20.500.12156.1/10042", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10042/1/007CX190-14.jpg"],
|
||||
["20.500.12156.1/10043", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10043/1/007CX190-16.jpg"],
|
||||
["20.500.12156.1/10044", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10044/1/007CX194-08.jpg"],
|
||||
["20.500.12156.1/10045", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10045/1/001CC007004.jpg"],
|
||||
["20.500.12156.1/10046", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10046/1/007CX194-10.jpg"],
|
||||
["20.500.12156.1/10047", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10047/1/007CX196-02.jpg"],
|
||||
["20.500.12156.1/10048", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10048/1/007CX196-07.jpg"],
|
||||
["20.500.12156.1/10049", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10049/1/007CX197-02.jpg"],
|
||||
["20.500.12156.1/10050", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10050/1/007CX197-05.jpg"],
|
||||
["20.500.12156.1/10051", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10051/1/007CX197-09.jpg"],
|
||||
["20.500.12156.1/10052", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10052/1/007CX197-10.jpg"],
|
||||
["20.500.12156.1/10053", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10053/1/007CX198-03.jpg"],
|
||||
["20.500.12156.1/10054", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10054/1/007CX199-03.jpg"],
|
||||
["20.500.12156.1/10055", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10055/1/007CX199-04.jpg"],
|
||||
["20.500.12156.1/10056", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10056/1/001CC007005.jpg"],
|
||||
["20.500.12156.1/10057", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10057/1/007CX214-12.JPG"],
|
||||
["20.500.12156.1/10058", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10058/1/007CX237-01.jpg"],
|
||||
["20.500.12156.1/10059", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10059/1/007SN-12.jpg"],
|
||||
["20.500.12156.1/10060", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10060/1/007_IMG_1807.jpg"],
|
||||
["20.500.12156.1/10061", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10061/1/007_IMG_1811.jpg"],
|
||||
["20.500.12156.1/10062", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10062/1/007_IMG_1812.jpg"],
|
||||
["20.500.12156.1/10063", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10063/1/007_IMG_1813.jpg"],
|
||||
["20.500.12156.1/10064", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10064/1/007_IMG_1855.jpg"],
|
||||
["20.500.12156.1/10065", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10065/1/007_IMG_1859.jpg"],
|
||||
["20.500.12156.1/10066", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10066/1/007_IMG_1993.jpg"],
|
||||
["20.500.12156.1/10067", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10067/1/001CC007007.jpg"],
|
||||
["20.500.12156.1/10068", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10068/1/007_IMG_2000.jpg"],
|
||||
["20.500.12156.1/10069", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10069/1/007_IMG_2006.jpg"],
|
||||
["20.500.12156.1/10070", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10070/1/007_IMG_2007.jpg"],
|
||||
["20.500.12156.1/10071", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10071/1/007_IMG_2010.jpg"],
|
||||
["20.500.12156.1/10072", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10072/1/007_IMG_2023.jpg"],
|
||||
["20.500.12156.1/10073", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10073/1/007_IMG_2493.jpg"],
|
||||
["20.500.12156.1/10074", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10074/1/007_IMG_2497.jpg"],
|
||||
["20.500.12156.1/10075", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10075/1/007_IMG_2500.jpg"],
|
||||
["20.500.12156.1/10076", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10076/1/007_IMG_2502.jpg"],
|
||||
["20.500.12156.1/10077", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10077/1/007_IMG_2503.jpg"],
|
||||
["20.500.12156.1/10078", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10078/1/001CC007014.jpg"],
|
||||
["20.500.12156.1/10079", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10079/1/007_IMG_2509.jpg"],
|
||||
["20.500.12156.1/10080", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10080/1/007_IMG_2511.jpg"],
|
||||
["20.500.12156.1/10081", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10081/1/007_IMG_2516.jpg"],
|
||||
["20.500.12156.1/10082", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10082/1/007_IMG_2519.jpg"],
|
||||
["20.500.12156.1/10083", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10083/1/007_IMG_2520.jpg"],
|
||||
["20.500.12156.1/10084", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10084/1/007_IMG_2522.jpg"],
|
||||
["20.500.12156.1/10085", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10085/1/007_IMG_2525.jpg"],
|
||||
["20.500.12156.1/10086", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10086/1/007_IMG_2526.jpg"],
|
||||
["20.500.12156.1/10087", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10087/1/007_IMG_2527.jpg"],
|
||||
["20.500.12156.1/10088", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10088/1/007_IMG_2531.jpg"],
|
||||
["20.500.12156.1/10089", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10089/1/001AN02001006.jpg"],
|
||||
["20.500.12156.1/10090", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10090/1/001CC007015.jpg"],
|
||||
["20.500.12156.1/10091", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10091/1/007_IMG_2836.jpg"],
|
||||
["20.500.12156.1/10092", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10092/1/007_IMG_2837.jpg"],
|
||||
["20.500.12156.1/10093", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10093/1/007_IMG_2838.jpg"],
|
||||
["20.500.12156.1/10094", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10094/1/007_IMG_2839.jpg"],
|
||||
["20.500.12156.1/10095", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10095/1/007_IMG_2897.jpg"],
|
||||
["20.500.12156.1/10096", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10096/1/007_IMG_2904.jpg"],
|
||||
["20.500.12156.1/10097", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10097/1/007_IMG_2912.jpg"],
|
||||
["20.500.12156.1/10098", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10098/1/007_IMG_2913.jpg"],
|
||||
["20.500.12156.1/10099", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10099/1/007_IMG_2914.jpg"],
|
||||
["20.500.12156.1/10100", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10100/1/007_IMG_2917.jpg"],
|
||||
["20.500.12156.1/10101", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10101/1/001CC007016.jpg"],
|
||||
["20.500.12156.1/10102", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10102/1/007_IMG_2934.jpg"],
|
||||
["20.500.12156.1/10103", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10103/1/007_IMG_2935.jpg"],
|
||||
["20.500.12156.1/10104", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10104/1/007_IMG_2944.jpg"],
|
||||
["20.500.12156.1/10105", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10105/1/007_IMG_3108.jpg"],
|
||||
["20.500.12156.1/10106", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10106/1/007_IMG_3110.jpg"],
|
||||
["20.500.12156.1/10107", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10107/1/007_IMG_3113.jpg"],
|
||||
["20.500.12156.1/10108", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10108/1/007_IMG_3114.jpg"],
|
||||
["20.500.12156.1/10109", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10109/1/007_IMG_3119.jpg"],
|
||||
["20.500.12156.1/10110", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10110/1/007_IMG_3165.jpg"],
|
||||
["20.500.12156.1/10111", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10111/1/007_IMG_3259.jpg"],
|
||||
["20.500.12156.1/10112", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10112/1/001CC007019.jpg"],
|
||||
["20.500.12156.1/10113", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10113/1/007_IMG_3285.jpg"],
|
||||
["20.500.12156.1/10114", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10114/1/007_IMG_3299.jpg"],
|
||||
["20.500.12156.1/10115", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10115/1/007_IMG_3309.jpg"],
|
||||
["20.500.12156.1/10116", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10116/1/007_IMG_3316.jpg"],
|
||||
["20.500.12156.1/10117", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10117/1/007_IMG_3330.jpg"],
|
||||
["20.500.12156.1/10118", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10118/1/007_IMG_3332.jpg"],
|
||||
["20.500.12156.1/10119", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10119/1/007_IMG_3347.jpg"],
|
||||
["20.500.12156.1/10120", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10120/1/007_IMG_3350.jpg"],
|
||||
["20.500.12156.1/10121", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10121/1/007_IMG_3352.jpg"],
|
||||
["20.500.12156.1/10122", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10122/1/007_IMG_3353.jpg"],
|
||||
["20.500.12156.1/10123", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10123/1/001CC007020.jpg"],
|
||||
["20.500.12156.1/10124", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10124/1/007_IMG_3437.jpg"],
|
||||
["20.500.12156.1/10125", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10125/1/007_IMG_3469.jpg"],
|
||||
["20.500.12156.1/10126", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10126/1/007_IMG_3527.jpg"],
|
||||
["20.500.12156.1/10127", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10127/1/007_IMG_3528.jpg"],
|
||||
["20.500.12156.1/10128", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10128/1/007_IMG_3533.jpg"],
|
||||
["20.500.12156.1/10129", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10129/1/007_IMG_3534.jpg"],
|
||||
["20.500.12156.1/10130", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10130/1/007_IMG_3535.jpg"],
|
||||
["20.500.12156.1/10131", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10131/1/007_IMG_3536.jpg"],
|
||||
["20.500.12156.1/10132", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10132/1/007_IMG_3539.jpg"],
|
||||
["20.500.12156.1/10133", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10133/1/007_IMG_3540.jpg"],
|
||||
["20.500.12156.1/10134", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10134/1/001CC007025.jpg"],
|
||||
["20.500.12156.1/10135", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10135/1/007_IMG_3547.jpg"],
|
||||
["20.500.12156.1/10136", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10136/1/007_IMG_3559.jpg"],
|
||||
["20.500.12156.1/10137", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10137/1/007_IMG_3585.jpg"],
|
||||
["20.500.12156.1/10138", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10138/1/007_IMG_3588.jpg"],
|
||||
["20.500.12156.1/10139", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10139/1/007_IMG_3590.jpg"],
|
||||
["20.500.12156.1/10140", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10140/1/007_IMG_3594.jpg"],
|
||||
["20.500.12156.1/10141", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10141/1/007_IMG_3596.jpg"],
|
||||
["20.500.12156.1/10142", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10142/1/007_IMG_3597.jpg"],
|
||||
["20.500.12156.1/10143", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10143/1/007_IMG_3598.jpg"],
|
||||
["20.500.12156.1/10144", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10144/1/007_IMG_3601.jpg"],
|
||||
["20.500.12156.1/10145", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10145/1/001CC008007.jpg"],
|
||||
["20.500.12156.1/10146", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10146/1/007_IMG_3603.jpg"],
|
||||
["20.500.12156.1/10147", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10147/1/007_IMG_3607.jpg"],
|
||||
["20.500.12156.1/10148", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10148/1/007_IMG_3631.jpg"],
|
||||
["20.500.12156.1/10149", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10149/1/007_IMG_3634.jpg"],
|
||||
["20.500.12156.1/10150", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10150/1/007_IMG_3636.jpg"],
|
||||
["20.500.12156.1/10151", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10151/1/007_IMG_3639.jpg"],
|
||||
["20.500.12156.1/10152", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10152/1/007_IMG_3642.jpg"],
|
||||
["20.500.12156.1/10153", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10153/1/007_IMG_3739.jpg"],
|
||||
["20.500.12156.1/10154", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10154/1/007_IMG_3740.jpg"],
|
||||
["20.500.12156.1/10155", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10155/1/007_IMG_3742.jpg"],
|
||||
["20.500.12156.1/14358", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14358/1/Alb%200261%20139.jpg"],
|
||||
["20.500.12156.1/14359", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14359/1/Alb%200261%20136.jpg"],
|
||||
["20.500.12156.1/14360", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14360/1/Alb%200261%20141.jpg"],
|
||||
["20.500.12156.1/14361", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14361/1/Alb%200261%20142.jpg"],
|
||||
["20.500.12156.1/14362", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14362/1/Alb%200261%20143.jpg"],
|
||||
["20.500.12156.1/14363", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14363/1/Alb%200261%20144.jpg"],
|
||||
["20.500.12156.1/14364", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14364/1/Alb%200261%20146.jpg"],
|
||||
["20.500.12156.1/14368", "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14368/1/Alb%200261%20163.jpg"],
|
||||
];
|
||||
|
||||
const PAUSA = 5000; // v1 usava 3000; mais folga depois do rate-limit
|
||||
const BACKOFF = 30000; // v1 dormia 60s por erro e moia os 71: 71 min
|
||||
const MAX_SEGUIDOS = 5; // desiste depois disso -- nao martela a origem
|
||||
const CHAVE = 'bras_faltantes_73';
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 1500);
|
||||
|
||||
const FEITOS = new Set(JSON.parse(localStorage.getItem(CHAVE) || '[]'));
|
||||
const marcar = h => { FEITOS.add(h);
|
||||
localStorage.setItem(CHAVE, JSON.stringify([...FEITOS])); };
|
||||
|
||||
const S = {fase: 'ocioso', feito: 0, erros: 0, total: 0, atual: '', parar: false};
|
||||
const FALHAS = [];
|
||||
|
||||
function resumo() {
|
||||
const c = {};
|
||||
FALHAS.forEach(f => { c[f.codigo] = (c[f.codigo] || 0) + 1; });
|
||||
return c;
|
||||
}
|
||||
|
||||
async function baixarUm(handle, url) {
|
||||
const r = await fetch(url, {credentials: 'same-origin'});
|
||||
if (!r.ok) { const e = new Error('HTTP ' + r.status); e.codigo = r.status; throw e; }
|
||||
const b = await r.blob();
|
||||
if (b.size < 1024) { const e = new Error('blob ' + b.size + 'B'); e.codigo = 'blob-curto'; throw e; }
|
||||
const orig = decodeURIComponent(url.split('/').pop());
|
||||
const nome = `BRAS__${handle.replace(/\//g, '_')}__${orig}`.replace(/[\\/:*?"<>|]/g, '_');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(b); a.download = nome;
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(a.href), 30000);
|
||||
return b.size;
|
||||
}
|
||||
|
||||
async function iniciar() {
|
||||
if (S.fase === 'baixando') { // <-- o defeito nº1 da v1
|
||||
console.warn('FALTA ja esta rodando (' + S.atual + '). Use FALTA.parar() antes.');
|
||||
return;
|
||||
}
|
||||
S.parar = false; S.fase = 'baixando'; FALHAS.length = 0;
|
||||
const lista = FALTANTES.filter(([h]) => !FEITOS.has(h));
|
||||
S.total = lista.length; S.feito = 0; S.erros = 0;
|
||||
console.log(`FALTA v2: ${lista.length} pendentes, ~${Math.ceil(lista.length * PAUSA / 60000)} min se tudo der certo`);
|
||||
|
||||
let seguidos = 0;
|
||||
for (let i = 0; i < lista.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; console.log('parado em ' + (i + 1)); return; }
|
||||
const [handle, url] = lista[i];
|
||||
S.atual = `${i + 1}/${lista.length} ${handle}`;
|
||||
try {
|
||||
const bytes = await baixarUm(handle, url);
|
||||
marcar(handle); S.feito++; seguidos = 0;
|
||||
console.log(` ok ${handle} ${(bytes / 1048576).toFixed(1)} MB (${i + 1}/${lista.length})`);
|
||||
} catch (e) {
|
||||
S.erros++; seguidos++;
|
||||
FALHAS.push({handle, url, codigo: e.codigo ?? 'rede', erro: e.message});
|
||||
console.warn(` ERRO ${handle}: ${e.message}`);
|
||||
if (seguidos >= MAX_SEGUIDOS) { // <-- o defeito nº2 da v1
|
||||
S.fase = 'abortado';
|
||||
console.error(`\nFALTA abortou: ${seguidos} erros seguidos. A origem esta recusando -- insistir so piora.`);
|
||||
console.table(resumo());
|
||||
console.log('Espere (30+ min) e rode FALTA.retomar(). O que ja baixou esta marcado.');
|
||||
return;
|
||||
}
|
||||
await sleep(BACKOFF);
|
||||
}
|
||||
await sleep(jit());
|
||||
}
|
||||
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nFALTA v2 concluido. ok=${S.feito} erro=${S.erros}`);
|
||||
if (FALHAS.length) { console.table(resumo()); console.table(FALHAS); }
|
||||
else console.log('Trilha 1 fechada: 13.059/13.059. O subidor sobe o resto sozinho.');
|
||||
}
|
||||
|
||||
window.FALTA = {
|
||||
iniciar, status: () => ({...S}), retomar: () => iniciar(),
|
||||
parar: () => { S.parar = true; },
|
||||
pendentes: () => FALTANTES.filter(([h]) => !FEITOS.has(h)).map(([h]) => h),
|
||||
erros: () => [...FALHAS], resumo,
|
||||
limparMarcas: () => localStorage.removeItem(CHAVE),
|
||||
};
|
||||
|
||||
console.log(`FALTA v2 pronto: ${window.FALTA.pendentes().length} pendentes de ${FALTANTES.length}.`);
|
||||
console.log('Rode FALTA.iniciar()');
|
||||
})();
|
||||
@@ -0,0 +1,2 @@
|
||||
{"handle":"20.500.12156.1/6066","title":"1. Centenário da Independência do Brasil : Exposição","community":"Biblioteca Nacional","preservation_url":"https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6066/1/imagem.jpg","legacy_url":"http://objdigital.bn.br/objdigital2/acervo_digital/div_iconografia/icon843660/icon843660.jpg","rights":"Biblioteca Nacional (Brasil)"}
|
||||
{"handle":"20.500.12156.1/10377","title":"Cemitério de Pistoia","community":"Instituto Moreira Salles","preservation_url":"https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10377/1/LB.001%288%29.dvft.JPG","legacy_url":null,"rights":null}
|
||||
@@ -0,0 +1,204 @@
|
||||
/* ===================================================================
|
||||
Coletor da Brasiliana Fotografica -- TRILHA PARALELA (Chrome), roda
|
||||
DENTRO da pagina.
|
||||
|
||||
Copia de coletor.js, com duas diferencas deliberadas:
|
||||
1. COM exclui '2' (Biblioteca Nacional) -- essa comunidade ja esta
|
||||
sendo colhida pela outra instancia (Brave) e e a maior das duas
|
||||
(2.726 itens), entao e a que menos risco de sobreposicao corre
|
||||
se for deixada so pro Brave terminar.
|
||||
2. exporta como brasiliana-catalogo-chrome.jsonl (nome diferente,
|
||||
pra nao colidir com o download da outra aba e facilitar o merge
|
||||
com dedupe por handle depois).
|
||||
|
||||
Ainda existe sobreposicao possivel: se o Brave terminar Biblioteca
|
||||
Nacional e avancar pras comunidades que esta aba tambem esta colhendo,
|
||||
ele vai re-colher itens que ja estao aqui (redundante, nao incorreto --
|
||||
dedupe por handle no merge final resolve). Aceito conscientemente ao
|
||||
rodar as duas trilhas em paralelo (dobra o volume de requisicoes no
|
||||
servidor da BN vs. so uma trilha).
|
||||
|
||||
IndexedDB proprio ('bras', separado por navegador/perfil -- sem colisao
|
||||
com o do Brave mesmo com o mesmo nome).
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const BASE = 'https://brasilianafotografica.bn.gov.br/brasiliana';
|
||||
const PREFIX= '20.500.12156.1';
|
||||
const RPP = 100;
|
||||
const PAUSA = 2000; // ms entre requisicoes. Nao baixe disso.
|
||||
|
||||
const COM = {
|
||||
'1753':'Instituto Moreira Salles',
|
||||
'3540':'Arquivo Geral da Cidade do Rio de Janeiro',
|
||||
'3541':'Diretoria do Patrimonio Historico e Documentacao da Marinha',
|
||||
'4342':'Leibniz-Institut fuer Laenderkunde, Leipzig', '4884':'Arquivo Nacional',
|
||||
'4886':'Museu da Republica', '5018':'Fiocruz', '6142':'Museu Historico Nacional',
|
||||
'6660':'Fundacao Joaquim Nabuco', '7072':'Museu Aeroespacial',
|
||||
'11464':'Escola de Ciencias Sociais (FGV CPDOC)',
|
||||
'12796':'Instituto Historico e Geografico Brasileiro',
|
||||
'13099':'Instituto Brasileiro de Geografia e Estatistica',
|
||||
'14087':'Museu Paraense Emilio Goeldi',
|
||||
};
|
||||
const ESPERADO = 10310; // 13036 - 2726 (Biblioteca Nacional, fica com o Brave)
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 600); // ritmo, nao metronomo
|
||||
|
||||
// ---------- IndexedDB ----------
|
||||
let db;
|
||||
const abrir = () => new Promise((res, rej) => {
|
||||
const q = indexedDB.open('bras', 1);
|
||||
q.onupgradeneeded = e => { const d = e.target.result;
|
||||
if (!d.objectStoreNames.contains('fila')) d.createObjectStore('fila');
|
||||
if (!d.objectStoreNames.contains('itens')) d.createObjectStore('itens'); };
|
||||
q.onsuccess = e => res(e.target.result);
|
||||
q.onerror = () => rej(q.error);
|
||||
});
|
||||
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); });
|
||||
const putMuitos = (st, pares) => new Promise((res, rej) => {
|
||||
const t = db.transaction(st, 'readwrite'), o = t.objectStore(st);
|
||||
for (const [k, v] of pares) o.put(v, k);
|
||||
t.oncomplete = res; t.onerror = () => rej(t.error); });
|
||||
const keys = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAllKeys();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.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 get = (st, k) => new Promise(res => {
|
||||
const r = db.transaction(st).objectStore(st).get(k);
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => res(undefined); });
|
||||
|
||||
async function pegar(u) {
|
||||
const r = await fetch(u, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.text();
|
||||
}
|
||||
|
||||
// ---------- extracao ----------
|
||||
function extrair(html, h, com) {
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const dc = [...d.querySelectorAll('meta[name]')]
|
||||
.filter(m => /^(DC|DCTERMS|citation)/.test(m.name))
|
||||
.map(m => ({name: m.name, value: m.content}));
|
||||
const a = n => (dc.find(x => x.name === n) || {}).value || null;
|
||||
// a tabela do ?show=full traz campos que nao viram meta tag, entre eles dc.rights
|
||||
const tb = [...d.querySelectorAll('table.detailtable tr, table.ds-includeSet-table tr')]
|
||||
.map(tr => { const td = [...tr.querySelectorAll('td')].map(c => c.textContent.trim());
|
||||
return td.length >= 2 ? {field: td[0], value: td[1]} : null; })
|
||||
.filter(Boolean);
|
||||
const leg = dc.filter(x => x.name === 'DC.identifier' && /objdigital/.test(x.value || ''))
|
||||
.map(x => x.value)[0] || null;
|
||||
return {
|
||||
handle: `${PREFIX}/${h}`,
|
||||
source_url: `${BASE}/handle/${PREFIX}/${h}?show=full`,
|
||||
fetched_at: new Date().toISOString(),
|
||||
title: a('DC.title'),
|
||||
community: com,
|
||||
// bitstream do DSpace = arquivo de PRESERVACAO (ate 8679x6484, 34,7 MB).
|
||||
// objdigital = copia MENOR (~2600x2100, ~330 KB), so procedencia.
|
||||
preservation_url: a('citation_pdf_url'),
|
||||
legacy_url: leg,
|
||||
rights: (tb.find(t => /^dc\.rights/i.test(t.field)) || {}).value || null,
|
||||
dublin_core: dc,
|
||||
full_table: tb,
|
||||
};
|
||||
}
|
||||
|
||||
const S = {fase: 'iniciando', atual: '', colhidos: 0, erros: 0, parar: false, t0: Date.now()};
|
||||
|
||||
// ---------- fase 1: enumerar ----------
|
||||
async function descobrir() {
|
||||
S.fase = 'enumerando';
|
||||
const RE = new RegExp('handle/' + PREFIX.replace(/\./g, '\\.') + '/(\\d+)', 'g');
|
||||
for (const [h, nome] of Object.entries(COM)) {
|
||||
if (S.parar) return;
|
||||
S.atual = nome;
|
||||
let off = 0, total = null, vistos = new Set();
|
||||
while (!S.parar) {
|
||||
let html;
|
||||
try { html = await pegar(`${BASE}/handle/${PREFIX}/${h}/browse?type=title&rpp=${RPP}&offset=${off}`); }
|
||||
catch (e) { console.warn(` ${nome} offset=${off}: ${e.message} -- pausa de 30s`);
|
||||
await sleep(30000); continue; }
|
||||
if (total === null) {
|
||||
const m = html.replace(/<[^>]+>/g, ' ')
|
||||
.match(/no momento\D{0,24}[\d.,]+\s*-\s*[\d.,]+\s*de\s*([\d.,]+)/i);
|
||||
total = m ? +m[1].replace(/[.,]/g, '') : null;
|
||||
}
|
||||
const ach = new Set([...html.matchAll(RE)].map(m => m[1])); ach.delete(h);
|
||||
let novos = 0; for (const x of ach) if (!vistos.has(x)) { vistos.add(x); novos++; }
|
||||
await putMuitos('fila', [...ach].map(x => [x, nome]));
|
||||
off += RPP;
|
||||
if (!novos || (total && off >= total)) break;
|
||||
await sleep(jit());
|
||||
}
|
||||
console.log(` ${nome.padEnd(46)} ${String(vistos.size).padStart(6)}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
const n = (await keys('fila')).length;
|
||||
console.log(`\nEnumerados: ${n} (esperado ${ESPERADO})`);
|
||||
}
|
||||
|
||||
// ---------- fase 2: metadados ----------
|
||||
async function coletar() {
|
||||
S.fase = 'colhendo';
|
||||
const fila = await keys('fila'), feitos = new Set(await keys('itens'));
|
||||
const pend = fila.filter(h => !feitos.has(h));
|
||||
console.log(`${pend.length} itens a colher (~${(pend.length * PAUSA / 3600000).toFixed(1)} h)`);
|
||||
for (let i = 0; i < pend.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const h = pend[i];
|
||||
S.atual = `${i + 1}/${pend.length}`;
|
||||
try {
|
||||
const html = await pegar(`${BASE}/handle/${PREFIX}/${h}?show=full`);
|
||||
await put('itens', h, extrair(html, h, await get('fila', h)));
|
||||
S.colhidos++;
|
||||
} catch (e) {
|
||||
await put('itens', h, {handle: `${PREFIX}/${h}`, erro: String(e).slice(0, 80)});
|
||||
S.erros++;
|
||||
// recuou? entao respire, nao insista -- insistir e o que vira bloqueio
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) { console.warn(` ${h}: ${e.message} -- pausa de 60s`);
|
||||
await sleep(60000); }
|
||||
}
|
||||
if (i % 100 === 0) console.log(` ${i}/${pend.length} ok=${S.colhidos} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nConcluido. ok=${S.colhidos} erro=${S.erros}. Agora: await BRAS_CHROME.exportar()`);
|
||||
}
|
||||
|
||||
// ---------- exportar ----------
|
||||
async function exportar() {
|
||||
const todos = await getAll('itens');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([todos.map(o => JSON.stringify(o)).join('\n')],
|
||||
{type: 'application/x-ndjson'}));
|
||||
a.download = 'brasiliana-catalogo-chrome.jsonl';
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
console.log(`exportadas ${todos.length} linhas para a pasta Downloads (brasiliana-catalogo-chrome.jsonl)`);
|
||||
return todos.length;
|
||||
}
|
||||
|
||||
window.BRAS_CHROME = {
|
||||
status: async () => {
|
||||
const naFila = (await keys('fila')).length, noBanco = (await keys('itens')).length;
|
||||
const h = (Date.now() - S.t0) / 3600000;
|
||||
return {fase: S.fase, atual: S.atual, naFila, noBanco, erros: S.erros,
|
||||
restam: naFila - noBanco, horas: +h.toFixed(2)};
|
||||
},
|
||||
parar: () => { S.parar = true; console.log('parando apos a requisicao atual'); },
|
||||
retomar: () => { S.parar = false; coletar(); },
|
||||
exportar,
|
||||
limpar: () => indexedDB.deleteDatabase('bras'),
|
||||
};
|
||||
|
||||
(async () => {
|
||||
db = await abrir();
|
||||
if ((await keys('fila')).length < ESPERADO) await descobrir();
|
||||
await coletar();
|
||||
})();
|
||||
|
||||
console.log('BRAS_CHROME iniciado (trilha paralela, sem Biblioteca Nacional). Acompanhe com: await BRAS_CHROME.status()');
|
||||
})();
|
||||
@@ -0,0 +1,93 @@
|
||||
/* ===================================================================
|
||||
Reparo do coletor.js -- reprocessa itens que ficaram marcados com
|
||||
erro (ex.: queda de rede no meio da colheita).
|
||||
|
||||
POR QUE PRECISA: coletar() grava {handle, erro} em 'itens' quando
|
||||
uma requisicao falha. Isso conta como "feito" pro loop principal --
|
||||
ele nao tenta de novo sozinho. Falhas de rede (TypeError: Failed to
|
||||
fetch) tambem nao disparam a pausa de 60s (so HTTP 4xx/5xx disparam),
|
||||
entao uma queda pode gerar uma rajada de erros em vez de so alguns.
|
||||
|
||||
COMO USAR
|
||||
1. Rode DEPOIS que await BRAS.status() mostrar fase 'concluido'
|
||||
(pra nao competir por rede com o loop principal).
|
||||
2. Cole este arquivo inteiro no mesmo console (mesma aba/origem).
|
||||
3. Acompanhe pelo proprio console.log -- ele avisa quantos achou
|
||||
e reporta ok/erro no final.
|
||||
4. Se ainda sobrar erro (rede caiu nesse meio-tempo tambem), rode
|
||||
de novo -- e idempotente.
|
||||
=================================================================== */
|
||||
(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);
|
||||
|
||||
const db = await 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); });
|
||||
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); });
|
||||
|
||||
async function pegar(u) {
|
||||
const r = await fetch(u, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.text();
|
||||
}
|
||||
|
||||
function extrair(html, h, com) {
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const dc = [...d.querySelectorAll('meta[name]')]
|
||||
.filter(m => /^(DC|DCTERMS|citation)/.test(m.name))
|
||||
.map(m => ({name: m.name, value: m.content}));
|
||||
const a = n => (dc.find(x => x.name === n) || {}).value || null;
|
||||
const tb = [...d.querySelectorAll('table.detailtable tr, table.ds-includeSet-table tr')]
|
||||
.map(tr => { const td = [...tr.querySelectorAll('td')].map(c => c.textContent.trim());
|
||||
return td.length >= 2 ? {field: td[0], value: td[1]} : null; })
|
||||
.filter(Boolean);
|
||||
const leg = dc.filter(x => x.name === 'DC.identifier' && /objdigital/.test(x.value || ''))
|
||||
.map(x => x.value)[0] || null;
|
||||
return {
|
||||
handle: `${PREFIX}/${h}`,
|
||||
source_url: `${BASE}/handle/${PREFIX}/${h}?show=full`,
|
||||
fetched_at: new Date().toISOString(),
|
||||
title: a('DC.title'),
|
||||
community: com,
|
||||
preservation_url: a('citation_pdf_url'),
|
||||
legacy_url: leg,
|
||||
rights: (tb.find(t => /^dc\.rights/i.test(t.field)) || {}).value || null,
|
||||
dublin_core: dc,
|
||||
full_table: tb,
|
||||
};
|
||||
}
|
||||
|
||||
const todos = await getAll('itens');
|
||||
const falhados = todos.filter(x => x.erro);
|
||||
console.log(`${falhados.length} itens com erro -- reprocessando`);
|
||||
|
||||
let ok = 0, erro = 0;
|
||||
for (const item of falhados) {
|
||||
const h = item.handle.split('/').pop();
|
||||
const com = await get('fila', h); // recupera o nome da comunidade
|
||||
try {
|
||||
const html = await pegar(`${BASE}/handle/${PREFIX}/${h}?show=full`);
|
||||
await put('itens', h, extrair(html, h, com));
|
||||
ok++;
|
||||
} catch (e) {
|
||||
await put('itens', h, {handle: item.handle, erro: String(e).slice(0, 80)});
|
||||
erro++;
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) await sleep(60000);
|
||||
}
|
||||
await sleep(jit());
|
||||
}
|
||||
console.log(`\nReparo concluido. ok=${ok} ainda-com-erro=${erro}`);
|
||||
})();
|
||||
@@ -0,0 +1,202 @@
|
||||
/* ===================================================================
|
||||
Coletor da Brasiliana Fotografica -- RETOMADA (resume + reparo juntos).
|
||||
|
||||
Diferenca do coletor.js original: coletar() considera "feito" so quem
|
||||
tem registro SEM campo .erro. Handles nunca tentados E handles que
|
||||
falharam antes (rede caiu, HTTP 4xx/5xx) entram juntos na mesma
|
||||
passada -- nao precisa colar coletor.js e depois coletor-reparo.js
|
||||
separado.
|
||||
|
||||
COMO USAR
|
||||
1. Abra https://brasilianafotografica.bn.gov.br/brasiliana/
|
||||
(mesma origem de sempre -- reaproveita o IndexedDB existente)
|
||||
2. Cole este arquivo inteiro no console
|
||||
3. Ele pula a descoberta (fila ja esta cheia) e colhe so o que
|
||||
falta: pendentes + os que tinham erro
|
||||
4. await BRAS.status() pra acompanhar; await BRAS.exportar() no fim
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const BASE = 'https://brasilianafotografica.bn.gov.br/brasiliana';
|
||||
const PREFIX= '20.500.12156.1';
|
||||
const RPP = 100;
|
||||
const PAUSA = 2000; // ms entre requisicoes. Nao baixe disso.
|
||||
|
||||
const COM = {
|
||||
'2':'Biblioteca Nacional', '1753':'Instituto Moreira Salles',
|
||||
'3540':'Arquivo Geral da Cidade do Rio de Janeiro',
|
||||
'3541':'Diretoria do Patrimonio Historico e Documentacao da Marinha',
|
||||
'4342':'Leibniz-Institut fuer Laenderkunde, Leipzig', '4884':'Arquivo Nacional',
|
||||
'4886':'Museu da Republica', '5018':'Fiocruz', '6142':'Museu Historico Nacional',
|
||||
'6660':'Fundacao Joaquim Nabuco', '7072':'Museu Aeroespacial',
|
||||
'11464':'Escola de Ciencias Sociais (FGV CPDOC)',
|
||||
'12796':'Instituto Historico e Geografico Brasileiro',
|
||||
'13099':'Instituto Brasileiro de Geografia e Estatistica',
|
||||
'14087':'Museu Paraense Emilio Goeldi',
|
||||
};
|
||||
const ESPERADO = 13036;
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 600);
|
||||
|
||||
// ---------- IndexedDB ----------
|
||||
let db;
|
||||
const abrir = () => new Promise((res, rej) => {
|
||||
const q = indexedDB.open('bras', 1);
|
||||
q.onupgradeneeded = e => { const d = e.target.result;
|
||||
if (!d.objectStoreNames.contains('fila')) d.createObjectStore('fila');
|
||||
if (!d.objectStoreNames.contains('itens')) d.createObjectStore('itens'); };
|
||||
q.onsuccess = e => res(e.target.result);
|
||||
q.onerror = () => rej(q.error);
|
||||
});
|
||||
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); });
|
||||
const putMuitos = (st, pares) => new Promise((res, rej) => {
|
||||
const t = db.transaction(st, 'readwrite'), o = t.objectStore(st);
|
||||
for (const [k, v] of pares) o.put(v, k);
|
||||
t.oncomplete = res; t.onerror = () => rej(t.error); });
|
||||
const keys = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAllKeys();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.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 getAll = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAll();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); });
|
||||
// so as chaves cujo valor NAO tem .erro -- feito de verdade
|
||||
const chavesOk = () => new Promise((res, rej) => {
|
||||
const s = new Set();
|
||||
const req = db.transaction('itens').objectStore('itens').openCursor();
|
||||
req.onsuccess = e => { const c = e.target.result;
|
||||
if (c) { if (!c.value.erro) s.add(c.key); c.continue(); } else res(s); };
|
||||
req.onerror = () => rej(req.error);
|
||||
});
|
||||
|
||||
async function pegar(u) {
|
||||
const r = await fetch(u, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.text();
|
||||
}
|
||||
|
||||
// ---------- extracao ----------
|
||||
function extrair(html, h, com) {
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const dc = [...d.querySelectorAll('meta[name]')]
|
||||
.filter(m => /^(DC|DCTERMS|citation)/.test(m.name))
|
||||
.map(m => ({name: m.name, value: m.content}));
|
||||
const a = n => (dc.find(x => x.name === n) || {}).value || null;
|
||||
const tb = [...d.querySelectorAll('table.detailtable tr, table.ds-includeSet-table tr')]
|
||||
.map(tr => { const td = [...tr.querySelectorAll('td')].map(c => c.textContent.trim());
|
||||
return td.length >= 2 ? {field: td[0], value: td[1]} : null; })
|
||||
.filter(Boolean);
|
||||
const leg = dc.filter(x => x.name === 'DC.identifier' && /objdigital/.test(x.value || ''))
|
||||
.map(x => x.value)[0] || null;
|
||||
return {
|
||||
handle: `${PREFIX}/${h}`,
|
||||
source_url: `${BASE}/handle/${PREFIX}/${h}?show=full`,
|
||||
fetched_at: new Date().toISOString(),
|
||||
title: a('DC.title'),
|
||||
community: com,
|
||||
preservation_url: a('citation_pdf_url'),
|
||||
legacy_url: leg,
|
||||
rights: (tb.find(t => /^dc\.rights/i.test(t.field)) || {}).value || null,
|
||||
dublin_core: dc,
|
||||
full_table: tb,
|
||||
};
|
||||
}
|
||||
|
||||
const S = {fase: 'iniciando', atual: '', colhidos: 0, erros: 0, parar: false, t0: Date.now()};
|
||||
|
||||
// ---------- fase 1: enumerar (so roda se a fila estiver incompleta) ----------
|
||||
async function descobrir() {
|
||||
S.fase = 'enumerando';
|
||||
const RE = new RegExp('handle/' + PREFIX.replace(/\./g, '\\.') + '/(\\d+)', 'g');
|
||||
for (const [h, nome] of Object.entries(COM)) {
|
||||
if (S.parar) return;
|
||||
S.atual = nome;
|
||||
let off = 0, total = null, vistos = new Set();
|
||||
while (!S.parar) {
|
||||
let html;
|
||||
try { html = await pegar(`${BASE}/handle/${PREFIX}/${h}/browse?type=title&rpp=${RPP}&offset=${off}`); }
|
||||
catch (e) { console.warn(` ${nome} offset=${off}: ${e.message} -- pausa de 30s`);
|
||||
await sleep(30000); continue; }
|
||||
if (total === null) {
|
||||
const m = html.replace(/<[^>]+>/g, ' ')
|
||||
.match(/no momento\D{0,24}[\d.,]+\s*-\s*[\d.,]+\s*de\s*([\d.,]+)/i);
|
||||
total = m ? +m[1].replace(/[.,]/g, '') : null;
|
||||
}
|
||||
const ach = new Set([...html.matchAll(RE)].map(m => m[1])); ach.delete(h);
|
||||
let novos = 0; for (const x of ach) if (!vistos.has(x)) { vistos.add(x); novos++; }
|
||||
await putMuitos('fila', [...ach].map(x => [x, nome]));
|
||||
off += RPP;
|
||||
if (!novos || (total && off >= total)) break;
|
||||
await sleep(jit());
|
||||
}
|
||||
console.log(` ${nome.padEnd(46)} ${String(vistos.size).padStart(6)}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
const n = (await keys('fila')).length;
|
||||
console.log(`\nEnumerados: ${n} (esperado ${ESPERADO})`);
|
||||
}
|
||||
|
||||
// ---------- fase 2: metadados (pendentes + com erro, juntos) ----------
|
||||
async function coletar() {
|
||||
S.fase = 'colhendo';
|
||||
const fila = await keys('fila'), ok = await chavesOk();
|
||||
const pend = fila.filter(h => !ok.has(h));
|
||||
console.log(`${pend.length} itens a colher/reparar (~${(pend.length * PAUSA / 3600000).toFixed(1)} h)`);
|
||||
for (let i = 0; i < pend.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const h = pend[i];
|
||||
S.atual = `${i + 1}/${pend.length}`;
|
||||
try {
|
||||
const html = await pegar(`${BASE}/handle/${PREFIX}/${h}?show=full`);
|
||||
await put('itens', h, extrair(html, h, await get('fila', h)));
|
||||
S.colhidos++;
|
||||
} catch (e) {
|
||||
await put('itens', h, {handle: `${PREFIX}/${h}`, erro: String(e).slice(0, 80)});
|
||||
S.erros++;
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) { console.warn(` ${h}: ${e.message} -- pausa de 60s`);
|
||||
await sleep(60000); }
|
||||
}
|
||||
if (i % 100 === 0) console.log(` ${i}/${pend.length} ok=${S.colhidos} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nConcluido. ok=${S.colhidos} erro=${S.erros}. Agora: await BRAS.exportar()`);
|
||||
}
|
||||
|
||||
// ---------- exportar ----------
|
||||
async function exportar() {
|
||||
const todos = await getAll('itens');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([todos.map(o => JSON.stringify(o)).join('\n')],
|
||||
{type: 'application/x-ndjson'}));
|
||||
a.download = 'brasiliana-catalogo.jsonl';
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
console.log(`exportadas ${todos.length} linhas para a pasta Downloads`);
|
||||
return todos.length;
|
||||
}
|
||||
|
||||
window.BRAS = {
|
||||
status: async () => {
|
||||
const naFila = (await keys('fila')).length, ok = await chavesOk();
|
||||
const h = (Date.now() - S.t0) / 3600000;
|
||||
return {fase: S.fase, atual: S.atual, naFila, noBanco: ok.size, erros: S.erros,
|
||||
restam: naFila - ok.size, horas: +h.toFixed(2)};
|
||||
},
|
||||
parar: () => { S.parar = true; console.log('parando apos a requisicao atual'); },
|
||||
retomar: () => { S.parar = false; coletar(); },
|
||||
exportar,
|
||||
limpar: () => indexedDB.deleteDatabase('bras'),
|
||||
};
|
||||
|
||||
(async () => {
|
||||
db = await abrir();
|
||||
if ((await keys('fila')).length < ESPERADO) await descobrir();
|
||||
await coletar();
|
||||
})();
|
||||
|
||||
console.log('BRAS retomado (pendentes + erros juntos). Acompanhe com: await BRAS.status()');
|
||||
})();
|
||||
@@ -0,0 +1,142 @@
|
||||
/* ===================================================================
|
||||
Coletor dos posts editoriais do WordPress (raiz do dominio, separado
|
||||
do /brasiliana/ DSpace). Roda igual o coletor.js -- dentro da pagina,
|
||||
fetch() same-origin, grava em IndexedDB proprio ('bras_wp').
|
||||
|
||||
Nao baixa imagem nenhuma: as fotos dos posts ja apontam (em miniatura)
|
||||
pros mesmos handles que o acervo principal ja cobre. Só o texto e o
|
||||
mapeamento pra handles tem valor novo aqui.
|
||||
|
||||
COMO USAR
|
||||
1. Cole este arquivo na mesma aba (ou qualquer aba da mesma origem)
|
||||
2. await WP.descobrir() -- enumera todos os posts (categorias x paginacao)
|
||||
3. await WP.colher() -- busca o conteudo de cada post
|
||||
4. await WP.status()
|
||||
5. await WP.exportar() -- baixa wordpress-posts.jsonl
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const BASE = 'https://brasilianafotografica.bn.gov.br';
|
||||
const CATS = ["25","26","311","5","9","35","189","1"];
|
||||
const PAUSA = 1500;
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 500);
|
||||
|
||||
let db;
|
||||
const abrir = () => new Promise((res, rej) => {
|
||||
const q = indexedDB.open('bras_wp', 1);
|
||||
q.onupgradeneeded = e => { const d = e.target.result;
|
||||
if (!d.objectStoreNames.contains('fila')) d.createObjectStore('fila');
|
||||
if (!d.objectStoreNames.contains('posts')) d.createObjectStore('posts'); };
|
||||
q.onsuccess = e => res(e.target.result); q.onerror = () => rej(q.error);
|
||||
});
|
||||
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);
|
||||
});
|
||||
const putMuitos = (st, pares) => new Promise((res, rej) => {
|
||||
const t = db.transaction(st, 'readwrite'), o = t.objectStore(st);
|
||||
for (const [k, v] of pares) o.put(v, k);
|
||||
t.oncomplete = res; t.onerror = () => rej(t.error);
|
||||
});
|
||||
const keys = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAllKeys();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.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', atual: '', colhidos: 0, erros: 0, parar: false};
|
||||
|
||||
async function descobrir() {
|
||||
S.fase = 'descobrindo';
|
||||
for (const cat of CATS) {
|
||||
if (S.parar) return;
|
||||
S.atual = `categoria ${cat}`;
|
||||
let pagina = 1, vazias = 0;
|
||||
while (vazias < 1 && pagina <= 100) {
|
||||
if (S.parar) return;
|
||||
const url = pagina === 1 ? `${BASE}/?cat=${cat}` : `${BASE}/?cat=${cat}&paged=${pagina}`;
|
||||
let html;
|
||||
try { const r = await fetch(url, {credentials:'same-origin'}); html = await r.text(); }
|
||||
catch(e) { vazias++; pagina++; await sleep(2000); continue; }
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const ids = [...d.querySelectorAll('article')]
|
||||
.map(a => a.id?.match(/post-(\d+)/)?.[1]).filter(Boolean);
|
||||
if (!ids.length) { vazias++; pagina++; await sleep(300); continue; }
|
||||
await putMuitos('fila', ids.map(id => [id, cat]));
|
||||
pagina++;
|
||||
await sleep(jit());
|
||||
}
|
||||
}
|
||||
const n = (await keys('fila')).length;
|
||||
console.log(`Descoberta: ${n} posts unicos`);
|
||||
}
|
||||
|
||||
function limpaTexto(el) {
|
||||
const clone = el.cloneNode(true);
|
||||
clone.querySelectorAll('.sharify-container, script, style').forEach(e => e.remove());
|
||||
return clone.textContent.replace(/[ \t]+/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
|
||||
}
|
||||
|
||||
async function colherUm(id) {
|
||||
const r = await fetch(`${BASE}/?p=${id}`, {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');
|
||||
const titulo = d.querySelector('.entry-title')?.textContent?.trim() || d.title;
|
||||
const autor = d.querySelector('.author')?.textContent?.trim() || null;
|
||||
const dataIso = d.querySelector('time')?.getAttribute('datetime') || null;
|
||||
const categorias = [...new Set([...d.querySelectorAll('a[href*="?cat="]')].map(a => a.textContent.trim()).filter(Boolean))];
|
||||
const contentEl = d.querySelector('.entry-content');
|
||||
const corpo = contentEl ? limpaTexto(contentEl) : null;
|
||||
const handles = [...new Set([...html.matchAll(/handle\/(20\.500\.12156\.1)\/(\d+)/g)].map(m => `${m[1]}/${m[2]}`))];
|
||||
return {
|
||||
id, url: `${BASE}/?p=${id}`, titulo, autor, data_iso: dataIso,
|
||||
categorias, corpo, handles_referenciados: handles, fetched_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function colher() {
|
||||
S.fase = 'colhendo';
|
||||
const fila = await keys('fila'), feitos = new Set(await keys('posts'));
|
||||
const pend = fila.filter(id => !feitos.has(id));
|
||||
console.log(`${pend.length} posts a colher`);
|
||||
for (let i = 0; i < pend.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const id = pend[i];
|
||||
S.atual = `${i + 1}/${pend.length}`;
|
||||
try {
|
||||
const post = await colherUm(id);
|
||||
await put('posts', id, post);
|
||||
S.colhidos++;
|
||||
} catch (e) {
|
||||
await put('posts', id, {id, erro: String(e).slice(0, 80)});
|
||||
S.erros++;
|
||||
}
|
||||
if (i % 25 === 0) console.log(` ${i}/${pend.length} ok=${S.colhidos} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nConcluido. ok=${S.colhidos} erro=${S.erros}`);
|
||||
}
|
||||
|
||||
async function exportar() {
|
||||
const todos = await getAll('posts');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([todos.map(o => JSON.stringify(o)).join('\n')],
|
||||
{type: 'application/x-ndjson'}));
|
||||
a.download = 'wordpress-posts.jsonl';
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
console.log(`exportados ${todos.length} posts`);
|
||||
return todos.length;
|
||||
}
|
||||
|
||||
window.WP = {
|
||||
descobrir, colher, exportar,
|
||||
status: async () => ({...S, naFila: (await keys('fila')).length, noBanco: (await keys('posts')).length}),
|
||||
parar: () => { S.parar = true; },
|
||||
};
|
||||
(async () => { db = await abrir(); console.log('WP pronto. await WP.descobrir() depois await WP.colher()'); })();
|
||||
})();
|
||||
@@ -0,0 +1,207 @@
|
||||
/* ===================================================================
|
||||
Coletor da Brasiliana Fotografica -- roda DENTRO da pagina.
|
||||
|
||||
POR QUE ASSIM: o Cloudflare do site bloqueia navegador dirigido por
|
||||
automacao (Playwright headless E headed levam 403), mas nao toca em
|
||||
fetch() disparado de dentro de uma pagina aberta no seu Chrome --
|
||||
mesma origem, sessao real, zero sinal de automacao.
|
||||
Medido: >100 requisicoes seguidas sem bloqueio, a ~2s de intervalo.
|
||||
|
||||
ARMAZENAMENTO: IndexedDB, nao localStorage. 13.036 itens com metadados
|
||||
completos dao ~39 MB; localStorage estoura em ~5 MB.
|
||||
|
||||
COMO USAR
|
||||
1. Abra https://brasilianafotografica.bn.gov.br/brasiliana/
|
||||
2. DevTools (Cmd+Option+J) > Console > cole este arquivo inteiro
|
||||
3. Ele comeca sozinho: enumera as 15 comunidades e segue colhendo.
|
||||
4. Deixe a aba aberta (~7 h). Pode usar o resto do Chrome normalmente.
|
||||
|
||||
ACOMPANHAR / CONTROLAR
|
||||
await BRAS.status() onde esta
|
||||
BRAS.parar() interrompe (nao perde nada)
|
||||
BRAS.retomar() continua de onde parou
|
||||
await BRAS.exportar() baixa brasiliana-catalogo.jsonl
|
||||
|
||||
Fechou a aba? Reabra a pagina, cole o arquivo de novo: ele retoma.
|
||||
=================================================================== */
|
||||
(() => {
|
||||
const BASE = 'https://brasilianafotografica.bn.gov.br/brasiliana';
|
||||
const PREFIX= '20.500.12156.1';
|
||||
const RPP = 100;
|
||||
const PAUSA = 2000; // ms entre requisicoes. Nao baixe disso.
|
||||
|
||||
const COM = {
|
||||
'2':'Biblioteca Nacional', '1753':'Instituto Moreira Salles',
|
||||
'3540':'Arquivo Geral da Cidade do Rio de Janeiro',
|
||||
'3541':'Diretoria do Patrimonio Historico e Documentacao da Marinha',
|
||||
'4342':'Leibniz-Institut fuer Laenderkunde, Leipzig', '4884':'Arquivo Nacional',
|
||||
'4886':'Museu da Republica', '5018':'Fiocruz', '6142':'Museu Historico Nacional',
|
||||
'6660':'Fundacao Joaquim Nabuco', '7072':'Museu Aeroespacial',
|
||||
'11464':'Escola de Ciencias Sociais (FGV CPDOC)',
|
||||
'12796':'Instituto Historico e Geografico Brasileiro',
|
||||
'13099':'Instituto Brasileiro de Geografia e Estatistica',
|
||||
'14087':'Museu Paraense Emilio Goeldi',
|
||||
};
|
||||
const ESPERADO = 13036;
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
const jit = () => PAUSA + Math.floor(Math.random() * 600); // ritmo, nao metronomo
|
||||
|
||||
// ---------- IndexedDB ----------
|
||||
let db;
|
||||
const abrir = () => new Promise((res, rej) => {
|
||||
const q = indexedDB.open('bras', 1);
|
||||
q.onupgradeneeded = e => { const d = e.target.result;
|
||||
if (!d.objectStoreNames.contains('fila')) d.createObjectStore('fila');
|
||||
if (!d.objectStoreNames.contains('itens')) d.createObjectStore('itens'); };
|
||||
q.onsuccess = e => res(e.target.result);
|
||||
q.onerror = () => rej(q.error);
|
||||
});
|
||||
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); });
|
||||
const putMuitos = (st, pares) => new Promise((res, rej) => {
|
||||
const t = db.transaction(st, 'readwrite'), o = t.objectStore(st);
|
||||
for (const [k, v] of pares) o.put(v, k);
|
||||
t.oncomplete = res; t.onerror = () => rej(t.error); });
|
||||
const keys = st => new Promise((res, rej) => {
|
||||
const r = db.transaction(st).objectStore(st).getAllKeys();
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.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 get = (st, k) => new Promise(res => {
|
||||
const r = db.transaction(st).objectStore(st).get(k);
|
||||
r.onsuccess = () => res(r.result); r.onerror = () => res(undefined); });
|
||||
|
||||
async function pegar(u) {
|
||||
const r = await fetch(u, {credentials: 'same-origin'});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.text();
|
||||
}
|
||||
|
||||
// ---------- extracao ----------
|
||||
function extrair(html, h, com) {
|
||||
const d = new DOMParser().parseFromString(html, 'text/html');
|
||||
const dc = [...d.querySelectorAll('meta[name]')]
|
||||
.filter(m => /^(DC|DCTERMS|citation)/.test(m.name))
|
||||
.map(m => ({name: m.name, value: m.content}));
|
||||
const a = n => (dc.find(x => x.name === n) || {}).value || null;
|
||||
// a tabela do ?show=full traz campos que nao viram meta tag, entre eles dc.rights
|
||||
const tb = [...d.querySelectorAll('table.detailtable tr, table.ds-includeSet-table tr')]
|
||||
.map(tr => { const td = [...tr.querySelectorAll('td')].map(c => c.textContent.trim());
|
||||
return td.length >= 2 ? {field: td[0], value: td[1]} : null; })
|
||||
.filter(Boolean);
|
||||
const leg = dc.filter(x => x.name === 'DC.identifier' && /objdigital/.test(x.value || ''))
|
||||
.map(x => x.value)[0] || null;
|
||||
return {
|
||||
handle: `${PREFIX}/${h}`,
|
||||
source_url: `${BASE}/handle/${PREFIX}/${h}?show=full`,
|
||||
fetched_at: new Date().toISOString(),
|
||||
title: a('DC.title'),
|
||||
community: com,
|
||||
// bitstream do DSpace = arquivo de PRESERVACAO (ate 8679x6484, 34,7 MB).
|
||||
// objdigital = copia MENOR (~2600x2100, ~330 KB), so procedencia.
|
||||
preservation_url: a('citation_pdf_url'),
|
||||
legacy_url: leg,
|
||||
rights: (tb.find(t => /^dc\.rights/i.test(t.field)) || {}).value || null,
|
||||
dublin_core: dc,
|
||||
full_table: tb,
|
||||
};
|
||||
}
|
||||
|
||||
const S = {fase: 'iniciando', atual: '', colhidos: 0, erros: 0, parar: false, t0: Date.now()};
|
||||
|
||||
// ---------- fase 1: enumerar ----------
|
||||
async function descobrir() {
|
||||
S.fase = 'enumerando';
|
||||
const RE = new RegExp('handle/' + PREFIX.replace(/\./g, '\\.') + '/(\\d+)', 'g');
|
||||
for (const [h, nome] of Object.entries(COM)) {
|
||||
if (S.parar) return;
|
||||
S.atual = nome;
|
||||
let off = 0, total = null, vistos = new Set();
|
||||
while (!S.parar) {
|
||||
let html;
|
||||
try { html = await pegar(`${BASE}/handle/${PREFIX}/${h}/browse?type=title&rpp=${RPP}&offset=${off}`); }
|
||||
catch (e) { console.warn(` ${nome} offset=${off}: ${e.message} -- pausa de 30s`);
|
||||
await sleep(30000); continue; }
|
||||
if (total === null) {
|
||||
const m = html.replace(/<[^>]+>/g, ' ')
|
||||
.match(/no momento\D{0,24}[\d.,]+\s*-\s*[\d.,]+\s*de\s*([\d.,]+)/i);
|
||||
total = m ? +m[1].replace(/[.,]/g, '') : null;
|
||||
}
|
||||
const ach = new Set([...html.matchAll(RE)].map(m => m[1])); ach.delete(h);
|
||||
let novos = 0; for (const x of ach) if (!vistos.has(x)) { vistos.add(x); novos++; }
|
||||
await putMuitos('fila', [...ach].map(x => [x, nome]));
|
||||
off += RPP;
|
||||
if (!novos || (total && off >= total)) break;
|
||||
await sleep(jit());
|
||||
}
|
||||
console.log(` ${nome.padEnd(46)} ${String(vistos.size).padStart(6)}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
const n = (await keys('fila')).length;
|
||||
console.log(`\nEnumerados: ${n} (esperado ${ESPERADO})`);
|
||||
}
|
||||
|
||||
// ---------- fase 2: metadados ----------
|
||||
async function coletar() {
|
||||
S.fase = 'colhendo';
|
||||
const fila = await keys('fila'), feitos = new Set(await keys('itens'));
|
||||
const pend = fila.filter(h => !feitos.has(h));
|
||||
console.log(`${pend.length} itens a colher (~${(pend.length * PAUSA / 3600000).toFixed(1)} h)`);
|
||||
for (let i = 0; i < pend.length; i++) {
|
||||
if (S.parar) { S.fase = 'parado'; return; }
|
||||
const h = pend[i];
|
||||
S.atual = `${i + 1}/${pend.length}`;
|
||||
try {
|
||||
const html = await pegar(`${BASE}/handle/${PREFIX}/${h}?show=full`);
|
||||
await put('itens', h, extrair(html, h, await get('fila', h)));
|
||||
S.colhidos++;
|
||||
} catch (e) {
|
||||
await put('itens', h, {handle: `${PREFIX}/${h}`, erro: String(e).slice(0, 80)});
|
||||
S.erros++;
|
||||
// recuou? entao respire, nao insista -- insistir e o que vira bloqueio
|
||||
if (/HTTP 4|HTTP 5/.test(String(e))) { console.warn(` ${h}: ${e.message} -- pausa de 60s`);
|
||||
await sleep(60000); }
|
||||
}
|
||||
if (i % 100 === 0) console.log(` ${i}/${pend.length} ok=${S.colhidos} erro=${S.erros}`);
|
||||
await sleep(jit());
|
||||
}
|
||||
S.fase = 'concluido';
|
||||
console.log(`\nConcluido. ok=${S.colhidos} erro=${S.erros}. Agora: await BRAS.exportar()`);
|
||||
}
|
||||
|
||||
// ---------- exportar ----------
|
||||
async function exportar() {
|
||||
const todos = await getAll('itens');
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([todos.map(o => JSON.stringify(o)).join('\n')],
|
||||
{type: 'application/x-ndjson'}));
|
||||
a.download = 'brasiliana-catalogo.jsonl';
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
console.log(`exportadas ${todos.length} linhas para a pasta Downloads`);
|
||||
return todos.length;
|
||||
}
|
||||
|
||||
window.BRAS = {
|
||||
status: async () => {
|
||||
const naFila = (await keys('fila')).length, noBanco = (await keys('itens')).length;
|
||||
const h = (Date.now() - S.t0) / 3600000;
|
||||
return {fase: S.fase, atual: S.atual, naFila, noBanco, erros: S.erros,
|
||||
restam: naFila - noBanco, horas: +h.toFixed(2)};
|
||||
},
|
||||
parar: () => { S.parar = true; console.log('parando apos a requisicao atual'); },
|
||||
retomar: () => { S.parar = false; coletar(); },
|
||||
exportar,
|
||||
limpar: () => indexedDB.deleteDatabase('bras'),
|
||||
};
|
||||
|
||||
(async () => {
|
||||
db = await abrir();
|
||||
if ((await keys('fila')).length < ESPERADO) await descobrir();
|
||||
await coletar();
|
||||
})();
|
||||
|
||||
console.log('BRAS iniciado. Acompanhe com: await BRAS.status()');
|
||||
})();
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Converte wordpress-posts.jsonl (saido do coletor-wordpress.js) em um .md
|
||||
por post, com front-matter YAML. Imagens NAO sao baixadas -- os posts so
|
||||
usam miniaturas dos mesmos handles que o acervo principal ja cobre (ver
|
||||
PLANO.md); o valor novo aqui e o texto + o mapeamento pra handles.
|
||||
|
||||
Uso: python3 converte-wordpress-md.py [wordpress-posts.jsonl] [pasta-saida]
|
||||
"""
|
||||
import json, re, sys, unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
ENTRADA = Path(sys.argv[1] if len(sys.argv) > 1 else "wordpress-posts.jsonl")
|
||||
SAIDA = Path(sys.argv[2] if len(sys.argv) > 2 else "../wordpress")
|
||||
SAIDA.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def slug(txt):
|
||||
txt = unicodedata.normalize("NFKD", txt).encode("ascii", "ignore").decode()
|
||||
txt = re.sub(r"[^a-zA-Z0-9]+", "-", txt).strip("-").lower()
|
||||
return txt[:80] or "sem-titulo"
|
||||
|
||||
|
||||
def yaml_str(s):
|
||||
if s is None:
|
||||
return "null"
|
||||
return '"' + str(s).replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||
|
||||
|
||||
def yaml_lista(items):
|
||||
if not items:
|
||||
return "[]"
|
||||
return "[" + ", ".join(yaml_str(i) for i in items) + "]"
|
||||
|
||||
|
||||
n_ok = n_erro = 0
|
||||
for linha in open(ENTRADA, encoding="utf-8"):
|
||||
if not linha.strip():
|
||||
continue
|
||||
post = json.loads(linha)
|
||||
if post.get("erro"):
|
||||
n_erro += 1
|
||||
continue
|
||||
|
||||
titulo = post.get("titulo") or f"post-{post['id']}"
|
||||
nome_arquivo = f"{post['id']}-{slug(titulo)}.md"
|
||||
|
||||
frontmatter = "\n".join([
|
||||
"---",
|
||||
f"id: {post['id']}",
|
||||
f"url: {yaml_str(post.get('url'))}",
|
||||
f"titulo: {yaml_str(titulo)}",
|
||||
f"autor: {yaml_str(post.get('autor'))}",
|
||||
f"data: {yaml_str(post.get('data_iso'))}",
|
||||
f"categorias: {yaml_lista(post.get('categorias') or [])}",
|
||||
f"handles_referenciados: {yaml_lista(post.get('handles_referenciados') or [])}",
|
||||
f"fetched_at: {yaml_str(post.get('fetched_at'))}",
|
||||
"---",
|
||||
])
|
||||
|
||||
corpo = post.get("corpo") or "*(sem conteúdo extraído)*"
|
||||
|
||||
# nota de referencia pros handles, com link relativo pro nosso acervo
|
||||
handles = post.get("handles_referenciados") or []
|
||||
nota_handles = ""
|
||||
if handles:
|
||||
linhas_h = "\n".join(f"- `{h}` — `itens/{h.replace('/', '_')}/`" for h in handles)
|
||||
nota_handles = f"\n\n---\n\n**Itens do acervo referenciados neste post:**\n\n{linhas_h}\n"
|
||||
|
||||
conteudo = f"{frontmatter}\n\n# {titulo}\n\n{corpo}{nota_handles}\n"
|
||||
(SAIDA / nome_arquivo).write_text(conteudo, encoding="utf-8")
|
||||
n_ok += 1
|
||||
|
||||
print(f"{n_ok} arquivos .md gerados em {SAIDA.resolve()}")
|
||||
print(f"{n_erro} posts com erro na coleta, pulados")
|
||||
@@ -0,0 +1,74 @@
|
||||
20.500.12156.1/7475
|
||||
20.500.12156.1/8785
|
||||
20.500.12156.1/7875
|
||||
20.500.12156.1/8075
|
||||
20.500.12156.1/7817
|
||||
20.500.12156.1/7326
|
||||
20.500.12156.1/7386
|
||||
20.500.12156.1/8912
|
||||
20.500.12156.1/8262
|
||||
20.500.12156.1/8161
|
||||
20.500.12156.1/8761
|
||||
20.500.12156.1/8762
|
||||
20.500.12156.1/7457
|
||||
20.500.12156.1/8804
|
||||
20.500.12156.1/8774
|
||||
20.500.12156.1/7516
|
||||
20.500.12156.1/7379
|
||||
20.500.12156.1/7344
|
||||
20.500.12156.1/7743
|
||||
20.500.12156.1/8313
|
||||
20.500.12156.1/8374
|
||||
20.500.12156.1/6545
|
||||
20.500.12156.1/8187
|
||||
20.500.12156.1/8133
|
||||
20.500.12156.1/8277
|
||||
20.500.12156.1/7355
|
||||
20.500.12156.1/7848
|
||||
20.500.12156.1/8267
|
||||
20.500.12156.1/6520
|
||||
20.500.12156.1/8275
|
||||
20.500.12156.1/7529
|
||||
20.500.12156.1/6570
|
||||
20.500.12156.1/8182
|
||||
20.500.12156.1/7034
|
||||
20.500.12156.1/7744
|
||||
20.500.12156.1/6568
|
||||
20.500.12156.1/8872
|
||||
20.500.12156.1/8396
|
||||
20.500.12156.1/7317
|
||||
20.500.12156.1/7517
|
||||
20.500.12156.1/8268
|
||||
20.500.12156.1/8141
|
||||
20.500.12156.1/6433
|
||||
20.500.12156.1/7438
|
||||
20.500.12156.1/6670
|
||||
20.500.12156.1/7440
|
||||
20.500.12156.1/7939
|
||||
20.500.12156.1/8825
|
||||
20.500.12156.1/7353
|
||||
20.500.12156.1/7393
|
||||
20.500.12156.1/7515
|
||||
20.500.12156.1/7814
|
||||
20.500.12156.1/7358
|
||||
20.500.12156.1/7463
|
||||
20.500.12156.1/7852
|
||||
20.500.12156.1/8796
|
||||
20.500.12156.1/8145
|
||||
20.500.12156.1/8254
|
||||
20.500.12156.1/7766
|
||||
20.500.12156.1/6674
|
||||
20.500.12156.1/7461
|
||||
20.500.12156.1/8128
|
||||
20.500.12156.1/8316
|
||||
20.500.12156.1/8249
|
||||
20.500.12156.1/6555
|
||||
20.500.12156.1/6669
|
||||
20.500.12156.1/7802
|
||||
20.500.12156.1/8739
|
||||
20.500.12156.1/7861
|
||||
20.500.12156.1/9148
|
||||
20.500.12156.1/6656
|
||||
20.500.12156.1/8781
|
||||
20.500.12156.1/8271
|
||||
20.500.12156.1/8286
|
||||
@@ -0,0 +1,73 @@
|
||||
{"handle": "20.500.12156.1/10993", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10993/1/Alb%200279%20113%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/10995", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10995/1/Alb%200279%20106%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/6433", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6433/1/0072430cx006-05t.jpg"}
|
||||
{"handle": "20.500.12156.1/6520", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6520/1/007_IMG_1008t.jpg"}
|
||||
{"handle": "20.500.12156.1/6545", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6545/1/CC-05-01-015-3.jpg"}
|
||||
{"handle": "20.500.12156.1/6555", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6555/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0009_m0003de0003.jpg"}
|
||||
{"handle": "20.500.12156.1/6568", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6568/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0019_m0001de0001.jpg"}
|
||||
{"handle": "20.500.12156.1/6570", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6570/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0024_m0001de0001.jpg"}
|
||||
{"handle": "20.500.12156.1/6669", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6669/1/BP-06-TP-01-012.jpg"}
|
||||
{"handle": "20.500.12156.1/6670", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6670/1/BP-06-TP-01-015.jpg"}
|
||||
{"handle": "20.500.12156.1/6674", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6674/1/BP-06-TP-01-022.jpg"}
|
||||
{"handle": "20.500.12156.1/7034", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7034/1/007A6P3F08-004.jpg"}
|
||||
{"handle": "20.500.12156.1/7317", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7317/4/Alb%200294%20069ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7326", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7326/4/Alb%200294%20037ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7344", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7344/4/Alb%200294%20093ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7353", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7353/4/Alb%200294%20097ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7355", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7355/4/Alb%200259%20073%20ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7358", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7358/1/BR_RJANRIO_OG_0_FOT_0001_021.jpg"}
|
||||
{"handle": "20.500.12156.1/7379", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7379/1/BR_RJANRIO_OG_0_FOT_0001_057.jpg"}
|
||||
{"handle": "20.500.12156.1/7386", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7386/1/BR_RJANRIO_OG_0_FOT_0001_067.jpg"}
|
||||
{"handle": "20.500.12156.1/7393", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7393/1/BR_RJANRIO_OG_0_FOT_0001_089.jpg"}
|
||||
{"handle": "20.500.12156.1/7438", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7438/1/BR_RJANRIO_OG_0_FOT_0001_085.jpg"}
|
||||
{"handle": "20.500.12156.1/7440", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7440/1/BR_RJANRIO_OG_0_FOT_0001_087.jpg"}
|
||||
{"handle": "20.500.12156.1/7457", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7457/4/Alb%200294%20127ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7461", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7461/4/Alb%200259%20081ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7463", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7463/4/Alb%200294%20106ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7475", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7475/4/Alb%200259%20096ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7515", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7515/4/Alb%200259%20145ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7516", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7516/4/Alb%200259%20139ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7517", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7517/3/Album%200035%20297%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/7529", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7529/5/Alb%200259%20130ct.jpg"}
|
||||
{"handle": "20.500.12156.1/7743", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7743/1/001ALA011011.jpg"}
|
||||
{"handle": "20.500.12156.1/7744", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7744/1/001ALA011012.jpg"}
|
||||
{"handle": "20.500.12156.1/7766", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7766/1/001ATK012010.jpg"}
|
||||
{"handle": "20.500.12156.1/7802", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7802/1/Alb%200036%20037%20CT%20ok.jpg"}
|
||||
{"handle": "20.500.12156.1/7814", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7814/1/Alb%200036%20081%20CT%20ok.jpg"}
|
||||
{"handle": "20.500.12156.1/7817", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7817/1/Alb%200036%20087%20CT%20ok.jpg"}
|
||||
{"handle": "20.500.12156.1/7848", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7848/1/Alb%200036%20013%20CT%20ok.jpg"}
|
||||
{"handle": "20.500.12156.1/7852", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7852/1/Album%200035%20105%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/7861", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7861/1/Album%200035%20088%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/7875", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7875/1/Album%200035%20078%20CT.jpg"}
|
||||
{"handle": "20.500.12156.1/8075", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8075/1/Alb%200038%20009%20CT%20ok.jpg"}
|
||||
{"handle": "20.500.12156.1/8128", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8128/1/014AM006009.jpg"}
|
||||
{"handle": "20.500.12156.1/8133", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8133/1/014AM010003.jpg"}
|
||||
{"handle": "20.500.12156.1/8141", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8141/1/014AM010014.jpg"}
|
||||
{"handle": "20.500.12156.1/8145", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8145/1/014AM012006.jpg"}
|
||||
{"handle": "20.500.12156.1/8161", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8161/1/014AM012058.jpg"}
|
||||
{"handle": "20.500.12156.1/8182", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8182/1/014AM019007.jpg"}
|
||||
{"handle": "20.500.12156.1/8187", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8187/1/014AM019013.jpg"}
|
||||
{"handle": "20.500.12156.1/8249", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8249/1/014AM002012.jpg"}
|
||||
{"handle": "20.500.12156.1/8254", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8254/1/014AM002019.jpg"}
|
||||
{"handle": "20.500.12156.1/8262", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8262/1/014AM003015.jpg"}
|
||||
{"handle": "20.500.12156.1/8267", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8267/1/014AM004003.jpg"}
|
||||
{"handle": "20.500.12156.1/8268", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8268/1/014AM005003.jpg"}
|
||||
{"handle": "20.500.12156.1/8271", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8271/1/014AM005005.jpg"}
|
||||
{"handle": "20.500.12156.1/8275", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8275/1/014AM005014.jpg"}
|
||||
{"handle": "20.500.12156.1/8277", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8277/1/014AM005020.jpg"}
|
||||
{"handle": "20.500.12156.1/8286", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8286/1/014AM006002.jpg"}
|
||||
{"handle": "20.500.12156.1/8313", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8313/1/001AAN005123.jpg"}
|
||||
{"handle": "20.500.12156.1/8316", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8316/1/001ACL019004.jpg"}
|
||||
{"handle": "20.500.12156.1/8374", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8374/1/002080NUS0309.jpg"}
|
||||
{"handle": "20.500.12156.1/8739", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8739/1/007ALA102.jpg"}
|
||||
{"handle": "20.500.12156.1/8761", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8761/1/007ALA015.jpg"}
|
||||
{"handle": "20.500.12156.1/8762", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8762/1/007ALA016.jpg"}
|
||||
{"handle": "20.500.12156.1/8774", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8774/1/007ALA027.jpg"}
|
||||
{"handle": "20.500.12156.1/8781", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8781/1/007ALA033.jpg"}
|
||||
{"handle": "20.500.12156.1/8785", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8785/1/007ALA037.jpg"}
|
||||
{"handle": "20.500.12156.1/8796", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8796/1/007ALA047.jpg"}
|
||||
{"handle": "20.500.12156.1/8804", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8804/1/007ALA054.jpg"}
|
||||
{"handle": "20.500.12156.1/8825", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8825/1/007ALA073.jpg"}
|
||||
{"handle": "20.500.12156.1/8872", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8872/1/A05F03P02.jpg"}
|
||||
{"handle": "20.500.12156.1/8912", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8912/1/pixinguinha04.jpg"}
|
||||
{"handle": "20.500.12156.1/9148", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/9148/1/007ALA091.jpg"}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,242 @@
|
||||
{"handle": "20.500.12156.1/101", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/101/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1004", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1004/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1005", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1005/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1006", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1006/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1007", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1007/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1008", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1008/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1009", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1009/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1010", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1010/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1011", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1011/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1012", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1012/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1013", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1013/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1014", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1014/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/1015", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/1015/1/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6433", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6433/1/0072430cx006-05t.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6520", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6520/1/007_IMG_1008t.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6545", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6545/1/CC-05-01-015-3.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6555", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6555/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0009_m0003de0003.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6568", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6568/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0019_m0001de0001.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6570", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6570/1/BR_RJANRIO_Q0_ADM_CPA_VFE_FOT_0024_m0001de0001.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6656", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6656/2/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6669", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6669/1/BP-06-TP-01-012.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6670", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6670/1/BP-06-TP-01-015.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/6674", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/6674/1/BP-06-TP-01-022.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7034", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7034/1/007A6P3F08-004.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7317", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7317/4/Alb%200294%20069ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7326", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7326/4/Alb%200294%20037ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7344", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7344/4/Alb%200294%20093ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7353", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7353/4/Alb%200294%20097ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7355", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7355/4/Alb%200259%20073%20ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7358", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7358/1/BR_RJANRIO_OG_0_FOT_0001_021.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7379", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7379/1/BR_RJANRIO_OG_0_FOT_0001_057.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7386", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7386/1/BR_RJANRIO_OG_0_FOT_0001_067.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7393", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7393/1/BR_RJANRIO_OG_0_FOT_0001_089.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7438", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7438/1/BR_RJANRIO_OG_0_FOT_0001_085.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7440", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7440/1/BR_RJANRIO_OG_0_FOT_0001_087.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7457", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7457/4/Alb%200294%20127ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7461", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7461/4/Alb%200259%20081ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7463", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7463/4/Alb%200294%20106ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7475", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7475/4/Alb%200259%20096ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7515", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7515/4/Alb%200259%20145ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7516", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7516/4/Alb%200259%20139ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7517", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7517/3/Album%200035%20297%20CT.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7523", "url": null, "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7529", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7529/5/Alb%200259%20130ct.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7743", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7743/1/001ALA011011.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7744", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7744/1/001ALA011012.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7766", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7766/1/001ATK012010.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7802", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7802/1/Alb%200036%20037%20CT%20ok.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7814", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7814/1/Alb%200036%20081%20CT%20ok.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7817", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7817/1/Alb%200036%20087%20CT%20ok.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7848", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7848/1/Alb%200036%20013%20CT%20ok.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7852", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7852/1/Album%200035%20105%20CT.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7861", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7861/1/Album%200035%20088%20CT.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7875", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7875/1/Album%200035%20078%20CT.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/7939", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/7939/2/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8075", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8075/1/Alb%200038%20009%20CT%20ok.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8128", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8128/1/014AM006009.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8133", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8133/1/014AM010003.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8141", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8141/1/014AM010014.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8145", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8145/1/014AM012006.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8161", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8161/1/014AM012058.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8182", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8182/1/014AM019007.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8187", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8187/1/014AM019013.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8249", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8249/1/014AM002012.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8254", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8254/1/014AM002019.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8262", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8262/1/014AM003015.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8267", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8267/1/014AM004003.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8268", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8268/1/014AM005003.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8271", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8271/1/014AM005005.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8275", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8275/1/014AM005014.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8277", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8277/1/014AM005020.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8286", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8286/1/014AM006002.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8313", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8313/1/001AAN005123.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8316", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8316/1/001ACL019004.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8374", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8374/1/002080NUS0309.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8396", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8396/2/imagem.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8739", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8739/1/007ALA102.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8761", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8761/1/007ALA015.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8762", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8762/1/007ALA016.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8774", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8774/1/007ALA027.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8781", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8781/1/007ALA033.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8785", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8785/1/007ALA037.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8796", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8796/1/007ALA047.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8804", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8804/1/007ALA054.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8825", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8825/1/007ALA073.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8872", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8872/1/A05F03P02.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/8912", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/8912/1/pixinguinha04.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/9148", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/9148/1/007ALA091.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10039", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10039/1/007CX190-01.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10040", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10040/1/007CX190-05.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10041", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10041/1/007CX190-11.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10042", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10042/1/007CX190-14.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10043", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10043/1/007CX190-16.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10044", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10044/1/007CX194-08.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10045", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10045/1/001CC007004.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10046", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10046/1/007CX194-10.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10047", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10047/1/007CX196-02.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10048", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10048/1/007CX196-07.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10049", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10049/1/007CX197-02.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10050", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10050/1/007CX197-05.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10051", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10051/1/007CX197-09.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10052", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10052/1/007CX197-10.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10053", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10053/1/007CX198-03.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10054", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10054/1/007CX199-03.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10055", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10055/1/007CX199-04.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10056", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10056/1/001CC007005.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10057", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10057/1/007CX214-12.JPG", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10058", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10058/1/007CX237-01.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10059", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10059/1/007SN-12.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10060", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10060/1/007_IMG_1807.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10061", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10061/1/007_IMG_1811.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10062", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10062/1/007_IMG_1812.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10063", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10063/1/007_IMG_1813.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10064", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10064/1/007_IMG_1855.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10065", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10065/1/007_IMG_1859.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10066", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10066/1/007_IMG_1993.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10067", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10067/1/001CC007007.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10068", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10068/1/007_IMG_2000.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10069", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10069/1/007_IMG_2006.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10070", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10070/1/007_IMG_2007.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10071", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10071/1/007_IMG_2010.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10072", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10072/1/007_IMG_2023.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10073", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10073/1/007_IMG_2493.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10074", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10074/1/007_IMG_2497.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10075", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10075/1/007_IMG_2500.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10076", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10076/1/007_IMG_2502.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10077", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10077/1/007_IMG_2503.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10078", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10078/1/001CC007014.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10079", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10079/1/007_IMG_2509.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10080", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10080/1/007_IMG_2511.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10081", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10081/1/007_IMG_2516.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10082", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10082/1/007_IMG_2519.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10083", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10083/1/007_IMG_2520.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10084", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10084/1/007_IMG_2522.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10085", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10085/1/007_IMG_2525.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10086", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10086/1/007_IMG_2526.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10087", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10087/1/007_IMG_2527.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10088", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10088/1/007_IMG_2531.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10089", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10089/1/001AN02001006.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10090", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10090/1/001CC007015.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10091", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10091/1/007_IMG_2836.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10092", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10092/1/007_IMG_2837.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10093", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10093/1/007_IMG_2838.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10094", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10094/1/007_IMG_2839.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10095", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10095/1/007_IMG_2897.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10096", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10096/1/007_IMG_2904.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10097", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10097/1/007_IMG_2912.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10098", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10098/1/007_IMG_2913.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10099", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10099/1/007_IMG_2914.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10100", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10100/1/007_IMG_2917.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10101", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10101/1/001CC007016.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10102", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10102/1/007_IMG_2934.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10103", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10103/1/007_IMG_2935.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10104", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10104/1/007_IMG_2944.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10105", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10105/1/007_IMG_3108.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10106", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10106/1/007_IMG_3110.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10107", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10107/1/007_IMG_3113.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10108", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10108/1/007_IMG_3114.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10109", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10109/1/007_IMG_3119.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10110", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10110/1/007_IMG_3165.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10111", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10111/1/007_IMG_3259.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10112", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10112/1/001CC007019.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10113", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10113/1/007_IMG_3285.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10114", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10114/1/007_IMG_3299.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10115", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10115/1/007_IMG_3309.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10116", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10116/1/007_IMG_3316.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10117", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10117/1/007_IMG_3330.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10118", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10118/1/007_IMG_3332.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10119", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10119/1/007_IMG_3347.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10120", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10120/1/007_IMG_3350.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10121", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10121/1/007_IMG_3352.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10122", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10122/1/007_IMG_3353.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10123", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10123/1/001CC007020.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10124", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10124/1/007_IMG_3437.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10125", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10125/1/007_IMG_3469.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10126", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10126/1/007_IMG_3527.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10127", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10127/1/007_IMG_3528.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10128", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10128/1/007_IMG_3533.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10129", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10129/1/007_IMG_3534.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10130", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10130/1/007_IMG_3535.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10131", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10131/1/007_IMG_3536.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10132", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10132/1/007_IMG_3539.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10133", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10133/1/007_IMG_3540.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10134", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10134/1/001CC007025.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10135", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10135/1/007_IMG_3547.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10136", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10136/1/007_IMG_3559.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10137", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10137/1/007_IMG_3585.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10138", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10138/1/007_IMG_3588.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10139", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10139/1/007_IMG_3590.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10140", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10140/1/007_IMG_3594.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10141", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10141/1/007_IMG_3596.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10142", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10142/1/007_IMG_3597.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10143", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10143/1/007_IMG_3598.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10144", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10144/1/007_IMG_3601.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10145", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10145/1/001CC008007.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10146", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10146/1/007_IMG_3603.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10147", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10147/1/007_IMG_3607.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10148", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10148/1/007_IMG_3631.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10149", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10149/1/007_IMG_3634.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10150", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10150/1/007_IMG_3636.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10151", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10151/1/007_IMG_3639.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10152", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10152/1/007_IMG_3642.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10153", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10153/1/007_IMG_3739.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10154", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10154/1/007_IMG_3740.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/10155", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/10155/1/007_IMG_3742.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14358", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14358/1/Alb%200261%20139.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14359", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14359/1/Alb%200261%20136.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14360", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14360/1/Alb%200261%20141.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14361", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14361/1/Alb%200261%20142.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14362", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14362/1/Alb%200261%20143.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14363", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14363/1/Alb%200261%20144.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14364", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14364/1/Alb%200261%20146.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14368", "url": "https://brasilianafotografica.bn.gov.br/brasiliana/bitstream/20.500.12156.1/14368/1/Alb%200261%20163.jpg", "nome": "imagem.jpg", "motivo": "sem preservacao"}
|
||||
{"handle": "20.500.12156.1/14293", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14295", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14357", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14357", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14365", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14365", "url": null, "nome": "extra_seq3.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14365", "url": null, "nome": "extra_seq4.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14365", "url": null, "nome": "extra_seq5.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14366", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14367", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14434", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14434", "url": null, "nome": "extra_seq3.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14434", "url": null, "nome": "extra_seq4.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14434", "url": null, "nome": "extra_seq5.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14437", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14437", "url": null, "nome": "extra_seq3.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14514", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14515", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14515", "url": null, "nome": "extra_seq3.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14516", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14518", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14518", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14518", "url": null, "nome": "extra_seq4.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14519", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14520", "url": null, "nome": "extra_seq1.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14520", "url": null, "nome": "extra_seq3.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14520", "url": null, "nome": "extra_seq4.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/14521", "url": null, "nome": "extra_seq2.jpg", "motivo": "extra apagado (tiff)"}
|
||||
{"handle": "20.500.12156.1/2384", "url": null, "nome": "001AAN014011.jpg.zip", "motivo": "extra apagado (zip)"}
|
||||
File diff suppressed because one or more lines are too long
@@ -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}`);
|
||||
})();
|
||||
@@ -0,0 +1,62 @@
|
||||
// reprojeta-meta-mapa.mjs — reescreve SÓ o `meta` de cada item do
|
||||
// web/atlas/atlas-map.json a partir do pipeline/catalogo.jsonl, sem re-renderizar
|
||||
// os atlas .webp (posições, cores e recortes das peças ficam intactos).
|
||||
//
|
||||
// Use quando as regras de metadados do web/build-atlas.js mudarem e não valer a
|
||||
// pena reconstruir 47 MB de imagem. As regras aqui têm de espelhar as de
|
||||
// summarize()/pickAll() lá — em 2026-09-12 as duas foram escritas juntas.
|
||||
//
|
||||
// node pipeline/reprojeta-meta-mapa.mjs (da raiz do projeto)
|
||||
import fs from 'node:fs';
|
||||
import readline from 'node:readline';
|
||||
|
||||
const MAP = 'web/atlas/atlas-map.json';
|
||||
const pickAll = (item, field, noUrls) => {
|
||||
const out = [];
|
||||
for (const row of item.full_table || []) {
|
||||
if (row.field !== field) continue;
|
||||
const v = String(row.value ?? '').trim();
|
||||
if (!v || out.includes(v)) continue;
|
||||
if (noUrls && /^https?:\/\//i.test(v)) continue;
|
||||
out.push(v);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
const some = (i, f, noUrls) => { const v = pickAll(i, f, noUrls); return v.length ? v : undefined; };
|
||||
const one = (i, f) => pickAll(i, f)[0] || undefined;
|
||||
|
||||
const d = JSON.parse(fs.readFileSync(MAP, 'utf8'));
|
||||
const wanted = new Set(Object.keys(d.map));
|
||||
const extra = new Map();
|
||||
|
||||
const rl = readline.createInterface({ input: fs.createReadStream('pipeline/catalogo.jsonl'), crlfDelay: Infinity });
|
||||
for await (const line of rl) {
|
||||
if (!line.trim()) continue;
|
||||
const o = JSON.parse(line);
|
||||
if (!wanted.has(o.handle)) continue;
|
||||
extra.set(o.handle, {
|
||||
subject: some(o, 'dc.subject'),
|
||||
description: some(o, 'dc.description'),
|
||||
coverage: some(o, 'dc.coverage'),
|
||||
source: one(o, 'dc.source'),
|
||||
publisher: one(o, 'dc.publisher'),
|
||||
relation: some(o, 'dc.relation', true),
|
||||
identifier: some(o, 'dc.identifier', true),
|
||||
});
|
||||
}
|
||||
|
||||
let enriched = 0, missing = 0;
|
||||
for (const [h, e] of Object.entries(d.map)) {
|
||||
const add = extra.get(h);
|
||||
if (!add) { missing++; continue; }
|
||||
let touched = false;
|
||||
for (const [k, v] of Object.entries(add)) if (v !== undefined) { e.meta[k] = v; touched = true; }
|
||||
// deriváveis da chave: não repetir (ver build-atlas.js)
|
||||
delete e.meta.handle;
|
||||
delete e.meta.source_url;
|
||||
if (touched) enriched++;
|
||||
}
|
||||
const out = JSON.stringify(d);
|
||||
fs.writeFileSync(MAP, out);
|
||||
console.log(`itens: ${wanted.size} | enriquecidos: ${enriched} | sem registro: ${missing}`);
|
||||
console.log(`atlas-map.json: ${(out.length / 1048576).toFixed(2)} MB`);
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Drena a fila de ~/Downloads pro S3 com o subidor-paralelo.py e depois arruma
|
||||
# o que o subidor.py antigo deixou: duplicatas "(N)" identicas e manifests com
|
||||
# linha de derivado. Log em pipeline/subidor-paralelo.log.
|
||||
# O caffeinate e amarrado a este script: segura o Mac acordado so enquanto roda.
|
||||
set -u
|
||||
cd "$(dirname "$0")"
|
||||
set -a; . ./.s3env; set +a
|
||||
exec >> subidor-paralelo.log 2>&1
|
||||
caffeinate -is -w $$ &
|
||||
echo "===== $(date -u +%FT%TZ) inicio (pid $$)"
|
||||
python3 -u subidor-paralelo.py --catalogo catalogo.jsonl --threads 6 --lote 400 || echo "!!! drenagem saiu com erro $?"
|
||||
echo "===== $(date -u +%FT%TZ) limpeza de duplicatas"
|
||||
python3 -u subidor-paralelo.py --catalogo catalogo.jsonl --limpar-duplicatas || echo "!!! limpeza saiu com erro $?"
|
||||
echo "===== $(date -u +%FT%TZ) regravando todos os manifests"
|
||||
python3 -u subidor-paralelo.py --catalogo catalogo.jsonl --so-manifestos --threads 6 || echo "!!! manifests saiu com erro $?"
|
||||
echo "===== $(date -u +%FT%TZ) FIM"
|
||||
@@ -0,0 +1,8 @@
|
||||
20.500.12156.1_7802
|
||||
20.500.12156.1_7814
|
||||
20.500.12156.1_7817
|
||||
20.500.12156.1_7848
|
||||
20.500.12156.1_7852
|
||||
20.500.12156.1_7861
|
||||
20.500.12156.1_7875
|
||||
20.500.12156.1_8075
|
||||
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
subidor-paralelo.py -- substitui o subidor.py para drenar a fila de ~/Downloads.
|
||||
|
||||
Por que existe (medido 2026-09-12): o subidor.py sobe um arquivo por vez e, a
|
||||
cada arquivo, espera 1,5 s de "estabilidade" e dispara tres `aws s3 cp`
|
||||
(arquivo, metadata.json, manifest). Cada um paga a partida do aws-cli: ~5 s por
|
||||
arquivo, seja thumbnail de 74 KB ou preservacao de 40 MB. Resultado: ~1,3 GB/h
|
||||
num link de 69 Mbps (~31 GB/h). O gargalo era o processo, nao o link.
|
||||
|
||||
O que muda:
|
||||
- upload em paralelo via boto3 (um cliente, N threads, sem partida de CLI),
|
||||
conferindo o tamanho no bucket antes de apagar o local
|
||||
- metadata.json + manifest-sha256.txt uma vez por item ao fim de cada lote
|
||||
- copia "(N)" do Chrome identica (mesmo handle, tipo, sha256 e nome-base de
|
||||
algo ja enviado) NAO sobe (achado 2.20)
|
||||
- arquivo que nao e imagem vai pra pipeline/rejeitados/ e ganha linha na
|
||||
tabela `rejeitados`, em vez de ser apagado sem rastro (achado 2.19)
|
||||
- manifest do item deixa de fora `derivado-thumb256`: esses objetos moram em
|
||||
derivados/thumb256/, nao dentro de itens/<handle>/
|
||||
|
||||
O que NAO muda: chaves no bucket, ledger `enviados`, validacao por magic bytes,
|
||||
conteudo de metadata.json.
|
||||
|
||||
Uso:
|
||||
set -a; . .s3env; set +a
|
||||
python3 subidor-paralelo.py --catalogo catalogo.jsonl # drena a fila
|
||||
python3 subidor-paralelo.py --limpar-duplicatas # apaga "(N)" identicas (bucket + ledger)
|
||||
python3 subidor-paralelo.py --catalogo catalogo.jsonl --so-manifestos # regrava metadata+manifest de todo item
|
||||
"""
|
||||
import argparse, hashlib, json, mimetypes, os, re, shutil, sqlite3, sys, time
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from pathlib import Path
|
||||
|
||||
import boto3
|
||||
from boto3.s3.transfer import TransferConfig
|
||||
from botocore.config import Config
|
||||
|
||||
S3_ENDPOINT = os.environ.get("S3_ENDPOINT", "https://hel1.your-objectstorage.com")
|
||||
BUCKET = os.environ.get("ARCHIVE_BUCKET", "brasiliana")
|
||||
PREFIXO = "BRAS__"
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
DB = ROOT / "subidor.sqlite"
|
||||
REJ_DIR = ROOT / "rejeitados"
|
||||
SUFIXO_N = re.compile(r"^(.*) \((\d+)\)(\.[^.]+)$")
|
||||
TIPOS_DO_ITEM = ("preservacao", "thumbnail", "legado") # o que mora em itens/<handle>/
|
||||
TRANSF = TransferConfig(multipart_threshold=64 * 2**20, multipart_chunksize=16 * 2**20,
|
||||
max_concurrency=4)
|
||||
|
||||
|
||||
def agora():
|
||||
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
|
||||
|
||||
def log(msg):
|
||||
print(f"{time.strftime('%H:%M:%S')} {msg}", flush=True)
|
||||
|
||||
|
||||
def base(nome):
|
||||
m = SUFIXO_N.match(nome)
|
||||
return m.group(1) + m.group(3) if m else nome
|
||||
|
||||
|
||||
def cliente(threads):
|
||||
return boto3.client(
|
||||
"s3", endpoint_url=S3_ENDPOINT,
|
||||
config=Config(max_pool_connections=threads * 5 + 8,
|
||||
retries={"max_attempts": 8, "mode": "adaptive"},
|
||||
request_checksum_calculation="when_required",
|
||||
response_checksum_validation="when_required"))
|
||||
|
||||
|
||||
def db():
|
||||
con = sqlite3.connect(DB, timeout=60)
|
||||
con.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS descartados (
|
||||
handle TEXT, tipo TEXT, nome TEXT, bytes INTEGER, sha256 TEXT,
|
||||
chave_no_bucket TEXT, quando TEXT, PRIMARY KEY (handle, tipo, nome, sha256));
|
||||
CREATE TABLE IF NOT EXISTS rejeitados (
|
||||
handle TEXT, tipo TEXT, nome TEXT, bytes INTEGER, sha256 TEXT,
|
||||
cabecalho TEXT, quando TEXT, PRIMARY KEY (handle, tipo, nome, sha256));""")
|
||||
return con
|
||||
|
||||
|
||||
def carrega_catalogo(caminho):
|
||||
d = {}
|
||||
if caminho:
|
||||
for linha in open(caminho, encoding="utf-8"):
|
||||
if linha.strip():
|
||||
r = json.loads(linha)
|
||||
d[r["handle"]] = r
|
||||
return d
|
||||
|
||||
|
||||
def interpreta(p):
|
||||
"""BRAS__20.500.12156.1_10377__[THUMB__]nome -> (handle, pasta, tipo, nome)"""
|
||||
resto = p.name[len(PREFIXO):]
|
||||
if "__" not in resto:
|
||||
return None
|
||||
pasta, nome = resto.split("__", 1)
|
||||
m = re.fullmatch(r"(.+)_(\d+)", pasta)
|
||||
if not m:
|
||||
return None
|
||||
tipo = "preservacao"
|
||||
if nome.startswith("THUMB__"):
|
||||
nome, tipo = nome[len("THUMB__"):], "thumbnail"
|
||||
return f"{m.group(1)}/{m.group(2)}", pasta, tipo, nome
|
||||
|
||||
|
||||
# assinaturas de imagem aceitas. Achado 2026-09-12: parte das "preservacao" com
|
||||
# extensao .jpg e TIFF de verdade (II*\0 / MM\0*, ~8 MB, faixa 145xx); a regra
|
||||
# antiga (so JPEG/PNG) do subidor.py apagava esses arquivos como se fossem o
|
||||
# bitstream de licenca. O que se quer barrar e texto, nao formato de imagem.
|
||||
ASSINATURAS = (
|
||||
(b"\xff\xd8", "jpeg"), (b"\x89PNG\r\n\x1a\n", "png"),
|
||||
(b"II*\x00", "tiff"), (b"MM\x00*", "tiff"), (b"GIF87a", "gif"), (b"GIF89a", "gif"),
|
||||
(b"\x00\x00\x00\x0cjP ", "jp2"), (b"\xff\x4f\xff\x51", "j2k"), (b"BM", "bmp"),
|
||||
)
|
||||
|
||||
|
||||
def formato(cab):
|
||||
for sig, nome in ASSINATURAS:
|
||||
if cab.startswith(sig):
|
||||
return nome
|
||||
if cab[:4] == b"RIFF" and cab[8:12] == b"WEBP":
|
||||
return "webp"
|
||||
return None
|
||||
|
||||
|
||||
def examina(p):
|
||||
h, cab = hashlib.sha256(), None
|
||||
with p.open("rb") as fh:
|
||||
for b in iter(lambda: fh.read(1 << 20), b""):
|
||||
if cab is None:
|
||||
cab = b[:20]
|
||||
h.update(b)
|
||||
cab = cab or b""
|
||||
return h.hexdigest(), p.stat().st_size, formato(cab) is not None, cab
|
||||
|
||||
|
||||
MIME = {"jpeg": "image/jpeg", "png": "image/png", "tiff": "image/tiff", "gif": "image/gif",
|
||||
"jp2": "image/jp2", "j2k": "image/jp2", "bmp": "image/bmp", "webp": "image/webp"}
|
||||
|
||||
|
||||
def tipo_mime(p):
|
||||
"""pelo conteudo, nao pela extensao: TIFF chega com nome .jpg"""
|
||||
with open(p, "rb") as fh:
|
||||
f = formato(fh.read(20))
|
||||
return MIME.get(f) or mimetypes.guess_type(str(p))[0] or "binary/octet-stream"
|
||||
|
||||
|
||||
def sobe_arquivo(s3, p, key, tam):
|
||||
s3.upload_file(str(p), BUCKET, key, Config=TRANSF,
|
||||
ExtraArgs={"ContentType": tipo_mime(p)})
|
||||
got = s3.head_object(Bucket=BUCKET, Key=key)["ContentLength"]
|
||||
if got != tam:
|
||||
raise RuntimeError(f"tamanho no bucket {got} != local {tam}")
|
||||
|
||||
|
||||
def confere_existente(s3, key, tam):
|
||||
"""True se o objeto ja esta no bucket com o mesmo tamanho."""
|
||||
try:
|
||||
return s3.head_object(Bucket=BUCKET, Key=key)["ContentLength"] == tam
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def sobe_bytes(s3, dados, key, ctype):
|
||||
s3.put_object(Bucket=BUCKET, Key=key, Body=dados, ContentType=ctype)
|
||||
|
||||
|
||||
def refaz_metadados(con, s3, cat, handles, threads):
|
||||
"""metadata.json + manifest-sha256.txt, mesmo formato do subidor.py."""
|
||||
trabalhos = []
|
||||
for h in sorted(handles):
|
||||
rec = cat.get(h)
|
||||
if not rec:
|
||||
continue
|
||||
mj = json.dumps(rec, ensure_ascii=False, indent=2).encode("utf-8")
|
||||
linhas = [f"{sha} {tipo}/{nome}" for nome, sha, tipo in con.execute(
|
||||
f"SELECT nome, sha256, tipo FROM enviados WHERE handle=? AND tipo IN "
|
||||
f"({','.join('?' * len(TIPOS_DO_ITEM))}) ORDER BY tipo, nome", (h, *TIPOS_DO_ITEM))]
|
||||
linhas.append(f"{hashlib.sha256(mj).hexdigest()} metadata.json")
|
||||
mf = ("\n".join(linhas) + "\n").encode("utf-8")
|
||||
pasta = h.replace("/", "_")
|
||||
trabalhos.append((mj, f"itens/{pasta}/metadata.json", "application/json"))
|
||||
trabalhos.append((mf, f"itens/{pasta}/manifest-sha256.txt", "text/plain"))
|
||||
falhas = 0
|
||||
with ThreadPoolExecutor(threads) as ex:
|
||||
for f in as_completed([ex.submit(sobe_bytes, s3, *t) for t in trabalhos]):
|
||||
try:
|
||||
f.result()
|
||||
except Exception as e:
|
||||
falhas += 1
|
||||
log(f"FALHA metadado: {e}")
|
||||
return len(trabalhos) // 2, falhas
|
||||
|
||||
|
||||
def drena(con, s3, cat, pasta, threads, lote, max_lotes=0):
|
||||
REJ_DIR.mkdir(exist_ok=True)
|
||||
env = dup = rej = 0
|
||||
bytes_env, t0 = 0, time.time()
|
||||
falhou = set()
|
||||
n_lotes = 0
|
||||
while True:
|
||||
if max_lotes and n_lotes >= max_lotes:
|
||||
log(f"parando depois de {n_lotes} lote(s) (--max-lotes)")
|
||||
break
|
||||
n_lotes += 1
|
||||
corte = time.time() - 10
|
||||
fila = []
|
||||
for p in pasta.glob(PREFIXO + "*"):
|
||||
if (not p.is_file() or p.suffix == ".crdownload" or p.name in falhou
|
||||
or p.stat().st_mtime > corte or Path(str(p) + ".crdownload").exists()):
|
||||
continue
|
||||
info = interpreta(p)
|
||||
if info:
|
||||
fila.append((p, *info))
|
||||
else:
|
||||
log(f"ignorado (nome inesperado): {p.name}")
|
||||
falhou.add(p.name)
|
||||
if not fila:
|
||||
break
|
||||
# nome sem "(N)" primeiro: entre duas copias identicas, fica o nome original
|
||||
fila.sort(key=lambda a: (SUFIXO_N.match(a[4]) is not None, a[0].name))
|
||||
pedaco = fila[:lote]
|
||||
|
||||
with ThreadPoolExecutor(threads) as ex:
|
||||
exames = list(zip(pedaco, ex.map(lambda a: examina(a[0]), pedaco)))
|
||||
|
||||
no_ledger = defaultdict(dict) # (handle, tipo, sha) -> {nome-base: nome ja enviado}
|
||||
for h in {a[1] for a, _ in exames}:
|
||||
for tipo, sha, nome in con.execute(
|
||||
"SELECT tipo, sha256, nome FROM enviados WHERE handle=?", (h,)):
|
||||
no_ledger[(h, tipo, sha)].setdefault(base(nome), nome)
|
||||
neste_lote = set() # (handle, tipo, sha, nome-base) que ainda vai subir agora
|
||||
|
||||
subir, conferir = [], []
|
||||
for (p, handle, pasta_h, tipo, nome), (sha, tam, eh_img, cab) in exames:
|
||||
if not eh_img:
|
||||
shutil.move(str(p), REJ_DIR / p.name)
|
||||
con.execute("INSERT OR REPLACE INTO rejeitados VALUES (?,?,?,?,?,?,?)",
|
||||
(handle, tipo, nome, tam, sha, cab.hex(), agora()))
|
||||
rej += 1
|
||||
log(f"REJEITADO (nao e imagem, {tam} bytes, inicio {cab[:12]!r}): {p.name} -> rejeitados/")
|
||||
continue
|
||||
k = (handle, tipo, sha)
|
||||
existente = no_ledger[k].get(base(nome))
|
||||
if existente:
|
||||
# so apaga o local depois do HEAD confirmar o objeto no bucket
|
||||
conferir.append((p, handle, pasta_h, tipo, nome, sha, tam, existente))
|
||||
continue
|
||||
if k + (base(nome),) in neste_lote:
|
||||
continue # copia identica sobe neste lote; esta fica pro proximo
|
||||
neste_lote.add(k + (base(nome),))
|
||||
subir.append((p, handle, pasta_h, tipo, nome, sha, tam))
|
||||
con.commit()
|
||||
|
||||
tocados = set()
|
||||
with ThreadPoolExecutor(threads) as ex:
|
||||
futs = {ex.submit(sobe_arquivo, s3, a[0], f"itens/{a[2]}/{a[3]}/{a[4]}", a[6]): a
|
||||
for a in subir}
|
||||
for f in as_completed(futs):
|
||||
p, handle, pasta_h, tipo, nome, sha, tam = futs[f]
|
||||
try:
|
||||
f.result()
|
||||
except Exception as e:
|
||||
falhou.add(p.name)
|
||||
log(f"FALHA no upload (mantido em disco): {p.name}: {e}")
|
||||
continue
|
||||
con.execute("INSERT OR REPLACE INTO enviados (handle,nome,bytes,sha256,quando,tipo) "
|
||||
"VALUES (?,?,?,?,?,?)", (handle, nome, tam, sha, agora(), tipo))
|
||||
con.commit()
|
||||
p.unlink()
|
||||
tocados.add(handle)
|
||||
env += 1
|
||||
bytes_env += tam
|
||||
with ThreadPoolExecutor(threads) as ex:
|
||||
futs = {ex.submit(confere_existente, s3, f"itens/{a[2]}/{a[3]}/{a[7]}", a[6]): a
|
||||
for a in conferir}
|
||||
for f in as_completed(futs):
|
||||
p, handle, pasta_h, tipo, nome, sha, tam, existente = futs[f]
|
||||
chave = f"itens/{pasta_h}/{tipo}/{existente}"
|
||||
if f.result():
|
||||
con.execute("INSERT OR REPLACE INTO descartados VALUES (?,?,?,?,?,?,?)",
|
||||
(handle, tipo, nome, tam, sha, chave, agora()))
|
||||
con.commit()
|
||||
p.unlink()
|
||||
dup += 1
|
||||
else:
|
||||
# ledger dizia que estava la, o bucket discorda: sobe de novo
|
||||
log(f"ledger x bucket divergem, reenviando: {chave}")
|
||||
try:
|
||||
sobe_arquivo(s3, p, chave, tam)
|
||||
except Exception as e:
|
||||
falhou.add(p.name)
|
||||
log(f"FALHA no reenvio (mantido em disco): {p.name}: {e}")
|
||||
continue
|
||||
con.execute("INSERT OR REPLACE INTO enviados (handle,nome,bytes,sha256,quando,tipo) "
|
||||
"VALUES (?,?,?,?,?,?)", (handle, existente, tam, sha, agora(), tipo))
|
||||
con.commit()
|
||||
p.unlink()
|
||||
tocados.add(handle)
|
||||
env += 1
|
||||
bytes_env += tam
|
||||
n_meta, f_meta = refaz_metadados(con, s3, cat, tocados, threads)
|
||||
|
||||
horas = (time.time() - t0) / 3600
|
||||
log(f"lote ok: {len(tocados)} itens | total {env} arq, {bytes_env/2**30:.1f} GB "
|
||||
f"({bytes_env/2**30/horas:.1f} GB/h) | dup {dup} | rej {rej} | falha {len(falhou)} "
|
||||
f"| meta {n_meta} (falha {f_meta}) | fila ~{len(fila) - len(pedaco)}")
|
||||
log(f"FIM da fila: {env} enviados, {bytes_env/2**30:.1f} GB, {dup} duplicatas descartadas, "
|
||||
f"{rej} rejeitados, {len(falhou)} falhas mantidas em disco")
|
||||
for n in sorted(falhou):
|
||||
log(f" pendente: {n}")
|
||||
|
||||
|
||||
def limpa_duplicatas(con, s3):
|
||||
grupos = defaultdict(list)
|
||||
for h, tipo, nome, sha in con.execute(
|
||||
"SELECT handle, tipo, nome, sha256 FROM enviados WHERE tipo IN ('preservacao','thumbnail')"):
|
||||
grupos[(h, tipo, sha)].append(nome)
|
||||
apagar = []
|
||||
for (h, tipo, sha), nomes in grupos.items():
|
||||
if len(nomes) < 2:
|
||||
continue
|
||||
for b, iguais in _por_base(nomes).items():
|
||||
if len(iguais) < 2:
|
||||
continue
|
||||
fica = sorted(iguais, key=lambda n: (SUFIXO_N.match(n) is not None, n))[0]
|
||||
apagar += [(h, tipo, n, fica) for n in iguais if n != fica]
|
||||
log(f"duplicatas identicas no ledger: {len(apagar)}")
|
||||
feitos, tocados = 0, set()
|
||||
|
||||
def um(a):
|
||||
h, tipo, n, fica = a
|
||||
pasta = h.replace("/", "_")
|
||||
k_dup, k_fica = f"itens/{pasta}/{tipo}/{n}", f"itens/{pasta}/{tipo}/{fica}"
|
||||
t1 = s3.head_object(Bucket=BUCKET, Key=k_dup)["ContentLength"]
|
||||
t2 = s3.head_object(Bucket=BUCKET, Key=k_fica)["ContentLength"]
|
||||
if t1 != t2:
|
||||
raise RuntimeError(f"tamanhos divergem no bucket: {k_dup}")
|
||||
s3.delete_object(Bucket=BUCKET, Key=k_dup)
|
||||
return a
|
||||
|
||||
with ThreadPoolExecutor(16) as ex:
|
||||
for f in as_completed([ex.submit(um, a) for a in apagar]):
|
||||
try:
|
||||
h, tipo, n, _ = f.result()
|
||||
except Exception as e:
|
||||
log(f" nao apagado: {e}")
|
||||
continue
|
||||
con.execute("DELETE FROM enviados WHERE handle=? AND tipo=? AND nome=?", (h, tipo, n))
|
||||
tocados.add(h)
|
||||
feitos += 1
|
||||
con.commit()
|
||||
log(f"duplicatas apagadas: {feitos}")
|
||||
return tocados
|
||||
|
||||
|
||||
def _por_base(nomes):
|
||||
d = defaultdict(list)
|
||||
for n in nomes:
|
||||
d[base(n)].append(n)
|
||||
return d
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--downloads", default=str(Path.home() / "Downloads"))
|
||||
ap.add_argument("--catalogo")
|
||||
# 6 e nao 16: com muitas threads o OpenSSL do macOS estoura na verificacao
|
||||
# de certificado (SIGSEGV em X509_STORE_CTX_get1_issuer, 13/09) -- corrida
|
||||
# dentro do libcrypto, nao do nosso codigo. 6 threads ja dao ~19 GB/h.
|
||||
ap.add_argument("--threads", type=int, default=6)
|
||||
ap.add_argument("--lote", type=int, default=400)
|
||||
ap.add_argument("--max-lotes", type=int, default=0, help="0 = ate esvaziar a fila")
|
||||
ap.add_argument("--limpar-duplicatas", action="store_true")
|
||||
ap.add_argument("--so-manifestos", action="store_true")
|
||||
args = ap.parse_args()
|
||||
if not os.environ.get("AWS_ACCESS_KEY_ID"):
|
||||
sys.exit("credenciais S3 ausentes. Rode: set -a; . .s3env; set +a")
|
||||
|
||||
con, s3 = db(), cliente(args.threads)
|
||||
cat = carrega_catalogo(args.catalogo)
|
||||
if args.limpar_duplicatas:
|
||||
tocados = limpa_duplicatas(con, s3)
|
||||
if cat and tocados:
|
||||
log("regravando manifest dos itens tocados: %d (falha %d)" %
|
||||
refaz_metadados(con, s3, cat, tocados, args.threads))
|
||||
elif args.so_manifestos:
|
||||
if not cat:
|
||||
sys.exit("--so-manifestos precisa de --catalogo")
|
||||
hs = {r[0] for r in con.execute("SELECT DISTINCT handle FROM enviados WHERE tipo IN (?,?,?)",
|
||||
TIPOS_DO_ITEM)}
|
||||
log(f"regravando metadata+manifest de {len(hs)} itens")
|
||||
log("feito: %d itens (falha %d)" % refaz_metadados(con, s3, cat, hs, args.threads))
|
||||
else:
|
||||
log(f"drenando {args.downloads} -> s3://{BUCKET}/ ({args.threads} threads, lotes de {args.lote})")
|
||||
drena(con, s3, cat, Path(args.downloads), args.threads, args.lote, args.max_lotes)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Trilha B, segunda metade: leva da pasta Downloads para o S3 e apaga.
|
||||
|
||||
O baixador-navegador.js despeja os arquivos em Downloads com o handle no
|
||||
proprio nome (BRAS__20.500.12156.1_10377__arquivo.jpg). Este script recolhe,
|
||||
confere SHA-256, sobe pro bucket e APAGA na hora -- o disco nunca acumula.
|
||||
|
||||
Rode em paralelo com o navegador, em modo --vigiar:
|
||||
|
||||
set -a; . .s3env; set +a
|
||||
python3 subidor.py --vigiar --catalogo catalogo.jsonl
|
||||
|
||||
Uma passada so, sem vigiar:
|
||||
python3 subidor.py --catalogo catalogo.jsonl
|
||||
"""
|
||||
import argparse, hashlib, json, os, sqlite3, subprocess, sys, time
|
||||
from pathlib import Path
|
||||
|
||||
S3_ENDPOINT = os.environ.get("S3_ENDPOINT", "https://hel1.your-objectstorage.com")
|
||||
BUCKET = os.environ.get("ARCHIVE_BUCKET", "brasiliana")
|
||||
PREFIXO = "BRAS__"
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
DB = ROOT / "subidor.sqlite"
|
||||
|
||||
|
||||
def db():
|
||||
con = sqlite3.connect(DB)
|
||||
con.executescript("""
|
||||
CREATE TABLE IF NOT EXISTS enviados (
|
||||
handle TEXT, nome TEXT, bytes INTEGER, sha256 TEXT, quando TEXT,
|
||||
PRIMARY KEY (handle, nome));""")
|
||||
cols = [r[1] for r in con.execute("PRAGMA table_info(enviados)")]
|
||||
if "tipo" not in cols:
|
||||
# tipo entra na migracao, mas a PK antiga (handle,nome) colidiria
|
||||
# entre preservacao/thumbnail com o mesmo nome de arquivo -- refaz
|
||||
# a tabela com PK composta, preservando as linhas ja existentes
|
||||
# (todas eram 'preservacao', de antes do thumbnail existir).
|
||||
con.executescript("""
|
||||
ALTER TABLE enviados RENAME TO enviados_old;
|
||||
CREATE TABLE enviados (
|
||||
handle TEXT, nome TEXT, bytes INTEGER, sha256 TEXT, quando TEXT,
|
||||
tipo TEXT DEFAULT 'preservacao',
|
||||
PRIMARY KEY (handle, tipo, nome));
|
||||
INSERT INTO enviados (handle,nome,bytes,sha256,quando,tipo)
|
||||
SELECT handle,nome,bytes,sha256,quando,'preservacao' FROM enviados_old;
|
||||
DROP TABLE enviados_old;
|
||||
""")
|
||||
con.commit()
|
||||
return con
|
||||
|
||||
|
||||
def s3_put(local, key):
|
||||
return subprocess.run(
|
||||
["aws", "--endpoint-url", S3_ENDPOINT, "s3", "cp", str(local),
|
||||
f"s3://{BUCKET}/{key}", "--only-show-errors"],
|
||||
capture_output=True, text=True).returncode == 0
|
||||
|
||||
|
||||
def sha256(p):
|
||||
h = hashlib.sha256()
|
||||
with p.open("rb") as fh:
|
||||
for b in iter(lambda: fh.read(1 << 20), b""):
|
||||
h.update(b)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def estavel(p):
|
||||
"""Chrome ainda escrevendo? Compara o tamanho com um intervalo curto."""
|
||||
try:
|
||||
a = p.stat().st_size
|
||||
time.sleep(1.5)
|
||||
return a == p.stat().st_size and a > 0
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def carrega_catalogo(caminho):
|
||||
if not caminho:
|
||||
return {}
|
||||
d = {}
|
||||
for linha in open(caminho, encoding="utf-8"):
|
||||
if linha.strip():
|
||||
r = json.loads(linha)
|
||||
d[r["handle"]] = r
|
||||
return d
|
||||
|
||||
|
||||
def escreve_metadados(con, handle, cat):
|
||||
"""metadata.json + manifest-sha256.txt do item."""
|
||||
rec = cat.get(handle)
|
||||
if not rec:
|
||||
return
|
||||
pasta = handle.replace("/", "_")
|
||||
# nome com PID: baixador.py roda na mesma pasta e pode escrever no
|
||||
# mesmo .tmp ao mesmo tempo -- nome fixo faz um processo apagar o
|
||||
# arquivo que o outro acabou de criar (achado 2026-09-10, crash real).
|
||||
tmp = ROOT / ".tmp"; tmp.mkdir(exist_ok=True)
|
||||
mj = tmp / f"metadata.{os.getpid()}.json"
|
||||
mj.write_text(json.dumps(rec, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
s3_put(mj, f"itens/{pasta}/metadata.json")
|
||||
linhas = [f"{sha} {tipo}/{nome}" for nome, sha, tipo in
|
||||
con.execute("SELECT nome,sha256,tipo FROM enviados WHERE handle=?", (handle,))]
|
||||
linhas.append(f"{sha256(mj)} metadata.json")
|
||||
mf = tmp / f"manifest-sha256.{os.getpid()}.txt"
|
||||
mf.write_text("\n".join(linhas) + "\n", encoding="utf-8")
|
||||
s3_put(mf, f"itens/{pasta}/manifest-sha256.txt")
|
||||
mj.unlink(missing_ok=True); mf.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def uma_passada(con, pasta_downloads, cat, verboso=True):
|
||||
n = 0
|
||||
for p in sorted(pasta_downloads.glob(PREFIXO + "*")):
|
||||
if p.suffix == ".crdownload" or not p.is_file():
|
||||
continue
|
||||
if not estavel(p):
|
||||
continue
|
||||
resto = p.name[len(PREFIXO):]
|
||||
if "__" not in resto:
|
||||
print(f" ignorado (nome inesperado): {p.name}")
|
||||
continue
|
||||
pasta_handle, nome = resto.split("__", 1)
|
||||
handle = pasta_handle.replace("_", "/", 1) if "/" not in pasta_handle else pasta_handle
|
||||
# 20.500.12156.1_10377 -> 20.500.12156.1/10377
|
||||
if "_" in pasta_handle:
|
||||
base, num = pasta_handle.rsplit("_", 1)
|
||||
handle = f"{base}/{num}"
|
||||
|
||||
# THUMB__ no nome roteia pra subpasta thumbnail/ em vez de preservacao/
|
||||
eh_thumb = nome.startswith("THUMB__")
|
||||
if eh_thumb:
|
||||
nome = nome[len("THUMB__"):]
|
||||
subpasta = "thumbnail" if eh_thumb else "preservacao"
|
||||
|
||||
# valida que e imagem de verdade antes de subir -- achado 2026-09-10:
|
||||
# o palpite de sequencia do achado 2.8 (seq+1 = thumbnail) as vezes
|
||||
# cai no bitstream de LICENCA do DSpace em vez da miniatura (62% dos
|
||||
# thumbs de alguns lotes!). Rejeita qualquer coisa que nao comece com
|
||||
# os magic bytes de JPEG/PNG -- mais barato que confiar no nome.
|
||||
with p.open("rb") as fh:
|
||||
cabecalho = fh.read(8)
|
||||
eh_jpeg = cabecalho[:2] == b"\xff\xd8"
|
||||
eh_png = cabecalho[:8] == b"\x89PNG\r\n\x1a\n"
|
||||
if not (eh_jpeg or eh_png):
|
||||
print(f" REJEITADO (nao e imagem, primeiros bytes: {cabecalho[:20]!r}): {p.name}", flush=True)
|
||||
p.unlink()
|
||||
continue
|
||||
|
||||
dig, tam = sha256(p), p.stat().st_size
|
||||
if s3_put(p, f"itens/{pasta_handle}/{subpasta}/{nome}"):
|
||||
con.execute("INSERT OR REPLACE INTO enviados VALUES (?,?,?,?,?,?)",
|
||||
(handle, nome, tam, dig,
|
||||
time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), subpasta))
|
||||
con.commit()
|
||||
escreve_metadados(con, handle, cat)
|
||||
p.unlink() # apaga SEMPRE que subiu
|
||||
n += 1
|
||||
if verboso:
|
||||
print(f" {handle:<24} {tam/1048576:6.1f} MB -> S3, local apagado", flush=True)
|
||||
else:
|
||||
print(f" FALHA no upload: {p.name} (mantido em disco)", flush=True)
|
||||
return n
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--downloads", default=str(Path.home() / "Downloads"))
|
||||
ap.add_argument("--catalogo")
|
||||
ap.add_argument("--vigiar", action="store_true")
|
||||
ap.add_argument("--intervalo", type=int, default=20)
|
||||
args = ap.parse_args()
|
||||
|
||||
if not os.environ.get("AWS_ACCESS_KEY_ID"):
|
||||
sys.exit("credenciais S3 ausentes. Rode: set -a; . .s3env; set +a")
|
||||
|
||||
con = db()
|
||||
pasta = Path(args.downloads)
|
||||
cat = carrega_catalogo(args.catalogo)
|
||||
print(f"vigiando {pasta} -> s3://{BUCKET}/ ({len(cat)} itens no catalogo)\n"
|
||||
if args.vigiar else f"passada unica em {pasta}\n")
|
||||
|
||||
total = 0
|
||||
try:
|
||||
while True:
|
||||
total += uma_passada(con, pasta, cat)
|
||||
if not args.vigiar:
|
||||
break
|
||||
time.sleep(args.intervalo)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
enviados, bytes_ = con.execute(
|
||||
"SELECT COUNT(*), COALESCE(SUM(bytes),0) FROM enviados").fetchone()
|
||||
print(f"\nnesta sessao: {total} | acumulado: {enviados} arquivos, {bytes_/1024**3:.2f} GB")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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); });
|
||||
Generated
+189
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"name": "tools",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"classic-level": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abstract-level": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-3.1.1.tgz",
|
||||
"integrity": "sha512-CW2gKbJFTuX1feMvOrvsVMmijAOgI9kg2Ie9Dq3gOcMt/dVVoVmqNlLcEUCT13NxHFMEajcUcVBIplbyDroDiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "^6.0.3",
|
||||
"is-buffer": "^2.0.5",
|
||||
"level-supports": "^6.2.0",
|
||||
"level-transcoder": "^1.0.1",
|
||||
"maybe-combine-errors": "^1.0.0",
|
||||
"module-error": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/classic-level": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/classic-level/-/classic-level-3.0.0.tgz",
|
||||
"integrity": "sha512-yGy8j8LjPbN0Bh3+ygmyYvrmskVita92pD/zCoalfcC9XxZj6iDtZTAnz+ot7GG8p9KLTG+MZ84tSA4AhkgVZQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abstract-level": "^3.1.0",
|
||||
"module-error": "^1.0.1",
|
||||
"napi-macros": "^2.2.2",
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
|
||||
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/level-supports": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/level-supports/-/level-supports-6.2.0.tgz",
|
||||
"integrity": "sha512-QNxVXP0IRnBmMsJIh+sb2kwNCYcKciQZJEt+L1hPCHrKNELllXhvrlClVHXBYZVT+a7aTSM6StgNXdAldoab3w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/level-transcoder": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz",
|
||||
"integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "^6.0.3",
|
||||
"module-error": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/maybe-combine-errors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/maybe-combine-errors/-/maybe-combine-errors-1.0.0.tgz",
|
||||
"integrity": "sha512-eefp6IduNPT6fVdwPp+1NgD0PML1NU5P6j1Mj5nz1nidX8/sWY7119WL8vTAHgqfsY74TzW0w1XPgdYEKkGZ5A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/module-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz",
|
||||
"integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/napi-macros": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz",
|
||||
"integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"classic-level": "^3.0.0"
|
||||
},
|
||||
"allowScripts": {
|
||||
"classic-level@3.0.0": true
|
||||
}
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# A cada 30 min: sobe pro S3 o que o navegador tiver deixado em ~/Downloads e
|
||||
# apaga o local do que subiu (o subidor-paralelo.py so apaga depois do HEAD
|
||||
# confirmar o objeto no bucket). Passada vazia nao faz nada.
|
||||
set -u
|
||||
cd "$(dirname "$0")"
|
||||
set -a; . ./.s3env; set +a
|
||||
exec >> subidor-paralelo.log 2>&1
|
||||
echo "===== $(date -u +%FT%TZ) vigia-30min iniciado (pid $$)"
|
||||
while true; do
|
||||
n=$(ls ~/Downloads 2>/dev/null | grep -c '^BRAS__' || true)
|
||||
if [ "${n:-0}" -gt 0 ]; then
|
||||
echo "----- $(date -u +%FT%TZ) vigia: $n arquivo(s) na fila"
|
||||
python3 -u subidor-paralelo.py --catalogo catalogo.jsonl --threads 6 --lote 400 || echo "!!! vigia: erro $?"
|
||||
fi
|
||||
sleep 1800
|
||||
done
|
||||
Reference in New Issue
Block a user