|
@@ -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);
|
|
|
|
+});
|