Created
March 7, 2021 15:02
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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