Skip to content

Instantly share code, notes, and snippets.

@v3l0c1r4pt0r
Created March 7, 2021 15:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save v3l0c1r4pt0r/15ef7181b7c4546963da68bc3b31c169 to your computer and use it in GitHub Desktop.
Save v3l0c1r4pt0r/15ef7181b7c4546963da68bc3b31c169 to your computer and use it in GitHub Desktop.
Convert places.sqlite to bookmarks.html (allows import from Firefox for Android to Chrome/Chromium)
#!/usr/bin/env python3
# convert places.sqlite to bookmarks.html
import sqlite3
import sys
import json
if len(sys.argv) < 2:
print('Usage: {} places.sqlite > bookmarks.html'.format(sys.argv[0]))
sys.exit(1)
def get_children(conn, parent, entry_type):
c = conn.cursor()
parm = (parent, entry_type)
c.execute('SELECT b.id as id, b.dateAdded as created, b.lastModified as modified, p.url as url, b.title as title, b.position as position FROM moz_bookmarks b left join moz_places p on b.fk = p.id WHERE b.parent = ? AND b.type = ? ORDER BY b.position ASC;', parm)
result = {}
for t in c.fetchall():
_id, created, modified, url, title, position = t
if _id == 0:
# special case - entry id 0 has id 0 as parent => avoid infinite recurrency
continue
result[_id] = {
'title': title,
'url': url,
'created': created,
'modified': modified,
'position': position,
}
return result
def get_dirs(conn, parent):
return get_children(conn, parent, 2)
def get_bookmarks(conn, parent):
return get_children(conn, parent, 1)
def populate_bookmarks(conn, dirs):
for _id in dirs:
directory = dirs[_id]
bookmarks = get_bookmarks(conn, _id)
directory['bookmarks'] = bookmarks
def populate(conn, dirs):
populate_bookmarks(conn, dirs)
for _id in dirs:
directory = dirs[_id]
subdirs = get_dirs(conn, _id)
directory['dirs'] = subdirs
populate(conn, subdirs)
def db2obj(browser_db_filename):
conn = sqlite3.connect(sys.argv[1])
# get root directory contents (hopefully no bookmarks here)
dirs = get_dirs(conn, 1)
# populate root dir entries with subdirs and bookmarks (recursively)
populate(conn, dirs)
return dirs
def print_dir(directory, level, recurse=False):
indent_hdr = '\t' * level
indent = '\t' * (level + 1)
# begin directory
print("""{}<DT><H3 ADD_DATE="{}" LAST_MODIFIED="{}">{}</H3>
{}<DL><p>""".format(indent_hdr, directory['created'], directory['modified'], directory['title'], indent_hdr))
# print all subdirs
if recurse:
for d in directory['dirs']:
#print(directory['dirs'][d]['title'], level+1)
print_dir(directory['dirs'][d], level+1, True)
# print all bookmarks
#dupa = {k: v for k, v in sorted(directory['bookmarks'].items(), key=lambda item: item['position'])}
bookmarks_by_position = dict(sorted(directory['bookmarks'].items(), key=lambda item: item[1]['position']))
for i in bookmarks_by_position:
bm = bookmarks_by_position[i]
print('{}<DT><A HREF="{}" ADD_DATE="{}">{}</A>'.format(indent, bm['url'], bm['created'], bm['title']))
#for bm in directory['bookmarks']:
#print(bm)
# close directory
print("""{}</DL><p>""".format(indent_hdr))
# HTML functions (lots of ugly stuff)
def print_html(dirs):
# just a comment
print("""<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->""")
# headers, booooring...
print("""<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>""")
# TODO: print dirs
for d in dirs:
print_dir(dirs[d], 1, True)
# close whole bookmarks object
print("""</DL><p>""")
dirs = db2obj(sys.argv[1])
#print(json.dumps(dirs, indent=2))
print_html(dirs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment