Skip to content

Instantly share code, notes, and snippets.

@jhonasn
Last active January 15, 2025 06:55
Show Gist options
  • Save jhonasn/479f28360041834a064163352d06e9fc to your computer and use it in GitHub Desktop.
Save jhonasn/479f28360041834a064163352d06e9fc to your computer and use it in GitHub Desktop.
A script to convert google keep notes to a bunch markdown files aiming mainly to nextcloud notes
#!/usr/bin/env python3
# from pdb import set_trace as bkp
from sys import argv, exit
from os import listdir, path, mkdir, utime
from json import loads as to_dict
from datetime import datetime as date
if '-h' in argv or '--help' in argv:
print('''
The commands will print those informations on notes:
-a all
-p pinned
-c note color
-l labels (it will print only when there's more than one)
-le last edit date
-s people who the note is shared with
None will be printed by default
''')
exit()
is_printing = {
'pinned': '-a' in argv or '-p' in argv,
'color': '-a' in argv or '-c' in argv,
'labels': '-a' in argv or '-l' in argv,
'last-edit': '-a' in argv or '-c' in argv,
'shared': '-a' in argv or '-c' in argv,
'none': len(argv) == 1
}
keep_dir = './Keep'
labels_file = 'Labels.txt'
nextcloud_dir = './nextcloud-notes'
if not path.exists(keep_dir):
print('"Keep" folder not found. Place this file on the same folder as "Keep" backup folder')
exit()
# get labels to create folders with the labels names
with open(path.join(keep_dir, labels_file), 'a+') as f:
f.seek(0)
labels = f.read().split('\n')
if len(labels): labels.pop()
labels.append('trash')
labels.append('archived')
# read files and separate the note jsons from attachments
files = listdir(keep_dir)
notes = list(filter(lambda f: '.json' in f, files))
attachments = list(filter(lambda f: '.json' not in f and '.html' not in f and f != labels_file, files))
print('{} notes found\n\n'.format(len(notes)))
def fix_filename(filename):
new_name = filename
for char in ['<', '>', ':', '"', '/', '\\', '|', '?', '*']:
new_name = new_name.replace(char, '-')
return new_name
# create nextcloud and label folders
if not path.exists(nextcloud_dir):
mkdir(nextcloud_dir)
print('created nextcloud folder')
for label in labels:
label_dir = path.join(nextcloud_dir, fix_filename(label))
if not path.exists(label_dir):
mkdir(label_dir)
print('created "{}" folder inside nextcloud folder'.format(label_dir))
# create notes
for note_file in notes:
# if note_file == 'file-to-debug.json': bkp()
print('processing note "{}"'.format(note_file))
note = to_dict(open(path.join(keep_dir, note_file), encoding='utf-8').read())
text = ''
note_labels = []
# simplify labels array
if 'labels' in note:
for l in note['labels']:
note_labels.append(l['name'])
# create note body
if note['title']: text += '# ' + note['title'] + '\n'
if is_printing['pinned'] and note['isPinned']: text += '**PINNED**\n\n'
elif note['title']: text += '\n'
#
# create note content
#
if 'textContent' in note: text += note['textContent']
elif 'listContent' in note:
# print MD list
for item in note['listContent']:
text += '- [{}] {}\n'.format('x' if item['isChecked'] else ' ', item['text'])
# attachments
if 'attachments' in note:
for a in note['attachments']:
text += '![]({})\n'.format(a['filePath'])
is_space_added = False
# add space before last information if it isn't added yet
def add_space():
global is_space_added
global text
if not is_space_added:
text += '\n'
is_space_added = True
# print color
if is_printing['color'] and ('color' in note and note['color'] != 'DEFAULT'):
add_space()
text += '\ncolor: ' + note['color']
# print label when it has more than one
if is_printing['labels'] and len(note_labels) > 1:
add_space()
text += '\nlabels: ' + str(note_labels)[1:len(str(note_labels))-1].replace('\'', '')
# print last edit time
if is_printing['last-edit']:
add_space()
timestamp = int(int(note['userEditedTimestampUsec']) / 1000 / 1000)
date_str = date.utcfromtimestamp(timestamp).strftime('%d %B %Y %H:%M:%S')
text += '\nlast edit: {}'.format(date_str)
# print if is shared
if is_printing['shared'] and 'sharees' in note:
add_space()
text += 'shared with: '
for u in note['sharees']:
text += '{} {}'.format(u['email'], 'is the owner\n' if u['isOwner'] else '')
#
# end note content creation
#
# decide path
path_name = ''
if len(note_labels): path_name = fix_filename(note_labels[0])
if note['isArchived']: path_name = 'archived'
elif note['isTrashed']: path_name = 'trash'
# create note
fname = '{}.{}'.format(note_file[0:len(note_file)-5], 'md')
with open(path.join(nextcloud_dir, path_name, fname), 'w', encoding='utf-8') as f:
# use the original modification time for the new file
original_mod_time = path.getmtime(path.join(keep_dir, note_file))
utime(path.join(nextcloud_dir, path_name, fname), (original_mod_time, original_mod_time))
f.write(text)
print('note created inside "{}" folder\n'.format(path_name or 'nextcloud'))
print('\nAll notes converted and placed into {} folder'.format(nextcloud_dir))
@jhonasn
Copy link
Author

jhonasn commented Nov 26, 2024

updated line 77 as @Expello mentioned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment