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,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")
|
||||
Reference in New Issue
Block a user