您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Helps download mangas from akuma.moe with ComicInfo.xml
// ==UserScript== // @name akuma.Moe Downloader // @namespace http://tampermonkey.net/ // @version 0.1a // @description Helps download mangas from akuma.moe with ComicInfo.xml // @author Nuark & temp.user // @connect s1.akuma.moe // @connect s2.akuma.moe // @match https://*akuma.moe/* // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js // @grant GM_xmlhttpRequest // @grant GM_download // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; let w = unsafeWindow; let jq = w.$; let blobholder = { init: function(filename, details, maxcount, caller) { if (!filename.endsWith('.zip')) { filename += '.zip'; } this.filename = filename; this.details = details; this.maxcount = maxcount; this.caller = caller; }, blobarray: [], currcount: 0, addBlob: function(blob) { this.blobarray.push(blob); this.currcount++; console.log("blob insertion dispatched", blob, this.currcount, this.maxcount); if (this.currcount === this.maxcount) { this.callback(); this.caller.text("Serializing..."); console.log("calling serialization", blob, this.currcount, this.maxcount); } }, callback: function() { let zip = new JSZip(); let comicInfoContent = createComicInfo(); zip.file("Comicinfo.xml", comicInfoContent); let img = zip.folder("images"); this.blobarray.forEach(file => { img.file(file[0], file[1]); }); zip.generateAsync({type:"blob"}, function updateCallback(metadata) { blobholder.caller.text(metadata.percent.toFixed(2) + '%'); }) .then(function(content) { saveAs(content, blobholder.filename); blobholder.caller.toggleClass("btn-info").toggleClass("btn-success"); blobholder.caller.text("Done"); }); } } w.zxc = async function (caller) { caller = jq(caller); let todo = w.pag.cnt; let tpl = "{origin}{pathname}/{page}".replace(/(\{origin\})/, location.origin).replace(/(\{pathname\})/, location.pathname); blobholder.init(jq(".entry-title").text(), "", todo, caller); caller.text(`Pages: ${todo}`); for (let i = 1; i <= todo; i++) { caller.text(`Fetching ${i} of ${todo}...`); let page_response = await fetch(tpl.replace(/(\{page\})/, i)); let html = await page_response.text(); let div = jq("<div>").html(html.replace(/(script)/g, "scr").replace(/(link)/g, "lnk"))[0]; let link = div.querySelector("#image-container > img").src; GM_xmlhttpRequest({ method: "GET", url: link, synchronous: true, responseType: "blob", onload: function (response) { let title = response.finalUrl .split("/").pop();; let blob = response.response; blobholder.addBlob([title, blob]); } }); } } function init() { //TODO: location.pathname detection let new_btn = jq("#start").clone(); new_btn.id = "download"; new_btn.text("Download"); new_btn.attr("href", "#download"); new_btn.attr("title", "Download"); new_btn.toggleClass("btn-primary").toggleClass("btn-info"); new_btn.attr("onclick", "window.zxc(this)"); jq("#start").parent().append(new_btn); } function createComicInfo() { const title = document.querySelector('h1.entry-title')?.textContent.trim(); const categories = Array.from(document.querySelectorAll('li.meta-data .value a[href*="category"]')).map(el => el.textContent.trim()); const languages = Array.from(document.querySelectorAll('li.meta-data .value a[href*="language"]')).map(el => el.textContent.trim()); const artists = Array.from(document.querySelectorAll('li.meta-data .value a[href*="artist"], li.meta-data .value a[href*="group"]')).map(el => el.textContent.trim()); const maleTags = Array.from(document.querySelectorAll('li.meta-data .value a[href*="male"]')).map(el => `male:${el.textContent.trim()}`); const femaleTags = Array.from(document.querySelectorAll('li.meta-data .value a[href*="female"]')).map(el => `female:${el.textContent.trim()}`); const mixedTags = Array.from(document.querySelectorAll('li.meta-data .value a[href*="mixed"]')).map(el => `mixed:${el.textContent.trim()}`); const otherTags = Array.from(document.querySelectorAll('li.meta-data .value a[href*="other"]')).map(el => `other:${el.textContent.trim()}`); const tags = [...maleTags, ...femaleTags, ...mixedTags, ...otherTags]; const date = document.querySelector('li.meta-data.date .value time')?.getAttribute('datetime').split(' ')[0]; // YYYY-MM-DD const pages = document.querySelector('li.meta-data.pages .value')?.textContent.trim(); const url = window.location.href; const comicInfo = `<?xml version="1.0"?> <ComicInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Title>${title}</Title> <Format>${categories.join(', ')}</Format> <Language>${languages.join(', ')}</Language> <Writer>${artists.join(', ')}</Writer> <Tags>${tags.join(', ')}</Tags> <Date>${date}</Date> <Pages>${pages}</Pages> <Web>${url}</Web> </Comicinfo>`; return comicInfo; } init(); })();