9.1 KiB
| canvas | UTI-roadmap | |
|---|---|---|
|
Kindle Zotero Importer Memory
Goal
Import Kindle My Clippings.txt highlights/notes into the corresponding Zotero library items as native Zotero annotations where possible.
Primary source of Kindle data:
/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/My Clippings.txt
Zotero database discovered at:
/Users/ubd/Zotero/zotero.sqlite
Do not write directly to SQLite. Use Zotero's local JavaScript API/data layer.
Confirmed title-to-Zotero mappings must be stored in match-overrides.json so they persist across cumulative Kindle My Clippings.txt exports. match-overrides.generated.json is disposable and should not be treated as authoritative.
Confirmed Architecture
Use a two-step importer:
- Python-side preview/import-plan generator.
- Parse
My Clippings.txt. - Match clipping titles to Zotero items/attachments.
- For EPUB, compute EPUB CFI selectors.
- For PDF, compute page rect coordinates.
- Emit JSON import plan.
- Parse
- Zotero-side JavaScript writer.
- Run in Zotero via
Tools > Developer > Run JavaScriptor later as a plugin. - Load/import generated plan.
- Call
Zotero.Annotations.saveFromJSON(attachment, annotation).
- Run in Zotero via
Working Zotero API
Native annotations can be created safely with:
const attachment = Zotero.Items.get(ATTACHMENT_ITEM_ID);
const saved = await Zotero.Annotations.saveFromJSON(attachment, annotation);
Annotation item type exists internally as Zotero item type annotation.
Native annotation table observed:
itemAnnotations- columns include
itemID,parentItemID,type,text,comment,color,pageLabel,sortIndex,position,isExternal
EPUB Test: Cyclonopedia
Zotero parent item:
- itemID:
51395 - key:
JTDWDKRH - type:
book - citationKey:
negarestani2008 - title:
Cyclonopedia: complicity with anonymous materials
Zotero EPUB attachment:
- itemID:
51402 - key:
P6XQUQXF - contentType:
application/epub+zip - path:
/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/03_Academia/ZoteroFiles/Negarestani_2008_Cyclonopedia complicity with anonymous materials.epub
Kindle clipping title:
(fixed) Negarestani_2008_Cyclonopedia complicity with anonymous materials (Reza Negarestani)
Text matching against the Zotero-linked EPUB worked:
- Cyclonopedia clipping bodies:
79 - Highlights:
69 - Notes:
10 - Exact text matches:
66 - Partial text matches:
5 - Unmatched:
8, all standalone notes.
EPUB spine inspection showed EPUB/paleopetrology.html is spine slot /6/30.
Working native EPUB test annotation:
const attachment = Zotero.Items.get(51402);
const annotation = {
key: Zotero.DataObjectUtilities.generateKey(),
type: "highlight",
text: "Oil as a lubricant or Tellurian Lube, upon which everything moves forward,",
comment: "Kindle import test annotation. Delete after verification.",
color: "#ffd400",
pageLabel: "",
sortIndex: "00014|00047607",
position: {
type: "FragmentSelector",
conformsTo: "http://www.idpf.org/epub/linking/cfi/epub-cfi.html",
value: "epubcfi(/6/30!/4/90/2,/1:3,/1:77)"
},
tags: [{ name: "kindle-import-test" }]
};
await Zotero.Annotations.saveFromJSON(attachment, annotation);
Important EPUB CFI lesson:
- Bad CFI navigated but did not render highlight:
epubcfi(/6/30!/4/90/2/1:3,/4/90/2/1:77) - Good CFI rendered highlight:
epubcfi(/6/30!/4/90/2,/1:3,/1:77) - Zotero's EPUB CFI uses a common parent element plus relative text-node offsets.
Manual Zotero highlight over a longer range produced:
{
"value": "epubcfi(/6/30!/4/90/2,/1:3,/1:317)"
}
PDF Test: Rethinking Algorithmic Regulation
Chose this because it is a normal Zotero item, has a real PDF path, directly matches Kindle clipping text, and had zero existing annotations before the test.
Zotero parent item:
- itemID:
350 - key:
SGZMZ9X7 - type:
journalArticle - citationKey:
medina2015 - title:
Rethinking algorithmic regulation - publicationTitle:
Kybernetes
Zotero PDF attachment:
- itemID:
673 - key:
4BPF7ZUR - contentType:
application/pdf - attachment title/path in DB:
attachments:Medina_2015_Rethinking algorithmic regulation.pdf - actual file found at:
/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/03_Academia/ZoteroFiles/Medina_2015_Rethinking algorithmic regulation.pdf - existing annotations before test:
0
Kindle clipping title:
Rethinking Algorithmic Regulation Kybern - Unknown
Tested clipping text:
increase worker participation in the economy and preserve the autonomy of factory managers, even with the expansion of state influence.
Located in PDF:
- PDF page:
5 - Zotero
pageIndex:4 - PDF page label:
1009 pdftohtmlXML page dimensions: width739, height1020pdfinfoPDF page size: width493.228, height680.315pts
Working native PDF test annotation:
const attachment = Zotero.Items.get(673);
const annotation = {
key: Zotero.DataObjectUtilities.generateKey(),
type: "highlight",
text: "increase worker participation in the economy and preserve the autonomy of factory managers, even with the expansion of state influence.",
comment: "Kindle import PDF test annotation. Delete after verification.",
color: "#ffd400",
pageLabel: "1009",
sortIndex: "00004|000435|00197",
position: {
pageIndex: 4,
rects: [
[197.252, 380.843, 445.841, 390.181],
[99.447, 369.504, 403.125, 378.842]
]
},
tags: [{ name: "kindle-import-pdf-test" }]
};
await Zotero.Annotations.saveFromJSON(attachment, annotation);
Important PDF lessons:
- Native PDF annotation writing works.
- PDF positions use
{ pageIndex, rects }. - Rect coordinates are in PDF points, origin bottom-left.
pdftohtml -xmlcoordinates are top-left in its own scaled XML coordinate system.- Convert from
pdftohtmlXML to PDF points using:scaleX = pdfPageWidth / xmlPageWidthscaleY = pdfPageHeight / xmlPageHeightx1 = xmlLeft * scaleXx2 = (xmlLeft + xmlWidth) * scaleXy1 = pdfPageHeight - (xmlTop + xmlHeight) * scaleYy2 = pdfPageHeight - xmlTop * scaleY
- PDF
sortIndexneeds three numeric parts:pageIndex|vertical|horizontal, e.g.00004|000435|00197. - EPUB
sortIndexaccepted two numeric parts, e.g.00014|00047607.
Test Scripts Created During Exploration
These were created outside this repo during probing and may be copied/adapted:
/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/create-cyclonopedia-test-annotation.js/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/list-cyclonopedia-annotations.js/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/create-latzer-pdf-test-annotation.js/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/select-medina-pdf-zotero-item.js/Users/ubd/Library/Mobile Documents/com~apple~CloudDocs/Projects/test/select-howaldt-pdf-zotero-item.js
Useful Commands/Tools
Query Zotero DB read-only while Zotero is running:
sqlite3 "file:/Users/ubd/Zotero/zotero.sqlite?mode=ro&immutable=1" "SQL HERE"
PDF text extraction available:
pdftotext -enc UTF-8 -f PAGE -l PAGE file.pdf -
PDF coordinate extraction available:
pdftohtml -f PAGE -l PAGE -xml -stdout file.pdf
PDF page size:
pdfinfo -f PAGE -l PAGE file.pdf
Python PDF libraries were not installed at exploration time:
fitz/ PyMuPDF: unavailablepypdf: unavailable
Repo/Workspace Decision
This project lives in the Obsidian vault project area:
/Users/ubd/Library/Mobile Documents/iCloud~md~obsidian/Documents/rhizome/06_projects/UTI/kindle-zotero-importer
The vault .gitignore ignores it:
06_projects/UTI/kindle-zotero-importer
The importer directory has its own independent Git repo:
## No commits yet on main
Next Roadmap
- Create project scaffold.
- Implement Kindle clipping parser.
- Implement Zotero DB read-only indexer.
- Implement matching between Kindle titles and Zotero items/attachments.
- Implement EPUB text matching and CFI generation.
- Implement PDF text matching and rect generation using
pdftotext,pdftohtml, andpdfinfofirst. - Emit import preview JSON with confidence flags.
- Build Zotero JavaScript writer that reads preview JSON and calls
Zotero.Annotations.saveFromJSON(). - Add deduplication. Candidate keys should include attachment ID/key, clipping text hash, Kindle location/page, timestamp, and annotation type.
- Attach standalone Kindle notes to nearest highlight/location as annotation comments where possible; otherwise import as separate text annotations or report unmatched.
- Generate import report: matched, ambiguous, unmatched, skipped duplicates, created annotations.
Cautions
- Never write directly to Zotero SQLite.
- Test on one item before bulk writes.
- Keep a dry-run/import-preview mode as default.
- Keep tags on imported annotations, e.g.
kindle-import, for easy deletion/filtering. - PDF text matching will be more fragile than EPUB because of hyphenation, line breaks, OCR quality, and coordinate extraction.
- Some Kindle notes are standalone and have no quote text; attach by location/page proximity.