Skip to content

Instantly share code, notes, and snippets.

@nickav
Created June 27, 2025 20:55
Show Gist options
  • Save nickav/7966127dd1a5d1c43ae897a7b90a0981 to your computer and use it in GitHub Desktop.
Save nickav/7966127dd1a5d1c43ae897a7b90a0981 to your computer and use it in GitHub Desktop.
Converts Google Keep exported JSON notes to Obsidian markdown files
//
// 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