Skip to content

Instantly share code, notes, and snippets.

@erbanku
Last active February 11, 2026 07:52
Show Gist options
  • Select an option

  • Save erbanku/e2f9dd6e4a98591e823b6fd0d05f5037 to your computer and use it in GitHub Desktop.

Select an option

Save erbanku/e2f9dd6e4a98591e823b6fd0d05f5037 to your computer and use it in GitHub Desktop.
Memos to Markdown Converter: Convert Memos (v0.21 tested) JSON export into a single Markdown file
import json
from datetime import datetime, timezone, timedelta
def safe_datetime(ts):
if ts is None or ts == 0:
return None
if ts > 9999999999:
ts = ts / 1000
try:
return datetime.fromtimestamp(ts, tz=timezone.utc)
except (OSError, ValueError, OverflowError):
return None
def to_blockquote(text):
return "\n".join(f"> {line}" if line.strip() else ">" for line in text.splitlines())
def convert_memos_to_markdown(input_file="memos.json", output_file="memos.md"):
with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
for key in ("data", "memos", "items"):
if key in data and isinstance(data[key], list):
data = data[key]
break
if not isinstance(data, list):
data = [data]
seen_ids = set()
unique_data = []
for memo in data:
mid = memo.get("id") or memo.get("name")
if mid not in seen_ids:
seen_ids.add(mid)
unique_data.append(memo)
data = unique_data
pinned = []
archived = []
normal = []
for m in data:
if m.get("rowStatus") == "ARCHIVED":
archived.append(m)
elif m.get("pinned"):
pinned.append(m)
else:
normal.append(m)
sort_key = lambda m: m.get("displayTs") or m.get("createdTs", 0)
pinned.sort(key=sort_key, reverse=True)
normal.sort(key=sort_key, reverse=True)
archived.sort(key=sort_key, reverse=True)
shanghai_tz = timezone(timedelta(hours=8))
now_str = datetime.now(tz=shanghai_tz).strftime("%Y-%m-%d %H:%M:%S")
lines = []
lines.append("# All Memos Notes")
lines.append("")
lines.append(f"> **All**: {len(data)} **Pinned**: {len(pinned)} **Normal**: {len(normal)} **Archived**: {len(archived)}")
lines.append(f"> Exported at: {now_str} Asia/Shanghai")
lines.append("")
skipped = 0
def write_memos(memos, label=""):
nonlocal skipped
for memo in memos:
ts = memo.get("displayTs") or memo.get("createdTs", 0)
dt = safe_datetime(ts)
if dt is None:
skipped += 1
print(f"Skipped memo id={memo.get('id')} (invalid timestamp: {ts})")
continue
time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
prefix = f"{label} " if label else ""
lines.append(f"## {prefix}{time_str}")
lines.append("")
block_lines = []
content = memo.get("content", "").strip()
if content:
block_lines.append(to_blockquote(content))
image_lines = []
resources = memo.get("resourceList", [])
for res in resources:
if res.get("type", "").startswith("image/"):
filename = res.get("filename", "image")
link = res.get("externalLink", "")
if link:
image_lines.append(f"> ![{filename}]({link})")
if image_lines:
block_lines.append("\n".join(image_lines))
if block_lines:
lines.append("\n>\n".join(block_lines))
lines.append("")
write_memos(pinned, label="Pinned")
write_memos(normal)
write_memos(archived, label="Archived")
with open(output_file, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
total = len(data) - skipped
print(f"Done. {total} note(s) (pinned: {len(pinned)}, normal: {len(normal)}, archived: {len(archived)}) written to '{output_file}' (skipped: {skipped})")
if __name__ == "__main__":
convert_memos_to_markdown()
@erbanku
Copy link
Author

erbanku commented Feb 11, 2026

Memos to Markdown Converter

Convert Memos (v0.21 tested) JSON export into a single Markdown file.

Usage

  1. Export memos as JSON

    Open in browser or curl:

    https://memos.yourdomain.com/api/v1/memo?openId=YOUR_OPEN_ID
    

    Save the returned JSON as memos.json.

  2. Run the script

    Place convert_memos.py in the same directory as memos.json, then:

    python convert_memos.py
  3. Output

    A memos.md file will be generated with:

    • Stats (total / pinned / normal / archived counts)
    • Pinned notes first (newest → oldest)
    • Normal notes (newest → oldest)
    • Archived notes last (newest → oldest)
    • All note content in blockquotes with inline images

Requirements

  • Python 3.6+
  • No external dependencies

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