Add initial files
This commit is contained in:
92
background.js
Normal file
92
background.js
Normal file
@ -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);
|
||||||
|
});
|
||||||
BIN
icons/check.png
Normal file
BIN
icons/check.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
BIN
icons/pause.png
Normal file
BIN
icons/pause.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
icons/stop.png
Normal file
BIN
icons/stop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
icons/wait.png
Normal file
BIN
icons/wait.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
29
manifest.json
Normal file
29
manifest.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
options.html
Normal file
18
options.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Life Scrobbler Options</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Life Scrobbler Settings</h2>
|
||||||
|
<label>Delay (seconds): <input id="delay" type="number" min="1" /></label>
|
||||||
|
<br /><br />
|
||||||
|
<label>Blacklist (one per line):</label><br />
|
||||||
|
<textarea id="blacklist" rows="6" cols="40"></textarea>
|
||||||
|
<br /><br />
|
||||||
|
<button id="save">Save</button>
|
||||||
|
<p id="status"></p>
|
||||||
|
<script src="options.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
options.js
Normal file
30
options.js
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user