commit 180108e4c22a6b31e59cd883e8978870cfc28b1f Author: Colin Powell Date: Tue Oct 7 16:01:32 2025 -0400 Add initial files diff --git a/background.js b/background.js new file mode 100644 index 0000000..02373d0 --- /dev/null +++ b/background.js @@ -0,0 +1,92 @@ +const SCROBBLE_ENDPOINT = "https://life.lab.unbl.ink/?scrobble_url="; +const STOP_ENDPOINT = "https://life.lab.unbl.ink/?action=stop&scrobble_url="; + +// Default settings +const DEFAULT_SETTINGS = { + delay: 10, + blacklist: ["*.unbl.ink", "moz-extension://"], + paused: false, +}; + +// Utility: wildcard match +function matchPattern(url, pattern) { + if (pattern.startsWith("*.")) { + const domain = pattern.slice(2); + return url.includes(domain); + } + return url.startsWith(pattern); +} + +function isBlacklisted(url, blacklist) { + return blacklist.some((p) => matchPattern(url, p)); +} + +let activeTabs = new Map(); + +// Load settings +async function getSettings() { + return new Promise((resolve) => { + browser.storage.local.get(DEFAULT_SETTINGS, (items) => resolve(items)); + }); +} + +// Save pause state +async function setPaused(paused) { + await browser.storage.local.set({ paused }); + updateIcon(paused ? "pause" : "wait"); +} + +function updateIcon(state) { + const icons = { + wait: "icons/wait.png", + scrobbled: "icons/check.png", + stopped: "icons/stop.png", + pause: "icons/pause.png", + }; + browser.browserAction.setIcon({ path: icons[state] }); +} + +// Handle tab updates +browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (changeInfo.status !== "complete" || !tab.url) return; + const settings = await getSettings(); + const { delay, blacklist, paused } = settings; + + if (paused || isBlacklisted(tab.url, blacklist)) return; + if (tab.url.startsWith("moz-extension://")) return; + + const url = encodeURIComponent(tab.url); + updateIcon("wait"); + + if (activeTabs.has(tabId)) clearTimeout(activeTabs.get(tabId)); + const timeout = setTimeout(() => { + fetch(`${SCROBBLE_ENDPOINT}${url}`).then(() => updateIcon("scrobbled")); + }, delay * 1000); + + activeTabs.set(tabId, timeout); +}); + +// Stop scrobble when navigating away or closing +function stopScrobble(tabId, url) { + getSettings().then(({ blacklist }) => { + if (!url || isBlacklisted(url, blacklist)) return; + if (url.startsWith("moz-extension://")) return; + fetch(`${STOP_ENDPOINT}${encodeURIComponent(url)}`).then(() => + updateIcon("stopped"), + ); + }); +} + +browser.tabs.onRemoved.addListener((tabId) => { + if (activeTabs.has(tabId)) clearTimeout(activeTabs.get(tabId)); +}); + +//browser.webNavigation.onBeforeNavigate.addListener((details) => { +// stopScrobble(details.tabId, details.url); +//}); + +// Toggle pause on icon click +browser.browserAction.onClicked.addListener(async () => { + const { paused } = await getSettings(); + setPaused(!paused); +}); diff --git a/icons/check.png b/icons/check.png new file mode 100644 index 0000000..c79b36b Binary files /dev/null and b/icons/check.png differ diff --git a/icons/pause.png b/icons/pause.png new file mode 100644 index 0000000..1c0027b Binary files /dev/null and b/icons/pause.png differ diff --git a/icons/stop.png b/icons/stop.png new file mode 100644 index 0000000..a0c55f3 Binary files /dev/null and b/icons/stop.png differ diff --git a/icons/wait.png b/icons/wait.png new file mode 100644 index 0000000..25c8c5b Binary files /dev/null and b/icons/wait.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..cb20d3f --- /dev/null +++ b/manifest.json @@ -0,0 +1,29 @@ +{ + "manifest_version": 2, + "name": "LifeScrobbler", + "version": "1.4", + "description": "Scrobble visited pages to life.lab.unbl.ink after a delay, skipping blacklisted or paused tabs.", + "permissions": [ + "tabs", + "storage", + "webNavigation", + "https://life.lab.unbl.ink/*" + ], + "background": { + "scripts": ["background.js"] + }, + "browser_action": { + "default_icon": { + "48": "icons/stop.png" + }, + "default_title": "LifeScrobbler" + }, + "options_ui": { + "page": "options.html", + "open_in_tab": true + }, + "icons": { + "48": "icons/check.png", + "96": "icons/check.png" + } +} diff --git a/options.html b/options.html new file mode 100644 index 0000000..af3d204 --- /dev/null +++ b/options.html @@ -0,0 +1,18 @@ + + + + + Life Scrobbler Options + + +

Life Scrobbler Settings

+ +

+
+ +

+ +

+ + + diff --git a/options.js b/options.js new file mode 100644 index 0000000..446a6ac --- /dev/null +++ b/options.js @@ -0,0 +1,30 @@ +document.addEventListener("DOMContentLoaded", restore); +document.getElementById("save").addEventListener("click", save); + +const DEFAULT_SETTINGS = { + delay: 10, + blacklist: ["*.unbl.ink", "moz-extension://"], + paused: false, +}; + +function restore() { + browser.storage.local.get(DEFAULT_SETTINGS, (items) => { + document.getElementById("delay").value = items.delay; + document.getElementById("blacklist").value = items.blacklist.join("\n"); + }); +} + +function save() { + const delay = parseInt(document.getElementById("delay").value, 10); + const blacklist = document + .getElementById("blacklist") + .value.split("\n") + .map((x) => x.trim()) + .filter(Boolean); + + browser.storage.local.set({ delay, blacklist }, () => { + const status = document.getElementById("status"); + status.textContent = "Settings saved."; + setTimeout(() => (status.textContent = ""), 1500); + }); +}