-
-
Save peterjmag/31648b818a0f9e8bcc59e5157ee33214 to your computer and use it in GitHub Desktop.
Generate a markdown links list from iCloud tabs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
/* | |
Generate a markdown links list from iCloud tabs, on macOS | |
Usage: | |
./icloudtabs2md.js > icloudtabs.md | |
./icloudtabs2md.js | pbcopy | |
Require: | |
- nodejs v8+ | |
- nodejs sqlite3 package, installed globally or locally (./node_modules) | |
- Safari macOS with iCloud tabs synchronized | |
From https://gist.github.com/mems/2c96233708c6b5b44ed1a26cb0ec5a0e | |
*/ | |
const os = require('os'); | |
const util = require('util'); | |
const inflate = util.promisify(require('zlib').inflate); | |
const sqlite = require('sqlite3'); | |
const dbFile = os.homedir() + "/Library/Safari/CloudTabs.db"; | |
const db = new sqlite.Database(dbFile); | |
function tabSortFunc(tabA, tabB){ | |
return tabA.position.sortValues[0].sortValue - tabB.position.sortValues[0].sortValue; | |
} | |
(async () => { | |
const [devices, tabs] = await Promise.all([ | |
util.promisify(db.all.bind(db))("SELECT device_uuid AS uuid, device_name AS name FROM cloud_tab_devices;"), | |
util.promisify(db.all.bind(db))("SELECT title, url, position, device_uuid AS device FROM cloud_tabs;"), | |
]); | |
if(tabs.length === 0){ | |
console.info("No tabs available"); | |
return; | |
} | |
const positionsResolutions = []; | |
for(const tab of tabs){ | |
tab.device = devices.find(device => device.uuid === tab.device); | |
const bytes = tab.position; | |
tab.position = null; | |
const promise = inflate(bytes).then(bytes => tab.position = JSON.parse(bytes.toString("utf8"))); | |
positionsResolutions.push(promise); | |
} | |
await Promise.all(positionsResolutions); | |
let firstDevice = true; | |
for(const device of devices){ | |
const deviceTabs = tabs.filter(tab => tab.device === device).sort(tabSortFunc); | |
if(deviceTabs.length === 0){ | |
continue; | |
} | |
console.log(`${firstDevice ? "" : "\n"}${device.name}:\n`); | |
for(const tab of deviceTabs){ | |
const url = tab.url.replace(/\(/g,"%2528").replace(/\)/g,"%2529");// escape "(" an ")", just in case | |
const escapedTitle = tab.title.replace(/([\\<>\[\]])/g,"\\$1");// escape some chars for the link title | |
console.log(`- [${escapedTitle}](${url})`); | |
} | |
firstDevice = false; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment