From 412b316bad7395025600f4c2a3510a74624ab5f2 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Fri, 23 Oct 2020 15:28:57 +0200 Subject: [PATCH 1/9] Add JoplinWinBackup.exe download link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a199ee8..1a37e09 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,8 @@ Since there is no possibility for an automatic backup under windows, the require Rename the `JoplinWinBackup.ini.example` to `JoplinWinBackup.ini` and place it in the same folder es the `JoplinWinBackup.au3` or `JoplinWinBackup.exe`. +The latest JoplinWinBackup.exe can be downloaded from the [latest release](https://github.com/JackGruber/Joplin-Tools/releases/latest/download/JoplinWinBackup.exe). + Options from the JoplinWinBackup.ini - `JoplinWinBackup` Defines the Path to Joplin exe. Default `C:\Program Files\Joplin\Joplin.exe` From bbcdad18a78789f3df56299ef8079b2618b9eef8 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Sat, 24 Oct 2020 09:54:17 +0200 Subject: [PATCH 2/9] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a37e09..5a875f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Joplin tools -Various python tools for Joplin. -For the communication with Joplin the API is used. +Various Python and AutoIt tools for Joplin. +Python use the Joplin API for communication. ## Additional python modules @@ -96,6 +96,8 @@ python todo_overview.py --title "Open ToDo's" --as-todo --tag "importend" ### JoplinWinBackup.au3 +--- + Since there is no possibility for an automatic backup under windows, the required key combinations are sent to joplin via autoit to create a backup. Rename the `JoplinWinBackup.ini.example` to `JoplinWinBackup.ini` and place it in the same folder es the `JoplinWinBackup.au3` or `JoplinWinBackup.exe`. From 6486425133852ab4471f7bf6a21720ebbbe1e63f Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Tue, 17 Nov 2020 14:50:06 +0100 Subject: [PATCH 3/9] Add pagination for search API --- joplin/joplinapi.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index 8691252..fa3f187 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -352,14 +352,16 @@ def GetFolderNotes(folder_id): return response.json() -def Search(query, type, fields=None): +def Search(query, type, limit=10, page=1, fields=None, order_by="", order_dir="ASC"): joplin = GetEndpoint() if fields is None: fields = "id,title" + if order_by != "": + order_by = "&order_by=" + order_by response = requests.get(joplin['endpoint'] + - "/search?token=" + joplin['token'] + "&query=" + query + "&type=" + type + "&fields=" + fields) + "/search?token=" + joplin['token'] + "&query=" + query + "&type=" + type + "&fields=" + fields + "&limit=" + str(limit) + "&page=" + str(page) + "&order_dir=" + order_dir + order_by) if response.status_code != 200: print("Search ERROR") return False From 4b04bc88e9a290b79eb6b544a010b22d4ae5b74c Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Tue, 17 Nov 2020 15:43:16 +0100 Subject: [PATCH 4/9] Use search for GetTagID --- joplin/joplinapi.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index fa3f187..a6393be 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -206,18 +206,14 @@ def LoadTags(reload=False): def GetTagID(search_tag): - tags = LoadTags() search_tag = search_tag.strip() + tags = Search(search_tag, "tag", limit=10, page=1, fields="id") - if tags == False: - return False - for tag in tags: - if tag['title'].lower() == search_tag.lower(): - return tag['id'] - + if len(tags['items']) == 1: + return tags['items'][0]['id'] + else: return False - def AddTagToNote(tag, note_id, create_tag=False): joplin = GetEndpoint() tag_id = GetTagID(tag) From 6ba0618dcc179039d14619a685ac68bcc8ffe601 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Tue, 17 Nov 2020 15:44:49 +0100 Subject: [PATCH 5/9] Remove LoadTags func --- joplin/joplinapi.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index a6393be..c6cc2a7 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -190,29 +190,14 @@ def EncodeResourceFile(filename, datatype): img = f"data:{datatype};base64,{encoded.decode()}" return img - -def LoadTags(reload=False): - joplin = GetEndpoint() - global JOPLIN_TAGS - if(reload == True or JOPLIN_TAGS is None): - response = requests.get(joplin['endpoint'] + - "/tags?token=" + joplin['token']) - if response.status_code != 200: - print("Tag load ERROR") - return False - else: - JOPLIN_TAGS = response.json() - return JOPLIN_TAGS - - def GetTagID(search_tag): search_tag = search_tag.strip() tags = Search(search_tag, "tag", limit=10, page=1, fields="id") - + if len(tags['items']) == 1: return tags['items'][0]['id'] else: - return False + return False def AddTagToNote(tag, note_id, create_tag=False): joplin = GetEndpoint() @@ -245,7 +230,6 @@ def CreateTag(tag): return False else: json_response = response.json() - LoadTags(True) return json_response['id'] return True From 39578f509f66607a58fb68eebf06a1379cdd6232 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Wed, 18 Nov 2020 16:15:04 +0100 Subject: [PATCH 6/9] GetNotebookID with pagination --- joplin/joplinapi.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index c6cc2a7..4c7d0e4 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -54,33 +54,25 @@ def GetEndpoint(): def GetNotebookID(notebook_name): - """ Find the ID of the destination folder - adapted logic from jhf2442 on Joplin forum - https://discourse.joplin.cozic.net/t/import-txt-files/692 - """ notebook_id = "" joplin = GetEndpoint() - try: - res = requests.get(joplin['endpoint'] + - "/folders?token=" + joplin['token']) + page = 1 + while True: + res = requests.get(joplin['endpoint'] + "/folders?token=" + joplin['token'] + "fields=id,title,parent_id&limit=100&page=" + str(page)) folders = res.json() + + for folder in folders['items']: + if folder["title"] == notebook_name: + notebook_id = folder["id"] + break + + page += 1 + + if folders.get('has_more') == None or folders.get('has_more') == False: + break - for folder in folders: - if folder.get("title") == notebook_name: - notebook_id = folder.get("id") - if notebook_id == "": - for folder in folders: - if "children" in folder: - for child in folder.get("children"): - if child.get("title") == notebook_name: - notebook_id = child.get("id") - except requests.ConnectionError as e: - print("Connection Error - Is Joplin Running?") - except Exception as e: - print("Error - on get joplin notebook id") - - if notebook_id == "" or notebook_id == "err": + if notebook_id == "": return False else: return notebook_id From 5f764427a07e0e1eeed555fdc98d5a5fb00281af Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Wed, 18 Nov 2020 16:15:43 +0100 Subject: [PATCH 7/9] Changes for pagination --- todo_overview.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/todo_overview.py b/todo_overview.py index 7be5fee..f2c30a5 100644 --- a/todo_overview.py +++ b/todo_overview.py @@ -68,12 +68,11 @@ def Main(notebook, title, token, url, add_tag, as_todo): add_tag = add_tag.replace(", ", ",") add_tag = add_tag.split(",") - note = joplinapi.Search('title:"' + title + '"', "note", "id,title,is_todo,body") + note = joplinapi.Search(query='title:"' + title + '"', type="note", fields="id,title,is_todo,body") - if len(note) == 1: - note_id = note[0]['id'] - note = joplinapi.GetNotes(note_id,"id,title,is_todo,body") - body_org = note['body'] + if len(note['items']) == 1: + note_id = note['items'][0]['id'] + body_org = note['items'][0]['body'] elif len(note) > 1: print("Error multiple matching notes found") sys.exit(1) @@ -90,20 +89,25 @@ def Main(notebook, title, token, url, add_tag, as_todo): print("No notebook defined and no note with title '" + title + "' found.") sys.exit(1) - todos = joplinapi.Search("type:todo iscompleted:0", - "note", "id,title,todo_due,todo_completed") - todos.sort(key=lambda r: r['todo_due']) - body = "| Date | Title |\n" body += "| --- | --- |\n" - for todo in todos: - if note_id != todo['id']: - epoch = int(todo['todo_due'] / 1000) - date = datetime.fromtimestamp(epoch).strftime('%Y-%m-%d %H:%M') - if epoch < int(time.time()): - date += " \U00002757" - body += "|" + date + \ - "|[" + todo['title'] + "](:/" + todo['id'] + ")|\n" + page = 1 + while True: + todos = joplinapi.Search(query="type:todo iscompleted:0", + type="note", fields="id,title,todo_due,todo_completed", + order_by="todo_due", order_dir="ASC", page=page) + for todo in todos['items']: + if note_id != todo['id']: + epoch = int(todo['todo_due'] / 1000) + date = datetime.fromtimestamp(epoch).strftime('%Y-%m-%d %H:%M') + if epoch < int(time.time()): + date += " \U00002757" + body += "|" + date + \ + "|[" + todo['title'] + "](:/" + todo['id'] + ")|\n" + + page += 1 + if todos['has_more'] == False: + break if note_id is None: note_id = joplinapi.CreateNote(title, body, notebook_id) From 99c48cfffebfdc30a9af66b293c05895d407d1f3 Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Wed, 18 Nov 2020 17:00:39 +0100 Subject: [PATCH 8/9] Add pagination --- add_pdf_previews.py | 31 +++++++------- joplin/joplinapi.py | 22 ++++++---- joplin/joplintools.py | 96 +++++++++++++++++++++++-------------------- 3 files changed, 82 insertions(+), 67 deletions(-) diff --git a/add_pdf_previews.py b/add_pdf_previews.py index 10efb28..b568f30 100644 --- a/add_pdf_previews.py +++ b/add_pdf_previews.py @@ -38,24 +38,23 @@ def Main(notebook, token, url): print("Wait for Joplin") time.sleep(10) + query = "resource:application/pdf" if notebook is not None: - notebook_id = joplinapi.GetNotebookID(notebook) - if notebook_id == False: - print("Notebook not found") - sys.exit(1) - else: - note_ids = joplinapi.GetFolderNotes(notebook_id) - else: - note_ids = joplinapi.GetNotes(None, "id") + query += ' notebook:"' + notebook + '"' - print("Process 1 of " + str(len(note_ids)) + " notes") - count = 1 - for note in note_ids: - if count % 10 == 0: - print("Process " + str(count) + " of " + str(len(note_ids)) + " notes") - joplintools.AddPDFPreviewToNote(note['id']) - count += 1 - + page = 1 + while True: + note_ids = joplinapi.Search(query=query,type="note", fields="id", limit=2, page=page) + count = 1 + for note in note_ids['items']: + if count % 10 == 0: + print("Process 1 of " + str(len(note_ids['items'])) + " notes from batch " + str(page)) + joplintools.AddPDFPreviewToNote(note['id']) + count += 1 + + page += page + if note_ids['has_more'] == False: + break; if __name__ == "__main__": Main() diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index 4c7d0e4..ed7dca0 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -242,7 +242,7 @@ def Ping(): return True -def GetNotes(note_id=None, fields=None): +def GetNotes(note_id=None, fields=None, limit=10, page=1, order_by="", order_dir="ASC"): joplin = GetEndpoint() if fields is None and note_id is not None: @@ -251,12 +251,14 @@ def GetNotes(note_id=None, fields=None): fields = "id,title,is_todo,todo_completed,parent_id,updated_time,user_updated_time,user_created_time,encryption_applied" if note_id is not None: - note_id = "/" + note_id + response = requests.get(joplin['endpoint'] + + "/notes/" + note_id + "?token=" + joplin['token'] + "&fields=" + fields) else: - note_id = "" + if order_by != "": + order_by = "&order_by=" + order_by + response = requests.get(joplin['endpoint'] + + "/notes?token=" + joplin['token'] + "&fields=" + fields + "&limit=" + str(limit) + "&page=" + str(page) + "&order_dir=" + order_dir + order_by) - response = requests.get(joplin['endpoint'] + - "/notes" + note_id + "?token=" + joplin['token'] + "&fields=" + fields) if response.status_code != 200: print("GetNotes ERROR") return False @@ -266,11 +268,17 @@ def GetNotes(note_id=None, fields=None): return response.json() -def GetNoteResources(note_id): +def GetNoteResources(note_id, fields, limit=10, page=1, order_by="", order_dir="ASC"): joplin = GetEndpoint() + if fields is None: + fields = "id,title" + + if order_by != "": + order_by = "&order_by=" + order_by + response = requests.get(joplin['endpoint'] + - "/notes/" + note_id + "/resources?token=" + joplin['token']) + "/notes/" + note_id + "/resources?token=" + joplin['token'] + "&fields=" + fields + "&limit=" + str(limit) + "&page=" + str(page) + "&order_dir=" + order_dir + order_by) if response.status_code != 200: print("GetResources ERROR") diff --git a/joplin/joplintools.py b/joplin/joplintools.py index 2f45ffc..8e8e2bd 100644 --- a/joplin/joplintools.py +++ b/joplin/joplintools.py @@ -51,47 +51,55 @@ def AddPDFPreviewToBody(body, pdf_id, preview_id): def AddPDFPreviewToNote(note_id): print("AddPDFPreviewToNote: " + note_id, end=" ") - res = joplinapi.GetNoteResources(note_id) - if res is None or res == False: - return True - - pdfs = GetAllMimeResources(res, "application/pdf") - if pdfs == False: - print("") - return True - - note = joplinapi.GetNotes(note_id, "body, title") - body_new = note['body'] - - print("\t" + note['title']) - - note_update = False - for pdf in pdfs: - print("\tResource: " + pdf['id'] + "\t" + pdf['title']) - tmp = os.path.join(tempfile.gettempdir(), pdf['title']) - png = tmp + ".png" - - if re.search(r"!\[" + pdf['id'] + r"\]", body_new) is not None: - print("\talready present") - continue - - if re.search(r"!\[.*\]\(:/[\da-z]+\)\n(\[.*\]\(:\/" + pdf['id'] + r"\))", body_new) is not None: - print("\tpossible present") - continue - - if joplinapi.GetResourcesFile(pdf['id'], tmp) == False: - return False - - if CreatePDFPreview(tmp, png, 1) == True: - img_res = joplinapi.CreateResource(png) - if img_res != False: - body_new = AddPDFPreviewToBody(body_new, pdf['id'], img_res['id']) - note_update = True - - if note_update == True: - data = {} - data['body'] = body_new - json_data = json.dumps(data) - joplinapi.UpdateNote(note_id, json_data) - - print("") + + page = 1 + while True: + res = joplinapi.GetNoteResources(note_id, fields="id,title,mime", limit=100, page=page) + + if res is None or res == False: + return True + elif len(res['items']) > 0: + pdfs = GetAllMimeResources(res['items'], "application/pdf") + + if pdfs == False: + print("") + return True + else: + note = joplinapi.GetNotes(note_id, "body, title") + body_new = note['body'] + + print("\t" + note['title']) + + note_update = False + + for pdf in pdfs: + print("\tResource: " + pdf['id'] + "\t" + pdf['title']) + tmp = os.path.join(tempfile.gettempdir(), pdf['title']) + png = tmp + ".png" + + if re.search(r"!\[" + pdf['id'] + r"\]", body_new) is not None: + print("\talready present") + continue + + if re.search(r"!\[.*\]\(:/[\da-z]+\)\n(\[.*\]\(:\/" + pdf['id'] + r"\))", body_new) is not None: + print("\tpossible present") + continue + + if joplinapi.GetResourcesFile(pdf['id'], tmp) == False: + return False + + if CreatePDFPreview(tmp, png, 1) == True: + img_res = joplinapi.CreateResource(png) + if img_res != False: + body_new = AddPDFPreviewToBody(body_new, pdf['id'], img_res['id']) + note_update = True + + if note_update == True: + data = {} + data['body'] = body_new + json_data = json.dumps(data) + joplinapi.UpdateNote(note_id, json_data) + print("") + + if res['has_more'] == False: + break From 95270489ed622ec7e80bb8157e5bfccdf84abeba Mon Sep 17 00:00:00 2001 From: JackGruber <24863925+JackGruber@users.noreply.github.com> Date: Wed, 18 Nov 2020 17:01:17 +0100 Subject: [PATCH 9/9] Remove GetFolderNotes (use search) --- joplin/joplinapi.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/joplin/joplinapi.py b/joplin/joplinapi.py index ed7dca0..d3a99c4 100644 --- a/joplin/joplinapi.py +++ b/joplin/joplinapi.py @@ -258,7 +258,7 @@ def GetNotes(note_id=None, fields=None, limit=10, page=1, order_by="", order_dir order_by = "&order_by=" + order_by response = requests.get(joplin['endpoint'] + "/notes?token=" + joplin['token'] + "&fields=" + fields + "&limit=" + str(limit) + "&page=" + str(page) + "&order_dir=" + order_dir + order_by) - + if response.status_code != 200: print("GetNotes ERROR") return False @@ -319,19 +319,6 @@ def UpdateNote(note_id, json_properties): else: return True - -def GetFolderNotes(folder_id): - joplin = GetEndpoint() - - response = requests.get(joplin['endpoint'] + - "/folders/" + folder_id + "/notes?token=" + joplin['token']) - if response.status_code != 200: - print("GetFolderNotes ERROR") - return False - else: - return response.json() - - def Search(query, type, limit=10, page=1, fields=None, order_by="", order_dir="ASC"): joplin = GetEndpoint()