-
-
Save spersico/19b92f2c37f01118c19f2ef9c113f0d7 to your computer and use it in GitHub Desktop.
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. | |
*/ |
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 '';
});
})
);
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)
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()
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!
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!
Thanks for the script Spersico, I've been trying to find something to export my huawei notes into some sort of readable format. For this script to run properly, which directory should I move the extracted notes (step 5 in your instructions) to? Leave it in user > downloads or move it to the root directory?
Also my knowledge in terms of coding is very very small, how would I add more note information under noteData? I would like to be able to retrieve the title and date data from the notes if possible.
For context I'm using nodejs v20.9
Update: I decided to just manually copy and paste the notes into notepad txt files, which took some time but worked in the end. All my data has now been saved from my old phone. Even though I didn't use the script, thanks for providing it :)