jung/tools/ia.py
Dmitry Kokorin 4420aef7c3 sec01 phase 2 (round 1): 17/55 items resolved
- new ✅: 03 (Структура и динамика психического, Когито), 10 (Эго и архетип, 2 изд.),
  13 (Лекции по юнговской типологии, b/509587), 29 (Юнговская карта души, Стайн Мюррей 2010)
- ❌ with evidence: 02 (CW7), 05 (Aion — 'Эйон' 0), 16 (Shadow and Self), 21 (Animus and Anima),
  38 (Woman in the Mirror), 47 (Way of All Women)
- flibusta.is OPDS sweep (tools/flib.py): 30+ author feeds checked; bonus books logged
  (Edinger x10, Hillman x8, Harding x4, Henderson x2, Douglas Cambridge Handbook, Jung-E Grail Legend)
- tools/ia.py (archive.org EN search/metadata/files); AGENTS.md sources 10+13 updated
2026-09-17 00:09:03 +03:00

78 lines
3.7 KiB
Python
Executable file

#!/usr/bin/env python3
"""archive.org — EN full-text search + metadata + file lists ("English libgen").
Usage:
tools/ia.py search "creator:(Carl Gustav Jung)" [rows] # advancedsearch, mediatype filtered
tools/ia.py meta <identifier> # /metadata/<id> — files, size, format
tools/ia.py title "Aion" # convenience: title:("...") AND mediatype:texts
Output (search): numFound + rows: identifier | title | year | downloads | restricted | mediatype.
Output (meta): item fields + list of files (name, format, size) — pick _pdf/_epub/_fb2/_djvu.
Notes:
- query syntax: Solr — creator:(...), title:(...), subject:(...), AND/OR/NOT; + = space.
- access-restricted-item=true → controlled digital lending (borrow, no direct download).
- creator: is loose (images/misattributions included) — add AND mediatype:texts in query
(the tool appends it for search/title unless the query already has 'mediatype').
- Politeness: 2s between calls. No key needed.
"""
import json, sys, time, urllib.parse, urllib.request
UA = "Mozilla/5.0 (research; contact: dmitry@kokorin.org)"
def fetch(url, tries=3):
last = None
for i in range(tries):
try:
req = urllib.request.Request(url, headers={"User-Agent": UA})
return urllib.request.urlopen(req, timeout=60).read().decode("utf-8", "replace")
except Exception as e:
last = e
time.sleep(3 + 3 * i)
raise SystemExit(f"fetch failed: {last}")
FL = "identifier,title,year,downloads,access-restricted-item,mediatype"
def main():
if len(sys.argv) < 3:
sys.exit(__doc__)
cmd, q = sys.argv[1], sys.argv[2]
if cmd == "search":
rows = int(sys.argv[3]) if len(sys.argv) > 3 else 20
if "mediatype" not in q:
q = q + " AND mediatype:texts"
url = "https://archive.org/advancedsearch.php?" + urllib.parse.urlencode(
{"q": q, "output": "json", "rows": rows, "fl[]": FL})
d = json.loads(fetch(url))
print("numFound:", d.get("response", {}).get("numFound"))
for x in d.get("response", {}).get("docs", []):
r = x.get("access-restricted-item", "")
print(f" {x.get('identifier')[:60]:60s} {x.get('year') or '?':>5} "
f"dl={x.get('downloads', 0):>7} {'RESTRICTED' if r == 'true' else 'open':10s} {x.get('title', '')[:60]}")
elif cmd == "title":
q = f'title:("{q}")'
rows = 20
if "mediatype" not in q:
q = q + " AND mediatype:texts"
url = "https://archive.org/advancedsearch.php?" + urllib.parse.urlencode(
{"q": q, "output": "json", "rows": rows, "fl[]": FL})
d = json.loads(fetch(url))
print("numFound:", d.get("response", {}).get("numFound"))
for x in d.get("response", {}).get("docs", []):
r = x.get("access-restricted-item", "")
print(f" {x.get('identifier')[:60]:60s} {x.get('year') or '?':>5} "
f"dl={x.get('downloads', 0):>7} {'RESTRICTED' if r == 'true' else 'open':10s} {x.get('title', '')[:60]}")
elif cmd == "meta":
d = json.loads(fetch(f"https://archive.org/metadata/{q}"))
meta = d.get("metadata", {})
print("title:", meta.get("title"), "| creator:", meta.get("creator"), "| year:", meta.get("year"))
print("restricted:", d.get("is_access_restricted"))
for f in d.get("files", []):
n, fmt, size = f.get("name"), f.get("format"), f.get("size", "?")
if fmt in ("Text PDF", "EPUB", "FB3", "DJVU", "Full Text OCR", "Mobi", "DAISY") or "pdf" in str(fmt):
print(f" {n[:70]:70s} {fmt:15s} {size}")
else:
sys.exit("cmd: search|title|meta")
if __name__ == "__main__":
main()