Hello everyone,
I’m trying to use Tinderbox to pull in PDF annotations from my Zotero library. I have a Bash script (pasted below) that finds a PDF in my Zotero storage/
folder based on a “citation key” (like geels2016
), figures out the attachment key, and calls the Zotero Web API to retrieve annotations. It then prints only a JSON array of annotation data—no extra log lines—so tools like Tinderbox can parse it easily.
Here’s the script:
#!/usr/bin/env bash
###############################################################################
# zotero_annotations_json.sh
#
# 1) CITATION_KEY: The portion before the underscore in your PDF filename,
# e.g., "geels2016".
# 2) This script searches your Zotero "storage" folder for a PDF that starts
# with CITATION_KEY_ (case-insensitive).
# 3) It extracts the folder name in 'storage/' => the attachment key.
# 4) It calls Zotero Web API to get annotations, printing ONLY the raw JSON
# (so Tinderbox can parse it directly).
###############################################################################
# --------------------- CONFIGURATION ---------------------
CITATION_KEY="geels2016" # E.g. "geels2016"
ZOTERO_STORAGE="$HOME/Zotero/storage" # Path to your Zotero 'storage' folder
ZOTERO_USER_ID="1234567" # Your Zotero user ID
ZOTERO_API_KEY="YOUR_ZOTERO_API_KEY" # Must have read permission
# If the PDF is in a group library, replace "users/$ZOTERO_USER_ID" in the URL
# with "groups/<GROUP_ID>" below.
# ------------------------------------------------------------------------------
# 1) Find the matching PDF.
foundPDF=$(find "$ZOTERO_STORAGE" -type f -iname "*.pdf" 2>/dev/null | grep -i "${CITATION_KEY}_" | head -n 1)
if [ -z "$foundPDF" ]; then
# If no PDF found, print an empty JSON array (or handle differently).
echo "[]"
exit 0
fi
# 2) The folder name in 'storage/' is the attachment key.
ATTACHMENT_KEY=$(basename "$(dirname "$foundPDF")")
# 3) Call Zotero Web API to get the annotation children for that attachment key.
ANNOTATIONS_JSON=$(curl -s \
-H "Zotero-API-Key: $ZOTERO_API_KEY" \
"https://api.zotero.org/users/$ZOTERO_USER_ID/items/$ATTACHMENT_KEY/children?itemType=annotation&format=json"
)
# If no annotations found or invalid key, print an empty array.
if [ -z "$ANNOTATIONS_JSON" ] || [ "$ANNOTATIONS_JSON" = "[]" ]; then
echo "[]"
exit 0
fi
# 4) Print ONLY the raw JSON.
echo "$ANNOTATIONS_JSON"
Result (example):
[
{
"key": "VGK94386",
"version": 48375,
"library": {
"type": "user",
"id": 123,
"name": "xxx",
"links": {
"alternate": {
"href": "https://www.zotero.org/xxx",
"type": "text/html"
}
}
},
"links": {
"self": {
"href": "https://api.zotero.org/users/123/items/VGK94386",
"type": "application/json"
},
"alternate": {
"href": "https://www.zotero.org/xxx/items/VGK94386",
"type": "text/html"
},
"up": {
"href": "https://api.zotero.org/users/123/items/V87689SF",
"type": "application/json"
}
},
"meta": {},
"data": {
"key": "VGK94386",
"version": 48375,
"parentItem": "V87689SF",
"itemType": "annotation",
"annotationType": "highlight",
"annotationText": "The four-phased pattern, as specified above, came out well in the case study, precisely because",
"annotationComment": "sdasdasda",
"annotationColor": "#ffd400",
"annotationPageLabel": "274",
"annotationSortIndex": "00009|001047|00511",
"annotationPosition": "{\"pageIndex\":9,\"rects\":[[56.801,318.335,279.517,330.556],[45.401,306.334,260.498,318.556]]}",
"tags": [
{
"tag": "❌ nosource"
}
],
"relations": {},
"dateAdded": "2024-12-29T08:33:19Z",
"dateModified": "2024-12-29T08:33:23Z"
}
}
]
What I’m trying to do in Tinderbox:
- Call this script from Tinderbox (using something like
runCommand()
), passing along a citation key. - Have the script’s JSON output placed into
$Text
. - Parse that JSON array in a Tinderbox action or stamp, to produce user-friendly text like this one:
Quote: "some highlighted text..."
Annotation: "my note"
Annotation Color: "#FFD700"
Page(s): "12-13"
Tag(s): "important, highlight"
Date Added: "2024-12-11T17:40:54Z"
Date Modified: "2024-12-28T23:04:11Z"
Quote: "another highlight..."
Annotation: "another comment"
...
I understand the general approach:
- The
runCommand()
operator can capture the script’s stdout into$Text
. - Then I can run something like:
$Text.json.each {
var:string quote = json["data.annotationText"];
// etc.
};
to iterate through the array. Do I understand it correctly?
I’m still a bit unclear about the best way to dynamically pass $Name
(my citation key) from Tinderbox into the script.
Could anyone offer tips or point me to an example of how to manage this workflow in Tinderbox? I’m tagging @satikusala because it seems you’ve been interested in getting annotations directly into your Tinderbox file.
Thanks in advance!
Arek