init
commit
0804095730
|
|
@ -0,0 +1,2 @@
|
|||
*.zip
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
THIS EXTENSION WILL NOT INSTALL UNLESS YOU SET
|
||||
|
||||
xpinstall.signatures.required = false
|
||||
|
||||
in the about:config page in LIBREWOLF.
|
||||
ONLY TESTED IN LIBREWOLF.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
browser.runtime.onMessage.addListener(async (msg) => {
|
||||
if (msg.type !== "send") return;
|
||||
|
||||
console.log("sending message to", msg.host);
|
||||
|
||||
await fetch(msg.host, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: msg.data
|
||||
});
|
||||
|
||||
return { ok: true };
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "downloader-server",
|
||||
"version": "0.1",
|
||||
"description": "Extension for downloader-server",
|
||||
"permissions": [
|
||||
"storage", "tabs"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"applications": { "gecko": { "id": "X@Y" } }
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
zip -r -FS downloader.zip \
|
||||
background.js \
|
||||
manifest.json \
|
||||
popup.html \
|
||||
popup.js
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="height:500px;width:300px;padding:10px;margin:0px;box-sizing:border-box">
|
||||
|
||||
<style>
|
||||
html { font-family: sans-serif; }
|
||||
body { margin: 0px; text-align: center; }
|
||||
hr { border: none; border-bottom: 1px solid #CCC; }
|
||||
.tab-panel { text-align: initial; }
|
||||
.tab-bar { display: inline-flex; flex-direction: row; gap: 4px; margin-bottom: 4px; }
|
||||
.tab-bar input[type=radio] { display: none; }
|
||||
.tab-bar > label { cursor: pointer; padding: 0px 4px; }
|
||||
.tab-bar > label:has(input[type=radio]:checked) { font-weight: 900; }
|
||||
|
||||
body:has(#tab-1:checked) .tab-panel:not(.tab-panel-1) { display: none; }
|
||||
body:has(#tab-2:checked) .tab-panel:not(.tab-panel-2) { display: none; }
|
||||
body:has(#tab-3:checked) .tab-panel:not(.tab-panel-3) { display: none; }
|
||||
|
||||
.status {
|
||||
font-size: 0.8em; border: 1px solid black;
|
||||
padding: 8px; border-radius: 8px; overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.status .status-title { white-space: nowrap; font-weight: 900; }
|
||||
.status-debug { background: #ff8cff }
|
||||
.status-success { background: #8cff8c }
|
||||
.status-info { background: #b3d9ff }
|
||||
.status-warning.status-warn { background: #ffff8c }
|
||||
.status-error { background: #ff8c8c }
|
||||
|
||||
fieldset.actionset { display: flex; flex-direction: column; gap: 6px; }
|
||||
|
||||
pre {
|
||||
white-space: no-wrap;
|
||||
overflow-x: auto;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border: 1px solid rgba(0, 0, 0, 0.35);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="tab-bar">
|
||||
<label for="tab-1"><input type="radio" name="tabs" id="tab-1" checked>Actions</label>
|
||||
<label for="tab-2"><input type="radio" name="tabs" id="tab-2">Statuses</label>
|
||||
<label for="tab-3"><input type="radio" name="tabs" id="tab-3">Settings</label>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="tab-panel tab-panel-1" id="actions">
|
||||
</div>
|
||||
|
||||
<div class="tab-panel tab-panel-2">
|
||||
<pre id="json_dump"></pre>
|
||||
<div id="statuses"></div>
|
||||
</div>
|
||||
|
||||
<div class="tab-panel tab-panel-3">
|
||||
<div style="display:flex; gap: 6px">
|
||||
<input style="flex-grow: 1;" id="host" placeholder="Downloader Domain" />
|
||||
<button id="save_domain">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
const get_saved_domain = async () => (await browser.storage.local.get("domain")).domain;
|
||||
const set_saved_domain = async (domain) => await browser.storage.local.set({ domain });
|
||||
|
||||
function load_domain_from_storage_and_enable_interactions() {
|
||||
const save_button = document.getElementById("save_domain");
|
||||
const host_input = document.getElementById("host");
|
||||
|
||||
const disable_save_button = () => save_button.setAttribute('disabled', true);
|
||||
const enable_save_button = () => save_button.removeAttribute('disabled');
|
||||
|
||||
async function populate_domain_textbox_from_storage() {
|
||||
const domain = await get_saved_domain()
|
||||
if (!domain) return;
|
||||
host_input.value = domain;
|
||||
disable_save_button();
|
||||
}
|
||||
|
||||
async function domain_input_changed() {
|
||||
const saved_domain = await get_saved_domain();
|
||||
const current_domain = host_input.value;
|
||||
if(saved_domain !== current_domain) enable_save_button();
|
||||
else disable_save_button();
|
||||
}
|
||||
|
||||
async function save_domain_input_to_storage() {
|
||||
if (!host_input.value.endsWith("/"))
|
||||
host_input.value = host_input.value + "/";
|
||||
const saved = set_saved_domain(host_input.value);
|
||||
|
||||
const granted = await browser.permissions.request({
|
||||
origins: [host_input.value]
|
||||
});
|
||||
if (!granted) throw new Error("Permission denied");
|
||||
|
||||
await saved;
|
||||
disable_save_button();
|
||||
}
|
||||
|
||||
populate_domain_textbox_from_storage()
|
||||
host_input.addEventListener('change', domain_input_changed);
|
||||
host_input.addEventListener('keypress', domain_input_changed);
|
||||
host_input.addEventListener('keyup', domain_input_changed);
|
||||
host_input.addEventListener('keydown', domain_input_changed);
|
||||
save_button.addEventListener('click', save_domain_input_to_storage);
|
||||
}
|
||||
|
||||
const colors = ['red', 'green', 'blue', 'cyan', 'magenta'];
|
||||
let current_color = -1;
|
||||
function get_color() { return colors[current_color]; }
|
||||
function next_color() { current_color ++; current_color = current_color % colors.length };
|
||||
|
||||
async function arm_action_buttons() {
|
||||
next_color();
|
||||
const actions_container = document.getElementById('actions');
|
||||
actions_container.innerHTML = "";
|
||||
|
||||
async function send_data(route, payload) {
|
||||
const domain = (await get_saved_domain()) + route;
|
||||
await browser.runtime.sendMessage({
|
||||
type: 'send',
|
||||
host: domain,
|
||||
data: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
async function get_current_tab_url() {
|
||||
return (await browser.tabs.query({ currentWindow: true, active: true }))[0].url
|
||||
}
|
||||
|
||||
const presets_req = await fetch(await get_saved_domain() + 'download/presets');
|
||||
const presets = await presets_req.json();
|
||||
|
||||
const groups = {};
|
||||
|
||||
function get_group(name) {
|
||||
if (!name) name = "Ungrouped";
|
||||
if (name in groups) return groups[name];
|
||||
const fieldset = document.createElement('fieldset');
|
||||
// fieldset.style.background = get_color();
|
||||
const legend = document.createElement('legend');
|
||||
legend.innerText = name;
|
||||
fieldset.className = "actionset";
|
||||
|
||||
fieldset.appendChild(legend);
|
||||
actions_container.appendChild(fieldset);
|
||||
|
||||
groups[name] = fieldset;
|
||||
return groups[name];
|
||||
}
|
||||
|
||||
for (const preset of presets) {
|
||||
const button = document.createElement('button');
|
||||
const fieldset = get_group(preset.folder);
|
||||
fieldset.appendChild(button);
|
||||
|
||||
|
||||
|
||||
button.innerText = preset.name;
|
||||
button.addEventListener('click', async function() {
|
||||
send_data('download', {
|
||||
url: await get_current_tab_url(),
|
||||
preset_id: preset.id,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const hr = document.createElement('hr');
|
||||
actions_container.appendChild(hr);
|
||||
|
||||
const reload_button = document.createElement('button');
|
||||
reload_button.innerText = "Reload";
|
||||
reload_button.addEventListener('click', arm_action_buttons);
|
||||
actions_container.appendChild(reload_button);
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function dismiss_notif(status_id) {
|
||||
url = await get_saved_domain() + 'notification/' + status_id
|
||||
fetch(url, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
|
||||
function create_notification_element_from_notification_object(notification) {
|
||||
|
||||
const e_container = document.createElement('div');
|
||||
e_container.classList = `status status-${notification.type}`;
|
||||
e_container.setAttribute('notification_id', "" + notification.id);
|
||||
e_container.setAttribute('notification_hash', "" + notification.hash);
|
||||
|
||||
const e_title = document.createElement('div');
|
||||
e_title.classList = 'status-title';
|
||||
e_title.innerText = notification.title;
|
||||
|
||||
const e_text = document.createElement('div');
|
||||
e_text.classList = 'status-text';
|
||||
e_text.innerHTML = notification.text;
|
||||
|
||||
const e_delete_button = document.createElement('button');
|
||||
e_delete_button.classList = 'dismiss';
|
||||
e_delete_button.innerText = "Dismis";
|
||||
e_delete_button.addEventListener('click', () => {
|
||||
dismiss_notif(notification.id);
|
||||
})
|
||||
|
||||
e_container.appendChild(e_title);
|
||||
e_container.appendChild(e_text);
|
||||
e_container.appendChild(e_delete_button);
|
||||
|
||||
return e_container;
|
||||
}
|
||||
|
||||
function begin_status_update_loop() {
|
||||
const statuses_container = document.getElementById('statuses');
|
||||
|
||||
function get_current_shown_status_ids() {
|
||||
let arr = [];
|
||||
for(const element of statuses_container.children) {
|
||||
arr.push(element.getAttribute("notification_id"))
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
async function check_statuses() {
|
||||
let domain = await get_saved_domain();
|
||||
let req = await fetch(domain + 'notifications');
|
||||
let json = await req.json();
|
||||
|
||||
const current_status_ids = get_current_shown_status_ids();
|
||||
const new_status_ids = json.map(v => v.id);
|
||||
|
||||
const combined_ids = Array.from(new Set([
|
||||
...current_status_ids,
|
||||
...new_status_ids
|
||||
]));
|
||||
|
||||
for(const status_id of combined_ids) {
|
||||
const in_current = current_status_ids.includes(status_id);
|
||||
const in_new = new_status_ids.includes(status_id);
|
||||
|
||||
if (in_current && !in_new) {
|
||||
const status_element = statuses_container.querySelector(`[notification_id="${status_id}"]`);
|
||||
status_element.outerHTML = "";
|
||||
} else if (!in_current && in_new) {
|
||||
const status_object = json.find(status => status.id === status_id);
|
||||
const new_element = create_notification_element_from_notification_object(status_object)
|
||||
statuses_container.appendChild(new_element);
|
||||
} else if (in_current && in_new) {
|
||||
const status_object = json.find(status => status.id === status_id);
|
||||
const current_element = statuses_container.querySelector(`[notification_id="${status_id}"]`);
|
||||
const new_element = create_notification_element_from_notification_object(status_object);
|
||||
current_element.replaceWith(new_element);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(check_statuses, 500);
|
||||
}
|
||||
check_statuses();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
load_domain_from_storage_and_enable_interactions();
|
||||
arm_action_buttons();
|
||||
begin_status_update_loop();
|
||||
});
|
||||
Loading…
Reference in New Issue