Couldn't had the will to handle under KoboRoot.tgz, so somebody else might do it maybe...
This script creates an .md file of annotations and bookmarks and copy the file to your dropbox account when you select "Sync Annotations" from nickel menu.
1. From
https://github.com/fsantini/KoboClou...ocal/kobocloud download "curl" and "ca-bundle.crt" and copy it under .adds/notes folder on Kobo
2. Go to
https://www.dropbox.com/developers/apps
Click “Create app”
Choose:
Scoped access
Select App Folder
Give your app a name (Should be unique worldwide)
Under OAuth 2, click “Generate access token”
Copy and use that token as DROPBOX_ACCESS_TOKEN in the script.
3. notes.sh:
Code:
#!/bin/sh
'
🧭 Steps
Go to https://www.dropbox.com/developers/apps
Click “Create app”
Choose:
Scoped access
Select App Folder
Give your app a name (Should be unique worldwide)
Under OAuth 2, click “Generate access token”
Use that token in your curl upload
From https://github.com/fsantini/KoboCloud/tree/master/src/usr/local/kobocloud
download curl and ca-bundle.crt to notes folder
'
DROPBOX_ACCESS_TOKEN="YOUR_DROPBOX_ACCESS_TOKEN"
NOTES="/mnt/onboard/.adds/notes"
# Note: Depending on your language this folder may have a different name
EXPORT_FOLDER="/mnt/onboard/Exported Annotations"
EXPORT_FILE="notes-$(date "+%Y-%m-%d").md"
EXPORT="$EXPORT_FOLDER/$EXPORT_FILE"
KEEP=21
DB="/mnt/onboard/.kobo/KoboReader.sqlite"
SQLITE="${NOTES}/sqlite3"
LD_LIBRARY_PATH="${NOTES}/lib:${LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
mkdir -p "$(dirname "$EXPORT")"
echo -e "# Kobo Notes\n" > $EXPORT
echo -e "*$(date -R)*\n" >> $EXPORT
echo -e "## Highlights\n" >> $EXPORT
# UTF-8 char table (decimal):
# 9: Tab
# 10: Line feed
# 32: Space
# 58: :
# 62: >
# 42: *
# 92: \
# 8230: …
# 9999: ✏
# 128278: 🔖
# 128196: 📄
SQL="SELECT TRIM(
'### ' ||
CASE
WHEN b.Type = 'dogear' THEN
char(128278, 32)
WHEN b.Type = 'note' THEN
char(9999, 32)
WHEN b.Type = 'highlight' THEN
char(128196, 32)
END
|| c.Title || ', ' || COALESCE(c.Attribution, 'N/A') || char(10, 10, 42)
|| datetime(b.dateCreated) || char(42, 92, 10) /* force Markdown newline */
|| COALESCE(c1.Title, '') || char(10, 10, 62, 32) ||
CASE
WHEN b.Type = 'dogear' THEN
COALESCE(ContextString, 'No context available') || char(8230, 10) /* only kepubs have context */
ELSE
REPLACE( /* start Markdown quote */
REPLACE(
TRIM( /* trim newlines */
TRIM( /* trim tabs */
TRIM(b.Text), /* trim spaces */
char(9)
),
char(10)
),
char(9), ''
),
char(10), char(10, 62, 32, 10, 62, 32)) /* continue Markdown quote for multiple paragraphs */
|| char(10, 10)
|| COALESCE(b.Annotation, '') || char(10)
END, char(10)
) || char(10, 10)
FROM Bookmark b
INNER JOIN content c ON b.VolumeID = c.ContentID
LEFT OUTER JOIN content c1 ON (c1.ContentId LIKE b.ContentId || '%' AND c1.ContentType != 9)
ORDER BY c.Title ASC,
c.VolumeIndex ASC,
b.ChapterProgress ASC,
b.DateCreated ASC;"
$SQLITE "$DB" "$SQL" >> $EXPORT
echo -e "## Bookmarks:\n" >> $EXPORT
SQL="SELECT TRIM(
'### ' ||
CASE
WHEN b.Type = 'dogear' THEN
char(128278, 32)
END
|| c.Title || ', ' || COALESCE(c.Attribution, 'N/A') || ', ' || c.Publisher || ', ' || strftime('%Y', date(datetime(c.dateCreated))) || char(10, 10)
|| strftime('%d/%m/%Y', date(datetime(b.dateCreated))) || ' ' || time(time(datetime(b.dateCreated)), '+1 hours') || char(10, 58, 10) /* force Markdown newline */
|| COALESCE(c1.Title, 'No chapter title') ||', ' || c.___PercentRead || '% read' || char(10, 10, 62, 32) ||
CASE
WHEN b.Type = 'dogear' THEN
COALESCE(ContextString, 'No context available') || char(8230, 10) /* only kepubs have context */
ELSE
REPLACE( /* start Markdown quote */
REPLACE(
TRIM( /* trim newlines */
TRIM( /* trim tabs */
TRIM(b.Text), /* trim spaces */
char(9)
),
char(10)
),
char(9), ''
),
char(10), char(10, 62, 32, 10, 62, 32)) /* continue Markdown quote for multiple paragraphs */
|| char(10, 10)
|| COALESCE(b.Annotation, '') || char(10)
END, char(10)
) || char(10)
FROM Bookmark b
INNER JOIN Event e ON e.ContentId = b.volumeId
INNER JOIN content c ON b.VolumeID = c.ContentID
LEFT OUTER JOIN content c1 ON (c1.ContentId LIKE b.ContentId || '%' AND c1.ContentType != 9)
WHERE EventType=8 AND b.Hidden='false' AND b.ContextString!=''
GROUP BY b.dateCreated, c.BookTitle
ORDER BY c.BookTitle ASC, b.dateCreated DESC;"
$SQLITE "$DB" "$SQL" >> $EXPORT
echo -e "## Currently reading:\n" >> $EXPORT
SQL="SELECT
'- ' || c.Title || COALESCE(', ' || c.Attribution, '')
|| ' (' || COALESCE(c1.Title || ', ', '') || c.___PercentRead || '% read' || ')'
FROM Content c
LEFT OUTER JOIN Content c1 ON (
c.ContentID = c1.BookID
AND c1.ContentType = 899
AND REPLACE(c1.ContentID, '!', '/') LIKE /* get chapter id without anchor or query string */
'%' || SUBSTR(c.ChapterIDBookmarked, 1, INSTR(c.ChapterIDBookmarked, '#') + INSTR(c.ChapterIDBookmarked, '?') - 1) || '%'
)
WHERE c.ContentType = 6
AND c.ReadStatus = 1
AND c.IsDownloaded = 'true'
ORDER BY c.___PercentRead DESC,
c.Title ASC,
c.Attribution ASC;"
$SQLITE "$DB" "$SQL" >> $EXPORT
# Clean up old notes
cd "$EXPORT_FOLDER"
for i in $(ls -v notes* | head -n -$KEEP); do
rm "$i"
done
'
# PHP Server:
- create "uploads" folder in www root folder with chown 777
- create "upload.php" file in www root folder
## PHP:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$uploadDir = __DIR__ . '/uploads/';
$filename = basename($_FILES['file']['name']);
$targetPath = $uploadDir . $filename;
// 🔒 Restrict allowed file extensions
$allowedExtensions = ['txt', 'md'];
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
http_response_code(400);
echo "Error: Only .txt and .md files are allowed.";
exit;
}
// Proceed with upload
if (move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) {
echo "File uploaded successfully: " . htmlspecialchars($filename);
} else {
echo "Upload failed.";
}
} else {
echo "No file uploaded.";
}
?>
## command in this script file:
/mnt/onboard/.adds/notes/curl -X POST -F "file=@$EXPORT" http://server-ip/upload.php
'
/mnt/onboard/.adds/notes/curl --cacert /mnt/onboard/.adds/notes/ca-bundle.crt -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer $DROPBOX_ACCESS_TOKEN" \
--header "Dropbox-API-Arg: {\"path\": \"/$EXPORT_FILE\",\"mode\": \"add\",\"autorename\": true,\"mute\": false}" \
--header "Content-Type: application/octet-stream" \
--data-binary @"$EXPORT"