With the help of IA I managed to get Firefox bookmarks
import os
import json
import sqlite3
import shutil
import tempfile
profile_name = "your-profile" # <-- cambia con il tuo
profile_path = os.path.expanduser(f"~/.mozilla/firefox/{profile_name}")
db_path = os.path.join(profile_path, "places.sqlite")
if not os.path.isfile(db_path):
print(f"Il file places.sqlite non esiste: {db_path}")
exit(1)
# Copia temporanea per evitare blocchi
tmp_dir = tempfile.gettempdir()
temp_db_path = os.path.join(tmp_dir, "places_copy.sqlite")
shutil.copy2(db_path, temp_db_path)
conn = sqlite3.connect(temp_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Preleva tutte le informazioni rilevanti
query = """
SELECT
b.id,
b.title,
b.parent,
b.type,
b.fk,
b.dateAdded,
p.url,
ia.content AS description
FROM moz_bookmarks b
LEFT JOIN moz_places p ON b.fk = p.id
LEFT JOIN moz_items_annos ia ON b.id = ia.item_id
LEFT JOIN moz_anno_attributes a_attr ON ia.anno_attribute_id = a_attr.id
WHERE (b.type = 1 OR b.type = 2)
AND (a_attr.name IS NULL OR a_attr.name = 'bookmarkProperties/description')
ORDER BY b.parent, b.position
"""
cursor.execute(query)
rows = cursor.fetchall()
# Costruisci dizionario gerarchico
items = {row["id"]: {
"id": row["id"],
"title": row["title"] if row["title"] else "(senza titolo)",
"type": "folder" if row["type"] == 2 else "bookmark",
"url": row["url"] if row["type"] == 1 else None,
"description": row["description"],
"children": []
} for row in rows}
# Organizza gerarchia
root = []
for row in rows:
item = items[row["id"]]
parent_id = row["parent"]
if parent_id in items:
items[parent_id]["children"].append(item)
else:
root.append(item)
# Salva in JSON
with open("bookmarks_full.json", "w", encoding="utf-8") as f:
json.dump(root, f, ensure_ascii=False, indent=2)
print(f"Esportazione completata: {len(root)} cartelle o elementi di primo livello.")
conn.close()
os.remove(temp_db_path)
also selecting a specific bookmark folder
import os
import sqlite3
import shutil
import tempfile
import sys
profile_name = "your-FF-profile"
folder_name_to_export = "[the folder name]" # Nome della cartella da esportare
output_path = "bookmarks-FF-[a name].php"
# Percorso del profilo
profile_path = os.path.expanduser(f"~/.mozilla/firefox/{profile_name}")
db_path = os.path.join(profile_path, "places.sqlite")
if not os.path.isfile(db_path):
print(f"Il file places.sqlite non esiste: {db_path}")
sys.exit(1)
# Copia temporanea del DB
tmp_dir = tempfile.gettempdir()
temp_db_path = os.path.join(tmp_dir, "places_copy.sqlite")
shutil.copy2(db_path, temp_db_path)
conn = sqlite3.connect(temp_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# 1. Query per cartelle e segnalibri (senza tag)
query = """
SELECT
b.id,
b.title,
b.parent,
b.type,
b.fk,
p.url
FROM moz_bookmarks b
LEFT JOIN moz_places p ON b.fk = p.id
WHERE b.type IN (1, 2)
ORDER BY b.parent, b.position
"""
cursor.execute(query)
rows = cursor.fetchall()
# Costruzione struttura
items = {row["id"]: {
"id": row["id"],
"title": row["title"] or "(senza titolo)",
"type": "folder" if row["type"] == 2 else "bookmark",
"url": row["url"] if row["type"] == 1 else None,
"description": None,
"children": []
} for row in rows}
# Costruzione gerarchia
for row in rows:
item = items[row["id"]]
parent_id = row["parent"]
if parent_id in items:
items[parent_id]["children"].append(item)
# 2. Query per associare tag reali ai segnalibri
tag_query = """
SELECT
b.id AS bookmark_id,
GROUP_CONCAT(tg.title, ', ') AS tags
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks bt ON bt.fk = b.fk
LEFT JOIN moz_bookmarks tg ON tg.id = bt.parent AND tg.parent = -3
WHERE b.type = 1
GROUP BY b.id
"""
cursor.execute(tag_query)
tag_rows = cursor.fetchall()
# Assegna i tag ai segnalibri nella struttura
for row in tag_rows:
bookmark_id = row["bookmark_id"]
tags = row["tags"]
if bookmark_id in items:
items[bookmark_id]["description"] = tags if tags else "(senza tag)"
# Cerca la cartella da esportare
matching_folders = [item for item in items.values()
if item["type"] == "folder" and item["title"] == folder_name_to_export]
if not matching_folders:
print(f"Nessuna cartella trovata con nome '{folder_name_to_export}'.")
conn.close()
os.remove(temp_db_path)
sys.exit(1)
# Funzione per scrivere array PHP
def to_php_array(item, indent=2):
space = ' ' * indent
lines = [f"{space}["]
lines.append(f"{space} 'title' => '{item['title'].replace('\'', '\\\'')}',")
if item.get("url"):
lines.append(f"{space} 'url' => '{item['url'].replace('\'', '\\\'')}',")
if item.get("description"):
lines.append(f"{space} 'description' => '{item['description'].replace('\'', '\\\'')}',")
if item.get("children"):
lines.append(f"{space} 'children' => [")
for child in item["children"]:
lines.append(to_php_array(child, indent + 4) + ",")
lines.append(f"{space} ],")
lines.append(f"{space}]")
return "\n".join(lines)
# Scrittura file PHP
with open(output_path, "w", encoding="utf-8") as f:
f.write("<?php\nreturn [\n")
for folder in matching_folders:
f.write(to_php_array(folder, indent=2))
f.write(",\n")
f.write("];\n")
print(f"Esportazione completata in {output_path}")
conn.close()
os.remove(temp_db_path)
and output them in a php webpage
<?php
$segnalibri = include 'bookmarks-FF-[a name].php';
function stampaSegnalibri($items) {
foreach ($items as $item) {
echo "<div style='margin-bottom:8px'>";
if (isset($item['url'])) {
// titolo come link
$title_html = htmlspecialchars($item['title'], ENT_QUOTES);
$url_html = htmlspecialchars($item['url'], ENT_QUOTES);
echo "<a href=\"$url_html\" target=\"_blank\">$title_html</a>";
} else {
// solo titolo testo normale
echo "<strong>" . htmlspecialchars($item['title'], ENT_QUOTES) . "</strong>";
}
// descrizione (se presente)
if (!empty($item['description'])) {
$desc_html = htmlspecialchars($item['description'], ENT_QUOTES);
echo "<br><small style='color: #555;'>$desc_html</small>";
}
// figli (ricorsivo)
if (isset($item['children']) && count($item['children']) > 0) {
echo "<div style='margin-left:20px; margin-top:4px;'>";
stampaSegnalibri($item['children']);
echo "</div>";
}
echo "</div>";
}
}
stampaSegnalibri($segnalibri);
?>
The problem is that FF doesn’t have a description field, and I didn’t manage tgo get it from another filed, such as ‘tags’ one.