// ==UserScript==
// @name 怠惰聚圖&下載
// @name:zh-CN 怠惰聚图&下载
// @name:zh-TW 怠惰聚圖&下載
// @version 0.2
// @description 自定義規則,透過CSS/XPath選擇器,定位圈選出要下載的DOM image對象,進行下載壓縮打包,也能聚集所有圖片到當前頁面裡。
// @description:zh-CN 自定义规则,透过CSS/XPath选择器,定位圈选出要下载的DOM image对象,进行下载压缩打包,也能聚集所有图片到当前页面里。
// @description:zh-TW 自定義規則,透過CSS/XPath選擇器,定位圈選出要下載的DOM image對象,進行下載壓縮打包,也能聚集所有圖片到當前頁面裡。
// @author tony0809
// @match *://*/*
// @exclude *hcaptcha*
// @exclude *iframe*
// @exclude *addthis*
// @exclude *youtube*
// @exclude *google*
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAtFBMVEVEREAAAABEREBEREAuuaIzqpkvr58yiHgzhnY6kYM7mYI4kog4s6A2tpo0r544ooo2nY40sJw0sp00rZg1sJw0sZ0zsp01sZw0sZw0rZk1sJw1sZw1rZk0sp00rpk1sp01sZs1rpk1rpk0sp01spw1sJ00rZo1rZk1rpo0r5k0sJw0sZ01r5o0sZw0rpk0sp01sZ01sJw1spw1r5k1sp01sZw0sZ01sZw1rpk1sp01sZw1r5pUzpTcAAAAOXRSTlMAAAUGCw8QERIUFxgbHB0hIieqq6yvtLa3vb/AwMHCxMXFxsfIyc3Oz+zt7vDx8fL4+Pn5+vr7/v7AEFI4AAABR0lEQVR42qWT11bDMAxApbhsmm5KKVD2KiUQwpD1///FkZcSmh4e8FOsex3LsgymPy6ISkRyA4FlWMRXomIyQOg/SbyKAmSOE2Ip02UOYxf/DILNjOOEWLnAEAqio0MMAzJjTAZh1p0SrYCIuu0cMZc9JbENXLa1NWGdI1nWeQuXGPzBTdzC8ws52UI5MwchrA/VTOuTEP9fFyTG7E+R9q8JLsbW1UHzU8HHrCuU1fyToDlB43xRqMUgfc+/KA77fZrWSH/47zPlzOcpJxG8u32r/OEg5SRCqO/WTeT3+5oTuP4LxrXnd5F7waZ+wM5c+NVeujMRrDYMYueE+VK5E5r3uzOb7TbvHH7f/1pP/L8nU9u38Nwyy8OZdjfwY+ZnmPinp28y3mglNeERDJYy/9A3GYVS+GMPMB+uyL67/qO68Mb8MurBD7foVTtvIbtnAAAAAElFTkSuQmCC
// @license MIT
// @namespace https://greasyfork.org/users/20361
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @grant unsafeWindow
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// ==/UserScript==
(async () => {
"use strict";
const options = { //true 開啟,false 關閉
enable: 0, //0為白名單模式根據自訂義規則啟用,1為全局啟用
one: false, //單線程下載
default: "img[src]" //預設CSS/Xpath選擇器/javascript代碼
//default: "js;return [...document.images];"
};
const siteUrl = location.href;
let siteData = {};
let globalImgArray = [];
let customTitle = null;
//自定義站點規則
const customData = [{
name: "小黃書/8色人體攝影 xchina.co/8se.me",//按數字鍵1、Enter插入圖片,按0、Enter、Enter壓縮打包下載
reg: /(xchina|8se)\.(co|me)\/photo\/id/, //網址正則匹配
imgs: "js;let numP=fun.geT('div[target][title]').match(/\\d+/)[0];let max=Math.ceil(numP/18);return fun.getImg('img.cr_only',max,2,['_600x0','']);",
insertImg: "//div[div[@class='photos']]", //清空元素內容把所有圖片插入到此元素
customTitle: "let s=document.title.split('-');let title='';if(/未分/.test(s[1])){title+=s[0].trim()}else{title+=s[1].trim()+' - ';title+=s[0].trim()}return title;",
css: "body{overflow:unset!important}.photos>div.item,.jquery-modal.blocker.current,.slider-ad,.article.ad,.pager>.tips,body>footer~*:not([id^='pv-']):not([class^='pv-']):not(.pagetual_tipsWords):not(.customPicDownloadMsg):not(#customPicDownload),.photoMask,.banner_ad{display: none!important;}",
category: "nsfw2"
}, {
name: "Women Legs Gallery - www.nlegs.com", //專用下載腳本https://greasyfork.org/scripts/463123
enable: 0, //0該站禁用
reg: /www\.nlegs\.com\/girls\//,
imgs: "img[src^=blob]",
category: "nsfw1"
}, {
name: "Hit-x-Hot www.hitxhot.org",
reg: /(www\.)?hitxhot\.(com|org)\/gallerys\/.+\.html/,
imgs: "js;let max=fun.geT('h1').match(/\\d+$/)[0];return fun.getImg('.contentme img',max);",
insertImg: ".contentme",
customTitle: "return document.title.split('|')[0].slice(10).trim()",
category: "nsfw2"
}, {
name: "秀人集 www.xiuren5.com",
reg: /www\.xiuren\d+\.com\/\w+\/\d+\.html/i,
imgs: "js;let max=fun.geT('.page a:last-child',2);return fun.getImg('.content>p img[alt]',max,3);",
insertImg: "//div[p[img[@alt and @title]]]",
customTitle: "return fun.geT('.item_title>h1');",
category: "nsfw1"
}, {
name: "HotAsiaGirl hotgirl.asia 分頁模式",
reg: /hotgirl\.asia\/.+\//,
include: ".galeria_img",
imgs: "js;if(fun.ge('.CustomPictureDownloadImage')){return [...fun.gae('.CustomPictureDownloadImage')]}else{return fun.getImgP('.galeria_img>img','.pagination a[href]')}",
insertImg: ".main-content",
customTitle: "return fun.geT('h3');",
category: "nsfw2"
}, {
name: "HotAsiaGirl hotgirl.asia 幻燈片模式",
reg: /hotgirl\.asia\/.+\//,
include: "#carouselImageIndicators",
imgs: "js;if(fun.ge('.CustomPictureDownloadImage')){return[...fun.gae('.CustomPictureDownloadImage')]}else{return[...fun.gae('#carouselImageIndicators img')]}",
insertImg: ".main-content",
customTitle: "return fun.geT('h3');",
category: "nsfw2"
}, {
name: "MrCong.com",
reg: /mrcong\.com\/.+\//,
imgs: "js;let max=fun.geT('.page-link>*:last-child');return fun.getImg('.entry img[decoding]',max,4)",
insertImg: "//p[img[@decoding]]",
customTitle: "return fun.geT('h1');",
category: "nsfw1"
}, {
name: "萌图社 www.446m.com",
reg: /www\.\d+m\.com\/index\.php\/\w+\/\d+\.html/,
imgs: ".post-item .img",
customTitle: "return document.title.slice(0,-6)",
category: "nsfw1"
}, {
name: "秀色女神 www.xsnvshen.co", //需搭配東方永頁機大圖規則
reg: /www\.xsnvshen\.co\/album\/\d+/,
imgs: ".longConWhite>img",
customTitle: "return fun.geT('h1')",
category: "nsfw1"
}, {
name: "Everia.club",
reg: /everia\.club/,
imgs: ".wp-block-image img",
customTitle: "return fun.geT('h1')",
category: "nsfw2"
}, {
name: "NongMo.Zone www.ilovexs.com",
reg: /www\.ilovexs\.com\/post_id\/\d+\//,
imgs: ".separator img",
customTitle: "return fun.geT('h1')",
category: "nsfw2"
}, {
name: "凸凹吧/撸女吧/女优吧/撸哥吧/欲女吧 www.tuao.one,www.63mm.cc,www.97mm.cc,www.luge8.co,luge8.co",
reg: /(www\.)?(tuao8?|tumm|\d+mm|luge8?)\.[a-z]{2,3}\/(post|web)\//,
imgs: "js;let max=fun.geT('.next-page',2);return fun.getImg('.entry img[title]',max);",
customTitle: "return fun.geT('h1.title');",
category: "nsfw2"
}, {
name: "Asian To Lick asiantolick.com",
reg: /asiantolick\.com\/post/,
imgs: "js;let arr=[];[...fun.gae('div[data-src]')].forEach(e=>{arr.push(e.dataset.src)});return arr;",
insertImg: ".spotlight-group",
customTitle: "return fun.geT('h1');",
category: "nsfw2"
}, {
name: "goddess247.com",
reg: /goddess247\.com\/.+\//,
imgs: ".elementor-widget-container p img[alt]",
customTitle: "return fun.title('-');",
category: "nsfw1"
}, {
name: "www.4kup.net",
reg: /www\.4kup\.net\/.+\.html/,
imgs: "js;let arr=[];[...fun.gae('a.thumb-photo')].forEach(a=>{arr.push(a.href)});return arr;",
insertImg: "#gallery",
customTitle: "return fun.geT('h1');",
next: ".next-page",
category: "nsfw2"
}, {
name: "牛C网导航|就爱你导航 niuc2.com",
reg: /niuc2\.com\/\d+\.html/,
imgs: "js;let max=fun.geT('.page-nav>*:last-child',3);return fun.getImg(\"[class*='wp-image']\",max,5);",
insertImg: "//p[a[img[@decoding]]]",
customTitle: "return document.title.split('|')[0].trim()",
css: ".post-apd{display: none!important;}",
category: "nsfw2"
}, {
name: "H漫畫貼圖 - 7mmtv.sx",
reg: /7mmtv\.sx\/.*hcomic/,
imgs: "js;return Large_cgurl;",
customTitle: "return fun.title('-');",
category: "hcomic"
}, {
name: "嗨皮漫畫 m.happymh.com",
reg: /m\.happymh\.com\/reads/,
imgs: "js;let lps=location.pathname.split('/'),mangaCode=lps[2],id=lps[3],apiUrl=`https://m.happymh.com/v2.0/apis/manga/read?code=${mangaCode}&cid=${id}`;return fetch(apiUrl).then(res=>res.text()).then(res=>{let jsonData=JSON.parse(res);let srcs=jsonData.data.scans;let arr=[];for(let i in srcs){arr.push(srcs[i].url)}return arr});",
customTitle: "let lps=location.pathname.split('/'),mangaCode=lps[2],id=lps[3],apiUrl=`https://m.happymh.com/v2.0/apis/manga/read?code=${mangaCode}&cid=${id}`;return fetch(apiUrl).then(res=>res.text()).then(res=>{let jsonData=JSON.parse(res);return jsonData.data.manga_name+' - '+jsonData.data.chapter_name});",
insertImg: "//article[div[contains(@id,'imageLoader')]]",
next: "//a[span[text()='下一話' or text()='下一话']]",
category: "comic"
}, {
name: "COLAMANHUA www.colamanhua.com", //下載不了...Picviewer CE+可以
enable: 0,
reg: /www\.colamanhua\.com\/manga-.+\.html$/,
imgs: ".mh_comicpic img[src^=blob]",
one: true,
category: "comic"
}, {
name: "8Comic無限動漫 www.comicabc.com",
reg: /(a|www)\.(comicabc|twobili)\.com\/(ReadComic|online)/,
imgs: "js;let code=[...document.scripts].find(s=>s.innerHTML.search(/ge\\(e\\)/)>-1).innerHTML;let cM=code.match(/ge\\([^.]+\\.src\\s?=\\s?([^;]+)/);let keyCode=cM[1];let arr=[];for(let i=1;i<=ps;i++){let r='('+i+')';let src='https:'+fun.run(keyCode.replace(/\\(pp?\\)/g,r));arr.push(src)}return arr;",
customTitle: "let t=document.title.split(' ')[0];return`${t}-第${ch}集`;",
next: "//button[text()='下一集']",
category: "comic"
}, {
name: "8Comic無限動漫手機版 m.comicbus.com",
reg: /8\.twobili\.com\/comic\/insurance/,
imgs: "js;let arr=[];for(let i=1;i<=ps;i++){let imgSrc='https://img'+ss(c,4,2)+'.8comic.com/'+ss(c,6,1)+'/'+ti+'/'+ss(c,0,4)+'/'+nn([i])+'_'+ss(c,mm([i])+10,3,f)+'.jpg';arr.push(imgSrc)}return arr;",
customTitle: "let t=document.title.split(' ')[0];let n=fun.geT('#chapter');return t+' - '+n;",
next: "#nextvol",
category: "comic"
}, {
name: "DM5/極速 分頁模式 www.dm5.com",
reg: /(www|tel|en|cnc|hk|m)?\.?(dm5|1kkk)\.(com|cn)\/(m|ch|vol|other)[-_0-9]+\//,
include: "#chapterpager",
imgs: "js;let get=async()=>{if(!mkey){var mkey=''}let arr=[];for(let i=1;i<=DM5_IMAGE_COUNT;i++){fun.show(`獲取資料中(${i}/${DM5_IMAGE_COUNT})`);let apiUrl=location.origin+DM5_CURL+'chapterfun.ashx'+`?cid=${DM5_CID}&page=${i}&key=${mkey}&language=1>k=6&_cid=${DM5_CID}&_mid=${DM5_MID}&_dt=${DM5_VIEWSIGN_DT}&_sign=${DM5_VIEWSIGN}`,res=await fetch(apiUrl),resText=await res.text(),src=await fun.run(resText)[0];arr.push(src)}return arr};return get();",
insertImg: "#cp_img",
customTitle: "return fun.title('_', 2);",
next: "//a[text()='下一章']",
category: "comic"
}, {
name: "DM5/極速 條漫模式 https://www.dm5.com/manhua-moutianchengweimoshen/",
reg: /(www|tel|en|cnc|hk|m)?\.?(dm5|1kkk)\.(com|cn)\/(m|ch|vol|other)[-_0-9]+\//,
include: "#barChapter",
imgs: "#barChapter>img",
customTitle: "return fun.title('_', 2);",
next: "//a[text()='下一章']",
category: "comic"
}, {
name: "DM5/極速 手機版 m.dm5.com",
reg: /(www|tel|en|cnc|hk|m)?\.?(dm5|1kkk)\.(com|cn)\/(m|ch|vol|other)[-_0-9]+\//,
include: "//script[contains(text(),'newImgs')]",
imgs: "js;return newImgs",
insertImg: "#cp_img", //清空元素內容把所有圖片插入到此元素
customTitle: "return fun.title('_', 2);",
next: "//a[text()='下一章']",
category: "comic"
}];
const fun = {
ge: (e, d) => {
if (/^\//.test(e)) {
return (d || document).evaluate(e, (d || document), null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
} else {
return (d || document).querySelector(e);
}
},
gae: (e, d) => {
if (/^\//.test(e)) {
let nodes = [];
let results = (d || document).evaluate(e, (d || document), null, XPathResult.ANY_TYPE, null);
let node;
while (node = results.iterateNext()) {
nodes.push(node);
}
return nodes;
} else {
return (d || document).querySelectorAll(e);
}
},
geT: (ele, mode = 1) => {
if (mode == 1) {
return fun.ge(ele).innerText;
} else if (mode == 2) {
return fun.ge(ele).previousElementSibling.innerText;
} else if (mode == 3) {
return fun.ge(ele).previousElementSibling.previousElementSibling.innerText;
}
},
run: code => new Function("return " + code)(),
html: str => new DOMParser().parseFromString(str, 'text/html'),
title: (str, mode = 1) => {
let s = document.title.split(str);
if (mode == 1) {
return s[0].replace(/,$/, '').trim();
} else if (mode == 2) {
return s[0] + str + s[1].replace(/,$/, '').trim();
}
return '參數錯誤';
},
show: text => {
let msg = fun.ge(".customPicDownloadMsg");
if (fun.ge(".customPicDownloadMsg[style]")) {
msg.removeAttribute('style');
}
msg.innerText = text;
},
checkDataset: img => {
if (img.tagName == "IMG" || img.tagName == "DIV") {
const setArr = ["data-src", "data-original", "data-url", "data-thumb", "data-lazy-src", "data-lazyload", "data-lazyload-src"];
//for (let i in setArr) {
for (let i = 0; i < setArr.length; i++) {
let imgSrc = img.getAttribute(setArr[i]);
if (imgSrc) {
return {
ok: true,
src: imgSrc
};
}
}
}
return {
ok: false
};
},
getImg: async (img, maxPage = 1, mode = 1, rText = [null, null]) => {
fun.show("獲取元素中");
let imgsArray = [];
let html = url => fetch(url).then(res => res.text());
let resArr = [];
resArr.push(html(siteUrl));
if (maxPage > 1) {
for (let i = 2; i <= maxPage; i++) {
if (mode == 1) {
//.html => .html?page=2 第二頁
resArr.push(html(siteUrl + "?page=" + i));
} else if (mode == 2) {
//.html ==> /2.html 第二頁
resArr.push(html(siteUrl.slice(0, -5) + '/' + i + '.html'));
} else if (mode == 3) {
//.html ==> _1.html 第二頁
resArr.push(html(siteUrl.slice(0, -5) + '_' + (i -1) + '.html'));
} else if (mode == 4) {
//【/ ==> /2/】 第二頁
resArr.push(html(siteUrl.slice(0, -1) + '/' + i + '/'));
} else if (mode == 5) {
//.html ==> -2.html 第二頁
resArr.push(html(siteUrl.slice(0, -5) + '-' + i + '.html'));
}
}
}
await Promise.all(resArr).then((htmls) => {
for (let i = 0; i < htmls.length; i++) {
let doc = fun.html(htmls[i]);
let imgs = [...fun.gae(img, doc)];
for (let i = 0; i < imgs.length; i++) {
if (rText[0]) {
imgs[i].src = imgs[i].src.replace(rText[0], rText[1]);
}
imgsArray.push(imgs[i]);
}
}
});
imgsArray = imgsArray.filter(i => i);
//imgsArray = imgsArray.filter(i => i.tagName == "IMG");
return imgsArray;
},
getImgP: async (img, paginationA, rText = [null, null] ) => {//從頁碼條抓鏈接
fun.show("獲取元素中");
let imgsArray = [];
let html = url => fetch(url).then(res => res.text());
let resArr = [];
resArr.push(html(siteUrl));
let links = fun.gae(paginationA);
for (let i = 0; i < links.length; i++) {
resArr.push(html(links[i].href));
}
await Promise.all(resArr).then((htmls) => {
for (let i = 0; i < htmls.length; i++) {
let doc = fun.html(htmls[i]);
debug("getImgP DOM", doc);
let imgs = [...fun.gae(img, doc)];
for (let i = 0; i < imgs.length; i++) {
if (rText[0]) {
imgs[i].src = imgs[i].src.replace(rText[0], rText[1]);
}
imgsArray.push(imgs[i]);
}
}
});
imgsArray = imgsArray.filter(i => i);
return imgsArray;
},
insertImg: (imgsArray, ele) => {
let srcArr = [];
//for (let i in imgsArray) {
for (let i = 0; i < imgsArray.length; i++) {
let imgSrc;
let check = fun.checkDataset(imgsArray[i]);
if (imgsArray[i].tagName == 'IMG' && check.ok) {
srcArr.push(check.src);
} else if (imgsArray[i].tagName == 'IMG') {
srcArr.push(imgsArray[i].src);
} else if (/^http/.test(imgsArray[i])) {
srcArr.push(imgsArray[i]);
} else {
debug("insertImg格式錯誤!", imgsArray[i]);
return;
}
}
let fragment = new DocumentFragment();
//for (let i in srcArr) {
for (let i = 0; i < srcArr.length; i++) {
let img = new Image();
img.className = "CustomPictureDownloadImage";
img.src = srcArr[i];
fragment.appendChild(img);
}
let E = fun.ge(ele);
if (E) {
E.innerHTML = "";
E.appendChild(fragment);
} else {
debug("用來定位插入的元素不存在");
}
},
css: css => {
let style = document.createElement("style");
style.type = "text/css";
style.id = "CustomPictureDownloadStyle";
style.innerHTML = css;
document.head.appendChild(style);
}
};
const debug = (str, obj = "", title = "debug") => {
console.log(
`%c[Custom Picture Download] ${title}:`,
'background-color: #C9FFC9;',
str, obj
);
};
let nsfw1Data = customData.filter(item => item.category == "nsfw1"); //列出寫真站
let nsfw2Data = customData.filter(item => item.category == "nsfw2"); //列出老司機站
let comicData = customData.filter(item => item.category == "comic"); //列出普漫站
let hcomicData = customData.filter(item => item.category == "hcomic"); //列出H漫站
let noneData = customData.filter(item => item.category == "none"); //列出未分類
//for (let i in customData) {
for (let i = 0; i < customData.length; i++) {
if (customData[i].reg.test(siteUrl)) {
options.enable = 1;
if (customData[i].enable == 0) {
options.enable = 0;
debug("禁用");
return;
}
if (customData[i].imgs) {
options.default = customData[i].imgs;
debug(`CSS/Xpath/JS選擇器:${options.default}`);
}
if (customData[i].one) {
options.one = customData[i].one;
debug("單線程下載");
}
let titleCode = customData[i].customTitle;
if (titleCode) {
customTitle = await new Function("fun", '"use strict";' + titleCode)(fun);
debug(`自定義標題:${customTitle}`);
}
let include = customData[i].include;
if (include) {
if (!fun.ge(include)) {
options.enable = 0;
console.clear();
debug("頁面沒有包含必須元素,跳出本次循環繼續遍歷");
continue;
}
}
let exclude = customData[i].exclude;
if (exclude) {
if (fun.ge(exclude)) {
options.enable = 0;
console.clear();
debug("頁面包含排除元素,跳出本次循環繼續遍歷");
continue;
}
}
let next = customData[i].next;
if (next) {
document.addEventListener('keydown', (event) => {
let key = window.event ? event.keyCode : event.which;
if (key == 39) {
let n = fun.ge(next);
if (n) n.click();
}
});
}
let css = customData[i].css;
if (css) {
fun.css(css);
}
siteData = customData[i];
break;
}
}
if (siteData.reg) {
debug("列出寫真站", nsfw1Data);
debug("列出老司機站", nsfw2Data);
debug("列出普漫站", comicData);
debug("列出H漫站", hcomicData);
debug("列出未分類", noneData);
debug("全局此站資料", siteData);
}
let promiseBlobArray = [];
let downloadNum = 0;
var _GM_xmlhttpRequest;
if (typeof GM_xmlhttpRequest != "undefined") {
_GM_xmlhttpRequest = GM_xmlhttpRequest;
} else if (typeof GM != "undefined" && typeof GM.xmlHttpRequest != "undefined") {
_GM_xmlhttpRequest = GM.xmlHttpRequest;
}
const ge = css => document.querySelector(css);
const gae = css => document.querySelectorAll(css);
const gx = xpath => document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
const gax = xpath => {
let nodes = [];
let results = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
let node;
while (node = results.iterateNext()) {
nodes.push(node);
}
return nodes;
};
const getNum = (i) => {
let n = parseInt(i) + 1;
let picNum = n;
if (i < 9) {
picNum = "000" + n;
} else if (i < 99) {
picNum = "00" + n;
} else if (i < 999) {
picNum = "0" + n;
}
return picNum;
};
const showMsg = (text, time = 1000) => {
ge(".customPicDownloadMsg").removeAttribute("style");
ge(".customPicDownloadMsg").innerText = text;
setTimeout(() => {
ge(".customPicDownloadMsg").innerText = "none";
ge(".customPicDownloadMsg").style = "display:none";
}, time);
};
const checkDataset = (img) => {
if (img.tagName == 'IMG') {
const setArr = ["data-src", "data-original", "data-url", "data-thumb", "data-lazy-src", "data-lazyload", "data-lazyload-src"];
//for (let i in setArr) {
for (let i = 0; i < setArr.length; i++) {
let imgSrc = img.getAttribute(setArr[i]);
if (imgSrc) {
return {
ok: true,
src: imgSrc
};
}
}
}
return {
ok: false
};
};
const getData = (srcUrl, picNum, imgsNum) => {
return new Promise((resolve) => {
const getMsg = (text) => {
downloadNum += 1;
ge(".customPicDownloadMsg").innerText = `第${downloadNum}/${imgsNum}張下載${text}`;
};
_GM_xmlhttpRequest({
method: "GET",
url: srcUrl,
responseType: "blob",
headers: {
referer: location.origin + "/"
},
onload: (data) => {
let blob = data.response;
if (/^image/.test(blob.type)) {
resolve({
blob: blob,
picNum: picNum
});
getMsg("完成");
} else {
resolve({
error: "下載錯誤",
picNum: picNum,
src: srcUrl,
blob: blob
});
getMsg("錯誤");
}
},
onerror: (error) => {
resolve({
error: "下載錯誤",
picNum: picNum,
src: srcUrl
});
getMsg("錯誤");
}
});
});
};
const imgZipDownload = async () => {
if (/\d+/.test(ge(".customPicDownloadMsg").innerText)) {
alert("下載&壓縮中請勿重複操作!");
return;
}
let selector = await prompt("請輸入自訂CSS/Xpath選擇器:\n範例:img#TheImg OR //img[@id='TheImg']\n也能使用JS代碼自己生成的IMG元素陣列\n範例:js;return [...document.images];", options.default);
let imgs;
if (!selector || selector === "") {
showMsg("已取消");
return;
} else if (selector.length < 3) {
showMsg("字數小於3已取消");
return;
} else if (/^js;/.test(selector)) {
imgs = await new Function("fun", '"use strict";' + selector.slice(3))(fun);
debug("JSimgs:", imgs);
} else if (/^\//.test(selector)) {
imgs = [...gax(selector)];
} else {
imgs = [...gae(selector)];
}
imgs = imgs.filter(item => item); //去除空、無用、重複
globalImgArray = imgs;
let titleText = await prompt("請輸入自訂壓縮檔資料夾名稱", (customTitle || document.title));
if (imgs[0] && titleText != null && titleText != "") {
debug("imgZipDownload():", imgs);
const imgsNum = imgs.length;
let title = titleText;
const zip = new JSZip();
const zipFolder = zip.folder(`${title} [${imgsNum}P]`);
ge(".customPicDownloadMsg").removeAttribute("style");
ge(".customPicDownloadMsg").innerText = "0101010101...";
//for (let i in imgs) {
for (let i = 0; i < imgs.length; i++) {
let n = parseInt(i) + 1;
let picNum = getNum(i);
let promiseBlob;
let imgSrc;
let check = fun.checkDataset(imgs[i]);
if (imgs[i].tagName == 'IMG' && check.ok) {
imgSrc = check.src;
} else if (imgs[i].tagName == 'IMG') {
imgSrc = imgs[i].src;
} else if (/^http/.test(imgs[i])) {
imgSrc = imgs[i];
} else {
debug("格式錯誤!", imgs[i]);
continue;
}
if (options.one) {
promiseBlob = await getData(imgSrc, picNum, imgsNum);
} else {
promiseBlob = getData(imgSrc, picNum, imgsNum);
}
promiseBlobArray.push(promiseBlob);
}
debug("PromiseBlobArray:", promiseBlobArray);
Promise.all(promiseBlobArray).then((data) => {
debug("PromiseAllData:", data);
let blobDataArray = data.filter(item => item.blob); //成功下載
let errorDataArray = data.filter(item => item.error); //下載錯誤
debug("NewDataArray:", blobDataArray);
debug("ErrorDataArray:", errorDataArray);
if (blobDataArray[0]) {
//for (let i in blobDataArray) {
for (let i = 0; i < blobDataArray.length; i++) {
let n = parseInt(i) + 1;
let ex = blobDataArray[i].blob.type.split("/")[1];
let fileName = `${blobDataArray[i].picNum}P.${ex}`;
//console.log(`第${n}/${blobDataArray.length}張,檔案名:${fileName},大小:${parseInt(blobDataArray[i].blob.size / 1024)} Kb`);
zipFolder.file(fileName, blobDataArray[i].blob, {
binary: true
});
}
zip.generateAsync({
type: "blob"
}, (metadata) => {
ge(".customPicDownloadMsg").innerText = "壓縮進度: " + metadata.percent.toFixed(2) + " %";
}).then(data => {
promiseBlobArray = [];
downloadNum = 0;
debug("ZIP壓縮檔數據:", data);
ge(".customPicDownloadMsg").innerText = "none";
ge(".customPicDownloadMsg").style = "display:none";
let a = document.createElement("a");
a.href = URL.createObjectURL(data);
a.download = `${title} [${imgsNum}P].zip`;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(data);
});
} else {
promiseBlobArray = [];
downloadNum = 0;
showMsg("下載失敗數據為空...");
}
});
} else {
showMsg("無圖或壓縮檔名稱為null", 3000);
}
};
const copyImgSrcText = async () => {
let selector = await prompt("請輸入自訂CSS/Xpath選擇器:\n範例:img#TheImg OR //img[@id='TheImg']\n也能使用JS代碼自己生成的IMG元素陣列\n範例:js;return [...document.images];", options.default);
let imgs;
let imgSrcArray = [];
if (!selector || selector === "") {
showMsg("已取消");
return;
} else if (selector.length < 3) {
showMsg("字數小於3已取消");
return;
} else if (/^js;/.test(selector)) {
imgs = await new Function("fun", '"use strict";' + selector.slice(3))(fun);
debug("JSimgs:", imgs);
} else if (/^\//.test(selector)) {
imgs = [...gax(selector)];
} else {
imgs = [...gae(selector)];
}
imgs = imgs.filter(item => item); //去除空、無用、重複
if (siteData.insertImg) {
debug("insertImg():", imgs);
} else {
debug("CopyImgSrcText():", imgs);
}
if (imgs[0]) {
//for (let i in imgs) {
for (let i = 0; i < imgs.length; i++) {
let imgSrc;
let check = fun.checkDataset(imgs[i]);
if (imgs[i].tagName == 'IMG' && check.ok) {
imgSrc = check.src;
} else if (imgs[i].tagName == 'IMG') {
imgSrc = imgs[i].src;
} else if (/^http/.test(imgs[i])) {
imgSrcArray = [...imgs].filter(item => item);
break;
} else {
debug("格式錯誤!", imgs[i]);
continue;
}
imgSrcArray.push(imgSrc);
}
} else {
showMsg("沒有任何圖片元素...");
return;
}
globalImgArray = imgSrcArray;
if (siteData.insertImg) {
fun.insertImg(globalImgArray, siteData.insertImg);
showMsg("已插入全部圖片");
return;
/*
let yes = confirm("已經插入所有圖片了,還要將網址複製到剪貼簿嗎?");
if (!yes) {
showMsg("已取消");
return;
}*/
}
imgSrcArray.push(customTitle || document.title);
debug("imgSrcArray:", imgSrcArray);
let str = imgSrcArray.join("\n");
console.log(str);
copyToClipboard(str);
showMsg(`圖片網址已複製(${imgs.length})`);
imgSrcArray = null;
};
const copyToClipboard = (textToCopy) => {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(textToCopy);
} else {
let textArea = document.createElement("textarea");
textArea.value = textToCopy;
textArea.style.position = "absolute";
textArea.style.opacity = 0;
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
return new Promise((res, rej) => {
document.execCommand('copy') ? res() : rej();
textArea.remove();
});
}
};
const oneSwitch = () => {
if (options.one) {
options.one = false;
showMsg("啟用多線程");
} else {
options.one = true;
showMsg("啟用單線程");
}
};
const addCustomPicDownloadButton = () => {
let img = new Image();
img.id = "customPicDownload";
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACFlBMVEVEREAAAABEREBEREBUXnFTXXBTXG5VXXJUXHFUW28grV0grV0hrl4hrV0hq10iql0oomAhrV2BiZiAiJd/h5V+h5Uiq16OlaKNlKGMk5+Mkp+KkZ89p28nq2Hp9+/n9u3t8PHs7/Dg9Ojp7O3d8+bo7O3o6+zn6+3m6uzl6evX8OLV8OHi5ujh5efO7dze4uTe4ePb3+LY3eDX29/W2t7T19u35Mu25Mr01lqw4sbz1Vmv4sXA1NWs4cPw0lnv0lmp4MCk3r3ozVuh3buu0M6vz83kylugysefycadx8OexsOdxsOF0qZ6zp5pyJK1p2S0pmOsoGRWv4SLlKEmuZpBsnc5t3AmuJmQi2ont5kmtpcptJiOiWg0tWwytGorr5Yqr5YsrpUws2kqrJRNmpMusmcsqpIuqJMssWkqsWQqsGUnsGUzoI8lsGGAf2wkr2Ekr2BzfI0irl8hrl5ye4x9fGogrWAhrV03mIwgrF9xeoshq10gql8dp2ccp2cdpmkcnIUanIYcm4QRoIUTnoURn4QdmYM/h4QcmIMSnYMXmoMWmoIbloIclYJDgYIYl4EaloAek4IfkYFqbm0jjoBqbW0ni4ApiX9Hd35kanBjaXBhaG9MbnpOanhOaXhPaHdDbXZHanZGanRLZnZJZ3RSYnVMZXRPY3ROY3VSYXRQYnRTYHRVX3NUX3RSYHNUXnNTXXJTXXFME6frAAAAHnRSTlMAAAUGZ2dqeHh7lrzNzc7O3O/09PT19fn5+fn5/f4hZOrvAAAB1UlEQVR42oWTzYoTQRSFv6q6RqOoqBicTTCKK1cOURBGBV9AXCgu9Vl8FF/ApcshI4qKgrtBByZK0HSGmMxkMrF/qq6LTjptErGX5zu3uu65dY3TYCh9ag2UNPsfjnmpZW4MgBbaIy/6PCnxvL7gEQhJ7AruAqifcZ96EHDX67ngKgCJn/k3B6AC1O9Cj9ri/2mBsZIf9iLi0rMFDhhjbO6NsL9er+CQG3pYMT+XuGVmuH9SzPF7S/UONDfUHl44/6A24wfdpMgsSO6tP52ffxjF6cUT056MBdxf/cVRzP6PiQG8KhZMJedhewik0QT43RsBQUEwLufZx53Td85p7wCwEu+Fs6qAGBsA1aO3XUZbG/EAsAJJlM/MGgOoDltdYNTam3JIOlVmOaj2N/sAjHf9lGdBqwACmmi0FU/TOWxfEY483o/9tcuvJBXU+90PaZHf5NtVFzTL+N7bqD4GS9DRmzl3btJWk2WFIKradqX8LfudU3VKBuKd0vwc+HCjNDJhkJXfB6jq+wZJBSAT5MmKN1/la7sJ+jmsiyQrdkZo9N819FNnXcSZFTt1rGnXhl/G6a26IP/YOXczi5prQmFY3snbkzNSXGyBV+28p+nNF+vn2h97PvHD5SOHkgAAAABJRU5ErkJggg==";
img.setAttribute("title", "左鍵:進行下載打包壓縮\n中鍵:切換單多線程\n右鍵:複製圖片網址和標題或插入所有圖片");
img.oncontextmenu = () => false;
img.addEventListener("click", () => {
imgZipDownload();
});
img.addEventListener("mousedown", (event) => {
if (event.button == 1) {
event.preventDefault();
oneSwitch();
}
if (event.button == 2) {
copyImgSrcText();
}
});
document.body.appendChild(img);
};
const addCustomPicDownloadMsg = () => {
let div = document.createElement("div");
div.className = "customPicDownloadMsg";
div.style = "display:none";
div.innerText = "none";
document.body.appendChild(div);
};
const addGlobalStyle = css => {
let style = document.createElement("style");
style.type = "text/css";
style.innerHTML = css;
document.head.appendChild(style);
};
const css = `
#customPicDownload {
width: 32px!important;
height: 32px!important;
position: fixed!important;
bottom: 20px!important;
left: 20px!important;
z-index:2147483647;
opacity: 0.8!important;
}
.customPicDownloadMsg {
font-family: Arial,sans-serif!important;
font-size: 26px;
font-weight: bold;
text-align: center;
line-height: 50px;
color: #ffffff;
width: 280px;
height: 50px;
top: 30%;
left: 50%;
margin-left: -140px;
background-color: #000;
border: 1px solid #303030;
border-radius: 10px;
position: fixed;
z-index:2147483647;
opacity: 0.7;
}
.CustomPictureDownloadImage {
width: auto !important;
height: auto !important;
max-width: 100% !important;
display: block !important;
margin: 0 auto !important
}
`;
if (options.enable == 1) {
addCustomPicDownloadButton();
addCustomPicDownloadMsg();
addGlobalStyle(css);
document.addEventListener('keydown', (e) => {
switch (e.keyCode) {
case 96: //數字鍵0
imgZipDownload();
break;
case 97: //數字鍵1
copyImgSrcText();
break;
}
});
}
})();