forked from shaunlgs/KeepToEnex
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathjoplin-update-frontmatter.py
102 lines (82 loc) · 3.85 KB
/
joplin-update-frontmatter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/python3
##
# joplin-update-frontmatter.py
#
# When Joplin exports files as MD (for use in Obsidian) it does not include Tags
# Obsidian stores Tags in a block at the front of the MD files called FrontMatter (https://help.obsidian.md/Advanced+topics/YAML+front+matter)
# This script is designed to create a YAML FrontMatter block, within each Joplin note, so that on export Obsidian notes will have Tags
# Additional fields can be added to the FrontMatter, but Obsidian only recognizes "aliases", "tags", and "cssclass"
#
## Joplin API Documentation (https://joplinapp.org/api/references/rest_api/):
# curl http://localhost:41184/notes?token=YOURBIGTOKEN
# Get a specific note body, created times, updated times:
# curl "http://localhost:41184/notes/NOTEID/?fields=body,created_time,user_created_time,updated_time,user_updated_time&token=YOURBIGTOKEN"
# Get a specific notes tags:
# curl "http://localhost:41184/notes/NOTEID/tags?token=YOURBIGTOKEN"
# Program creates YAML FrontMatter in the following format:
# ---
# created: 2012-11-10T09:25:49-0800
# updated: 2012-11-10T09:29:35-0800
# tags: [Note, Multi-Word-Tag, lower-case-tag]
# ---
import sys, re, json, random, requests
from datetime import datetime, timezone
TOKEN = sys.argv[1]
NOTES_ENDPOINT = "http://localhost:41184/notes"
TITLE_CHARS = 55
TITLE_LEEWAY = 10
def get_note_metadata(noteid):
return requests.get('{}/{}/?fields=body,title,user_created_time,user_updated_time&token={}'.format(NOTES_ENDPOINT, noteid, TOKEN))
def get_note_tags(noteid):
note_tags = []
res = requests.get('{}/{}/tags?token={}'.format(NOTES_ENDPOINT, noteid, TOKEN)).json()["items"]
return [re.sub(r'[^a-zA-Z_-]', '', tag.get("title").replace(" ", "-")) for tag in res]
def get_note_ids(page=0):
res = requests.get('{}?order_by=user_updated_time&order_dir=DESC&page={}&token={}'.format(NOTES_ENDPOINT, page, TOKEN))
return res
def fuzzy_title_length(title):
if len(title) > TITLE_CHARS:
ind = title.find(" ", TITLE_CHARS - TITLE_LEEWAY)
return title[0: ind].strip()
return title.strip()
def process_notes(page=0):
res = get_note_ids(page)
for note in res.json()["items"]:
note_metadata = get_note_metadata(note["id"]).json()
body = note_metadata["body"]
title = note_metadata["title"].strip()
created = datetime.fromtimestamp(round(note_metadata["user_created_time"] / 1000), timezone.utc).astimezone()
updated = datetime.fromtimestamp(round(note_metadata["user_updated_time"] / 1000), timezone.utc).astimezone()
print("original title: %s " % title)
if title.startswith("Keep Note"):
title = body.replace('\n', ' ')
title = re.sub(r'[^a-zA-Z0-9\s\.,\&\)\(_-]', '', fuzzy_title_length(title))
front_matter = ""
if body.strip().startswith("---"):
print("Note <%s> already has frontmatter: %s" % (title, body))
else:
front_matter = f"""---
created: {created}
updated: {updated}
"""
tags = get_note_tags(note["id"])
if tags:
front_matter += "tags: [" + ", ".join(tags) + "]\n"
front_matter += "---\n\n"
# front_matter += "# " + title + "\n"
body = front_matter + body
print("id:", note["id"])
print("filename", title)
print("body", body)
if body and title and (body != note_metadata["body"] or title != note_metadata["title"]):
# Only update if the new body is not empty (just a safeguard)
requests.put(
'{}/{}?token={}'.format(NOTES_ENDPOINT, note["id"], TOKEN),
data='{{ "body" : {}, "title": {} }}'.format(json.dumps(body), json.dumps(title))
)
else:
print("skipping put request: %s" % title)
if res.json()["has_more"]:
process_notes(page+1)
if __name__ == "__main__":
process_notes()