Created
June 27, 2025 20:55
-
-
Save nickav/7966127dd1a5d1c43ae897a7b90a0981 to your computer and use it in GitHub Desktop.
Converts Google Keep exported JSON notes to Obsidian markdown files
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
// | |
// Converts Google Keep exported JSON notes to Obsidian markdown files | |
// Usage: node keep2obsidian.js | |
// Place files in a sibling directory called: `notes` | |
// | |
const fs = require('fs'); | |
const path = require('path'); | |
const readJSON = (filePath) => { | |
const contents = fs.readFileSync(filePath).toString(); | |
let result = null; | |
try { | |
result = JSON.parse(contents); | |
} catch (e) { | |
} | |
const name = path.basename(filePath); | |
return { path: filePath, name, json: result }; | |
}; | |
const OS_ScanFiles = (rootDir, ignoreFiles = []) => { | |
const result = []; | |
const ignoreDirs = ignoreFiles.filter((it) => it.includes('/')); | |
const scan = (dir) => { | |
fs.readdirSync(dir).forEach((file) => { | |
if (ignoreFiles.includes(file)) return; | |
if (ignoreDirs.some((ignoreDir) => dir.replaceAll('\\', '/').includes(ignoreDir))) return; | |
const fullPath = path.join(dir, file); | |
if (fs.statSync(fullPath).isDirectory()) { | |
return scan(fullPath); | |
} else { | |
return result.push(fullPath); | |
} | |
}); | |
}; | |
scan(rootDir); | |
return result; | |
}; | |
const OS_DeleteEntireDirectory = (dir) => { | |
try { | |
fs.rmSync(dir, { recursive: true }); | |
} catch (e) {} | |
}; | |
const OS_WriteEntireFile = (fp, c) => { | |
fs.writeFileSync(fp, c, 'utf8'); | |
}; | |
const PrettyDate = (d) => { | |
const date = new Date(d); | |
return date.toISOString().split('T')[0]; | |
} | |
// | |
// Main | |
// | |
const inputPath = path.join(__dirname, './notes/'); | |
const outputDir = path.join(__dirname, './out/'); | |
OS_DeleteEntireDirectory(outputDir); | |
if (!fs.existsSync(outputDir)){ | |
fs.mkdirSync(outputDir); | |
} | |
const files = OS_ScanFiles(inputPath).map(readJSON).filter((it) => it.json && !it.json.isArchived).map((it) => { | |
const name = it.name.replace(/\.json$/, ''); | |
const createdAt = PrettyDate(it.json.createdTimestampUsec / 1000); | |
const fname = it.json.title ? `${createdAt}_${name}.md` : `${createdAt}_${it.json.createdTimestampUsec / 1000}.md`; | |
const dest = path.join(outputDir, fname); | |
let content = it.json.title ? `# ${it.json.title}\n${it.json.textContent || ''}` : it.json.textContent || ''; | |
if (it.json.listContent) | |
{ | |
content += it.json.listContent.map((item) => `- ${item.text}`).join('\n'); | |
} | |
OS_WriteEntireFile(dest, content); | |
return { name, createdAt, it }; | |
}); | |
console.log(files.length); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment