- // ==UserScript==
- // @name 写真的保存
- // @namespace http://tampermonkey.net/
- // @version 0.2
- // @description 不知道说什么...
- // @license MIT
- // @author kOda
- // @match https://everia.club/*
- // @match https://everiaeveria.b-cdn.net/*
- // @match https://www.everiaclub.com/*
- // @require https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js
- // @icon https://www.google.com/s2/favicons?sz=64&domain=everia.club
- // @grant GM_xmlhttpRequest
- // @grant GM_addElement
- // @grant GM_addStyle
- // ==/UserScript==
-
- // 将请求到的子页面转化成DOM对象,在DOM对象上进行操作。
-
- 'use strict';
-
- // 时间单位 毫秒
- // 延时尽量别整太低,限制访问就不好了
- const CONFIG = {
- // 单图片保存后延时
- photoWait: 300,
- // 单写真页保存后延时
- pageWait: 3000,
- // 写真页的文件夹名是否简化
- cleanDirName: true,
- // 简单图片文件名
- simplePhotoName: false,
- };
-
- $(unsafeWindow.document).ready(function() {
- CONFIG.iseveriaclub = unsafeWindow.location.host == "www.everiaclub.com";
-
- var downBtn = document.createElement("button");
- var multiSaver = new MultiPhotoSaver(unsafeWindow.document);
-
- downBtn.style = ` display: block;
- position: fixed;
- left: 10%;
- bottom: 15%;
- text-align: center;
- width: 10%;
- height: 4%; `;
- multiSaver.document.body.appendChild(downBtn);
-
- // 检测当前页面为写真页或主页。
- if (CONFIG.isSenntakuPeeji) {
- downBtn.innerText = "下载本页全部写真";
- downBtn.addEventListener("click", () => {
- multiSaver.downStart().then(() => {
- console.log("本页已下载完毕!(●ˇ∀ˇ●)");
- downBtn.innerText = "已全部下载完成"
- })
- downBtn.disabled = true;
- })
- } else {
- downBtn.innerText = "下载至本地"
- downBtn.addEventListener("click", (e) => {
- unsafeWindow.showDirectoryPicker({mode: "readwrite"}).then((value) => {
- // 油猴处于沙盒时(无// @grant none)使用unsafeWindow访问网页
- new PhotoSaver(unsafeWindow.document, value).pageSave().then(() => {
- console.log(unsafeWindow.document.title + " 下载成功。");
- downBtn.innerText = "已全部下载完成";
- downBtn.disabled = true;
- })
- })
- })
- }
- })
-
- // 用于保存单页写真
- class PhotoSaver {
- constructor(newDoc, relativeDir) {
- this.window = unsafeWindow;
- this.document = newDoc;
- this.topDir = relativeDir;
- this.index = 0;
- this.srcAttr = "";
- this.title = "";
- this.retryList = [];
- this.imgs = [];
-
- this.init();
- }
-
- init() {
- // https://everia.club/*
- // https://everiaeveria.b-cdn.net/*
- // 这两个网站写真页的有多个不同的网页结构,大概是更新了却没有把旧的去除。
- let selectors = ["body > div:nth-child(5) > div.mainleft > img", "#content > article > div.entry-content.clr > figure > figure > img", "#content > article > div.entry-content.clr > div > a > img"]
-
- // 标题,用于文件夹名.
- this.title = this.document.title;
- this.srcAttr = CONFIG.iseveriaclub ? "data-original" : "data-src";
-
- if (CONFIG.cleanDirName) {
- this.title = CONFIG.iseveriaclub ? this.title.split(", ").pop().split("-Everia club")[0] :
- this.title.split(", ").pop().split(" \u2013 EVERIA.CLUB")[0]
- }
-
- for (let i = 0; i < selectors.length; i++) {
- this.imgs = this.document.querySelectorAll(selectors[i]);
- if (this.imgs.length) {
- if (!CONFIG.iseveriaclub) {
- // 2023 10月左右,部分页面除src外没有其它属性存储url。
- this.imgs[0].getAttribute(this.srcAttr) ? 0 :
- this.srcAttr = "src";
- }
- break;
- }
- }
- }
-
- isExist() {
- return new Promise(function(exist, noexist) {
- this.topDir.getDirectoryHandle(this.title).then(() => noexist(this.title)).catch(() => exist(this));
- }.bind(this))
- }
-
- getFileName(response, base) {
- // 这里用png只是随便选的,没什么深意。
- var temp = `${base}.png`;
-
- if (!CONFIG.simplePhotoName) {
- // 这个网站的文件名妹法从url上获取,但在返回头有着原文件名。
- if (response.finalUrl.includes("blogger.googleusercontent.com")) temp = response.responseHeaders.split("filename=\"").pop().split("\"")[0];
- else temp = decodeURIComponent(decodeURIComponent(response.finalUrl.split("/").pop()));
- }
-
- return temp;
- }
-
- pageSave() {
- return new Promise(function(saveSuccess, saveFail) {
- this.topDir.getDirectoryHandle(this.title, {create: true})
- .then(function(saveDir) {
- var timer = 0;
- var currentList = [];
- this.retryList.push(saveDir);
-
- var temp2 = function() {
- if (this.index == this.imgs.length) {
- clearTimeout(timer);
-
- this.retryList.push(currentList);
- this.saveRetry(saveSuccess, saveFail);
-
- return;
- }
-
- var url = this.imgs[this.index].getAttribute(this.srcAttr)
- this.photoSave(url, this.index, saveDir).then(() => {
- timer = setTimeout(temp2, CONFIG.photoWait)
- }).catch(function(e) {
- console.error(e)
-
- currentList.push([url, this.index])
- timer = setTimeout(temp2, 1)
- }.bind(this)).finally(() => this.index++)
- }.bind(this)
-
- temp2();
- }.bind(this), (e) => saveFail(e));
- }.bind(this))
- }
-
- saveRetry(retrySuccess, retryFail) {
- // 目前只处理因网络波动而造成下载失败的图片
- if (this.retryList[1].length) {
- var tryCount = 0;
- var index = 0;
- var timer = 0;
-
- console.log(`开始重新下载:`)
- console.log(this.retryList);
-
- var temp3 = function () {
- if (index == this.retryList[1].length) {
- console.log(`重下完成,大成功!`)
- clearTimeout(timer);
- retrySuccess();
- return
- }
-
- var args = this.retryList[1][index];
- this.photoSave(args[0], args[1], this.retryList[0]).then(() => {
- tryCount = 0;
- console.log(`${args[0]} 重下成功!`)
- timer = setTimeout(temp3, CONFIG.photoWait)
- }).catch((error) => {
- if (tryCount++ > 2) {
- tryCount = 0;
- console.log(`${args[0]} \n 编号为:${args[1]} \n重下失败.... 建议自行下载。`)
- timer = setTimeout(temp3, 1);
- }
-
- index--;
- timer = setTimeout(temp3, CONFIG.photoWait);
- }).finally(() => index++)
- }.bind(this)
-
- temp3();
- } else {
- retrySuccess();
- }
- }
-
- photoSave(url, index, saveDir) {
- return new Promise(function(resolve, reject) {
- GM_xmlhttpRequest({
- method: "GET",
- url: url,
- responseType: "blob",
- timeout: 5000,
- ontimeout: (e) => reject(new Error("Timeout!")),
- onload: function(response) {
- var name = this.getFileName(response, String(index))
-
- saveDir.getFileHandle(name, {create: true})
- .then((value) => {
- return value.createWritable()})
- .then((writer) => {
- console.log(`保存图片:${name}`)
- writer.write({
- type: "write",
- data: response.response
- })
- writer.close().then(function() {
- resolve()
- });
- }).catch((e) => reject(e))
- }.bind(this)
- });
- }.bind(this))
- }
- }
-
- class MultiPhotoSaver {
- constructor(newDoc) {
- this.document = newDoc;
- this.existedDirs = [];
- this.photoSelector = "";
- this.photoLinks = [];
-
- this.init();
- }
-
- // initialization
- init() {
- // 指向写真页的a标签的选择器
- // https://www.everiaclub.com: .leftp a(search、tags)
- // https://everia.club/ .search-entry-inner .thumbnail-link(search) .blog-entry-inner div > a(tags)
- // https://everiaeveria.b-cdn.net
-
- // 就两三个选择器,懒得再去做什么判断了
- if (!CONFIG.iseveriaclub) {
- let t1 = this.document.querySelectorAll(".search-entry-inner .thumbnail-link");
- let t2 = this.document.querySelectorAll(".blog-entry-inner div > a");
-
- t1.length ? this.photoLinks = t1 : this.photoLinks = t2;
- } else {
- this.photoLinks = this.document.querySelectorAll(".leftp > a");
- }
-
- this.photoLinks.length ? CONFIG.isSenntakuPeeji = true : 0;
- }
-
- downStart() {
- return new Promise(function(resolve, reject) {
- unsafeWindow.showDirectoryPicker({mode: "readwrite"}).then(function(picker) {
- var index = 0;
- var retryCount = 0;
- var timer = 0;
-
- var saverHand = function (photoDoc) {
- new PhotoSaver(photoDoc, picker).isExist().then((saver) => {saver.pageSave()
- .then(() => {
- console.log(photoDoc.title + " 下载成功。");
- timer = setTimeout(temp1, CONFIG.pageWait);
- }, (error) => {
- console.log(photoDoc.title + " 下载失败了");
- console.error(error)
- })
- }, function(fileName) {
- console.log(`${fileName} 已存在`)
- this.existedDirs.push(fileName);
- timer = setTimeout(temp1, 1);
- }.bind(this));
- }.bind(this)
-
- // 为避免ip封禁
- // 使用该函数进行请求延时处理。
- var temp1 = function () {
- if (index == this.photoLinks.length) {
- if (this.existedDirs.length) {
- console.log("以下写真已存在,如需下载,请先删除同名文件夹...")
- console.log(this.existedDirs)
- }
-
- resolve();
- clearTimeout(timer);
- return;
- }
-
- GM_xmlhttpRequest({
- method: "GET",
- url: this.photoLinks[index++].href,
- timeout: 5000,
- ontimeout: function(error) {
- if (retryCount++ > 2) {
- console.log("重试失败...")
- console.log("IP可能被限制")
- console.log("请您在一段时间重试。")
- }
- console.log("请求文件超时,自动重试...");
- temp1();
- },
- onload: function(response) {
- retryCount = 0;
- saverHand(new DOMParser().parseFromString(response.response, "text/html"));
- }
- });
- }.bind(this)
-
- temp1();
- }.bind(this));
- }.bind(this))
- }
- }