#!/usr/bin/env python3 """ # Script ob-calibre ## Metadata **Kind**: #obsidian/ob-script **Language**: #python **Parent**:: [[Obsidian Chore Scripts]], [[♯ Calibre]], [[Executable Markdown File]] ## Synopsis Export Calibre Library into Obsidian. Example:: [[Calibre 374 - Jamis Buck - The Ray Tracer Challenge|Jamis Buck - The Ray Tracer Challenge]] ## Script ```python # """ import subprocess import tempfile import os import csv import re import urllib.parse import filecmp import shutil import unicodedata from pathlib import Path from datetime import datetime INVALID_CHARS_RE = re.compile(r'[\[\]\'?*#:||,\\/:" ]+') PROPERTY_RE = re.compile(r"^(\*\*[^*]+\*\*) :: ", re.M) def safe_name(name): return unicodedata.normalize("NFC", INVALID_CHARS_RE.sub(" ", name).strip()) def brief_authors(authors): if "&" in authors: return safe_name(authors.split("&")[0].strip()) + " et al." return safe_name(authors) def format_comments(comments): return "\n".join( line.rstrip() for line in row["comments"].rstrip().replace(" \n", "").split("\n") ) export_dir = Path.home() / "Dropbox" / "Brain" / "robot" / "Calibre Library" (export_dir / "res").mkdir(parents=True, exist_ok=True) csv_fd, csv_path = tempfile.mkstemp(suffix=".csv", text=True) csv_path = Path(csv_path) try: subprocess.check_call( [ "calibredb", "catalog", str(csv_path), "--with-library", Path.home() / "Dropbox" / "Calibre Library", ] ) csv_io = os.fdopen(csv_fd, encoding="utf-8-sig") csv_reader = csv.DictReader(csv_io) for row in csv_reader: tmp_md_fd, tmp_md_path = tempfile.mkstemp(suffix=".md", text=True) row_brief_authors = brief_authors(row["authors"]) md_file = export_dir / "Calibre {} - {} - {}.md".format( row["id"], row_brief_authors, safe_name(row["title"]) ) cover_path = Path(row["cover"]) target_cover_file_name = f'cover-calibre-{row["id"]}{cover_path.suffix}' with os.fdopen(tmp_md_fd, "w", newline="\n") as md_io: print( f'---\naliases: [{repr(" - ".join([row_brief_authors, row["title"]]))}]\n---', file=md_io, ) print(f'# {row["title"]}', file=md_io) print("", file=md_io) if row["cover"] != "": target_cover_path = export_dir / "res" / target_cover_file_name if not target_cover_path.exists() or not filecmp.cmp( cover_path, target_cover_path, shallow=False ): print("New: {}".format(target_cover_path.name)) shutil.copyfile(cover_path, target_cover_path) print(f"![[{target_cover_file_name}|256]]", file=md_io) print("", file=md_io) print("## Metadata", file=md_io) print("", file=md_io) print("**Source**:: #from/calibre", file=md_io) print("**Kind**:: #books", file=md_io) print("**Generated by**:: [[ob-calibre]]", file=md_io) if row["#topic"] != "": topic_line = " ".join( [f"[[♯ {topic}]]" for topic in row["#topic"].split(", ")] ) print(f"**Topic**:: {topic_line}", file=md_io) tags = row["tags"].split(", ") if "now" in tags: status = "#now" tags.remove("now") elif "later" in tags: status = "#later" tags.remove("later") elif row["rating"] != "" or row["#date_read"] != "": status = "#x" else: status = "#someday" print(f"**Status**:: {status}", file=md_io) if row["rating"] != "": print(f'**Rating**:: {row["rating"]}', file=md_io) print(f'**Rating Tag**:: #rating/{row["rating"]}', file=md_io) if row["#date_read"] != "": date_read = datetime.fromisoformat(row["#date_read"]) print( f'**Date Read**:: [[{date_read.strftime("%Y-%m-%d")}]]', file=md_io ) if len(tags) > 0: print(f'**Tags**:: #{" #".join(tags)}', file=md_io) if row["#pages"] != "": print(f'**Pages**:: {row["#pages"]}', file=md_io) if row["#progress"] != "": print( f'**Progress**:: <progress value="{row["#progress"]}" max="100"></progress>', file=md_io, ) print(f'**Title**:: {row["title"]}', file=md_io) authors = [ f"[[{safe_name(author.strip())}]]" for author in row["authors"].split("&") ] if len(authors) > 0: print(f'**Authors**:: {", ".join(authors)}', file=md_io) if row["publisher"] != "": print("**Publisher**:: [[{}]]".format(row["publisher"]), file=md_io) pubdate = datetime.fromisoformat(row["pubdate"]) if pubdate.year > 1000: print( "**Published**:: [[{}]]".format(pubdate.strftime("%Y-%m-%d")), file=md_io, ) updated = datetime.fromisoformat(row["timestamp"]) if updated.year > 1000: print( "**Updated**:: [[{}]]".format(updated.strftime("%Y-%m-%d")), file=md_io, ) if row["series"] != "": print( "**Series**:: [[{}]]".format(safe_name(row["series"])), file=md_io ) if row["isbn"] != "": print(f'**ISBN**:: {row["isbn"]}', file=md_io) languages = [] if "eng" in row["languages"]: languages.append("#lang/en") if "zho" in row["languages"] or "och" in row["languages"]: languages.append("#lang/zh") if len(languages) > 0: print("**Languages**:: {}".format(" ".join(languages)), file=md_io) if row["formats"] != "": formats = ["#format/{}".format(f) for f in row["formats"].split(", ")] print(f'**Formats**:: {", ".join(formats)}', file=md_io) if row["cover"] != "": print(f"**Cover**:: {target_cover_file_name}", file=md_io) dir = cover_path.parent dropbox_path = Path( *dir.parts[dir.parts.index("Dropbox") + 1 :] ).as_posix() print( "**Dropbox Directory**:: [Open Directory in Dropbox](https://www.dropbox.com/home/{})".format( urllib.parse.quote(str(dropbox_path)) ), file=md_io, ) print( "**macOS Local Directory**:: [Open Directory Locally](file://{})".format( urllib.parse.quote(str(dir.as_posix())) ), file=md_io, ) print( f'**Calibre Link**:: [Open in Calibre](calibre://show-book/Calibre_Library/{row["id"]})', file=md_io, ) if row["#metadata"] != "": print(row["#metadata"], file=md_io) if row["comments"] != "": print("\n## Comments\n", file=md_io) print(format_comments(row["comments"]), file=md_io) if row["#mdnotes"] != "": print("\n## Notes\n", file=md_io) print(row["#mdnotes"], file=md_io) tmp_md_path = Path(tmp_md_path) if not md_file.exists() or not filecmp.cmp(tmp_md_path, md_file, shallow=False): print("New: {}".format(md_file.name)) os.replace(tmp_md_path, md_file) else: # print('Same: {}'.format(md_file.name)) tmp_md_path.unlink() # example_row = { # 'author_sort': 'Young, Scott', # 'authors': 'Scott Young', # 'comments': '', # 'cover': '/Users/ian/Dropbox/Calibre Library/Scott Young/Ultralearning - Shortform Summary (362)/cover.jpg', # 'formats': 'pdf', # 'id': '362', # 'identifiers': '', # 'isbn': '', # 'languages': '', # 'library_name': 'Calibre Library', # 'pubdate': '0101-01-01T08:00:00+08:00', # 'publisher': 'Shortform', # 'rating': '', # 'series': '', # 'series_index': '1.0', # 'size': '760825', # 'tags': 'shortform', # 'timestamp': '2021-08-01T12:06:12+08:00', # 'title': 'Ultralearning - Shortform Summary', # 'title_sort': 'Ultralearning - Shortform Summary', # 'uuid': 'f892b83f-8591-4575-8679-e68720c70ff7' # } finally: os.close(csv_fd) csv_path.unlink() """ # vim: ft=python ``` """