Metadata never fails a drop: deterministic title/author detection (metadata, first-page text, OCR of scanned first pages, file name), sources recorded in book.json; decrypt scratch copy before OCR
This commit is contained in:
+13
-33
@@ -27,6 +27,7 @@ import acx_check
|
||||
import assemble
|
||||
import bookio
|
||||
import ingest
|
||||
import meta
|
||||
import settings
|
||||
import voice
|
||||
|
||||
@@ -59,12 +60,7 @@ def sidecar(pdf):
|
||||
|
||||
|
||||
def guess_from_filename(pdf):
|
||||
"""'The Title - by Glenn Clark (Extra).pdf' or 'Title by Glenn Clark.pdf' -> (title, author), else (None, None)."""
|
||||
stem = os.path.splitext(os.path.basename(pdf))[0]
|
||||
m = re.match(r"^(?P<title>.+?)[\s\-–—]+by\s+(?P<author>[^()]+?)\s*(\(.*\))?\s*$", stem, re.I)
|
||||
if m:
|
||||
return m.group("title").strip(" -–—"), m.group("author").strip()
|
||||
return None, None
|
||||
return meta.guess_from_filename(pdf)
|
||||
|
||||
|
||||
def voice_fingerprint(cfg, script_path):
|
||||
@@ -112,24 +108,9 @@ def step(name):
|
||||
print("\n=== %s ===" % name, flush=True)
|
||||
|
||||
|
||||
def resolve_meta(pdf, title=None, author=None):
|
||||
"""(title, author, note). Order: explicit arguments, the .json next to the PDF, the PDF itself, the file name.
|
||||
Raises Failed, telling the operator what to add, if none of them works."""
|
||||
meta = sidecar(pdf)
|
||||
title, author = title or meta.get("title"), author or meta.get("author")
|
||||
try:
|
||||
title, author = ingest.detect(pdf, title, author)
|
||||
except Exception as e:
|
||||
raise Failed("could not read the PDF: %s" % e)
|
||||
note = None
|
||||
if not (title and author):
|
||||
t, a = guess_from_filename(pdf)
|
||||
title, author = title or t, author or a
|
||||
note = "title/author not found in the PDF; taking them from the file name: '%s' by '%s'" % (title, author)
|
||||
if not (title and author):
|
||||
raise Failed("ingest: could not find title and author in the PDF or its file name. Put a file with the same name and "
|
||||
'.json next to the PDF: {"title": "...", "author": "..."}')
|
||||
return title, author, note
|
||||
def resolve_meta(cfg, pdf, title=None, author=None, name_hints=None):
|
||||
"""Title and author with their sources (see meta.py). Never fails: gaps are filled and logged."""
|
||||
return meta.resolve(cfg, pdf, title, author, sidecar(pdf), name_hints)
|
||||
|
||||
|
||||
def check_ocr(cfg, book_dir):
|
||||
@@ -158,19 +139,17 @@ def check_ocr(cfg, book_dir):
|
||||
"OCR found no text (blank page or picture only)")])
|
||||
|
||||
|
||||
def run_text_stages(cfg, pdf, title, author):
|
||||
def run_text_stages(cfg, pdf, title, author, source_url=None, name_hints=None):
|
||||
"""Ingest through license for every chapter. Returns (book_dir, book)."""
|
||||
import cli
|
||||
title, author, note = resolve_meta(pdf, title, author)
|
||||
step("metadata")
|
||||
m = resolve_meta(cfg, pdf, title, author, name_hints)
|
||||
print("\n".join(m["log"]))
|
||||
step("ingest")
|
||||
if note:
|
||||
print(note)
|
||||
try:
|
||||
book_dir = ingest.run(cfg, pdf, title, author)
|
||||
book_dir = ingest.run(cfg, pdf, meta=m, source_url=source_url)
|
||||
except SystemExit as e:
|
||||
raise Failed("ingest: %s" % e)
|
||||
if note:
|
||||
bookio.write_warnings(book_dir, "ingest-metadata", [("metadata-from-filename", os.path.basename(pdf), "title '%s', author '%s'" % (title, author))])
|
||||
print("ingest done ->", book_dir)
|
||||
check_ocr(cfg, book_dir)
|
||||
for name in ("structure",):
|
||||
@@ -238,7 +217,8 @@ def run_audio_stages(cfg, book_dir, book, force):
|
||||
return rendered
|
||||
|
||||
|
||||
def process(cfg, pdf, dry_run=False, force=False, title=None, author=None, compare=None, result_file=None, compare_strict=False):
|
||||
def process(cfg, pdf, dry_run=False, force=False, title=None, author=None, compare=None, result_file=None, compare_strict=False,
|
||||
source_url=None, name_hints=None):
|
||||
"""Returns 0 on success, 1 on failure (message printed)."""
|
||||
pdf = os.path.abspath(pdf)
|
||||
if not os.path.isfile(pdf):
|
||||
@@ -251,7 +231,7 @@ def process(cfg, pdf, dry_run=False, force=False, title=None, author=None, compa
|
||||
rendered = False
|
||||
try:
|
||||
print("process %s%s" % (os.path.basename(pdf), " [DRY RUN: text stages only]" if dry_run else ""))
|
||||
book_dir, book = run_text_stages(cfg, pdf, title, author)
|
||||
book_dir, book = run_text_stages(cfg, pdf, title, author, source_url, name_hints)
|
||||
if compare:
|
||||
old = os.path.join(compare, os.path.relpath(book_dir, settings.resolve(cfg, "books_dir")))
|
||||
step("compare with the existing book folder %s" % old)
|
||||
|
||||
Reference in New Issue
Block a user