1+ import sqlite3
2+ from bs4 import BeautifulSoup
3+ from PIL import Image , UnidentifiedImageError
4+ from io import BytesIO
5+ import re
6+ import base64
7+ from datetime import datetime , timezone
8+ import os
9+ import argparse
10+
11+
12+ """
13+ Imports html bookmarks file into Flame.
14+ Tested only on Firefox html exports so far.
15+
16+ Usage:
17+ python3 bookmarks_importer.py --bookmarks <path to bookmarks file> --data <path to flame data dir>
18+
19+ """
20+
21+ parser = argparse .ArgumentParser ()
22+ parser .add_argument ('--bookmarks' , type = str , required = True )
23+ parser .add_argument ('--data' , type = str , required = True )
24+ args = parser .parse_args ()
25+
26+ bookmarks_path = args .bookmarks
27+ data_path = args .data
28+ created = datetime .now ().strftime ('%Y-%m-%d %H:%M:%S.%f' )[:- 3 ] + datetime .now ().astimezone ().strftime (" %z" )
29+ updated = created
30+ if data_path [- 1 ] != '/' :
31+ data_path = data_path + '/'
32+
33+
34+
35+
36+ def Base64toPNG (codec , name ):
37+
38+ """
39+ Convert base64 encoded image to png file
40+ Reference: https://github.com/python-pillow/Pillow/issues/3400#issuecomment-428104239
41+
42+ Parameters:
43+ codec (str): icon in html bookmark format.e.g. 'data:image/png;base64,<image encoding>'
44+ name (str): name for export file
45+
46+ Returns:
47+ icon_name(str): name of png output E.g. 1636473849374--mybookmark.png
48+ None: if image not produced successfully
49+
50+ """
51+
52+ try :
53+ unix_t = str (int (datetime .now (tz = timezone .utc ).timestamp () * 1000 ))
54+ icon_name = unix_t + '--' + re .sub (r'\W+' , '' , name ).lower () + '.png'
55+ image_path = data_path + 'uploads/' + icon_name
56+ if os .path .exists (image_path ):
57+ return image_path
58+ base64_data = re .sub ('^data:image/.+;base64,' , '' , codec )
59+ byte_data = base64 .b64decode (base64_data )
60+ image_data = BytesIO (byte_data )
61+ img = Image .open (image_data )
62+ img .save (image_path , "PNG" )
63+ return icon_name
64+ except UnidentifiedImageError :
65+ return None
66+
67+
68+
69+
70+ def FlameBookmarkParser (bookmarks_path ):
71+
72+ """
73+ Parses HTML bookmarks file
74+ Reference: https://stackoverflow.com/questions/68621107/extracting-bookmarks-and-folder-hierarchy-from-google-chrome-with-beautifulsoup
75+
76+ Parameters:
77+ bookmarks_path (str): path to bookmarks.html
78+
79+ Returns:
80+ None
81+
82+ """
83+
84+ soup = BeautifulSoup ()
85+ with open (bookmarks_path ) as f :
86+ soup = BeautifulSoup (f .read (), 'lxml' )
87+
88+ dt = soup .find_all ('dt' )
89+ folder_name = ''
90+ for i in dt :
91+ n = i .find_next ()
92+ if n .name == 'h3' :
93+ folder_name = n .text
94+ continue
95+ else :
96+ url = n .get ("href" )
97+ website_name = n .text
98+ icon = n .get ("icon" )
99+ if icon != None :
100+ icon_name = Base64toPNG (icon , website_name )
101+ cat_id = AddFlameCategory (folder_name )
102+ AddFlameBookmark (website_name , url , cat_id , icon_name )
103+
104+
105+
106+
107+ def AddFlameCategory (cat_name ):
108+ """
109+ Parses HTML bookmarks file
110+
111+ Parameters:
112+ cat_name (str): category name
113+
114+ Returns:
115+ cat_id (int): primary key id of cat_name
116+
117+ """
118+
119+
120+
121+ con = sqlite3 .connect (data_path + 'db.sqlite' )
122+ cur = con .cursor ()
123+ count_sql = ("SELECT count(*) FROM categories WHERE name = ?;" )
124+ cur .execute (count_sql , [cat_name ])
125+ count = int (cur .fetchall ()[0 ][0 ])
126+ if count > 0 :
127+ getid_sql = ("SELECT id FROM categories WHERE name = ?;" )
128+ cur .execute (getid_sql , [cat_name ])
129+ cat_id = int (cur .fetchall ()[0 ][0 ])
130+ return cat_id
131+
132+ is_pinned = 1
133+
134+ insert_sql = "INSERT OR IGNORE INTO categories(name, isPinned, createdAt, updatedAt) VALUES (?, ?, ?, ?);"
135+ cur .execute (insert_sql , (cat_name , is_pinned , created , updated ))
136+ con .commit ()
137+
138+ getid_sql = ("SELECT id FROM categories WHERE name = ?;" )
139+ cur .execute (getid_sql , [cat_name ])
140+ cat_id = int (cur .fetchall ()[0 ][0 ])
141+ return cat_id
142+
143+
144+
145+
146+ def AddFlameBookmark (website_name , url , cat_id , icon_name ):
147+ con = sqlite3 .connect (data_path + 'db.sqlite' )
148+ cur = con .cursor ()
149+ if icon_name == None :
150+ insert_sql = "INSERT OR IGNORE INTO bookmarks(name, url, categoryId, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?);"
151+ cur .execute (insert_sql , (website_name , url , cat_id , created , updated ))
152+ con .commit ()
153+ else :
154+ insert_sql = "INSERT OR IGNORE INTO bookmarks(name, url, categoryId, icon, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?);"
155+ cur .execute (insert_sql , (website_name , url , cat_id , icon_name , created , updated ))
156+ con .commit ()
157+
158+
159+
160+
161+
162+
163+
164+
165+ if __name__ == "__main__" :
166+ FlameBookmarkParser (bookmarks_path )
0 commit comments