162 lines
4.4 KiB
JavaScript
162 lines
4.4 KiB
JavaScript
|
|
// Investor RPG — HyRead 閱讀器內容擷取
|
||
|
|
|
||
|
|
const API_HOST = "http://localhost:3000";
|
||
|
|
|
||
|
|
function isReaderPage() {
|
||
|
|
return /\/reader\//.test(window.location.pathname) ||
|
||
|
|
/reader\.html/.test(window.location.href) ||
|
||
|
|
document.querySelector(".hyread-reader") ||
|
||
|
|
document.querySelector("#readerContainer") ||
|
||
|
|
document.querySelector("[class*='reader']");
|
||
|
|
}
|
||
|
|
|
||
|
|
function getBookTitle() {
|
||
|
|
const el = document.querySelector(".book-title") ||
|
||
|
|
document.querySelector("h1") ||
|
||
|
|
document.querySelector("meta[property='og:title']") ||
|
||
|
|
document.querySelector("title");
|
||
|
|
return el?.textContent?.trim() || el?.getAttribute("content") || "HyRead 書籍";
|
||
|
|
}
|
||
|
|
|
||
|
|
function extractPageContent() {
|
||
|
|
const contentSelectors = [
|
||
|
|
"#pageContent", ".page-content", ".reader-content",
|
||
|
|
"#contentArea", ".content-area", "#textLayer",
|
||
|
|
"section.page", ".book-page", "[class*='page-text']",
|
||
|
|
"#iframeContent", "article",
|
||
|
|
];
|
||
|
|
|
||
|
|
for (const sel of contentSelectors) {
|
||
|
|
const el = document.querySelector(sel);
|
||
|
|
if (el && el.textContent.trim().length > 50) {
|
||
|
|
return {
|
||
|
|
text: el.textContent.trim(),
|
||
|
|
html: el.innerHTML,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const iframes = document.querySelectorAll("iframe");
|
||
|
|
for (const iframe of iframes) {
|
||
|
|
try {
|
||
|
|
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
||
|
|
if (doc && doc.body.textContent.trim().length > 50) {
|
||
|
|
return {
|
||
|
|
text: doc.body.textContent.trim(),
|
||
|
|
html: doc.body.innerHTML,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
} catch {}
|
||
|
|
}
|
||
|
|
|
||
|
|
const mainText = document.body.innerText?.trim() || "";
|
||
|
|
return {
|
||
|
|
text: mainText,
|
||
|
|
html: document.body.innerHTML,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function getAllPageContent() {
|
||
|
|
const allTexts = [];
|
||
|
|
const allHtmls = [];
|
||
|
|
|
||
|
|
const content = extractPageContent();
|
||
|
|
if (content.text.length > 50) {
|
||
|
|
allTexts.push(content.text);
|
||
|
|
allHtmls.push(content.html);
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
text: allTexts.join("\n\n---\n\n"),
|
||
|
|
html: allHtmls.join("\n"),
|
||
|
|
pageCount: allTexts.length,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
async function captureBook() {
|
||
|
|
const title = getBookTitle();
|
||
|
|
const content = getAllPageContent();
|
||
|
|
|
||
|
|
if (content.text.length < 50) {
|
||
|
|
throw new Error("無法擷取到足夠的內容,請確認閱讀器已開啟");
|
||
|
|
}
|
||
|
|
|
||
|
|
const payload = {
|
||
|
|
kind: "hyread",
|
||
|
|
title,
|
||
|
|
url: window.location.href,
|
||
|
|
html: content.html,
|
||
|
|
metadata: {
|
||
|
|
title,
|
||
|
|
url: window.location.href,
|
||
|
|
pageCount: content.pageCount,
|
||
|
|
textLength: content.text.length,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const res = await fetch(`${API_HOST}/api/content/capture`, {
|
||
|
|
method: "POST",
|
||
|
|
headers: { "Content-Type": "application/json" },
|
||
|
|
body: JSON.stringify(payload),
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!res.ok) {
|
||
|
|
const err = await res.json().catch(() => ({}));
|
||
|
|
throw new Error(err.message || `伺服器回應 ${res.status}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return await res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
function injectCaptureButton() {
|
||
|
|
if (document.querySelector("#irpg-capture-btn")) return;
|
||
|
|
if (!isReaderPage()) return;
|
||
|
|
|
||
|
|
const btn = document.createElement("button");
|
||
|
|
btn.id = "irpg-capture-btn";
|
||
|
|
btn.textContent = "📖 擷取到 RPG";
|
||
|
|
btn.style.cssText = `
|
||
|
|
position: fixed; top: 12px; right: 12px; z-index: 99999;
|
||
|
|
background: #2a6c3b; color: #fff; border: none; border-radius: 8px;
|
||
|
|
padding: 8px 16px; font-size: 14px; cursor: pointer;
|
||
|
|
font-weight: 500; box-shadow: 0 2px 8px rgba(0,0,0,.3);
|
||
|
|
transition: background .15s;
|
||
|
|
`;
|
||
|
|
btn.onmouseenter = () => btn.style.background = "#1e7e34";
|
||
|
|
btn.onmouseleave = () => btn.style.background = "#2a6c3b";
|
||
|
|
|
||
|
|
btn.onclick = async () => {
|
||
|
|
btn.textContent = "擷取中...";
|
||
|
|
btn.disabled = true;
|
||
|
|
try {
|
||
|
|
const result = await captureBook();
|
||
|
|
btn.textContent = `✓ ${result.count || "已送出"}`;
|
||
|
|
btn.style.background = "#1e7e34";
|
||
|
|
setTimeout(() => {
|
||
|
|
btn.remove();
|
||
|
|
}, 3000);
|
||
|
|
} catch (err) {
|
||
|
|
btn.textContent = `✕ ${err.message.slice(0, 25)}`;
|
||
|
|
btn.style.background = "#a13a3a";
|
||
|
|
setTimeout(() => {
|
||
|
|
btn.textContent = "📖 擷取到 RPG";
|
||
|
|
btn.style.background = "#2a6c3b";
|
||
|
|
btn.disabled = false;
|
||
|
|
}, 3000);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
document.body.appendChild(btn);
|
||
|
|
}
|
||
|
|
|
||
|
|
setTimeout(injectCaptureButton, 2000);
|
||
|
|
|
||
|
|
let lastHref = window.location.href;
|
||
|
|
setInterval(() => {
|
||
|
|
if (window.location.href !== lastHref) {
|
||
|
|
lastHref = window.location.href;
|
||
|
|
document.querySelector("#irpg-capture-btn")?.remove();
|
||
|
|
setTimeout(injectCaptureButton, 1500);
|
||
|
|
}
|
||
|
|
}, 1500);
|