diff options
Diffstat (limited to 'add_bookmark_to_wishlist.js')
| -rwxr-xr-x | add_bookmark_to_wishlist.js | 97 |
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, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + +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); +} |
