Skip to content

Commit

Permalink
Merge pull request #21 from marph91/add-evernote
Browse files Browse the repository at this point in the history
add support for evernote
  • Loading branch information
marph91 authored Nov 27, 2024
2 parents ad67d8f + 55e41fa commit cc10d38
Show file tree
Hide file tree
Showing 10 changed files with 895 additions and 77 deletions.
21 changes: 21 additions & 0 deletions docs/formats/evernote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
This page describes how to convert notes from Evernote to Markdown.

## General Information

- [Website](https://evernote.com/)
- Typical extension: `.enex` or folder of `.enex` files

## Instructions

1. Export as described [at the website](https://help.evernote.com/hc/en-us/articles/209005557-Export-notes-and-notebooks-as-ENEX-or-HTML)
1. Choose "enex"
2. [Install jimmy](../index.md#installation)
3. Convert to Markdown. Example: `jimmy-cli-linux backup.enex --format evernote`
1. To decrypt encrypted notes, specify your password, like `jimmy-cli-linux backup.enex --format evernote --password 1234`
4. [Import to your app](../import_instructions.md)

## Import Structure

- Each `.enex` file is converted to a folder.
- Notes inside the `.enex` file are converted to Markdown files.
- Note links are recovered by matching the note name. This might not work sometimes, if the name was changed after creating the link.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Export data from your app and convert it to Markdown. For details, click on the
| **B** | <img src="https://bear.app/images/logo.png" style="height:100px;max-width:100px;"><br>[Bear](https://marph91.github.io/jimmy/formats/bear/) |
| **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/) |
| **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/) |
| **E** | <img src="https://avatars.githubusercontent.com/u/1120885" style="height:100px;max-width:100px;"><br>[Evernote](https://marph91.github.io/jimmy/formats/evernote/) |
| **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/) |
| **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/) |
| **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/) |
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ nav:
- ColorNote: formats/colornote.md
- Day One: formats/day_one.md
- Dynalist: formats/dynalist.md
- Evernote: formats/evernote.md
- Facebook: formats/facebook.md
# fusebase == nimbus note
- FuseBase: formats/nimbus_note.md
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Export data from your app and convert it to Markdown. For details, click on the
| **B** | <img src="https://bear.app/images/logo.png" style="height:100px;max-width:100px;"><br>[Bear](https://marph91.github.io/jimmy/formats/bear/) |
| **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/) |
| **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/) |
| **E** | <img src="https://avatars.githubusercontent.com/u/1120885" style="height:100px;max-width:100px;"><br>[Evernote](https://marph91.github.io/jimmy/formats/evernote/) |
| **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/) |
| **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/) |
| **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/) |
Expand Down
67 changes: 67 additions & 0 deletions src/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,73 @@
###########################################################


def safe_path(path: Path | str, max_name_length: int = 50) -> Path | str:
r"""
Return a safe version of the provided path or string.
Only the last part is considered if a path is provided.
>>> str(safe_path(Path("a/.")))
'a'
>>> str(safe_path(Path("ab\x00c")))
'ab_c'
>>> str(safe_path(Path("CON")))
'CON_'
>>> str(safe_path(Path("LPT7")))
'LPT7_'
>>> str(safe_path(Path("bc.")))
'bc_'
>>> safe_path("b:c")
'b_c'
>>> str(safe_path(Path("b*c")))
'b_c'
>>> safe_path("a/b/c")
'a_b_c'
>>> safe_path("") # doctest:+ELLIPSIS
'unnamed_...'
>>> safe_path("g" * 50, max_name_length=4)
'gggg'
"""
safe_name = path if isinstance(path, str) else path.name
if safe_name == "":
return unique_title()

# https://stackoverflow.com/a/31976060
# Windows restrictions
# fmt: off
forbidden_chars = [
"<", ">", ":", "\"", "/", "\\", "|", "?", "*",
] + [chr(value) for value in range(32)]
# fmt: on
for char in forbidden_chars:
safe_name = safe_name.replace(char, "_")

forbidden_names = (
["CON", "PRN", "AUX", "NUL"]
+ [f"COM{i}" for i in range(1, 10)]
+ [f"LPT{i}" for i in range(1, 10)]
)
if safe_name in forbidden_names:
safe_name += "_"

forbidden_last_chars = [" ", "."]
if safe_name[-1] in forbidden_last_chars:
safe_name = safe_name[:-1] + "_"

# Linux and MacOS restrictions
forbidden_chars = ["/", "\x00"]
for char in forbidden_chars:
safe_name = safe_name.replace(char, "_")

forbidden_names = [".", ".."]
if safe_name in forbidden_names:
safe_name += "_"

# Limit filename length: https://serverfault.com/a/9548
safe_name = safe_name[:max_name_length]

return safe_name if isinstance(path, str) else path.with_name(safe_name)


def get_available_formats() -> dict:
formats_dict = {}
for module in pkgutil.iter_modules(formats.__path__):
Expand Down
Loading

0 comments on commit cc10d38

Please sign in to comment.