reilly-downloader/firefox/background.js
Joey Yakimowich-Payne 7a44902433 feat: surface detailed download progress
Changelog:
- forward download progress from background/offscreen workers to content frames
- add progress UI and button state management in Safari Books page script
- emit structured progress callbacks and faster image fetching in SafariBooksDownloader
- sync Firefox bundle with progress plumbing and concurrency improvements
- ignore packaged extension archives

Description:
Expose progress events through the downloader so the content script can present real-time status, including chapter and asset fetching milestones. Added concurrency controls, page detection, and packaging telemetry, while keeping Firefox build behavior in sync and ignoring generated .xpi bundles.
2025-10-16 10:30:18 -06:00

95 lines
2.5 KiB
JavaScript

import { SafariBooksDownloader } from "./safaribooks/downloader.js";
import { collectSessionCookies } from "./safaribooks/cookies.js";
const runtime = typeof browser !== "undefined" ? browser : chrome;
const pendingDownloads = new Map();
const downloader = new SafariBooksDownloader((message) => {
console.log(`[BackgroundDownloader] ${message}`);
}, (payload) => {
const entry = payload?.bookId ? pendingDownloads.get(payload.bookId) : null;
if (!entry) {
return;
}
sendProgress(entry, payload);
});
function sendProgress(entry, payload) {
if (!entry?.tabId) {
return;
}
const progressMessage = {
type: "download-progress",
payload
};
try {
if (typeof browser !== "undefined" && browser.tabs?.sendMessage) {
browser.tabs.sendMessage(entry.tabId, progressMessage).catch(() => {});
} else {
chrome.tabs.sendMessage(entry.tabId, progressMessage, () => {
const error = chrome.runtime.lastError;
if (error) {
console.debug("Progress send error", error.message);
}
});
}
} catch (error) {
console.debug("Unable to dispatch progress message", error);
}
}
runtime.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (!message || message.type !== "downloadBook") {
return undefined;
}
const bookId = message.bookId;
if (!bookId) {
sendResponse({ ok: false, error: "Missing book ID." });
return false;
}
if (pendingDownloads.has(bookId)) {
sendResponse({ ok: false, error: "Download already in progress." });
return false;
}
const entry = {
bookId,
options: {
theme: message.theme ?? "none",
kindle: Boolean(message.kindle)
},
tabId: sender?.tab?.id ?? null,
sendResponse
};
pendingDownloads.set(bookId, entry);
startDownload(entry).catch((error) => {
console.error("[BackgroundDownloader] unexpected failure", error);
finalizeDownload(entry, { ok: false, error: error.message || String(error) });
});
return true;
});
async function startDownload(entry) {
try {
await collectSessionCookies();
await downloader.download(entry.bookId, entry.options);
finalizeDownload(entry, { ok: true });
} catch (error) {
console.error("[BackgroundDownloader] Download failed:", error);
finalizeDownload(entry, { ok: false, error: error.message || String(error) });
}
}
function finalizeDownload(entry, response) {
pendingDownloads.delete(entry.bookId);
try {
entry.sendResponse?.(response);
} catch (error) {
console.debug("sendResponse failed", error);
}
}