Skip to content

Commit cc10d38

Browse files
authored
Merge pull request #21 from marph91/add-evernote
add support for evernote
2 parents ad67d8f + 55e41fa commit cc10d38

File tree

10 files changed

+895
-77
lines changed

10 files changed

+895
-77
lines changed

docs/formats/evernote.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
This page describes how to convert notes from Evernote to Markdown.
2+
3+
## General Information
4+
5+
- [Website](https://evernote.com/)
6+
- Typical extension: `.enex` or folder of `.enex` files
7+
8+
## Instructions
9+
10+
1. Export as described [at the website](https://help.evernote.com/hc/en-us/articles/209005557-Export-notes-and-notebooks-as-ENEX-or-HTML)
11+
1. Choose "enex"
12+
2. [Install jimmy](../index.md#installation)
13+
3. Convert to Markdown. Example: `jimmy-cli-linux backup.enex --format evernote`
14+
1. To decrypt encrypted notes, specify your password, like `jimmy-cli-linux backup.enex --format evernote --password 1234`
15+
4. [Import to your app](../import_instructions.md)
16+
17+
## Import Structure
18+
19+
- Each `.enex` file is converted to a folder.
20+
- Notes inside the `.enex` file are converted to Markdown files.
21+
- Note links are recovered by matching the note name. This might not work sometimes, if the name was changed after creating the link.

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Export data from your app and convert it to Markdown. For details, click on the
2828
| **B** | <img src="https://bear.app/images/logo.png" style="height:100px;max-width:100px;"><br>[Bear](https://marph91.github.io/jimmy/formats/bear/) |
2929
| **C** | <img src="https://raw.githubusercontent.com/CacherApp/cacher-cli/e241f06867dba740131db5314ef7fe279135baf6/images/cacher-icon.png" style="height:100px;max-width:100px;"><br>[Cacher](https://marph91.github.io/jimmy/formats/cacher/) | <img src="https://raw.githubusercontent.com/giuspen/cherrytree/c822b16681b002b8882645d8d1e8f109514ddb58/icons/cherrytree.svg" style="height:100px;max-width:100px;"><br>[CherryTree](https://marph91.github.io/jimmy/formats/cherrytree/) | <img src="https://avatars.githubusercontent.com/u/53916365?s=200&v=4" style="height:100px;max-width:100px;"><br>[Clipto](https://marph91.github.io/jimmy/formats/clipto/) | <img src="https://www.colornote.com/wp-content/uploads/2016/05/cropped-favicon.png" style="height:100px;max-width:100px;"><br>[ColorNote](https://marph91.github.io/jimmy/formats/colornote/) |
3030
| **D** | <img src="https://seeklogo.com/images/D/day-one-logo-F4CA245C26-seeklogo.com.png" style="height:100px;max-width:100px;"><br>[Day&nbsp;One](https://marph91.github.io/jimmy/formats/day_one/) | <img src="https://images.saasworthy.com/dynalist_5288_logo_1576239391_xhkcg.jpg" style="height:100px;max-width:100px;"><br>[Dynalist](https://marph91.github.io/jimmy/formats/dynalist/) |
31+
| **E** | <img src="https://avatars.githubusercontent.com/u/1120885" style="height:100px;max-width:100px;"><br>[Evernote](https://marph91.github.io/jimmy/formats/evernote/) |
3132
| **F** | <img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/2021_Facebook_icon.svg" style="height:100px;max-width:100px;"><br>[Facebook](https://marph91.github.io/jimmy/formats/facebook/) | <img src="https://wavebox.pro/store2/store/0b46bf0a-107c-4fa2-a657-3df7412e3d3d.png" style="height:100px;max-width:100px;"><br>[FuseBase, Nimbus&nbsp;Note](https://marph91.github.io/jimmy/formats/fusebase/) |
3233
| **G** | <img src="https://www.gstatic.com/images/branding/product/1x/docs_2020q4_96dp.png" style="height:100px;max-width:100px;"><br>[Google&nbsp;Docs](https://marph91.github.io/jimmy/formats/google_docs/) | <img src="https://www.gstatic.com/images/branding/product/1x/keep_2020q4_96dp.png" style="height:100px;max-width:100px;"><br>[Google&nbsp;Keep](https://marph91.github.io/jimmy/formats/google_keep/) |
3334
| **J** | <img src="https://github.com/laurent22/joplin/blob/dev/Assets/LinuxIcons/128x128.png?raw=true" style="height:100px;max-width:100px;"><br>[Joplin](https://marph91.github.io/jimmy/formats/joplin/) | <img src="https://raw.githubusercontent.com/jrnl-org/jrnl/85a98afcd91ed873c0eceba9893c3ec424f201b8/docs_theme/img/logo.svg" style="height:100px;max-width:100px;"><br>[jrnl](https://marph91.github.io/jimmy/formats/jrnl/) |

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ nav:
6161
- ColorNote: formats/colornote.md
6262
- Day One: formats/day_one.md
6363
- Dynalist: formats/dynalist.md
64+
- Evernote: formats/evernote.md
6465
- Facebook: formats/facebook.md
6566
# fusebase == nimbus note
6667
- FuseBase: formats/nimbus_note.md

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Export data from your app and convert it to Markdown. For details, click on the
3434
| **B** | <img src="https://bear.app/images/logo.png" style="height:100px;max-width:100px;"><br>[Bear](https://marph91.github.io/jimmy/formats/bear/) |
3535
| **C** | <img src="https://raw.githubusercontent.com/CacherApp/cacher-cli/e241f06867dba740131db5314ef7fe279135baf6/images/cacher-icon.png" style="height:100px;max-width:100px;"><br>[Cacher](https://marph91.github.io/jimmy/formats/cacher/) | <img src="https://raw.githubusercontent.com/giuspen/cherrytree/c822b16681b002b8882645d8d1e8f109514ddb58/icons/cherrytree.svg" style="height:100px;max-width:100px;"><br>[CherryTree](https://marph91.github.io/jimmy/formats/cherrytree/) | <img src="https://avatars.githubusercontent.com/u/53916365?s=200&v=4" style="height:100px;max-width:100px;"><br>[Clipto](https://marph91.github.io/jimmy/formats/clipto/) | <img src="https://www.colornote.com/wp-content/uploads/2016/05/cropped-favicon.png" style="height:100px;max-width:100px;"><br>[ColorNote](https://marph91.github.io/jimmy/formats/colornote/) |
3636
| **D** | <img src="https://seeklogo.com/images/D/day-one-logo-F4CA245C26-seeklogo.com.png" style="height:100px;max-width:100px;"><br>[Day&nbsp;One](https://marph91.github.io/jimmy/formats/day_one/) | <img src="https://images.saasworthy.com/dynalist_5288_logo_1576239391_xhkcg.jpg" style="height:100px;max-width:100px;"><br>[Dynalist](https://marph91.github.io/jimmy/formats/dynalist/) |
37+
| **E** | <img src="https://avatars.githubusercontent.com/u/1120885" style="height:100px;max-width:100px;"><br>[Evernote](https://marph91.github.io/jimmy/formats/evernote/) |
3738
| **F** | <img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/2021_Facebook_icon.svg" style="height:100px;max-width:100px;"><br>[Facebook](https://marph91.github.io/jimmy/formats/facebook/) | <img src="https://wavebox.pro/store2/store/0b46bf0a-107c-4fa2-a657-3df7412e3d3d.png" style="height:100px;max-width:100px;"><br>[FuseBase, Nimbus&nbsp;Note](https://marph91.github.io/jimmy/formats/fusebase/) |
3839
| **G** | <img src="https://www.gstatic.com/images/branding/product/1x/docs_2020q4_96dp.png" style="height:100px;max-width:100px;"><br>[Google&nbsp;Docs](https://marph91.github.io/jimmy/formats/google_docs/) | <img src="https://www.gstatic.com/images/branding/product/1x/keep_2020q4_96dp.png" style="height:100px;max-width:100px;"><br>[Google&nbsp;Keep](https://marph91.github.io/jimmy/formats/google_keep/) |
3940
| **J** | <img src="https://github.com/laurent22/joplin/blob/dev/Assets/LinuxIcons/128x128.png?raw=true" style="height:100px;max-width:100px;"><br>[Joplin](https://marph91.github.io/jimmy/formats/joplin/) | <img src="https://raw.githubusercontent.com/jrnl-org/jrnl/85a98afcd91ed873c0eceba9893c3ec424f201b8/docs_theme/img/logo.svg" style="height:100px;max-width:100px;"><br>[jrnl](https://marph91.github.io/jimmy/formats/jrnl/) |

src/common.py

+67
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,73 @@
2828
###########################################################
2929

3030

31+
def safe_path(path: Path | str, max_name_length: int = 50) -> Path | str:
32+
r"""
33+
Return a safe version of the provided path or string.
34+
Only the last part is considered if a path is provided.
35+
36+
>>> str(safe_path(Path("a/.")))
37+
'a'
38+
>>> str(safe_path(Path("ab\x00c")))
39+
'ab_c'
40+
>>> str(safe_path(Path("CON")))
41+
'CON_'
42+
>>> str(safe_path(Path("LPT7")))
43+
'LPT7_'
44+
>>> str(safe_path(Path("bc.")))
45+
'bc_'
46+
>>> safe_path("b:c")
47+
'b_c'
48+
>>> str(safe_path(Path("b*c")))
49+
'b_c'
50+
>>> safe_path("a/b/c")
51+
'a_b_c'
52+
>>> safe_path("") # doctest:+ELLIPSIS
53+
'unnamed_...'
54+
>>> safe_path("g" * 50, max_name_length=4)
55+
'gggg'
56+
"""
57+
safe_name = path if isinstance(path, str) else path.name
58+
if safe_name == "":
59+
return unique_title()
60+
61+
# https://stackoverflow.com/a/31976060
62+
# Windows restrictions
63+
# fmt: off
64+
forbidden_chars = [
65+
"<", ">", ":", "\"", "/", "\\", "|", "?", "*",
66+
] + [chr(value) for value in range(32)]
67+
# fmt: on
68+
for char in forbidden_chars:
69+
safe_name = safe_name.replace(char, "_")
70+
71+
forbidden_names = (
72+
["CON", "PRN", "AUX", "NUL"]
73+
+ [f"COM{i}" for i in range(1, 10)]
74+
+ [f"LPT{i}" for i in range(1, 10)]
75+
)
76+
if safe_name in forbidden_names:
77+
safe_name += "_"
78+
79+
forbidden_last_chars = [" ", "."]
80+
if safe_name[-1] in forbidden_last_chars:
81+
safe_name = safe_name[:-1] + "_"
82+
83+
# Linux and MacOS restrictions
84+
forbidden_chars = ["/", "\x00"]
85+
for char in forbidden_chars:
86+
safe_name = safe_name.replace(char, "_")
87+
88+
forbidden_names = [".", ".."]
89+
if safe_name in forbidden_names:
90+
safe_name += "_"
91+
92+
# Limit filename length: https://serverfault.com/a/9548
93+
safe_name = safe_name[:max_name_length]
94+
95+
return safe_name if isinstance(path, str) else path.with_name(safe_name)
96+
97+
3198
def get_available_formats() -> dict:
3299
formats_dict = {}
33100
for module in pkgutil.iter_modules(formats.__path__):

0 commit comments

Comments
 (0)