Skip to content

Instantly share code, notes, and snippets.

@spersico
Created October 22, 2022 15:56
Show Gist options
  • Save spersico/19b92f2c37f01118c19f2ef9c113f0d7 to your computer and use it in GitHub Desktop.
Save spersico/19b92f2c37f01118c19f2ef9c113f0d7 to your computer and use it in GitHub Desktop.
Huawei Notes HTML to TXT
import { writeFile, readdir, readFile } from 'node:fs/promises';
import path from 'path';
/*
Steps to get/export your notes from Huawei Notes:
1. Login into a Huawei Account in the phone.
2. Activate in your phone, the Notes Syncing, which is inside of Huawei Account > Cloud
3. Log in into https://cloud.huawei.com
4. Go to https://cloud.huawei.com/home#/account/gdpr, and click on Download Notes
5. This will give you a zip file with a password. Extract the zip file into a folder.
6. Copy this file into the folder as index.mjs
7. You'll need NodeJs installed for this: https://nodejs.org/en/
I made and tested this on v18+, but v19 and v16 should also work
8. open a console/terminal (see how to do that in your OS), and run in it "node index.mjs"
9. Your notes should be in the notes.txt file inside of the same folder.
Extra:
The script only copies the text content, as simple text,
and it doesn't copy the title or other stuff that your notes might contain.
I left a comment in the portion of code that might be helpful to modify if
you want more information, such as the creation time, or other info.
*/
async function readNotes() {
console.log(`πŸ“ | Huawei Notes HTML -> TXT`);
const __dirname = path.resolve(path.dirname(''));
console.log(`πŸ“ | > Reading Directory`, __dirname);
const folders = [];
const files = await readdir(__dirname, {
withFileTypes: true,
});
files.forEach((file) => {
if (file.isDirectory()) folders.push(file.name);
});
console.log(`πŸ“ | > Notes found: `, folders.length);
const notes = await Promise.all(
folders.map(async (folder) => {
const route = `${__dirname}/${folder}/json.js`;
return readFile(route, 'utf8')
.then((weirdJs) => {
const noteData = JSON.parse(
weirdJs.replace('var data = ', '')
).content;
/*
noteData has all the notes information, AFAICS.
If you want to have more info, such as timestamps,
this is the place to look into to add more info.
I only needed the text content in my case
*/
return noteData.content.split('|')[1].trim();
})
.catch((reason) => {
console.error(`πŸ› | > Error: `, route, reason);
return '';
});
})
);
const cleanedUpNotes = notes.filter(Boolean);
console.log(
`πŸ“ | > Total after removing empty or errored: `,
cleanedUpNotes.length
);
await writeFile('notes.txt', cleanedUpNotes.join('\n'), 'utf8');
console.log(`πŸ“ | Notes succesfully exported into notes.txt file! πŸŽ‰`);
}
readNotes();
/*
MIT License
Copyright Β© 2022 Santiago Persico
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the β€œSoftware”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@macs-massimopiazza
Copy link

macs-massimopiazza commented Apr 20, 2024

Yeah thanks man. It was super useful!

I also needed to save timestamps and other informations. So i made a custom template that prints each note in a prettier way and with more informations in the notes.txt. Not the best, but is was a run once thing for me eheh.
I'm sharing it here in case anyone needs it. (it's just the notes generation part, rows from 38 to 59 of the original script)

 const notes = await Promise.all(
    folders.map(async (folder, i) => {
      const route = `${__dirname}/${folder}/json.js`;
      return readFile(route, 'utf8')
        .then((weirdJs) => {
          const noteData = JSON.parse(
            weirdJs.replace('var data = ', '')
          ).content;
          return `---------START-----------\ncount: ${i + 1}/${folders.length}\ncreated: ${new Date(noteData.created).toLocaleDateString()}\nmodified: ${new Date(noteData.modified).toLocaleDateString()}\nfavorite: ${noteData.favorite == 0 ? 'no' : 'yes'}\ntag_id: ${noteData.tag_id}\n--------------------\n${noteData.content.split('|')[1].trim()}\n----------END----------`;;
        })
        .catch((reason) => {
          console.error(`πŸ› | > Error: `, route, reason);
          return '';
        });
    })
  );

@hsapaul
Copy link

hsapaul commented Jun 6, 2024

It didn't work for me so I coded it with python, just put the directory Name where the "Notes" are into the variable current_dir:

it's written in python

import json
import os
import re

python: open json.js in same directory, load in json, get title
PUT DIRECTORY OF Notes FOLDER IN TO VARIABLE BELOW
current_dir = r"C:\Users\paul\Desktop\Notes-2024060518384725762\Notes"

list of all folders in the current directory
entries = os.listdir(current_dir)
directories = [entry for entry in entries if os.path.isdir(os.path.join(current_dir, entry))]

for directory in directories:

Open the json.js file in the same directory

with open(os.path.join(current_dir, directory, 'json.js'), 'r', encoding='utf-8') as file:
content = file.read()

remove the first 13 characters

content = content[11:]
data = json.loads(content)
title = data['content']['title'].split('\n')[0]
content = data['content']['content']
print("Title: " + title)
print("Content: " + content)
title_no_special_chars = re.sub(r'[<>:"/|?*]', '_', title)

save content into text file with title name into current directory

with open(os.path.join(current_dir, f"{title_no_special_chars}.txt"), 'w', encoding='utf-8') as text_file:
text_file.write(content)

@titanismo
Copy link

My 5 cents for all that want to use Python instead of NodeJS. Place it in the folder where the zip is.

import os
import json
from datetime import datetime

def read_notes():
    print("πŸ“ | Huawei Notes HTML -> TXT")

    # Get the current working directory
    current_dir = os.getcwd()
    print(f"πŸ“ | > Reading Directory: {current_dir}")

    # List all subdirectories
    folders = [entry for entry in os.listdir(current_dir) if os.path.isdir(os.path.join(current_dir, entry))]
    print(f"πŸ“ | > Notes found: {len(folders)}")

    notes = []

    for i, folder in enumerate(folders):
        json_file_path = os.path.join(current_dir, folder, 'json.js')
        try:
            # Read and parse the JSON file
            with open(json_file_path, 'r', encoding='utf-8') as file:
                weird_js = file.read()

            # Remove the JavaScript variable assignment and parse JSON
            note_data = json.loads(weird_js.replace('var data = ', ''))['content']

            # Format note content
            note = (
                "---------START-----------\n"
                f"count: {i + 1}/{len(folders)}\n"
                f"created: {datetime.fromtimestamp(note_data['created'] / 1000).strftime('%Y-%m-%d')}\n"
                f"modified: {datetime.fromtimestamp(note_data['modified'] / 1000).strftime('%Y-%m-%d')}\n"
                f"favorite: {'yes' if note_data['favorite'] != 0 else 'no'}\n"
                f"tag_id: {note_data['tag_id']}\n"
                "--------------------\n"
                f"{note_data['content'].split('|')[1].strip()}\n"
                "----------END----------"
            )
            notes.append(note)

        except (FileNotFoundError, KeyError, json.JSONDecodeError) as e:
            print(f"πŸ› | > Error processing {json_file_path}: {e}")
        except Exception as e:
            print(f"πŸ› | > Unexpected error: {e}")

    # Remove empty or errored notes
    cleaned_up_notes = [note for note in notes if note]
    print(f"πŸ“ | > Total after removing empty or errored: {len(cleaned_up_notes)}")

    # Write notes to a file
    with open('notes.txt', 'w', encoding='utf-8') as output_file:
        output_file.write('\n'.join(cleaned_up_notes))

    print("πŸ“ | Notes successfully exported into notes.txt file! πŸŽ‰")

if __name__ == "__main__":
    read_notes()

@ndandanov
Copy link

Many thanks to all who contributed their efforts in this gist!
Inspired by your work, I created a Python script which converts Huawei notes by keeping:

  • the title
  • any checklist items
  • attachments
  • metadata of notes (creation and modification time, etc.).

It can also export your notes to a Joplin-compatible format.

You can try it out here:
https://gist.github.com/ndandanov/31d820108d44779cfb973e4ac56a959a

I would love to hear your feedback!

@spersico
Copy link
Author

Hey folks! This is so cool to see! This was honestly a one time thing for me (I did this to help my mum change phones and not lose her notes πŸ˜›), and I even lost my original source files to test stuff, but I think it's awesome that you improved the initial implementation this much. I'm so sorry for not being able to provide feedback, but this is great. Thanks for sharing!

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