feat(options,popup,background): add per-page aria2 dir option and popup toggle

- Options: add default toggle and base path ()
- Popup: allow per-send enable + preview; passes AGENTS.md  LICENSE	  node_modules	package-lock.json  releases  src
dist	   manifest.json  package.json	README.md	   scripts to aria2
- Background: quick actions honor default, set AGENTS.md  LICENSE	  node_modules	package-lock.json  releases  src
dist	   manifest.json  package.json	README.md	   scripts to <base>/<identifier>
- Storage: new prefs  and
This commit is contained in:
Jordan Wages 2025-08-23 00:53:04 -05:00
commit 0d944892d8
5 changed files with 89 additions and 7 deletions

View file

@ -74,6 +74,8 @@ async function getPrefs() {
return {
defaultAction: prefs?.defaultAction || 'download',
allIncludeMeta: !!prefs?.allIncludeMeta,
perPageDirEnabled: !!prefs?.perPageDirEnabled,
perPageDirBase: prefs?.perPageDirBase || '/aria2/data',
};
}
@ -108,7 +110,7 @@ async function rebuildContextSubmenu(tab) {
let items = [];
try { items = await collectFromTab(tab.id); } catch (_) { items = []; }
const identifier = parseIdentifierFromUrl(tab.url || '');
const { defaultAction, allIncludeMeta } = await getPrefs();
const { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase } = await getPrefs();
const verb = defaultAction === 'copy' ? 'Copy' : 'Download';
const top = computeTopTypes(items, identifier, 4);
@ -256,7 +258,7 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => {
const action = dynamicActions.get(String(info.menuItemId));
if (!action || !tab?.id) return;
try {
const { defaultAction, allIncludeMeta } = await getPrefs();
const { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase } = await getPrefs();
const identifier = parseIdentifierFromUrl(tab.url || '');
const items = await collectFromTab(tab.id);
let selected = items || [];
@ -276,10 +278,19 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => {
color: '#10b981',
title: `Copied ${count} link(s)`
});
} else {
} else {
const { endpoint, secret } = await getAria2();
if (!endpoint) { console.warn('aria2 endpoint not set'); return; }
try { await globalThis.addUrisBatch({ endpoint, secret, uris }); } catch (e) { console.warn('aria2 send failed', e); }
// Build aria2 options (dir) based on prefs
let options = {};
try {
if (perPageDirEnabled && perPageDirBase) {
const base = String(perPageDirBase).replace(/\/+$/, '');
const dir = identifier ? `${base}/${identifier}` : base;
if (dir) options.dir = dir;
}
} catch (_) { /* noop */ }
try { await globalThis.addUrisBatch({ endpoint, secret, uris, options }); } catch (e) { console.warn('aria2 send failed', e); }
await setBadgeForTab(tab.id, {
text: String(count),
color: '#3b82f6',

View file

@ -41,6 +41,12 @@
<div class="row">
<label class="chk"><input type="checkbox" id="include-meta" /> Include metadata in “All”</label>
</div>
<div class="row">
<label class="chk"><input type="checkbox" id="per-page-dir-enabled" /> Use perpage download directory by default</label>
</div>
<div class="row">
<label>Base directory <input id="per-page-dir-base" placeholder="/aria2/data" /></label>
</div>
</section>
</main>
<script type="module" src="index.js"></script>

View file

@ -5,6 +5,8 @@ const els = {
secret: document.getElementById('rpc-secret'),
defaultAction: document.getElementById('default-action'),
includeMeta: document.getElementById('include-meta'),
perPageDirEnabled: document.getElementById('per-page-dir-enabled'),
perPageDirBase: document.getElementById('per-page-dir-base'),
btnSave: document.getElementById('btn-save'),
btnReset: document.getElementById('btn-reset'),
btnTest: document.getElementById('btn-test'),
@ -19,8 +21,12 @@ async function restore() {
const { prefs } = await browser.storage.local.get('prefs');
const defaultAction = prefs?.defaultAction || 'download';
const includeMeta = !!prefs?.allIncludeMeta;
const perPageDirEnabled = !!prefs?.perPageDirEnabled;
const perPageDirBase = prefs?.perPageDirBase || '/aria2/data';
els.defaultAction.value = defaultAction;
els.includeMeta.checked = includeMeta;
els.perPageDirEnabled.checked = perPageDirEnabled;
els.perPageDirBase.value = perPageDirBase;
}
async function save() {
@ -28,9 +34,11 @@ async function save() {
const secret = els.secret.value.trim();
const defaultAction = els.defaultAction.value;
const allIncludeMeta = !!els.includeMeta.checked;
const perPageDirEnabled = !!els.perPageDirEnabled.checked;
const perPageDirBase = els.perPageDirBase.value.trim() || '/aria2/data';
await browser.storage.local.set({
aria2: { endpoint, secret },
prefs: { defaultAction, allIncludeMeta }
prefs: { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase }
});
els.status.textContent = 'Saved.';
}

View file

@ -49,6 +49,12 @@
<div class="row">
<label>Secret <input id="rpc-secret" type="password" placeholder="your-token" /></label>
</div>
<div class="row">
<label class="chk"><input type="checkbox" id="use-perpage-dir" /> Use perpage download directory</label>
</div>
<div class="row">
<small id="dir-preview"></small>
</div>
<div class="row actions">
<button id="btn-copy">Copy Links</button>
<button id="btn-send">Send to aria2</button>

View file

@ -15,6 +15,8 @@ const els = {
btnSend: document.getElementById('btn-send'),
rpcEndpoint: document.getElementById('rpc-endpoint'),
rpcSecret: document.getElementById('rpc-secret'),
usePerPageDir: document.getElementById('use-perpage-dir'),
dirPreview: document.getElementById('dir-preview'),
count: document.getElementById('count'),
status: document.getElementById('status'),
openOptions: document.getElementById('open-options'),
@ -42,6 +44,21 @@ async function getActiveTabId() {
return tabs[0]?.id;
}
function parseIdentifierFromUrl(url) {
try {
const u = new URL(url);
const parts = u.pathname.split('/').filter(Boolean);
const idx = parts.indexOf('download');
if (idx >= 0 && parts[idx + 1]) return parts[idx + 1];
} catch (_) {}
return '';
}
async function getActiveTabUrl() {
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
return tabs[0]?.url || '';
}
async function refresh() {
els.status.textContent = '';
try {
@ -58,6 +75,7 @@ async function refresh() {
}
allItems = items || [];
await updateCount();
await updateDirPreview();
} catch (e) {
els.status.textContent = String(e?.message || e);
}
@ -95,10 +113,21 @@ async function sendToAria2() {
// Persist settings
await browser.storage.local.set({ aria2: { endpoint, secret } });
els.status.textContent = 'Sending to aria2…';
// aria2 options
let options = {};
try {
if (els.usePerPageDir?.checked) {
const { prefs } = await browser.storage.local.get('prefs');
const base = (prefs?.perPageDirBase || '/aria2/data').replace(/\/+$/, '');
const url = await getActiveTabUrl();
const id = parseIdentifierFromUrl(url);
const dir = id ? `${base}/${id}` : base;
if (dir) options.dir = dir;
}
} catch (_) {}
const res = await browser.runtime.sendMessage({
type: 'aria2.send',
payload: { endpoint, secret, uris }
payload: { endpoint, secret, uris, options }
});
if (res?.ok) {
els.status.textContent = `Sent ${uris.length} link(s) to aria2.`;
@ -115,6 +144,11 @@ async function restoreSettings() {
} else {
els.rpcEndpoint.value = 'http://localhost:6800/jsonrpc';
}
try {
const { prefs } = await browser.storage.local.get('prefs');
const enabled = !!prefs?.perPageDirEnabled;
els.usePerPageDir.checked = enabled;
} catch (_) {}
}
function wire() {
@ -126,6 +160,9 @@ function wire() {
els.fType, els.fTypeRe, els.fName, els.fNameRe, els.fSizeMin, els.fSizeMax,
els.fDateFrom, els.fDateTo, els.fCase
].forEach(el => el.addEventListener('input', () => { updateCount(); }));
if (els.usePerPageDir) {
els.usePerPageDir.addEventListener('change', () => { updateDirPreview(); });
}
}
document.addEventListener('DOMContentLoaded', async () => {
@ -150,3 +187,17 @@ function renderPreview() {
els.preview.appendChild(li);
}
}
async function updateDirPreview() {
if (!els.dirPreview) return;
try {
const { prefs } = await browser.storage.local.get('prefs');
const base = (prefs?.perPageDirBase || '/aria2/data').replace(/\/+$/, '');
const url = await getActiveTabUrl();
const id = parseIdentifierFromUrl(url);
const dir = id ? `${base}/${id}` : base;
els.dirPreview.textContent = els.usePerPageDir?.checked ? `Will set aria2 dir: ${dir}` : '';
} catch (_) {
els.dirPreview.textContent = '';
}
}