卡在正在前往阅读器以获取url了
Closed this issue · 5 comments
MASONDADA commented
cpuopt commented
xingoxu commented
研究了一个下午的API
把作者的内容全部替换成下面这段的话就能下载全部
const workNo = location.href.match(/\/work\/(\w+)\//)[1];
if (!workNo) {
throw new Error("no workNo");
}
let scripts = [
"https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.1/cropper.min.js",
"https://cdn.jsdelivr.net/npm/streamsaver@2.0.3/StreamSaver.min.js",
"https://jimmywarting.github.io/StreamSaver.js/examples/zip-stream.js",
// "https://unpkg.com/mersenne-twister@1.1.0/src/mersenne-twister.js",
];
scripts.forEach((url) => {
let script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = url;
document.documentElement.appendChild(script);
});
async function getDownloadCredential() {
const response = await fetch(
`https://play.dl.dlsite.com/api/download/sign/cookie?workno=${workNo}`,
{
method: "GET",
headers: {
Accept: "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
},
referrer: "https://play.dlsite.com/",
credentials: "include",
}
);
return await response.json();
}
async function getDownloadUrls(prefix) {
const response = await fetch(`${prefix}ziptree.json`, {
referrer: "https://play.dlsite.com/",
credentials: "include",
});
const zipTree = await response.json();
const result = [];
const travel = (fileObj, index, path) => {
if (fileObj.type === "folder") {
fileObj.children.forEach((child, index) =>
travel(child, index, fileObj.path)
);
}
if (fileObj.type === "file" && !fileObj.hashname.endsWith(".pdf")) {
result.push({
filename: `${path ? `${path}/` : ""}${fileObj.name}`,
optimized: zipTree.playfile[fileObj.hashname].image.optimized,
});
}
};
zipTree.tree.forEach(travel);
// console.log(result);
return result;
}
function getDecryptedImageData(optimized) {
const qv = (t, s) => {
const MersenneTwister = unsafeWindow.module.exports;
// const MersenneTwister = window.module.exports;
const n = new MersenneTwister(t);
for (let r = s.length - 1; r > 0; r--) {
const o = Math.floor(n.random() * (r + 1));
[s[r], s[o]] = [s[o], s[r]];
}
return s;
},
Ir = (t, s) => (t >= s ? t % s : t),
Lr = (t, s) => (t >= s ? Math.floor(t / s) : 0);
const n = {
w: Math.ceil(optimized.width / 128),
h: Math.ceil(optimized.height / 128),
},
r = parseInt(optimized.name.substring(5, 12), 16),
i = qv(r, [...Array(n.w * n.h).keys()]).map((value, index) => ({
sx: 128 * Ir(index, n.w),
sy: 128 * Lr(index, n.w),
dx: 128 * Ir(value, n.w),
dy: 128 * Lr(value, n.w),
}));
return { sourceCropSize: 128, cropCount: n, coordinates: i };
}
async function imagePuzzle(downloadPrefix, { filename, optimized }) {
let canvas = document.createElement("canvas");
canvas.width = optimized.width;
canvas.height = optimized.height;
let ctx = canvas.getContext("2d");
let binResponse = await fetch(
`${downloadPrefix}optimized/${optimized.name}`,
{
method: "GET",
headers: {
Accept:
"image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Sec-Fetch-Dest": "image",
},
referrer: "https://play.dlsite.com/",
credentials: "include",
}
);
let blob = await binResponse.blob();
const img = new Image();
img.src = URL.createObjectURL(blob);
return new Promise((resolve) => {
img.onload = function () {
const {
sourceCropSize: sourceCropSize,
cropCount: cropCount,
coordinates: coordinates,
} = getDecryptedImageData(optimized),
// g = isSpread && m === 1 ? t[0].width : 0,
g = 0,
// y = t[isSpread && m === 0 ? 1 : 0].height,
// w = isSpread && optimized.height < y ? Math.round((y - optimized.height) / 2) : 0,
w = 0,
x = {
w: img.width - optimized.width,
h: img.height - optimized.height,
};
for (const coordinate of coordinates) {
const k =
coordinate.dx + sourceCropSize === sourceCropSize * cropCount.w
? sourceCropSize - x.w
: sourceCropSize,
O =
coordinate.dy + sourceCropSize === sourceCropSize * cropCount.h
? sourceCropSize - x.h
: sourceCropSize;
ctx.drawImage(
img,
coordinate.sx,
coordinate.sy,
k,
O,
coordinate.dx + g,
coordinate.dy + w,
k,
O
);
}
canvas.toBlob(function (blob) {
resolve({ filename, blob });
});
};
});
}
function save(mangaName, blobs) {
const fileStream = streamSaver.createWriteStream(`${mangaName}.zip`);
const readableZipStream = new ZIP({
start(ctrl) {
blobs.forEach(({ blob, filename }, arrayIndex) => {
let file = {
// name: `${mangaName}/${(index + 1).toString().padStart(4, "0")}.jpg`,
// name: `${(index + 1).toString().padStart(4, "0")}.png`,
name: `${filename.split(".")[0]}.png`,
stream: () => blob.stream(),
};
ctrl.enqueue(file);
});
ctrl.close();
},
});
// more optimized
if (window.WritableStream && readableZipStream.pipeTo) {
return readableZipStream
.pipeTo(fileStream)
.then(() => console.debug("done writing"));
}
// less optimized
const writer = fileStream.getWriter();
const reader = readableZipStream.getReader();
const pump = () =>
reader
.read()
.then((res) =>
res.done ? writer.close() : writer.write(res.value).then(pump)
);
pump();
}
async function getMangaName() {
const response = await fetch(`https://play.dlsite.com/api/work/${workNo}`, {
referrer: "https://play.dlsite.com/",
credentials: "include",
});
const result = await response.json();
return result.name["ja_JP"];
}
async function downloadFlow() {
const { url: downloadPrefix, cookies } = await getDownloadCredential();
const [mangaName, downloadUrls] = await Promise.all([
getMangaName(),
getDownloadUrls(downloadPrefix),
]);
const downloadResults = await Promise.all(
downloadUrls.map((value) => imagePuzzle(downloadPrefix, value))
);
save(mangaName, downloadResults);
}
class DownloadButton {
button;
constructor(className, father) {
let button = document.createElement("button");
button.className = className;
button.innerText = "使用脚本下载";
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
downloadFlow();
});
father.appendChild(button);
this.button = button;
}
}
GM_addStyle(`
.button-down{
border: none;
background-color: #007aff;
color: white;
padding-inline: 0.6rem;
position: absolute;
right: 0;
height: 100%;
z-index: 2;
font-weight: bolder;
transition: background-color .5s;
}
.button-down:hover{
background-color: #000000;
}
`);
setTimeout(() => {
scripts = [
"https://unpkg.com/mersenne-twister@1.1.0/src/mersenne-twister.js",
];
// window.module = {
// exports: {}, // for mersenneTwister
// };
// tamperMonkey
unsafeWindow.module = {
exports: {},
};
scripts.forEach((url) => {
let script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = url;
document.documentElement.appendChild(script);
});
new DownloadButton(
"button-down",
document.querySelector("ol[class^='_tree_'] > li[class^='_item_']")
);
}, 10 * 1000);
如果我回头还记得起这件事情的话再提交PR 🤣
xingoxu commented
// g = isSpread && m === 1 ? t[0].width : 0,
g = 0,
// y = t[isSpread && m === 0 ? 1 : 0].height,
// w = isSpread && optimized.height < y ? Math.round((y - optimized.height) / 2) : 0,
w = 0,
关于g和w是0这个问题,我看了手头的几个作品,在页面中debug出来都是0,所以暂时置0
Heboon123 commented
Heboon123 commented
如果我回头还记得起这件事情的话再提交PR
部分解决了,换了个7zip能改文件名了(