"""Normalize per-archive origin license metadata to the hef.catalog LICENSES vocab. Maps Creative Commons URLs / identifiers and public-domain markers to {public_domain, cc0, cc_by, cc_by_nc}. For the attribution licenses (cc_by, cc_by_nc) it builds a non-empty attribution string — hef.catalog.validate() rejects those licenses with an empty attribution. Anything unmappable is rejected (a piece whose license can't be established is not ingested). """ from __future__ import annotations import re def _attribution(creator: str, license_name: str, raw: str, default_label: str) -> str: label = license_name or default_label or raw parts = [] if creator: parts.append(creator) parts.append(label) return " — ".join(parts) def normalize_license(raw, *, creator="", license_name=""): """Return (license, attribution) for an origin license string. Raises ValueError when `raw` does not map to an allowed license. """ text = (raw or "").strip().lower() if not text: raise ValueError(f"unmappable license: {raw!r}") # CC0 / public-domain dedication (check before the generic publicdomain # markers, since the CC0 URL also contains "publicdomain"). if "publicdomain/zero" in text or re.search(r"\bcc[ _-]?0\b", text): return ("cc0", "") # Public Domain Mark / explicit public domain / no-known-copyright. pd_markers = ( "publicdomain/mark", "public_domain", "public domain", "publicdomain", "no known copyright", "pdm", ) if any(m in text for m in pd_markers): return ("public_domain", "") # CC BY-NC must be checked before the bare CC BY. if re.search(r"by[ _-]?nc", text): return ("cc_by_nc", _attribution(creator, license_name, raw, "CC BY-NC")) # CC BY. if re.search(r"\bcc[ _-]?by\b", text) or "licenses/by" in text: return ("cc_by", _attribution(creator, license_name, raw, "CC BY")) raise ValueError(f"unmappable license: {raw!r}") def librivox_license(): """LibriVox recordings are public domain by charter (attribution not required).""" return ("public_domain", "")