#!/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
```
"""