summaryrefslogtreecommitdiff
path: root/add_bookmark_to_wishlist.js
diff options
context:
space:
mode:
Diffstat (limited to 'add_bookmark_to_wishlist.js')
-rwxr-xr-xadd_bookmark_to_wishlist.js97
1 files changed, 97 insertions, 0 deletions
diff --git a/add_bookmark_to_wishlist.js b/add_bookmark_to_wishlist.js
new file mode 100755
index 0000000..a488174
--- /dev/null
+++ b/add_bookmark_to_wishlist.js
@@ -0,0 +1,97 @@
+#!/usr/bin/env node
+// Add a bookmark to Music/Wishlist folder in Floccus bookmarks
+// Usage: node add_bookmark_to_wishlist.js <url> <title>
+
+const crypto = require('crypto');
+const fs = require('fs');
+
+const BOOKMARKS_PATH = '/var/www/webdav/bookmarks.xbel';
+const PASSWORD = process.env.BOOKMARKS_PASSWORD || JSON.parse(require('fs').readFileSync('/etc/automation/bookmarks.json', 'utf8')).password;
+const WISHLIST_FOLDER_TITLE = 'Wishlist';
+
+const url = process.argv[2];
+const title = process.argv[3];
+
+if (!url || !title) {
+ console.error('Usage: node add_bookmark_to_wishlist.js <url> <title>');
+ process.exit(1);
+}
+
+function decrypt(data, password) {
+ const ciphertext = Buffer.from(data.ciphertext, 'base64');
+ const salt = data.salt;
+ const key = crypto.pbkdf2Sync(password, salt, 250000, 32, 'sha256');
+ const iv = ciphertext.slice(0, 16);
+ const encrypted = ciphertext.slice(16, -16);
+ const tag = ciphertext.slice(-16);
+
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
+ decipher.setAuthTag(tag);
+ return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
+}
+
+function encrypt(plaintext, password, salt) {
+ const key = crypto.pbkdf2Sync(password, salt, 250000, 32, 'sha256');
+ const iv = crypto.randomBytes(16);
+
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
+ const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
+ const tag = cipher.getAuthTag();
+
+ return Buffer.concat([iv, encrypted, tag]).toString('base64');
+}
+
+const escapeXml = (str) => str
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&apos;');
+
+try {
+ const data = JSON.parse(fs.readFileSync(BOOKMARKS_PATH, 'utf8'));
+ let xml = decrypt(data, PASSWORD);
+
+ // Check if bookmark already exists
+ if (xml.includes(escapeXml(url)) || xml.includes(url)) {
+ console.log('Bookmark already exists');
+ process.exit(0);
+ }
+
+ // Find highest ID
+ const idMatch = xml.match(/highestId :(\d+):/);
+ let highestId = idMatch ? parseInt(idMatch[1]) : 100;
+ const newId = highestId + 1;
+
+ // Find the Wishlist folder and add bookmark inside it
+ const wishlistPattern = /<folder id="\d+">\s*<title>Wishlist<\/title>/;
+
+ if (wishlistPattern.test(xml)) {
+ // Add bookmark inside existing Wishlist folder
+ const newBookmark = `<bookmark href="${escapeXml(url)}" id="${newId}">
+ <title>${escapeXml(title)}</title>
+ </bookmark>
+ </folder>`;
+
+ // Find Wishlist folder's closing tag and insert before it
+ xml = xml.replace(
+ /(<folder id="\d+">\s*<title>Wishlist<\/title>[\s\S]*?)(<\/folder>)/,
+ (match, folderContent, closingTag) => folderContent + newBookmark
+ );
+ } else {
+ console.error('Wishlist folder not found');
+ process.exit(1);
+ }
+
+ // Update highest ID
+ xml = xml.replace(/highestId :(\d+):/, `highestId :${newId}:`);
+
+ // Save
+ const newCiphertext = encrypt(xml, PASSWORD, data.salt);
+ fs.writeFileSync(BOOKMARKS_PATH, JSON.stringify({ ciphertext: newCiphertext, salt: data.salt }));
+
+ console.log(`Added: ${title}`);
+} catch (e) {
+ console.error('Error:', e.message);
+ process.exit(1);
+}