Skip to content

Instantly share code, notes, and snippets.

@mbarrben
Last active February 23, 2025 19:08
Show Gist options
  • Save mbarrben/c6d205dcb1631b29e070308daf5ace46 to your computer and use it in GitHub Desktop.
Save mbarrben/c6d205dcb1631b29e070308daf5ace46 to your computer and use it in GitHub Desktop.
Script to parse a Markdown file with notes exported from Xiaomi Cloud following and then upload them to Google Keep. Notes exported using https://github.com/nogiszd/xiaomi-note-exporter
import gkeepapi # https://github.com/kiwiz/gkeepapi
import gpsoauth # https://github.com/simon-weber/gpsoauth
import re
import os
import readline
import threading
import time
import sys
# INSTRUCTIONS
# 1. First export your Xiaomi Cloud notes to a Markdown file using https://github.com/nogiszd/xiaomi-note-exporter?tab=readme-ov-file
# 2. Get and ANDROID_ID from your old Android device (ANDROID_ID is sort of deprecated, I was able to get one from a Xiaomi Redmi Note 5 with Android 9)
# 3. Use that Android device to log in to your Google Account where you want to import the notes to
# 4. Get an OAuth token following https://github.com/simon-weber/gpsoauth?tab=readme-ov-file#alternative-flow
# 5. Run this script entering your email, ANDROID_ID, token and file path and hope for the best
# Enable tab-completion for file paths
def complete_path(text, state):
"""Autocompletes file and folder paths dynamically."""
dirname = os.path.dirname(text)
prefix = os.path.basename(text)
# If no directory is specified, search in the current directory
search_path = os.path.expanduser(dirname) if dirname else "."
try:
# List matching files and directories
matches = [os.path.join(dirname, f) for f in os.listdir(search_path) if f.startswith(prefix)]
except FileNotFoundError:
matches = []
return matches[state] if state < len(matches) else None
readline.set_completer_delims(" \t\n") # Avoids breaking on spaces
readline.parse_and_bind("tab: complete") # Enable tab completion
readline.set_completer(complete_path) # Set our custom completer
# Your Google account credentials
EMAIL = input("Enter your Google email: ").strip()
ANDROID_ID = input("Enter your Android ID: ").strip()
OAUTH_TOKEN = input("Enter your OAuth token: ").strip()
# Path to your Markdown file
MD_FILE = input("Enter the path to your Markdown file with notes: ").strip()
def get_master_token():
master_response = gpsoauth.exchange_token(EMAIL, OAUTH_TOKEN, ANDROID_ID)
master_token = master_response['Token']
return master_token
# Extract notes from the MD file taking into account they are formatted as the output from https://github.com/nogiszd/xiaomi-note-exporter
def extract_notes(filename):
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
notes = []
current_title = None
current_body = ""
for line in content.split("\n"):
if line.startswith("****"): # Separator between notes
if current_title or current_body.strip():
notes.append((current_title, current_body.strip()))
current_title = None
current_body = ""
elif re.match(r"\*\*(.+?)\*\*", line): # Title in bold **Title**
current_title = re.sub(r"\*\*(.+?)\*\*", r"\1", line).strip()
else:
current_body += line + "\n"
if current_title or current_body.strip():
notes.append((current_title, current_body.strip()))
return notes
# Login to Google Keep
try:
token = get_master_token()
keep = gkeepapi.Keep()
keep.authenticate(EMAIL, token)
print("Authentication successful!")
except Exception as e:
print(f"Authentication failed: {e}")
exit(1)
# Extract notes
notes = extract_notes(md_file)
# Add notes to Google Keep
for title, body in notes:
note = keep.createNote(title if title else "", body)
print(f"Added note: {title if title else body[:30]}...")
# Blinking animation while syncing
def show_upload_animation():
"""Shows a blinking 'Uploading notes...' message."""
animation = ["Uploading your notes to Google Keep ",
"Uploading your notes to Google Keep. ",
"Uploading your notes to Google Keep.. ",
"Uploading your notes to Google Keep..."]
i = 0
while not sync_done:
print(animation[i % len(animation)], end="\r", flush=True)
i += 1
time.sleep(0.5)
# Start the animation in a separate thread
sync_done = False
animation_thread = threading.Thread(target=show_upload_animation)
animation_thread.start()
# Sync with Google Keep
keep.sync()
# Stop the animation and clear the line
sync_done = True
animation_thread.join()
print("Upload complete! ")
print("All notes have been imported!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment