您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add link to download Google Books preview pages into a ZIP file including a book viewer HTML application for use with a web browser (use web browser's zoom feature to zoom in/out pages).
- // ==UserScript==
- // @name Google Books Preview Downloader
- // @namespace https://greasyfork.org/en/users/85671-jcunews
- // @version 1.0.3
- // @license AGPLv3
- // @author jcunews
- // @description Add link to download Google Books preview pages into a ZIP file including a book viewer HTML application for use with a web browser (use web browser's zoom feature to zoom in/out pages).
- // @include /^https:\/\/books\.google\.com\/books\?.*$/
- // @include /^https:\/\/books\.google\.com?\.[a-z]{2}\/books\?.*$/
- // @include /^https:\/\/books\.google\.[a-z]{2}\/books\?.*$/
- // @require https://cdn.jsdelivr.net/gh/nindogo/tiny_zip_js@4fa0ae770e32bac5181c83d8c5c6a9f59f853e12/tiny_zip.js
- // @grant none
- // ==/UserScript==
- ((em, dd, dp, dt, ic, dl, rl, pi, pc, dc, tz, ns, fe, ou, z) => {
- function cl(e) {
- clearInterval(dt);
- if (fe && (fe !== 1)) alert(em);
- dp.remove();
- document.body.style.pointerEvents = ""
- }
- function fer(e) {
- fe = e;
- if (--dc === 0) cl(e)
- }
- function html(s, e) {
- (e = document.createElement("DIV")).textContent = s;
- return e.innerHTML
- }
- function gi(ii, ti, si, ct) {
- if (fe) return;
- dc++;
- fetch(ii.page[si].src + "&w=" + dd[1].max_resolution_image_width).then(r => ((ct = r.headers.get("content-type")), r.arrayBuffer())).then((a, b) => {
- dc--;
- tz.add(ns[ti] = `${("000" + (ti + 1)).substr(-4)}-${ii.page[si].pid}${
- dd[0].page[ti].title ? " " + dd[0].page[ti].title.replace(/[\*:\\\|\<\>\/\?]/, "-") : ""
- }.${ct ? ct.match(/\/(.*)/)[1].replace("jpeg", "jpg") : "png"}`, new Uint8Array(a));
- if (++pc === dl) {
- (new Blob([`<!DOCTYPE html>
- <html>
- <head>
- <meta charset=utf-8 />
- <!--
- Downloaded on $(dltime) from: $(gburl)
- Using Google Books Preview Downloader v1.0.1.
- https://greasyfork.org/en/users/85671-jcunews
- -->
- <title>$(title)</title>
- <style>
- body{
- margin:1.7vw 0 0 0;background:#bbb;overflow:hidden;text-align:center;
- font-family:sans-serif;font-size:1.2vw;
- }
- button,input{
- box-sizing:border-box;border-width:.1vw;padding:0 .25vw;height:1.45vw;
- font:inherit;line-height:1vw;
- }
- #tnav{ display:none; }
- #panel{
- position:fixed;left:0;top:0;right:0;border-bottom:.1vw solid #000;
- box-sizing:border-box;height:1.7vw;background:#ddd;
- }
- #lnav{
- position:absolute;z-index:1;left:1.0vw;top:.1vw;box-sizing:border-box;
- border:.1vw solid #55f;border-radius:.3vw;;padding:0 .3vw;
- line-height:1.3vw;cursor:pointer;
- }
- #tnav:checked+#panel #lnav{
- border-color:transparent;background:#55f;color:#fff;
- }
- #title{
- position:absolute;left:6vw;width:36vw;height:1.6vw;overflow:hidden;
- text-align:left;white-space:nowrap;text-overflow:ellipsis;
- }
- #pnc{ display:inline-block;margin-top:.1vw;vertical-align:top; }
- #pnc button{ margin:0 1vw;width:1.6vw; }
- #pn{
- vertical-align:top;border-width:.1vw;padding-right:0;width:4vw;
- text-align:right;
- }
- #vgb{ position:absolute;right:.3vw;text-decoration:none }
- #nav{
- position:fixed;left:0;top:1.7vw;bottom:0;box-sizing:border-box;
- border-right:.1vw solid #000;padding-bottom:.4vw;width:6vw;
- overflow-y:scroll;background:#ddd;counter-reset:pg;
- }
- #tnav:not(:checked)~#nav{ display:none; }
- #nav a{
- display:inline-block;margin-top:.4vw;box-sizing:border-box;
- border:.1vw solid #000;width:4vw;background:#fff;
- text-decoration:none!important;counter-increment:a;
- }
- #nav a:after{
- display:block;color:#000;font-size:1vw;line-height:1vw;
- content:counter(a);
- }
- #nav a:hover:after{ background:#bbf; }
- #nav img{
- vertical-align:top;width:100%;object-fit:contain;object-position:0 0;
- }
- #pages{
- position:fixed;left:6vw;top:1.7vw;right:0;bottom:0;padding-bottom:.4vw;
- overflow:auto;
- }
- #tnav:not(:checked)~#pages{ left:0; }
- #pages img{
- vertical-align:top;margin:.4vw 0 0 .4vw;border:.1vw solid #000;
- box-sizing:border-box;width:calc(100% - .8vw);background:#fff;
- object-fit:contain;object-position:0 0;
- }
- </style>
- <style id=css></style>
- </head>
- <body>
- <input id=tnav type=checkbox checked />
- <div id=panel>
- <label id=lnav for=tnav>Nav</label>
- <div id=title title="$(title2)">$(title2)</div>
- <div id=pnc>
- <button id=pp><</button>
- Page <input id=pn type=number value=1 min=1 max=$(pagemax)/>
- <button id=np>></button>
- </div>
- <a id=vgb href="$(gburl)">View in Google Books</a>
- </div>
- <div id=nav></div>
- <div id=pages></div>
- <script>
- a = b = "";
- "${ns.join("|")}".split("|").forEach((n, i) => {
- a += \`<a href=#p$[i + 1]><img src="$[n]" /></a>\`;
- b += \`<img id=p$[i + 1] src="$[n]" />\`
- });
- nav.innerHTML = a;
- nav.querySelectorAll("A").forEach((e, i) => e.page = i + 1);
- pages.innerHTML = b;
- (ps = Array.from(pages.children)).forEach((e, i) => e.page = i + 1);
- (tnav.onchange = onresize = () => setTimeout(() => {
- css.innerHTML = \`#pages img{width:calc($[pages.offsetWidth - pages.offsetTop * 2]px * $[devicePixelRatio])}\`
- }, 0))();
- pn.oninput = e => (e = nav.querySelector(\`a[href="#p$[pn.value]"]\`)) && e.click();
- pages.onscroll = (vh, st, pm) => {
- vh = pages.offsetHeight / 2;
- st = pages.scrollTop;
- pm = ps[0].offsetTop;
- ps.some(e => {
- if ((st >= (e.offsetTop - pm - vh)) && (st < (e.offsetTop + e.offsetHeight - vh))) {
- pn.value = e.page;
- return true
- }
- })
- };
- addEventListener("keydown", ev => {
- if (!ev.altKey && !ev.ctrlKey && !ev.shiftKey) switch (ev.key) {
- case "ArrowLeft":
- pp.click()
- break;
- case "ArrowRight":
- np.click()
- break;
- }
- });
- addEventListener("click", (ev, e) => {
- switch ((e = ev.target).id) {
- case "pp":
- pn.oninput(pn.value = pn.value > 1 ? parseInt(pn.value) - 1 : 1);
- break;
- case "np":
- if (document.querySelector("#p" + (e = (p = parseInt(pn.value)) + 1))) {
- pn.oninput(pn.value = e);
- if (parseInt(pn.value) <= e) pn.oninput(pn.value = e + 2)
- }
- break;
- default:
- if (e.matches("#nav *")) {
- if (!e.href) e = e.parentNode;
- document.querySelector(e.hash).scrollIntoView();
- pages.scrollBy(0, -pages.children[0].offsetTop);
- ev.preventDefault(pn.value = e.page)
- }
- }
- onfocus()
- });
- onfocus = () => {
- if (!document.activeElement || (document.activeElement.id !== "pn")) pages.focus()
- };
- onload = () => setTimeout(() => pages.onscroll(onresize(pages.focus())), 0)
- </script>
- </body>
- </html>`.replace(/\$\(dltime\)/g, (new Date).toGMTString()
- ).replace(/\$\(gburl\)/g, location.href.match(/^[^#]+/)[0]
- ).replace(/\$\(title\)/,
- b = `${dd[1].title}${(si = document.querySelector('.gb-volume-title span')?.textContent?.trim()) ? ", " + si : ""
- }${dd[1].publisher ? ", " + dd[1].publisher : ""}, ${dd[1].volume_id}`
- ).replace(/\$\(title2\)/g, html(b)
- ).replace(/\$\(pagemax\)/, dd[0].page.length
- ).replace(/\$\[([^\]]+)\]/g, "${$1}"
- )], {type: "text/html"})).arrayBuffer().then(a => {
- tz.add("viewer.html", new Uint8Array(a));
- if (ou) URL.revokeObjectURL(ou);
- (ct = document.createElement("A")).href = ou = URL.createObjectURL(tz.generate());
- ct.download = `${b}.zip`;
- ct.style.display = "none";
- document.body.appendChild(ct).click();
- ct.remove();
- cl()
- })
- }
- }).catch(fer)
- }
- function nx(av) {
- try {
- if (!confirm(
- `Book preview has ${dd[0].page.length} page(s).
- Rough estimated download size is ${
- (() => {
- if (av >= 1024*1048576) {
- return parseFloat((av / 1024*1048576).toFixed(2)) + "GB"
- } else if (av >= 1048576) {
- return parseFloat((av / 1048576).toFixed(2)) + "MB"
- } else return parseFloat((av / 1024).toFixed(2)) + "KB"
- })()} based only from ${rl.length} already loaded page(s).
- \nProceed with the download?`)) return;
- ic = document.createElement("DIV");
- dl = dd[0].page.length;
- rl = Math.ceil(dl / 4);
- pi = 0;
- pc = 0;
- dc = 0;
- tz = new tiny_zip;
- ns = [];
- document.body.style.pointerEvents = "none";
- dpp.textContent = `Retrieving pages (1/${dl})...`;
- document.documentElement.append(dp);
- dt = setInterval(() => (dpp.textContent = `Retrieving pages (${pc}/${dl})...`), 100);
- for (let ri = 0; ri < rl; ri++) {
- if (fe) break;
- let i = ri;
- dc++;
- fetch(`/books?id=${dd[1].volume_id}&pg=${dd[0].page[i * 4].pid}&jscmd=click3`).then(r => r.json()).then((j, d, k, x, y) => {
- dc--;
- if (fe) return;
- gi(j, i * 4, 0);
- z++;
- d = 1;
- if (i) { x = 2; y = 5
- } else { x = 1; y = 4 }
- while ((x < y) && (pi < dl)) {
- if ((k = i * 4 + d) >= dl) break;
- gi(j, k, x);
- d++;
- x++
- }
- }).catch(fer)
- }
- if (fe) cl()
- } catch(z) {
- alert(em, cl())
- }
- }
- em = "Failed to retrieve data due to site changes.";
- if (dd = document.querySelector(".gback+script")) try {
- (dp = document.createElement("DIV")).id = "gbpdujs";
- dp.innerHTML = `<style>
- #gbpdujs{all:revert;position:fixed;z-index:999;left:0;top:0;right:0;bottom:0;background:#0007}
- #gbpdujspop{
- position:absolute;left:50%;top:50%;transform:translate(-50%);
- border:.2em solid #00b;border-radius:.5em;padding:.5em 1em;background:#fff;font-family:sans-serif;
- }
- </style><div id="gbpdujspop"></div>`;
- dpp = dp.lastChild;
- dd = JSON.parse("[" + dd.text.match(/_OC_Run\((.*)\);/)[1] + "]");
- if (!(ic = document.querySelector(".menu_content p"))) throw 0;
- ic.insertAdjacentHTML("afterend", '<p><a href="javascript:void(0)" class="gb-left-nav-link"><span>Download preview pages</span></a></p>');
- ic.nextElementSibling.onclick = function() {
- this.style.pointerEvents = "none";
- if (!(rl = Array.from(viewport.querySelectorAll('img[src*="/books/content"]')).splice(0, 3)).length) return alert(em);
- dl = 0;
- (function gii(this_, i) {
- fetch(rl[i].src, {method: "HEAD"}).then(r => {
- dl += parseInt(r.headers.get("content-length"));
- if (++i < rl.length) {
- gii(this_, i)
- } else {
- this_.style.pointerEvents = "";
- nx(dl / rl.length * dd[0].page.length)
- }
- }).catch(e => {
- this_.style.pointerEvents = "";
- fer(e)
- })
- })(this, 0)
- }
- } catch(z) {
- alert(em)
- }
- })()