diff --git a/src/download_templates.py b/src/download_templates.py new file mode 100644 index 0000000..e7e6bba --- /dev/null +++ b/src/download_templates.py @@ -0,0 +1,162 @@ +""" +Download Adobe Sign library templates to the local downloads/ folder. + +Usage: + python3 src/download_templates.py list + List all templates available in the account (no download). + + python3 src/download_templates.py download + python3 src/download_templates.py download --all + Download all templates. + + python3 src/download_templates.py download "Template Name" + Download the named template. If multiple templates share the same name, + the most recently modified one is used. +""" + +import argparse +import json +import os +import sys + +from dotenv import load_dotenv + +load_dotenv() +sys.path.insert(0, os.path.dirname(__file__)) + +from adobe_api import adobe_api_get, adobe_api_get_bytes + +DOWNLOADS_DIR = os.path.join(os.path.dirname(__file__), "..", "downloads") + + +def safe_dirname(name): + return "".join(c if c.isalnum() or c in " -_" else "_" for c in name).strip() + + +def save_json(path, data): + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + +def fetch_template_list(): + result = adobe_api_get("libraryDocuments") + return result.get("libraryDocumentList", []) + + +def cmd_list(_args): + print("Fetching template list...") + templates = fetch_template_list() + if not templates: + print("No templates found.") + return + print(f"{'Name':<45} {'Modified':<25} {'ID'}") + print("-" * 100) + for t in sorted(templates, key=lambda x: x.get("modifiedDate", ""), reverse=True): + print(f"{t['name']:<45} {t.get('modifiedDate', 'n/a'):<25} {t['id']}") + + +def download_one(template): + template_id = template["id"] + template_name = template["name"] + dir_name = f"{safe_dirname(template_name)}__{template_id[:8]}" + out_dir = os.path.join(DOWNLOADS_DIR, dir_name) + os.makedirs(out_dir, exist_ok=True) + + print(f"\n--- {template_name} ---") + + metadata = adobe_api_get(f"libraryDocuments/{template_id}") + save_json(os.path.join(out_dir, "metadata.json"), metadata) + + try: + form_fields = adobe_api_get(f"libraryDocuments/{template_id}/formFields") + save_json(os.path.join(out_dir, "form_fields.json"), form_fields) + print(f" {len(form_fields.get('fields', []))} form fields saved.") + except Exception as e: + print(f" WARNING: Could not fetch form fields: {e}") + save_json(os.path.join(out_dir, "form_fields.json"), {"error": str(e)}) + + docs = adobe_api_get(f"libraryDocuments/{template_id}/documents") + save_json(os.path.join(out_dir, "documents.json"), docs) + + for doc in docs.get("documents", []): + doc_id = doc["id"] + doc_name = doc.get("name", doc_id) + if not doc_name.lower().endswith(".pdf"): + doc_name += ".pdf" + safe_name = safe_dirname(doc_name) + pdf_path = os.path.join(out_dir, safe_name) + try: + pdf_bytes = adobe_api_get_bytes( + f"libraryDocuments/{template_id}/documents/{doc_id}" + ) + with open(pdf_path, "wb") as f: + f.write(pdf_bytes) + print(f" PDF saved ({len(pdf_bytes) // 1024}KB) → {safe_name}") + except Exception as e: + print(f" WARNING: Could not download PDF: {e}") + + print(f" Done → downloads/{dir_name}/") + return out_dir + + +def cmd_download(args): + os.makedirs(DOWNLOADS_DIR, exist_ok=True) + templates = fetch_template_list() + + if not templates: + print("No templates found in the account.") + return + + # Specific template name requested + if args.template_name: + matches = [t for t in templates if t["name"] == args.template_name] + if not matches: + print(f"ERROR: No template named '{args.template_name}' found.") + print("Run 'list' to see available templates.") + sys.exit(1) + if len(matches) > 1: + print(f" {len(matches)} templates named '{args.template_name}' — using most recently modified.") + target = max(matches, key=lambda t: t.get("modifiedDate", "")) + download_one(target) + return + + # --all or default (no name given) + print(f"Downloading all {len(templates)} template(s)...") + for t in templates: + download_one(t) + print(f"\nAll templates downloaded to: {os.path.abspath(DOWNLOADS_DIR)}") + + +def main(): + parser = argparse.ArgumentParser( + description="Download Adobe Sign library templates", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + subparsers = parser.add_subparsers(dest="command") + + subparsers.add_parser("list", help="List all templates in the account") + + dl_parser = subparsers.add_parser("download", help="Download templates") + dl_parser.add_argument( + "template_name", nargs="?", default=None, + metavar="TEMPLATE_NAME", + help="Name of template to download. Omit (or use --all) to download all.", + ) + dl_parser.add_argument( + "--all", dest="download_all", action="store_true", + help="Download all templates (default when no name is given)", + ) + + args = parser.parse_args() + + if args.command == "list": + cmd_list(args) + elif args.command == "download": + cmd_download(args) + else: + parser.print_help() + + +if __name__ == "__main__": + main()