164 lines
4.1 KiB
JavaScript
164 lines
4.1 KiB
JavaScript
// Embermarks - Background Script
|
|
// Handles bookmark analysis and forgotten bookmark detection
|
|
|
|
// Minimum valid timestamp (Jan 1, 2010) - older dates are likely corrupted imports
|
|
const MIN_VALID_TIMESTAMP = 1262304000000;
|
|
|
|
const DEFAULT_SETTINGS = {
|
|
excludedFolders: [],
|
|
numBookmarks: 5,
|
|
maxVisitCount: 2, // Consider "forgotten" if visited 2 or fewer times
|
|
minAgeDays: 7, // Only show bookmarks older than 7 days
|
|
};
|
|
|
|
// Get user settings
|
|
async function getSettings() {
|
|
const result = await browser.storage.local.get("settings");
|
|
return { ...DEFAULT_SETTINGS, ...result.settings };
|
|
}
|
|
|
|
// Save settings
|
|
async function saveSettings(settings) {
|
|
await browser.storage.local.set({
|
|
settings: { ...DEFAULT_SETTINGS, ...settings },
|
|
});
|
|
}
|
|
|
|
// Get all bookmarks recursively, respecting excluded folders
|
|
async function getAllBookmarks(excludedFolderIds = []) {
|
|
const tree = await browser.bookmarks.getTree();
|
|
const bookmarks = [];
|
|
|
|
function traverse(nodes, currentPath = "") {
|
|
for (const node of nodes) {
|
|
// Skip excluded folders and their children
|
|
if (excludedFolderIds.includes(node.id)) {
|
|
continue;
|
|
}
|
|
|
|
if (node.url) {
|
|
// It's a bookmark
|
|
bookmarks.push({
|
|
id: node.id,
|
|
title: node.title || "Untitled",
|
|
url: node.url,
|
|
dateAdded: node.dateAdded,
|
|
path: currentPath,
|
|
});
|
|
}
|
|
|
|
if (node.children) {
|
|
const folderName = node.title || "";
|
|
const newPath = currentPath
|
|
? `${currentPath} / ${folderName}`
|
|
: folderName;
|
|
traverse(node.children, newPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
traverse(tree);
|
|
return bookmarks;
|
|
}
|
|
|
|
// Get visit count for a URL
|
|
async function getVisitCount(url) {
|
|
try {
|
|
const visits = await browser.history.getVisits({ url });
|
|
return visits.length;
|
|
} catch (e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Get forgotten bookmarks (low visit count, old enough)
|
|
async function getForgottenBookmarks() {
|
|
const settings = await getSettings();
|
|
const bookmarks = await getAllBookmarks(settings.excludedFolders);
|
|
|
|
const now = Date.now();
|
|
const minAgeMs = settings.minAgeDays * 24 * 60 * 60 * 1000;
|
|
|
|
// Filter by age and get visit counts
|
|
const candidates = [];
|
|
|
|
for (const bookmark of bookmarks) {
|
|
// Skip if too new
|
|
if (bookmark.dateAdded && now - bookmark.dateAdded < minAgeMs) {
|
|
continue;
|
|
}
|
|
|
|
// Skip non-http(s) URLs
|
|
if (
|
|
!bookmark.url.startsWith("http://") &&
|
|
!bookmark.url.startsWith("https://")
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const visitCount = await getVisitCount(bookmark.url);
|
|
|
|
if (visitCount <= settings.maxVisitCount) {
|
|
// Check if dateAdded is valid (not corrupted/too old)
|
|
const hasValidDate =
|
|
bookmark.dateAdded && bookmark.dateAdded >= MIN_VALID_TIMESTAMP;
|
|
|
|
candidates.push({
|
|
...bookmark,
|
|
visitCount,
|
|
daysSinceAdded: hasValidDate
|
|
? Math.floor((now - bookmark.dateAdded) / (24 * 60 * 60 * 1000))
|
|
: null,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Shuffle and pick random ones
|
|
const shuffled = candidates.sort(() => Math.random() - 0.5);
|
|
return shuffled.slice(0, settings.numBookmarks);
|
|
}
|
|
|
|
// Get all bookmark folders for settings UI
|
|
async function getBookmarkFolders() {
|
|
const tree = await browser.bookmarks.getTree();
|
|
const folders = [];
|
|
|
|
function traverse(nodes, depth = 0) {
|
|
for (const node of nodes) {
|
|
if (node.children) {
|
|
folders.push({
|
|
id: node.id,
|
|
title: node.title || "Root",
|
|
depth,
|
|
});
|
|
traverse(node.children, depth + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
traverse(tree);
|
|
return folders;
|
|
}
|
|
|
|
// Handle messages from popup/newtab/options
|
|
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
switch (message.action) {
|
|
case "getForgottenBookmarks":
|
|
return getForgottenBookmarks();
|
|
|
|
case "getBookmarkFolders":
|
|
return getBookmarkFolders();
|
|
|
|
case "getSettings":
|
|
return getSettings();
|
|
|
|
case "saveSettings":
|
|
return saveSettings(message.settings);
|
|
|
|
case "deleteBookmark":
|
|
return browser.bookmarks.remove(message.bookmarkId);
|
|
|
|
default:
|
|
return Promise.resolve(null);
|
|
}
|
|
});
|