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