Skip to content

Instantly share code, notes, and snippets.

@mbafford
Last active August 20, 2022 12:08
Show Gist options
  • Save mbafford/2c18f5c4d7b0dab673fddb1af2126680 to your computer and use it in GitHub Desktop.
Save mbafford/2c18f5c4d7b0dab673fddb1af2126680 to your computer and use it in GitHub Desktop.
Convert Journey.Cloud (Diary) JSON Export to Evernote Export (ENEX)
#!/usr/bin/env python3
# Converts the JSON export of Journey.Cloud diary entries into an Evernote Note Export format (ENEX) for easy import back into Evernote.
#
# This was a quick and dirty script to save someone the trouble of doing this process manually.
#
# Create/update date, journal text, location, and photos are preserved in the resulting Evernote Note.
import sys
import os
import json
import base64
import hashlib
from datetime import datetime
from xml.sax.saxutils import escape
def md5sum( file ):
m = hashlib.md5()
m.update( file )
return m.hexdigest()
def load_photos( journal_id, files ):
ret = {}
for file in files:
try:
with open(file, "rb") as f:
ret[ file ] = f.read()
except:
print("Journal %s - Unable to find photo file %s" % ( journal_id, file ) )
sys.exit(1)
return ret
def convert_journal( journal ):
created = datetime.fromtimestamp( journal['date_journal']/1000 )
updated = datetime.fromtimestamp( journal['date_modified']/1000 )
print("%s: Converting journal from %s (modified %s)" % ( journal['id'], created.strftime("%Y-%m-%d %H:%M:%S"), updated.strftime("%Y-%m-%d %H:%M:%S") ) )
photos = load_photos( journal['id'], journal['photos'] )
print("%s: Loaded: %d photos" % ( journal['id'], len(photos) ) )
resources_xml = ""
images = ""
for photo in photos:
mime = ""
if photo.lower().endswith( "jpg" ): mime = "image/jpeg"
elif photo.lower().endswith( "sticker" ): mime = "image/gif"
elif photo.lower().endswith( "png" ): mime = "image/png"
else:
print("Unable to determine MIME mime from filename: %s" % photo)
sys.exit(1)
resources_xml += """
<resource><data encoding="base64">
%(base64)s
</data><mime>%(mime)s</mime>
<resource-attributes>
<source-url>file://%(filename)s</source-url>
<file-name>%(filename)s</file-name>
</resource-attributes>
</resource>
""" % {
"base64": base64.b64encode(photos[photo]).decode(),
"filename": photo,
"mime": mime,
}
images += """ <div><en-media hash="%(hash)s" type="%(mime)s"/></div> """ % { "hash": md5sum( photos[photo] ), "mime": mime }
xml = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export2.dtd">
<en-export export-date="%(updated)s" application="Evernote/Windows" version="6.x">
<note>
<title>%(title)s</title>
<content><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><div>
%(text)s
</div>
%(images)s
</en-note>]]></content>
<created>%(created)s</created>
<updated>%(updated)s</updated>
<note-attributes>
<author>AUTHOR_NAME</author>
<source>desktop.win</source>
<source-application>evernote.win32</source-application>
<latitude>%(latitude)s</latitude>
<longitude>%(longitude)s</longitude>
</note-attributes>
%(resources)s
</note>
</en-export>""" % {
"title": created.strftime("%Y%m%d - %d %B, %Y"),
"text": escape( journal['text'] ).replace("\n", "<br/>"),
"created": created.strftime("%Y%m%dT%H%M%%SZ"),
"updated": updated.strftime("%Y%m%dT%H%M%%SZ"),
"latitude": journal.get('lat', ""),
"longitude": journal.get('lon', ""),
"resources": resources_xml,
"images": images,
}
out_file = "%s.enex" % journal['id']
with open( out_file, "w" ) as f:
f.write( xml )
print("%s: Wrote %s" % ( journal['id'], out_file ))
def find_and_convert():
for fn in [fn for fn in os.listdir() if fn.endswith(".json")]:
with open(fn, "r") as f:
journal = json.loads( f.read() )
convert_journal( journal )
find_and_convert()
@rogatty
Copy link

rogatty commented Sep 5, 2019

Thanks, it was really helpful! I improved it a bit in https://gist.github.com/rogatty/8a65ebc01062ebc48fac7473792ae993:

  • Added support for tags
  • Fixed UTF-8 issues
  • Combined all notes into a single ENEX file so Joplin imports them to a single notebook

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