Make extension auto grab book id

This commit is contained in:
Joey Yakimowich-Payne 2025-10-14 10:36:08 -06:00
commit 622a2045e5
3 changed files with 113 additions and 6 deletions

View file

@ -23,6 +23,41 @@ function setDisabled(disabled) {
kindleCheckbox.disabled = disabled;
}
function extractBookIdFromUrl(url) {
try {
const { pathname } = new URL(url);
const segments = pathname.split("/").filter(Boolean).reverse();
return segments.find((segment) => /^[0-9]{9,}$/.test(segment)) || null;
} catch (_) {
return null;
}
}
async function detectActiveBook() {
if (!chrome.tabs?.query) {
return;
}
try {
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!activeTab?.url || !activeTab.url.includes("/library/view/")) {
return;
}
const detectedBookId = extractBookIdFromUrl(activeTab.url);
if (!detectedBookId) {
return;
}
if (bookIdInput.value !== detectedBookId) {
bookIdInput.value = detectedBookId;
appendLog(`Detected book ID ${detectedBookId} from the active tab.`);
}
} catch (error) {
console.warn("SafariBooks Downloader: unable to detect active book.", error);
}
}
form.addEventListener("submit", async (event) => {
event.preventDefault();
const bookId = bookIdInput.value.trim();
@ -48,3 +83,5 @@ form.addEventListener("submit", async (event) => {
setDisabled(false);
}
});
detectActiveBook();

View file

@ -32,7 +32,8 @@
"permissions": [
"downloads",
"cookies",
"storage"
"storage",
"tabs"
],
"host_permissions": [
"https://learning.oreilly.com/*",

View file

@ -539,14 +539,83 @@ export class SafariBooksDownloader {
async fetchImages() {
for (const [relative, url] of this.imageDownloads.entries()) {
const response = await fetch(url, { credentials: "include" });
if (!response.ok) {
this.log(`Warning: unable to fetch image ${url} (status ${response.status})`);
const result = await this.fetchImageWithFallback(relative, url);
if (result) {
this.imageDownloads.set(relative, result);
}
}
}
async fetchImageWithFallback(relative, primaryUrl) {
const attempts = [];
const tried = new Set();
const queue = [primaryUrl, ...this.buildImageFallbackUrls(relative, primaryUrl)];
for (const candidate of queue) {
if (!candidate || tried.has(candidate)) {
continue;
}
const data = new Uint8Array(await response.arrayBuffer());
this.imageDownloads.set(relative, { data, sourceUrl: url });
tried.add(candidate);
try {
const response = await fetch(candidate, { credentials: "include" });
if (!response.ok) {
attempts.push(`${candidate} (status ${response.status})`);
continue;
}
const data = new Uint8Array(await response.arrayBuffer());
if (candidate !== primaryUrl) {
this.log(`Info: fetched image ${relative} via fallback ${candidate}`);
}
return { data, sourceUrl: candidate };
} catch (error) {
attempts.push(`${candidate} (${error.message})`);
}
}
if (attempts.length) {
this.log(
`Warning: unable to fetch image ${primaryUrl}. Attempts: ${attempts.join("; ")}`
);
}
return null;
}
buildImageFallbackUrls(relative, url) {
const candidates = new Set();
if (url.includes("/Images/")) {
candidates.add(url.replace("/Images/", "/images/"));
}
if (url.includes("/images/")) {
candidates.add(url.replace("/images/", "/Images/"));
}
if (url.includes("/files/Images/")) {
const base = url.replace(/Images\/[^/]*$/i, "Images/");
["cover.jpg", "cover.jpeg", "cover.png", "cover-large.jpg"].forEach((file) => {
candidates.add(`${base}${file}`);
});
}
const isbnMatch = url.match(/Images\/(\d{10,13})\.(jpg|jpeg|png|gif)$/i);
if (isbnMatch) {
const isbn = isbnMatch[1];
const sizes = ["600w", "400w", "250w", ""];
sizes.forEach((size) => {
const trimmedSize = size ? `/${size}` : "";
candidates.add(`https://learning.oreilly.com/library/cover/${isbn}${trimmedSize}/`);
});
}
if (this.bookInfo?.cover && url !== this.bookInfo.cover) {
candidates.add(this.bookInfo.cover);
}
if (relative && this.bookInfo?.cover_url) {
candidates.add(this.bookInfo.cover_url);
}
return Array.from(candidates).filter(Boolean);
}
async fetchCssImages() {