🏠 Home 

BookliveDownloader

Manga downloader for booklive.jp


安装此脚本?
// ==UserScript==
// @name         BookliveDownloader
// @namespace    https://github.com/Timesient/manga-download-scripts
// @version      0.7
// @license      GPL-3.0
// @author       Timesient
// @description  Manga downloader for booklive.jp
// @icon         https://booklive.jp/favicon.ico
// @homepageURL  https://greasyfork.org/scripts/452562-booklivedownloader
// @supportURL   https://github.com/Timesient/manga-download-scripts/issues
// @match        https://booklive.jp/bviewer/*
// @require      https://unpkg.com/[email protected]/dist/axios.min.js
// @require      https://unpkg.com/[email protected]/dist/jszip.min.js
// @require      https://unpkg.com/[email protected]/dist/FileSaver.min.js
// @require      https://update.greasyfork.org/scripts/451810/1398192/ImageDownloaderLib.js
// @require      https://update.greasyfork.org/scripts/456423/1128886/SpeedReaderTools.js
// @grant        GM_info
// @grant        GM_xmlhttpRequest
// ==/UserScript==
(async function(axios, JSZip, saveAs, ImageDownloader, SpeedReaderTools) {
'use strict';
// collect essential params
const cid = new URL(window.location.href).searchParams.get('cid');
const randomString = SpeedReaderTools.generateRandomString32(cid);
// generate config data
const config = await axios({
method: 'GET',
url: `https://booklive.jp/bib-api/bibGetCntntInfo?cid=${cid}&dmytime=${Date.now()}&k=${randomString}`
}).then(res => {
const data = res.data.items[0];
return {
title: data.Title,
contentServer: data.ContentsServer,
ctbl: SpeedReaderTools.getDecryptedTable(cid, randomString, data.ctbl),
ptbl: SpeedReaderTools.getDecryptedTable(cid, randomString, data.ptbl),
p: data.p
}
});
// check if trial or not
const isTrial = config.contentServer.includes('trial');
// get content data
const contentData = isTrial
? await axios.get(`${config.contentServer}/content.js`).then(res => res.data).then(jsonp => JSON.parse(jsonp.slice(jsonp.indexOf('{'), jsonp.lastIndexOf('}') + 1)))
: await axios.get(`${config.contentServer}/sbcGetCntnt.php?cid=${cid}&p=${config.p}`).then(res => res.data);
// generate data of image files
const doc = (new DOMParser()).parseFromString(contentData.ttx, 'text/html');
let files = Array.from(doc.querySelectorAll('t-img')).map(element => {
const filename = element.getAttribute('src');
const width = element.getAttribute('orgwidth');
const height = element.getAttribute('orgheight');
const src = isTrial ? `${config.contentServer}/${filename}/M_H.jpg` : `${config.contentServer}/sbcGetImg.php?cid=${cid}&src=${encodeURIComponent(filename)}&p=${config.p}`;
return { filename, width, height, src };
});
files = files.slice(0, files.length / 2);
// setup ImageDownloader
ImageDownloader.init({
maxImageAmount: files.length,
getImagePromises,
title: config.title
});
// collect promises of image
function getImagePromises(startNum, endNum) {
return files
.slice(startNum - 1, endNum)
.map(file => getDecryptedImage(file)
.then(ImageDownloader.fulfillHandler)
.catch(ImageDownloader.rejectHandler)
);
}
// get promise of decrypted image
function getDecryptedImage(file) {
return new Promise(async resolve => {
const imageArrayBuffer = await axios.get(file.src, { responseType: 'arraybuffer' }).then(res => res.data);
const image = document.createElement('img');
image.src = 'data:image/jpg;base64,' + window.btoa(new Uint8Array(imageArrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
image.onload = function () {
// create canvas
const canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
canvas.width = file.width;
canvas.height = file.height;
// get coords
const key = SpeedReaderTools.getDecryptionKey(file.filename, config.ctbl, config.ptbl);
const decoder = new SpeedReaderTools.CoordDecoder(key[0], key[1]);
const coords = decoder.getCoords(this);
// draw pieces on correct position
for (const { srcX, srcY, destX, destY, width, height } of coords) {
ctx.drawImage(this, srcX, srcY, width, height, destX, destY, width, height);
}
// if trial, clear those transparent pixel
if (isTrial) {
let originalWidth;
for (let w = canvas.width; w >= 0; w--) {
const px = canvas.getContext('2d').getImageData(w, 0, 1, 1);
if (!Array.from(px.data).every(data => data === 0)) {
originalWidth = w;
break;
}
}
let originalHeight;
for (let h = canvas.height; h >= 0; h--) {
const px = canvas.getContext('2d').getImageData(0, h, 1, 1);
if (!Array.from(px.data).every(data => data === 0)) {
originalHeight = h;
break;
}
}
canvas.width = originalWidth;
canvas.height = originalHeight;
ctx = canvas.getContext('2d');
for (const { srcX, srcY, destX, destY, width, height } of coords) {
ctx.drawImage(this, srcX, srcY, width, height, destX, destY, width, height);
}
}
canvas.toBlob(resolve);
}
});
}
})(axios, JSZip, saveAs, ImageDownloader, SpeedReaderTools);