Greasy Fork is available in English.

Save Pixiv Pictures to Eagle

Collect pictures in pixiv to eagle.

  1. // ==UserScript==
  2. // @name Save Pixiv Pictures to Eagle
  3. // @name:zh 下载Pixiv图片到Eagle
  4. // @name:zh-CN 下载Pixiv图片到Eagle
  5. // @description Collect pictures in pixiv to eagle.
  6. // @description:zh-CN *不维护了,能用多久是多久吧。*可通过油猴插件提供的按键修改部分功能设置。在Pixiv上添加可以导入图片到Eagle的下载按钮,默认保存所有标签以及标签翻译,以创作者名创建文件夹保存,能力有限暂无法处理动图。首页、排行榜、关注用户新作品页、收藏页添加下载按钮,添加复选框。自动将用户id添加进文件夹注释,同名文件夹注释中不存在id则更新注释添加id,尽量避免添加进同名不同id文件夹中。可批量下载全部作品和收藏。
  7. // @description:zh *不维护了,能用多久是多久吧。*可通过油猴插件提供的按键修改部分功能设置。在Pixiv上添加可以导入图片到Eagle的下载按钮,默认保存所有标签以及标签翻译,以创作者名创建文件夹保存,能力有限暂无法处理动图。首页、排行榜、关注用户新作品页、收藏页添加下载按钮,添加复选框。自动将用户id添加进文件夹注释,同名文件夹注释中不存在id则更新注释添加id,尽量避免添加进同名不同id文件夹中。可批量下载全部作品和收藏。
  8.  
  9. // @namespace https://github.com/miracleXL/scripts-for-Eagle
  10. // @icon https://www.pixiv.net/favicon.ico
  11. // @version 0.6.9
  12. // @author miracleXL
  13. // @match https://www.pixiv.net/*
  14. // @connect localhost
  15. // @connect www.pixiv.net
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM_addElement
  21. // @require https://code.jquery.com/jquery-3.5.1.min.js
  22. // ==/UserScript==
  23.  
  24. // 太麻烦了不想修了,再大改就不管了,心累
  25.  
  26. // 更新设置项
  27. // 不再使用!!请在打开pixiv的网页后,点击油猴插件,再点击本脚本下面的“更新设置”,在网页中添加的设置页面中修改并保存。后续更新将不会再清空设置
  28. const PATT = / *[@@◆■◇☆⭐️🌟🦇💎🔞🍅🌱🐻🍬::\\\/].*/; // 处理作者名多余后缀的正则
  29. const SAVE_TAGS = true; // 是否保存标签
  30. const TAG_AUTHOR = true; // 是否将作者名加入标签
  31. const TAG_TRANSLATION = 2; // 标签翻译处理方式,0:仅加入原标签;1:仅加入翻译标签;2:均加入标签
  32. const ADD_TO_FAVOR = true; // 下载时是否同时加入收藏
  33. const DL_Multiple = true; // 通过缩略图下载时,下载多P
  34. const CREATE_SUBFOLDER = false; // 多图时创建子文件夹
  35. const SEARCH_DIR_NAME = ""; // 在需要创建新文件夹时,新建文件夹的父文件夹名,在引号内输入文件夹名。留空则直接创建
  36. const SEARCH_DIR_ID = ""; // 一般无需填写,上一行所指定文件夹的id(eagle中选中文件夹右键复制链接,获得如‘eagle://folder/K4130PELEY5W9’字符串,文件夹id就是其中K4130PELEY5W9部分)。填写会忽略上一行设置,可用来设置新建文件夹创建到某个子文件夹中。
  37. const DIR_NAME_FORMATER = "${authorName}" // 文件夹名称格式,默认为作者名,可用变量包括 ${authorName} 和 ${pid} 两个。
  38. const USE_CHECK_BOX = true; // 为true时在每一张图上添加复选框代替下载键,此时下载键将移至图片所在区域上方标题处
  39. const WAIT_TIME = 1000;
  40. // 设置项结束
  41.  
  42. // 读取已存储设置
  43. var patt = new RegExp(GM_getValue("patt", PATT.source));
  44. var saveTags = GM_getValue("saveTags", SAVE_TAGS);
  45. var tagAuthor = GM_getValue("tagAuthor", TAG_AUTHOR);
  46. var tagTranslation = GM_getValue("tagTranslation", TAG_TRANSLATION);
  47. var addToFavor = GM_getValue("addToFavor", ADD_TO_FAVOR);
  48. var DLMultiple = GM_getValue("DLMultiple", DL_Multiple);
  49. var createSubfolder = GM_getValue("createSubfolder", CREATE_SUBFOLDER);
  50. var searchDirName = GM_getValue("searchDirName", SEARCH_DIR_NAME);
  51. var searchDirId = GM_getValue("searchDirId", SEARCH_DIR_ID);
  52. var dirNameFormater = GM_getValue("dirNameFormater", DIR_NAME_FORMATER);
  53. var useCheckbox = GM_getValue("useCheckbox", USE_CHECK_BOX);
  54. var waitTime = GM_getValue("waitTime", WAIT_TIME);
  55. // 读取结束
  56. // 尝试避免正则保存错误带来的后果
  57. if(patt.source === "[object Object]"){
  58. patt = new RegExp(PATT);
  59. GM_setValue("patt", patt.source);
  60. }
  61.  
  62. // Eagle支持不同功能的版本号
  63. const edit_folder_info = 20210401; // 支持修改文件夹信息的版本build号
  64. const create_child_folder = 20210806; // 支持创建子文件夹的版本build号
  65.  
  66. // 各种页面元素JQuery选择器
  67. const PAGE_SELECTOR = "div[type=illust] .sc-rp5asc-0"; // Pixiv首页及用户页图片选择器
  68. const DIV_SECTION = ".sc-1xj6el2-3"; // 个人主页的插画删去了section元素,用来代替的元素
  69. const BUTTON_SELECTOR = ".sc-7zddlj-1"; // 使用添加选择框的方式时的下载按钮位置
  70. const NEW_ILLUST_BUTTON = ".sc-93qi7v-2"; // 新作品页按键位置
  71. const RANK_PAGE_BUTTON = "nav.column-menu"; // 排行榜按键位置
  72. const DL_ILLUST_BUTTON = ".sc-iasfms-2"; // 不使用复选框时,下载单张图片的按键位置
  73. const SHOW_ALL = "a.sc-d98f2c-0.sc-s46o24-1" // 用户页面显示全部图片的按键位置
  74. const FIRST_PAGE = "a.sc-xhhh7v-1-filterProps-Styled-Component:eq(1)" // 翻页第一页按键位置
  75. const NEXT_PAGE = "a.sc-xhhh7v-1-filterProps-Styled-Component:eq(-1)" // 用户artwork页面翻页按键位置
  76.  
  77. // 收藏页
  78. const BOOKMARKS_BUTTON = "div.sc-1u8zqt7-0"; // 管理收藏按键
  79. const BDL_BUTTON_POS = "div.sc-13ywrd6-4"; // 管理收藏中下载按键位置
  80. const OVER_BUTTON = ".sc-1ij5ui8-0"; // 管理收藏结束按键
  81. const BOOKMARK_SELECT = "div[type=illust]"; // 管理收藏页图片选择器
  82. const SELECT_URL = "span:first";
  83. const SELECT_CHECK = "input.sc-8ggyxi-4";
  84.  
  85. // 作品详细页面
  86. const BUTTON_POS = ".sc-181ts2x-0"; // 下载按键位置
  87. const PIC_SRC = ".sc-1qpw8k9-3"; // 图片位置
  88. const SHOW_ALL_BUTTON = ".sc-emr523-0"; // 多图时显示全部的按键
  89. const MANGA_SRC = ".gtm-expand-full-size-illust" // ".sc-1oz5uvo-4"; // 漫画图片位置
  90. const READ_MANGA = ".sc-emr523-2" // 多图或漫画时下方按键的显示内容
  91. const MANGA_POS = ".sc-1qrul0z-8" // 漫画下方下载按键位置
  92. const PIC_END = ".gtm-illust-work-scroll-finish-reading" // 展开多图时结束元素
  93. const UGO_SRC = ".sc-tu09d3-1"; // 动图
  94. const TAG_SELECTOR = ".sc-pj1a4x-1"; // 标签和标签翻译
  95. const AUTHOR = ".sc-10gpz4q-6"; // 作者
  96.  
  97. const HEADERS = {
  98. "referer": "https://www.pixiv.net/",
  99. "sec-fetch-dest": "image",
  100. "sec-fetch-mode": "no-cors",
  101. "sec-fetch-site": "cross-site",
  102. };
  103.  
  104. // Eagle API 服务器位置
  105. const EAGLE_SERVER_URL = "http://localhost:41595";
  106. const EAGLE_APP_INFO_URL = `${EAGLE_SERVER_URL}/api/application/info`;
  107. const EAGLE_IMPORT_API_URL = `${EAGLE_SERVER_URL}/api/item/addFromURL`;
  108. const EAGLE_IMPORT_API_URLS = `${EAGLE_SERVER_URL}/api/item/addFromURLs`;
  109. const EAGLE_CREATE_FOLDER_API_URL = `${EAGLE_SERVER_URL}/api/folder/create`;
  110. const EAGLE_UPDATE_FOLDER_API_URL = `${EAGLE_SERVER_URL}/api/folder/update`;
  111. const EAGLE_GET_FOLDERS_API_URL = `${EAGLE_SERVER_URL}/api/folder/list`;
  112.  
  113.  
  114. // 全局变量
  115. var folders = [];
  116. var folders_need_create = []; // {author, pid}
  117. var download_list = []; // {urls, allPage}
  118. var data_list = {}; // {url: {data, author, authorId}}
  119. var build_ver = ""; // Eagle build version
  120. var run_mode = "else"; // "else" || "image" || "manga" || "ugoira
  121.  
  122. function isDarkMode(){
  123. return document.getElementsByTagName("html")[0].getAttribute("data-theme") === "dark";
  124. }
  125.  
  126. const config_div = createConfigPage();
  127. const sleep = (delay) => {return new Promise((resolve) => {return setTimeout(resolve, delay)})}
  128.  
  129.  
  130. (function(){
  131. 'use strict';
  132.  
  133. if (location.href.indexOf("pixiv.net") === -1) {
  134. console.log("This script only works on pixiv.net.");
  135. return;
  136. }
  137.  
  138. function checkEagleStatus(){
  139. if (build_ver != "") return;
  140. // 获取应用版本
  141. GM_xmlhttpRequest({
  142. url: EAGLE_APP_INFO_URL,
  143. method: "GET",
  144. onload: function(response) {
  145. if(response.statusText !== "OK"){
  146. console.log(`请检查eagle是否打开!`);
  147. console.log(response);
  148. alert("下载失败!")
  149. }
  150. else{
  151. build_ver = JSON.parse(response.response).data.buildVersion;
  152. }
  153. }
  154. });
  155. // 获取文件夹列表
  156. GM_xmlhttpRequest({
  157. url: EAGLE_GET_FOLDERS_API_URL,
  158. method: "GET",
  159. redirect:'follow',
  160. onload: function(response) {
  161. if(response.status !== 200){
  162. alert(`请检查eagle是否打开!`);
  163. reject();
  164. }
  165. folders = JSON.parse(response.response).data;
  166. }
  167. });
  168. }
  169. build_ver = "123"
  170. checkEagleStatus();
  171.  
  172. function download(data){
  173. // return console.log(data);
  174. if(!data) return;
  175. GM_xmlhttpRequest({
  176. url: EAGLE_IMPORT_API_URL,
  177. method: "POST",
  178. data: JSON.stringify(data),
  179. onload: function(response) {
  180. if(response.statusText !== "OK"){
  181. console.log(`请检查eagle是否打开!`);
  182. console.log(response);
  183. console.log(data);
  184. alert("下载失败!")
  185. }
  186. }
  187. });
  188. }
  189.  
  190. // 为确保不反复创建文件夹以及网站报错429,先将所有待下载数据保存到列表
  191. function addToDownloadList(url, allPage = false){
  192. if (url in data_list) return;
  193. download_list.push({url, allPage})
  194. }
  195.  
  196. async function parseDownloadList(){
  197. let count = 0;
  198. for(let page_num in download_list){
  199. let url = download_list[page_num]["url"];
  200. let allPage = download_list[page_num]["allPage"];
  201. let data /* [{data, author, authorId}] */;
  202. await sleep(waitTime);
  203. if (allPage){
  204. data = await getImagesPage(url);
  205. getFolderId(data[0].author, data[0].authorId);
  206. // console.log(data);
  207. data_list[url] = data;
  208. count += data.length;
  209. }
  210. else{
  211. data = await getImagePage(url);
  212. getFolderId(data.author, data.authorId).then((dlFolderId)=>{
  213. if(dlFolderId === undefined){
  214. console.log(`创建文件夹失败!artist: ${data.author}, id: ${data.authorId}`)
  215. }
  216. });
  217. data_list[url] = [data];
  218. count += 1;
  219. }
  220. console.log(`已解析${page_num+1}个链接,共${count}个项目`);
  221. }
  222. download_list = []
  223. return count;
  224. }
  225.  
  226. async function downloadList(){
  227. if (build_ver === ""){
  228. alert(`请检查eagle是否打开!`);
  229. checkEagleStatus();
  230. return;
  231. }
  232. // return console.log(download_list);
  233. console.log(`开始解析下载列表,一共${download_list.length}条链接`);
  234. let items_num = await parseDownloadList();
  235. console.log("解析完成");
  236. console.log(`需要创建文件夹:${folders_need_create.length}`)
  237. for(let folder of folders_need_create){
  238. console.log(folder);
  239. await createFolder(folder.author, folder.pid);
  240. }
  241. console.log(`文件夹创建完成!开始下载,共${items_num}项`);
  242. for(let url in data_list){
  243. for (let data of data_list[url]){
  244. // console.log(data); /* {data|item, author, authorId} */
  245. getFolderId(data.author, data.authorId).then((dlFolderId)=>{
  246. if(dlFolderId === undefined){
  247. console.log("创建文件夹失败!尝试直接下载……")
  248. }
  249. else{
  250. data.item.folderId = dlFolderId;
  251. }
  252. download(data.item);
  253. });
  254. }
  255. }
  256. data_list = {};
  257. folders_need_create = [];
  258. }
  259.  
  260. function downloadAll(data){
  261. // return console.log(data);
  262. if(!data || data.length === 0) return;
  263. GM_xmlhttpRequest({
  264. url: EAGLE_IMPORT_API_URLS,
  265. method: "POST",
  266. data: JSON.stringify(data),
  267. onload: function(response) {
  268. if(response.statusText !== "OK"){
  269. alert("下载失败!");
  270. console.log(`请检查eagle是否打开!`);
  271. console.log(response);
  272. }
  273. }
  274. });
  275. }
  276.  
  277. // 侦听URL是否发生变化,代码来自 https://blog.csdn.net/liubangbo/article/details/103272393
  278. let _wr = function(type) {
  279. var orig = history[type];
  280. return function() {
  281. var rv = orig.apply(this, arguments);
  282. var e = new Event(type);
  283. e.arguments = arguments;
  284. window.dispatchEvent(e);
  285. return rv;
  286. };
  287. };
  288. history.pushState = _wr('pushState');
  289. history.replaceState = _wr('replaceState')
  290. window.addEventListener('replaceState', function(e) {
  291. main();
  292. });
  293. window.addEventListener('pushState', function(e) {
  294. main();
  295. });
  296.  
  297. function main(){
  298.  
  299. // 新版通用页面
  300. waitForKeyElements(BUTTON_POS, setMode, false); // artwork/** 图片详情页面
  301. waitForKeyElements("section", newPageCommon, false); // 通用样式
  302. if(useCheckbox){
  303. // 为所有图片添加复选框,但是不一定有对应的下载按键
  304. waitForKeyElements(PAGE_SELECTOR, (elem)=>{
  305. elem.prepend(createCheckbox());
  306. }, false);
  307. }
  308.  
  309. // 先处理还未完成改版的旧页面和一些特殊情况
  310. if(document.URL.startsWith("https://www.pixiv.net/bookmark_new_illust.php")){
  311. // 关注用户新作品
  312. waitForKeyElements(NEW_ILLUST_BUTTON, newIllustPage, true);
  313. }
  314. else if(document.URL.startsWith("https://www.pixiv.net/ranking.php")){
  315. // 排行榜
  316. waitForKeyElements(".ranking-image-item", (element)=>{
  317. element.before(createCheckbox());
  318. }, false);
  319. rankingPage();
  320. }
  321. else if (document.URL.startsWith("https://www.pixiv.net/users/")){
  322. // 用户主页通用
  323. waitForKeyElements(BUTTON_SELECTOR, userPage, true, undefined, true);
  324. }
  325. }
  326.  
  327. main();
  328.  
  329. // 网站改版后页面通用样式
  330. function newPageCommon(element){
  331. if(useCheckbox){
  332. let [button1, button2, button3] = createThreeButtons(element);
  333. $(BUTTON_SELECTOR, element).append(button1);
  334. $(BUTTON_SELECTOR, element).append(button2);
  335. $(BUTTON_SELECTOR, element).append(button3);
  336. }else{
  337. waitForKeyElements(PAGE_SELECTOR,(elem)=>{
  338. elem.find(DL_ILLUST_BUTTON).append(addDownloadButton());
  339. }, true);
  340. }
  341. }
  342.  
  343. // 收藏页面
  344. function bookmarksPage(element){
  345. $(".sc-1dg0za1-7", element).text("下载/管理收藏")
  346. function bookmarkAppendButton(){
  347. let button = document.createElement("div");
  348. button.className = "sc-1ij5ui8-0 QihHO sc-13ywrd6-7 tPCje";
  349. button.setAttribute("aria-disabled", "false");
  350. button.setAttribute("role", "button");
  351. if(isDarkMode()){
  352. button.innerHTML='<div aria-disabled="false" class="sc-4a5gah-0 hCTOkT"><div class="sc-4a5gah-1 kHyYuA">下载</div></div>';
  353. }
  354. else{
  355. button.innerHTML='<div aria-disabled="false" class="sc-4a5gah-0 bmIdgb"><div class="sc-4a5gah-1 kHyYuA">下载</div></div>';
  356. }
  357. button.addEventListener("click", ()=>{
  358. if (build_ver === ""){
  359. checkEagleStatus();
  360. }
  361. let count = $(BOOKMARK_SELECT).length;
  362. $(BOOKMARK_SELECT).each((index, elem)=>{
  363. let e = $(SELECT_CHECK, elem)[0];
  364. if(e && e.checked){
  365. addToDownloadList("https://www.pixiv.net" + $(SELECT_URL, elem).attr("to"), DLMultiple);
  366. if(--count === 0){
  367. downloadList();
  368. }
  369. e.checked = false;
  370. }
  371. else if(--count === 0){
  372. downloadList();
  373. }
  374. })
  375. if(isDarkMode()){
  376. button.setAttribute("class", "sc-4a5gah-0 dydUg")
  377. }
  378. else{
  379. button.setAttribute("class", "sc-4a5gah-0 jbzOgz")
  380. }
  381. });
  382. $(BDL_BUTTON_POS).append(button);
  383. $(PAGE_SELECTOR+":first").click();
  384. $(PAGE_SELECTOR+":first").click();
  385. $(OVER_BUTTON+":last").click(()=>{
  386. waitForKeyElements(BOOKMARKS_BUTTON, bookmarksPage, true);
  387. });
  388. }
  389. element.click(()=>{
  390. setTimeout(bookmarkAppendButton, 10);
  391. })
  392. $(".to_eagle").parent().hide()
  393. }
  394.  
  395. // 关注用户新作品页
  396. function newIllustPage(){
  397. if(useCheckbox){
  398. let element = $("section");
  399. let [button1, button2, button3] = createThreeButtons(element);
  400. $(NEW_ILLUST_BUTTON).append(button1);
  401. $(NEW_ILLUST_BUTTON).append(button2);
  402. $(NEW_ILLUST_BUTTON).append(button3);
  403. }
  404. $(PAGE_SELECTOR).each((index, elem)=>{
  405. if(useCheckbox){
  406. elem.parentElement.append(createCheckbox());
  407. }else{
  408. elem.append(addDownloadButton());
  409. }
  410. })
  411. let dl_page_between = createMultiPageButton();
  412. $(NEW_ILLUST_BUTTON).append(dl_page_between);
  413. }
  414.  
  415. // 用户作品页
  416. function userPage(){
  417. newPageCommon($(DIV_SECTION));
  418. // userId = document.URL.split("/")[4];
  419. let page = document.URL.split("/")[5]?.split("?")[0];
  420. let pageCount = document.URL.split("=")[1];
  421. let button;
  422. if (page === "request" || page === "artworks"){
  423. button = createCommonButton("下载全部作品");
  424. }
  425. else if (page === "illustrations"){
  426. button = createCommonButton("下载全部插画");
  427. }
  428. else if (page === "manga"){
  429. button = createCommonButton("下载全部漫画");
  430. }
  431. else if (page === "bookmarks"){
  432. button = createCommonButton("下载全部作品");
  433. waitForKeyElements(BOOKMARKS_BUTTON, bookmarksPage, true);
  434. // waitForKeyElements(".button_to_eagle", (e)=>{e.css("display", "none")}, true);
  435. waitForKeyElements(".to_eagle", (e)=>{e.parent().css("display", "none")}, true);
  436. }
  437. else{
  438. // 个人主页,不创建按键
  439. return;
  440. }
  441. let section = $("section")[0];
  442. button.addEventListener("click", ()=>{
  443. if (build_ver == ""){
  444. checkEagleStatus();
  445. }
  446. if (!confirm("该操作将下载当前筛选结果中的全部内容,下载过程中会自动翻页,确认继续?")) return;
  447. if (page === undefined){
  448. $(SHOW_ALL)[0].click();
  449. }
  450. else if(pageCount && pageCount != "1"){
  451. $(FIRST_PAGE)[0].click();
  452. }
  453. waitForPageLoaded(undefined, addAllArtToList);
  454. button.style.color = "rgb(0 150 250 / 70%)";
  455. });
  456. $(BUTTON_SELECTOR, section).append(button);
  457. let dl_page_between = createMultiPageButton();
  458. $(BUTTON_SELECTOR, section).append(dl_page_between);
  459. }
  460.  
  461. // 排行榜
  462. function rankingPage(){
  463. if(document.URL.search("content=ugoira") !== -1){
  464. return;
  465. }
  466. let pos = document.createElement("ul");
  467. pos.className = "menu-items";
  468. let button1 = document.createElement("li");
  469. button1.innerHTML = '<a style="cursor: pointer;color: #258fb8;padding: 10px;background: none;border: none;">全选</a>';
  470. let button2 = document.createElement("li");
  471. button2.innerHTML = '<a style="cursor: pointer;color: #258fb8;padding: 10px;background: none;border: none;">取消</a>';
  472. let button3 = document.createElement("li");
  473. button3.innerHTML = '<a style="cursor: pointer;color: #258fb8;padding: 10px;background: none;border: none;">下载</a>';
  474. button1.addEventListener("click", ()=>{
  475. $(".to_eagle").each((i,e)=>{
  476. e.checked = true;
  477. });
  478. });
  479. button2.addEventListener("click", ()=>{
  480. $(".to_eagle").each((i,e)=>{
  481. e.checked = false;
  482. });
  483. });
  484. button3.addEventListener("click", ()=>{
  485. if (build_ver === ""){
  486. checkEagleStatus();
  487. }
  488. let count = $(".to_eagle").length;
  489. $(".to_eagle").each(async (i,e)=>{
  490. if(e.checked){
  491. addToDownloadList(e.parentElement.nextElementSibling.firstElementChild.href, DLMultiple);
  492. if(--count === 0){
  493. downloadList();
  494. }
  495. e.checked = false;
  496. }
  497. else if(--count === 0){
  498. downloadList();
  499. }
  500. });
  501. $("button",button3).css("color", "black");
  502. });
  503. pos.appendChild(button1);
  504. pos.appendChild(button2);
  505. pos.appendChild(button3);
  506. $(RANK_PAGE_BUTTON)[0].append(pos);
  507. }
  508.  
  509. // 图片详情页
  510. function setMode(){
  511.  
  512. // 单图
  513. function imagePage(){
  514. run_mode = "image";
  515. function getImageData(){
  516. let image = document.getElementsByClassName("sc-1qpw8k9-3")[0];// 单图
  517. if(!image){
  518. alert("下载失败!");
  519. return;
  520. }
  521. let [name, annotation, tags, author, id] = getCommonInfo();
  522. let data = {
  523. "url": image.href,
  524. "name": name,
  525. "website": document.URL,
  526. "tags": tags,
  527. "annotation": annotation,
  528. "headers": HEADERS
  529. }
  530. return [data, author, id];
  531. };
  532.  
  533. let pos = $(BUTTON_POS);
  534. if(pos.length === 0) return;
  535. let button = createNormalButton("下载");
  536. pos[0].appendChild(button);
  537. button.addEventListener("click", async function(){
  538. if (build_ver === ""){
  539. checkEagleStatus();
  540. }
  541. add_to_favor();
  542. let [data, author, id] = getImageData();
  543. // console.log(data)
  544. let dlFolderId = await getFolderId(author, id);
  545. if(dlFolderId === undefined){
  546. console.log("创建文件夹失败!尝试直接下载……")
  547. }
  548. else{
  549. data.folderId = dlFolderId;
  550. }
  551. download(data);
  552. changeStyle(button);
  553. });
  554. }
  555.  
  556. // 多图
  557. function multiImagesPage(){
  558. run_mode = "multi_images";
  559. let pos = $(BUTTON_POS);
  560. if(pos.length === 0) return;
  561. let button = createNormalButton("下载");
  562. pos[0].appendChild(button);
  563. //绑定点击按钮时下载事件
  564. button.addEventListener("click", async () => {
  565. if (build_ver === ""){
  566. checkEagleStatus();
  567. }
  568. add_to_favor();
  569. let [data, author, id, name] = getImagesData($(PIC_SRC));
  570. let dlFolderId = await getFolderId(author, id);
  571. if(data.items.length > 1 && createSubfolder){
  572. let data = await createFolder(name, id, dlFolderId, true);
  573. dlFolderId = data.id;
  574. }
  575. if(dlFolderId === undefined){
  576. console.log("创建文件夹失败!尝试直接下载……");
  577. }
  578. else{
  579. data.folderId = dlFolderId;
  580. }
  581. downloadAll(data);
  582. changeStyle(button);
  583. });
  584. // let added = false;
  585. // function changeButton(){
  586. // // console.log("changed")
  587. // if(added) return;
  588. // added = true;
  589. // $("span",button)[0].innerText = "下载全部";
  590. // let button2 = createNormalButton("下载选择");
  591. // pos[0].appendChild(button2);
  592. // button2.addEventListener("click", async () => {
  593. // let [data, author, id, name] = getSelectData();
  594. // let dlFolderId = await getFolderId(author, id);
  595. // if(data.items.length > 1 && createSubfolder){
  596. // let data = await createFolder(name, id, dlFolderId, true);
  597. // dlFolderId = data.id;
  598. // }
  599. // if (dlFolderId === undefined) {
  600. // console.log("创建文件夹失败!尝试直接下载……");
  601. // }
  602. // else {
  603. // data.folderId = dlFolderId;
  604. // }
  605. // downloadAll(data);
  606. // changeStyle(button2);
  607. // });
  608.  
  609. // function addImagesCheckbox(){
  610. // let imgs = $(PIC_SRC);
  611. // imgs.each((index,element)=>{
  612. // element.before(createCheckbox());
  613. // });
  614. // }
  615. // waitForKeyElements(PIC_END, addImagesCheckbox, true);
  616. // }
  617. // let clickpos = $(PIC_SRC);
  618. // if(clickpos.length !== 0){
  619. // clickpos[0].addEventListener("click",changeButton)
  620. // }
  621. // clickpos = $(SHOW_ALL_BUTTON);
  622. // if(clickpos.length !== 0){
  623. // clickpos[0].addEventListener("click",changeButton)
  624. // }
  625. // clickpos = $(".gtm-main-bookmark");
  626. // if(clickpos.length !== 0){
  627. // clickpos[0].addEventListener("click",changeButton)
  628. // }
  629. }
  630. // 漫画
  631. function mangaPage(){
  632. run_mode = "manga";
  633. let pos = $(BUTTON_POS);
  634. if(pos.length === 0) return;
  635. let button = createNormalButton("下载");
  636. pos[0].appendChild(button);
  637. //绑定点击按钮时下载事件
  638. button.addEventListener("click", async () => {
  639. if (build_ver === ""){
  640. checkEagleStatus();
  641. }
  642. let [data, author, id, name] = getImagesData($(PIC_SRC));
  643. let dlFolderId = await getFolderId(author, id);
  644. if(data.items.length > 1 && createSubfolder){
  645. let data = await createFolder(name, id, dlFolderId, true);
  646. dlFolderId = data.id;
  647. }
  648. if(dlFolderId === undefined){
  649. console.log("创建文件夹失败!尝试直接下载……");
  650. }
  651. else{
  652. data.folderId = dlFolderId;
  653. }
  654. downloadAll(data);
  655. changeStyle(button);
  656. });
  657. // let added = false;
  658. // function createButtons(){
  659. // if(added) return;
  660. // added = true;
  661. // function createMangaButton(name){
  662. // let button = document.createElement("button");
  663. // button.className = "sc-1qrul0z-0 bWWzsr";
  664. // button.innerHTML = `<svg viewBox="0 0 48 48" width="32" height="32"><path fill-rule="evenodd" clip-rule="evenodd" d="M31.4142 26.5858C32.1953 27.3668 32.1953 28.6332 31.4142 29.4142L25.4142 35.4142C24.6332 36.1953 23.3668 36.1953 22.5858 35.4142C21.8047 34.6332 21.8047 33.3668 22.5858 32.5858L28.5858 26.5858C29.3668 25.8047 30.6332 25.8047 31.4142 26.5858Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16.5858 26.5858C17.3668 25.8047 18.6332 25.8047 19.4142 26.5858L25.4142 32.5858C26.1953 33.3668 26.1953 34.6332 25.4142 35.4142C24.6332 36.1953 23.3668 36.1953 22.5858 35.4142L16.5858 29.4142C15.8047 28.6332 15.8047 27.3668 16.5858 26.5858Z"></path><path d="M22 14C22 12.8954 22.8954 12 24 12V12C25.1046 12 26 12.8954 26 14L26 34C26 35.1046 25.1046 36 24 36V36C22.8954 36 22 35.1046 22 34L22 14Z"></path></svg>`;
  665. // let span = document.createElement("span");
  666. // span.innerText = name;
  667. // button.appendChild(span);
  668. // return button;
  669. // }
  670. // let button1 = createMangaButton("下载全部");
  671. // let button2 = createMangaButton("下载选择");
  672. // button1.addEventListener("click", async () => {
  673. // let [data, author, id, name] = getImagesData($(MANGA_SRC));
  674. // let dlFolderId = await getFolderId(author, id);
  675. // if(data.items.length > 1 && createSubfolder){
  676. // let data = await createFolder(name, id, dlFolderId, true);
  677. // dlFolderId = data.id;
  678. // }
  679. // if (dlFolderId === undefined) {
  680. // console.log("创建文件夹失败!尝试直接下载……");
  681. // }
  682. // else {
  683. // data.folderId = dlFolderId;
  684. // }
  685. // downloadAll(data);
  686. // // changeStyle(button1);
  687. // });
  688. // button2.addEventListener("click", async () => {
  689. // let [data, author, id, name] = getSelectData();
  690. // let dlFolderId = await getFolderId(author, id);
  691. // if(data.items.length > 1 && createSubfolder){
  692. // let data = await createFolder(name, id, dlFolderId, true);
  693. // dlFolderId = data.id;
  694. // }
  695. // if (dlFolderId === undefined) {
  696. // console.log("创建文件夹失败!尝试直接下载……");
  697. // }
  698. // else {
  699. // data.folderId = dlFolderId;
  700. // }
  701. // downloadAll(data);
  702. // // changeStyle(button2);
  703. // });
  704. // function addButtons(){
  705. // // let imgs = $(MANGA_SRC);
  706. // // imgs.each((index,element)=>{
  707. // // element.before(createCheckbox());
  708. // // });
  709. // let pos = $(MANGA_POS);
  710. // pos[0].appendChild(button1);
  711. // // pos[0].appendChild(button2);
  712. // }
  713. // waitForKeyElements(MANGA_POS, addButtons, true);
  714. // }
  715. // let clickpos = $(PIC_SRC);
  716. // if(clickpos.length !== 0){
  717. // clickpos[0].addEventListener("click",createButtons)
  718. // }
  719. // clickpos = $(SHOW_ALL_BUTTON);
  720. // if(clickpos.length !== 0){
  721. // clickpos[0].addEventListener("click",createButtons)
  722. // }
  723. }
  724.  
  725. // 动图
  726. function ugoiraPage(){
  727. run_mode = "ugoira";
  728. console.log("暂无法处理动图!")
  729. }
  730.  
  731. function getImagesData(images){
  732. if(images.length === 0){
  733. alert("下载失败!");
  734. return [null, null];
  735. }
  736. let data = {"items":[]};
  737. let [name, annotation, tags, author, id] = getCommonInfo();
  738. images.each((index, url) => {
  739. if(url === undefined) return;
  740. data.items.push({
  741. "url": url.href,
  742. "name": name + `_${index}`,
  743. "website": document.URL,
  744. "annotation": annotation,
  745. "tags": tags,
  746. "headers": HEADERS
  747. });
  748. index++;
  749. });
  750. return [data, author, id, name];
  751. };
  752.  
  753. // 有问题,不想修了
  754. // function getSelectData(){
  755. // let checkbox = $(".to_eagle");
  756. // let [name, annotation, tags, author, id] = getCommonInfo();
  757. // let data = {"items":[]};
  758. // checkbox.each((index, element)=>{
  759. // if(element.checked === true){
  760. // data.items.push({
  761. // "url": element.parentElement.nextElementSibling.href,
  762. // "name": name + `_${index}`,
  763. // "website": document.URL,
  764. // "annotation": annotation,
  765. // "tags": tags,
  766. // "headers": HEADERS
  767. // })
  768. // }
  769. // });
  770. // return [data, author, id, name];
  771. // };
  772.  
  773. function add_to_favor(){
  774. //下载同时自动点赞+收藏
  775. if(addToFavor){
  776. try{
  777. document.getElementsByClassName("_35vRH4a")[0].click();
  778. document.getElementsByClassName("gtm-main-bookmark")[0].click();
  779. }catch(e){}
  780. }
  781. }
  782.  
  783. function changeStyle(button){
  784. button.className = "hZvyDT cwSaXU";
  785. }
  786.  
  787. function createNormalButton(text){
  788. let button = document.createElement('div');
  789. button.setAttribute('class', 'sc-181ts2x-01');
  790. button.setAttribute('style', 'margin-right: 23px;');
  791. button.innerHTML = `<button type="button" id="download" class="hZvyDT dMrNyO">${text}</button>`;
  792. return button;
  793. }
  794.  
  795. if($(UGO_SRC).length !== 0){
  796. return ugoiraPage();
  797. }
  798. if($(SHOW_ALL_BUTTON).length !== 0){
  799. if($(READ_MANGA).text() === "查看全部"){
  800. return multiImagesPage();
  801. }
  802. else{
  803. return mangaPage();
  804. }
  805. }
  806. // console.log(run_mode);
  807. return imagePage();
  808. };
  809.  
  810. // 等待页面加载完成
  811. function waitForPageLoaded(lastFirst, callback){
  812. return new Promise((resolve, reject) => {
  813. let timeControl = setInterval(()=>{
  814. if (lastFirst === undefined){
  815. reject(lastFirst)
  816. }
  817. let tmp = $(".to_eagle");
  818. if (tmp.length > 0 && tmp[0] != lastFirst){
  819. clearInterval(timeControl);
  820. if (callback){
  821. resolve([tmp, callback(tmp)]);
  822. }
  823. else{
  824. resolve(tmp);
  825. }
  826. }
  827. }, waitTime);
  828. });
  829. }
  830.  
  831. function addThisPageToList(){
  832. let elements = $(".to_eagle");
  833. let count = elements.length;
  834. console.log("从", document.URL,"获取到", count, "个作品链接");
  835. elements.each((i,e)=>{
  836. addToDownloadList(e.parentElement.nextElementSibling.href, DLMultiple);
  837. if(--count === 0){
  838. downloadList().then(()=>{
  839. console.log(document.URL, "解析完成");
  840. })
  841. }
  842. });
  843. }
  844.  
  845. async function addAllArtBetweenPages(start_page, end_page){
  846. console.log(`准备下载第${start_page}页到第${end_page}页内容`);
  847. let page = document.URL.split("=")[1];
  848. if (page === undefined){
  849. page = "1";
  850. }
  851. let elements = $(".to_eagle");
  852. if (page != start_page){
  853. if (page != "1"){
  854. $(FIRST_PAGE)[0].click();
  855. elements = await waitForPageLoaded(elements[0]);
  856. }
  857. if (start_page != "1"){
  858. console.log(`将从第1页开始翻页至第${start_page}页`);
  859. while (page != start_page){
  860. $(NEXT_PAGE)[0].click();
  861. elements = await waitForPageLoaded(elements[0]);
  862. page = document.URL.split("=")[1];
  863. }
  864. }
  865. }
  866. for (let i = start_page; i < end_page; i++){
  867. console.log(`开始解析第${i}页`);
  868. addThisPageToList();
  869. $(NEXT_PAGE)[0].click();
  870. elements = await waitForPageLoaded(elements[0]);
  871. }
  872. addThisPageToList();
  873. }
  874.  
  875. function addAllArtToList(elements){
  876. let count = elements.length;
  877. console.log("从", document.URL,"获取到", count, "个作品链接");
  878. // if (count < 48){
  879. // console.log("当前页面疑似未能加载完成,请之后手动下载……");
  880. // }
  881. elements.each((i,e)=>{
  882. addToDownloadList(e.parentElement.nextElementSibling.href, true);
  883. if(--count === 0){
  884. downloadList().then(() => {
  885. let nextpage = $(NEXT_PAGE)[0];
  886. if (nextpage === undefined || nextpage.hidden){
  887. console.log("全部页面解析完成");
  888. }
  889. else{
  890. nextpage.click();
  891. waitForPageLoaded(elements[0], addAllArtToList);
  892. }
  893. });
  894. }
  895. });
  896. }
  897.  
  898. // 获取文件夹id
  899. async function getFolderId(author, pid){
  900. // 搜索同名或注释中包含有pid信息的文件夹
  901. function searchFolder(folders, author, pid){
  902. for(let folder of folders){
  903. let description = folder.description;
  904. description = description ? description.match(/(?<=pid ?[:=] ?)\d+/) : "";
  905. if((description && description[0] === pid) || folder.name === author){
  906. if(description){
  907. if(description[0] !== pid){
  908. continue;
  909. }
  910. }
  911. else{
  912. let d = "";
  913. for(let s of folder.description.split("\n")){
  914. if(!/^ *pid ?[:=] ?/.test(s)){
  915. d += "\n" + s;
  916. }
  917. }
  918. updateFolder({
  919. "folderId": folder.id,
  920. "newDescription":`pid = ${pid}${d}`
  921. })
  922. }
  923. return folder;
  924. }
  925. }
  926. for(let folder of folders){
  927. let target = searchFolder(folder.children, author, pid);
  928. if(target) return target;
  929. }
  930. }
  931.  
  932. if(!pid){
  933. console.log("获取用户id失败!");
  934. }
  935. if(!author && !pid) return;
  936. let dlFolder;
  937. if(folders){
  938. if(searchDirName === ""){
  939. dlFolder = searchFolder(folders, author, pid);
  940. }
  941. else{
  942. for(let folder of folders){
  943. if(folder.name === searchDirName){
  944. if(searchDirId === ""){
  945. searchDirId = folder.id;
  946. }
  947. // console.log(searchDirId);
  948. dlFolder = searchFolder(folder.children, author, pid);
  949. }
  950. else{
  951. let description = folder.description?.match(/(?<=pid ?[:=] ?)\d+/);
  952. if((description && description[0] === pid) || folder.name === author){
  953. if(description){
  954. if(description[0] !== pid){
  955. continue;
  956. }
  957. }
  958. else{
  959. let d = "";
  960. for(let s of description.split("\n")){
  961. if(!/^ *pid ?[:=] ?/.test(s)){
  962. d += "\n" + s;
  963. }
  964. }
  965. updateFolder({
  966. "folderId": folder.id,
  967. "newDescription":`pid = ${pid}${d}`
  968. })
  969. }
  970. dlFolder = folder;
  971. break;
  972. }
  973. }
  974. }
  975. }
  976. }
  977. else{
  978. console.log("获取文件夹信息失败!");
  979. alert("下载失败!");
  980. return;
  981. }
  982. if(!dlFolder){
  983. if(run_mode == "else"){
  984. if(folders_need_create){
  985. for(let f of folders_need_create){
  986. if(f.pid === pid){
  987. return undefined;
  988. }
  989. }
  990. }
  991. folders_need_create.push({author,pid});
  992. return undefined;
  993. }
  994. else{
  995. dlFolder = await createFolder(author, pid);
  996. }
  997. }
  998. return dlFolder?.id;
  999. }
  1000.  
  1001. // 创建文件夹
  1002. function createFolder(authorName, pid, parentFolderId, subfolder=false){
  1003. // return undefined
  1004. if (build_ver == ""){
  1005. checkEagleStatus();
  1006. }
  1007. let folderName = dirNameFormater.replaceAll(/\$\{authorName\}/g, authorName).replaceAll(/\$\{pid\}/g, pid);
  1008. if(subfolder){
  1009. folderName = authorName;
  1010. }
  1011. return new Promise((resolve, reject) => {
  1012. parentFolderId = parentFolderId || searchDirId;
  1013. if(parentFolderId === ""){
  1014. parentFolderId = undefined;
  1015. }
  1016. GM_xmlhttpRequest({
  1017. url: EAGLE_CREATE_FOLDER_API_URL,
  1018. method: "POST",
  1019. data: JSON.stringify({ folderName: folderName, parent: parentFolderId }),
  1020. onload: function(response) {
  1021. var result = JSON.parse(response.response);
  1022. if (result.status === "success" && result.data && result.data.id) {
  1023. updateFolder({
  1024. "folderId":result.data.id,
  1025. "newDescription":`pid = ${pid}`
  1026. });
  1027. folders.push({
  1028. id: result.data.id,
  1029. name: folderName,
  1030. description: `pid = ${pid}`
  1031. });
  1032. // console.log(folders);
  1033. return resolve(result.data);
  1034. }
  1035. else{
  1036. console.log(`请检查eagle是否打开!`);
  1037. alert("文件夹创建失败!");
  1038. return reject();
  1039. }
  1040. }
  1041. })
  1042. })
  1043. }
  1044.  
  1045. // 更新文件夹信息
  1046. function updateFolder(data){
  1047. if(Number(build_ver) < edit_folder_info){
  1048. return;
  1049. }
  1050. GM_xmlhttpRequest({
  1051. url: EAGLE_UPDATE_FOLDER_API_URL,
  1052. method: "POST",
  1053. data: JSON.stringify(data),
  1054. onload: function(response) {
  1055. if(response.statusText !== "OK"){
  1056. console.log(`请检查eagle是否打开!`);
  1057. console.log(response);
  1058. console.log(data);
  1059. alert("下载失败!");
  1060. }
  1061. }
  1062. });
  1063. }
  1064.  
  1065. // 格式化作者名,删除多余后缀,为避免误伤,同时匹配到多次不作处理
  1066. function authorTrim(author){
  1067. let test = author.match(patt);
  1068. if(test && test.length === 1){
  1069. let tmp = author.replace(test[0],"");
  1070. author = tmp === "" ? author : tmp;
  1071. }
  1072. // 删掉“接稿中”三个字
  1073. author = author.replace(/接稿中$/, "");
  1074. return author
  1075. }
  1076.  
  1077. function getCommonInfo(){
  1078. //获取标题
  1079. let name = document.getElementsByClassName("sc-1u8nu73-3")[0];
  1080. if(name === undefined){
  1081. name = document.title;
  1082. }else{
  1083. name = name.textContent;
  1084. }
  1085. //获取描述(Eagle2.0版本以下因bug无法生效)
  1086. let annotation = document.getElementById("expandable-paragraph-0");
  1087. if(annotation){annotation = annotation.textContent;}
  1088. else{annotation = "";}
  1089. //把pixiv标签和标签翻译添加进eagle标签
  1090. let tags = [];
  1091. if(saveTags){
  1092. $(TAG_SELECTOR).each((index,elem)=>{
  1093. $("a", elem).each((i,tag)=>{
  1094. if((i == 0 && tagTranslation != 1) || (i == 1 && tagTranslation != 0)){
  1095. if(tag.textContent) tags.push(tag.textContent);
  1096. }
  1097. })
  1098. })
  1099. }
  1100. let author = $(`${AUTHOR} div`).text();
  1101. let id = $(AUTHOR).attr("data-gtm-value");
  1102. author = authorTrim(author)
  1103. if(tagAuthor){
  1104. tags.push(author);
  1105. }
  1106. return [name, annotation, tags, author, id];
  1107. }
  1108.  
  1109. function createCommonButton(text){
  1110. let button = document.createElement('button');
  1111. button.style.border = "none";
  1112. button.style.background = "none";
  1113. button.style.marginLeft = "20px";
  1114. button.style.fontSize = "x-small";
  1115. button.style.fontWeight = "bold";
  1116. button.style.color = "gray";
  1117. button.style.cursor = "pointer";
  1118. button.innerText = text;
  1119. return button;
  1120. }
  1121.  
  1122. // 创建全选、取消、下载三个按键
  1123. function createThreeButtons(element){
  1124. let button1 = createCommonButton("全选");
  1125. let button2 = createCommonButton("取消");
  1126. let button3 = createCommonButton("下载");
  1127. button1.className = "button_to_eagle";
  1128. button2.className = "button_to_eagle";
  1129. button3.className = "button_to_eagle";
  1130. button1.addEventListener("click", ()=>{
  1131. $(".to_eagle", element).each((i,e)=>{
  1132. e.checked = true;
  1133. });
  1134. });
  1135. button2.addEventListener("click", ()=>{
  1136. $(".to_eagle", element).each((i,e)=>{
  1137. e.checked = false;
  1138. });
  1139. });
  1140. button3.addEventListener("click", ()=>{
  1141. if (build_ver == ""){
  1142. checkEagleStatus();
  1143. }
  1144. let count = $(".to_eagle", element).length;
  1145. $(".to_eagle", element).each((i,e)=>{
  1146. if(e.checked){
  1147. addToDownloadList(e.parentElement.nextElementSibling.href, DLMultiple);
  1148. if(--count === 0){
  1149. downloadList();
  1150. }
  1151. e.checked = false;
  1152. }
  1153. else if(--count === 0){
  1154. downloadList();
  1155. }
  1156. });
  1157. button3.style.color = "rgb(0 150 250 / 70%)";
  1158. });
  1159. return [button1, button2, button3]
  1160. }
  1161.  
  1162. //
  1163. function createMultiPageButton(){
  1164. let pageCount = document.URL.split("=")[1];
  1165. if (pageCount === undefined){
  1166. pageCount = 1;
  1167. }
  1168. let dl_page_between = document.createElement("div");
  1169. dl_page_between.innerHTML = `<button class="button_to_eagle" style="border: none; background: none; margin-left: 20px; font-size: x-small; font-weight: bold; color: gray; cursor: pointer;">翻页下载</button><input type="number" name="start_page" min="1" max="34" value="1"><a>-</a><input type="number" name="end_page" min="1" max="34" value="${pageCount}">`
  1170. dl_page_between.style = "display: flex;align-items: center;"
  1171. $("button", dl_page_between).click(()=>{
  1172. if (build_ver === ""){
  1173. checkEagleStatus();
  1174. }
  1175. let start_page = $("input[name=start_page]").val();
  1176. let end_page = $("input[name=end_page]").val();
  1177. if (start_page > end_page){
  1178. alert("请输入正确页码区间!");
  1179. return;
  1180. }
  1181. addAllArtBetweenPages(start_page, end_page);
  1182. })
  1183. return dl_page_between;
  1184. }
  1185.  
  1186. // 创建选择框
  1187. function createCheckbox(){
  1188. let input_container = document.createElement("div");
  1189. let checkbox = document.createElement("input");
  1190. checkbox.setAttribute("class","to_eagle");
  1191. checkbox.setAttribute("type","checkbox");
  1192. input_container.appendChild(checkbox);
  1193. input_container.style.position = "absolute";
  1194. input_container.style.zIndex = 3;
  1195. input_container.style.display = "flex";
  1196. input_container.style.backgroundColor = "rgba(0,0,0,.1)";
  1197. input_container.style.padding = "9px";
  1198. // input_container.className = "cb_div";
  1199. return input_container;
  1200. };
  1201.  
  1202. // 创建每张图片上的下载图标
  1203. function addDownloadButton(){
  1204. let pos = document.createElement("div");
  1205. pos.style.zIndex = 3;
  1206. let button = document.createElement("button");
  1207. pos.appendChild(button);
  1208. button.setAttribute("class","dl_to_eagle iPGEIN");
  1209. button.setAttribute("type", "button");
  1210. button.setAttribute("title", "下载这张图到Eagle");
  1211. button.style.backgroundColor = "rgba(0,0,0,.1)";
  1212. button.style.border = "none";
  1213. button.innerHTML = '<svg viewBox="0 0 120 120" style="width: 22px;height: 22px;stroke: white;fill: none;stroke-width: 10;"><polyline style="stroke: black; stroke-width: 15;" points="60,102 60,8"></polyline><polyline style="stroke: black; stroke-width: 15;" points="10,55 60,105 110,55"></polyline><polyline points="60,100 60,10"></polyline><polyline points="12,57 60,105 108,57"></polyline></svg>';
  1214. button.addEventListener("click", ()=>{
  1215. if (build_ver === ""){
  1216. checkEagleStatus();
  1217. }
  1218. getImagePage(pos.parentElement.previousSibling.href).then(async (data /** item, author, authorId **/)=>{
  1219. console.log(data)
  1220. let dlFolderId = await getFolderId(data.author, data.authorId);
  1221. if(dlFolderId === undefined){
  1222. console.log("创建文件夹失败!尝试直接下载……")
  1223. }
  1224. else{
  1225. data.item.folderId = dlFolderId;
  1226. }
  1227. download(data.item);
  1228. $("svg", button)[0].style.stroke = "gray";
  1229. });
  1230. });
  1231. return pos;
  1232. }
  1233.  
  1234. // 获取新页面并返回图片信息
  1235. async function getImagePage(url){
  1236. return new Promise((resolve, reject)=>{
  1237. let item = {
  1238. "website": url,
  1239. "tags": [],
  1240. "headers": HEADERS
  1241. };
  1242. $.ajax({
  1243. type: "GET",
  1244. url: url,
  1245. dataType: "html",
  1246. success: async (data)=>{
  1247. try{
  1248. let html = $(data);
  1249. let preloadData = html.filter("#meta-preload-data")[0];
  1250. let illust = JSON.parse(preloadData.content)["illust"];
  1251. let id = Object.keys(illust)[0];
  1252. let illustData = illust[id];
  1253. item.url = illustData.urls.original;
  1254. item.name = illustData.title;
  1255. item.annotation = illustData.description;
  1256. if (saveTags){
  1257. for(let tag of illustData.tags.tags){
  1258. if(tag.translation){
  1259. if(tagTranslation != 1){
  1260. item.tags.push(tag.tag);
  1261. }
  1262. if(tagTranslation != 0){
  1263. for(let trans of Object.values(tag.translation)){
  1264. item.tags.push(trans);
  1265. }
  1266. }
  1267. }
  1268. else{
  1269. item.tags.push(tag.tag);
  1270. }
  1271. }
  1272. }
  1273. let author = illustData.userName || illustData.userAccount;
  1274. let authorId = illustData.userId;
  1275. author = authorTrim(author)
  1276. if(tagAuthor){
  1277. item.tags.push(author);
  1278. }
  1279. if(!authorId){
  1280. console.log("获取用户id失败!")
  1281. console.log(illustData);
  1282. }
  1283. resolve({item, author, authorId});
  1284. }
  1285. catch(e){
  1286. reject(e);
  1287. }
  1288. }
  1289. });
  1290. });
  1291. }
  1292.  
  1293. // 获取新页面并返回所有图片信息
  1294. async function getImagesPage(url){
  1295. await sleep(waitTime);
  1296. return new Promise((resolve, reject)=>{
  1297. $.ajax({
  1298. type: "GET",
  1299. url: url,
  1300. dataType: "html",
  1301. success: async (data)=>{
  1302. try{
  1303. let items = [];
  1304. let html = $(data);
  1305. let preloadData = html.filter("#meta-preload-data")[0];
  1306. let illust = JSON.parse(preloadData.content)["illust"];
  1307. let id = Object.keys(illust)[0];
  1308. let illustData = illust[id];
  1309. let item = {
  1310. "website": url,
  1311. "tags": [],
  1312. "headers": HEADERS
  1313. };
  1314. item.url = illustData.urls.original;
  1315. item.name = illustData.title;
  1316. item.annotation = illustData.description;
  1317. for(let tag of illustData.tags.tags){
  1318. item.tags.push(tag.tag);
  1319. if(tag.translation){
  1320. for(let trans of Object.values(tag.translation)){
  1321. item.tags.push(trans);
  1322. }
  1323. }
  1324. }
  1325. let author = illustData.userName || illustData.userAccount;
  1326. let authorId = illustData.userId;
  1327. author = authorTrim(author)
  1328. if(tagAuthor){
  1329. item.tags.push(author);
  1330. }
  1331. if(!authorId){
  1332. console.log("获取用户id失败!")
  1333. console.log(illustData);
  1334. }
  1335. items.push({item, author, authorId});
  1336. let url0 = item.url.replace(/0\.[a-z]+/, "");
  1337. let suffix = item.url.match(/(?<=0)\.[a-z]+/);
  1338. for(let i = 1; i < illustData.pageCount; i++){
  1339. let item = {
  1340. "website": items[0].item.website,
  1341. "url": url0 + i + suffix,
  1342. "name": items[0].item.name,
  1343. "annotation": items[0].item.annotation,
  1344. "tags": items[0].item.tags,
  1345. "headers": items[0].item.headers
  1346. };
  1347. items.push({item, author, authorId});
  1348. }
  1349. resolve(items);
  1350. }
  1351. catch(e){
  1352. reject(e);
  1353. }
  1354. }
  1355. });
  1356. });
  1357. }
  1358. })();
  1359.  
  1360.  
  1361. // @require https://gist.github.com/raw/2625891/waitForKeyElements.js
  1362. // 因外部链接无法通过油猴检测,将源代码复制粘贴在这
  1363. // 进行了修改,添加了参数bCallOnce,为true时仅在选择元素出现时,调用一次actionFunction,不再为每个元素分别调用,且不再传回任何参数
  1364. /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
  1365. that detects and handles AJAXed content.
  1366.  
  1367. Usage example:
  1368.  
  1369. waitForKeyElements (
  1370. "div.comments"
  1371. , commentCallbackFunction
  1372. );
  1373.  
  1374. //--- Page-specific function to do what we want when the node is found.
  1375. function commentCallbackFunction (jNode) {
  1376. jNode.text ("This comment changed by waitForKeyElements().");
  1377. }
  1378.  
  1379. IMPORTANT: This function requires your script to have loaded jQuery.
  1380. */
  1381. function waitForKeyElements (
  1382. selectorTxt, /* Required: The jQuery selector string that
  1383. specifies the desired element(s).
  1384. */
  1385. actionFunction, /* Required: The code to run when elements are
  1386. found. It is passed a jNode to the matched
  1387. element.
  1388. */
  1389. bWaitOnce=false, /* Optional: If false, will continue to scan for
  1390. new elements even after the first match is
  1391. found.
  1392. */
  1393. iframeSelector=undefined,/* Optional: If set, identifies the iframe to
  1394. search.
  1395. */
  1396. bCallOnce=false // 修改添加参数,
  1397. ) {
  1398. var targetNodes, btargetsFound;
  1399.  
  1400. if (typeof iframeSelector == "undefined")
  1401. targetNodes = $(selectorTxt);
  1402. else
  1403. targetNodes = $(iframeSelector).contents ()
  1404. .find (selectorTxt);
  1405.  
  1406. if (targetNodes && targetNodes.length > 0) {
  1407. btargetsFound = true;
  1408. /*--- Found target node(s). Go through each and act if they
  1409. are new.
  1410. */
  1411. if(bCallOnce){
  1412. var alreadyFound = targetNodes.data ('alreadyFound') || false;
  1413.  
  1414. if (!alreadyFound) {
  1415. //--- Call the payload function.
  1416. var cancelFound = actionFunction ();
  1417. if (cancelFound)
  1418. btargetsFound = false;
  1419. else
  1420. targetNodes.data ('alreadyFound', true);
  1421. }
  1422. }
  1423. else{
  1424. targetNodes.each ( function () {
  1425. var jThis = $(this);
  1426. var alreadyFound = jThis.data ('alreadyFound') || false;
  1427.  
  1428. if (!alreadyFound) {
  1429. //--- Call the payload function.
  1430. var cancelFound = actionFunction (jThis);
  1431. if (cancelFound)
  1432. btargetsFound = false;
  1433. else
  1434. jThis.data ('alreadyFound', true);
  1435. }
  1436. } );
  1437. }
  1438. }
  1439. else {
  1440. btargetsFound = false;
  1441. }
  1442.  
  1443. //--- Get the timer-control variable for this selector.
  1444. var controlObj = waitForKeyElements.controlObj || {};
  1445. var controlKey = selectorTxt.replace (/[^\w]/g, "_");
  1446. var timeControl = controlObj [controlKey];
  1447.  
  1448. //--- Now set or clear the timer as appropriate.
  1449. if (btargetsFound && bWaitOnce && timeControl) {
  1450. //--- The only condition where we need to clear the timer.
  1451. clearInterval (timeControl);
  1452. delete controlObj [controlKey]
  1453. }
  1454. else {
  1455. //--- Set a timer, if needed.
  1456. if ( ! timeControl) {
  1457. timeControl = setInterval ( function () {
  1458. waitForKeyElements ( selectorTxt,
  1459. actionFunction,
  1460. bWaitOnce,
  1461. iframeSelector,
  1462. bCallOnce
  1463. );
  1464. },
  1465. 200
  1466. );
  1467. controlObj [controlKey] = timeControl;
  1468. }
  1469. }
  1470. waitForKeyElements.controlObj = controlObj;
  1471. }
  1472.  
  1473.  
  1474. // 脚本设置选项
  1475. GM_registerMenuCommand("更新设置", updateConfig);
  1476.  
  1477. function updateConfig(){
  1478. config_div.style.background = isDarkMode() ? "black" : "white";
  1479. if (config_div.style.display === "none"){
  1480. config_div.style.display = "inline";
  1481. }
  1482. else{
  1483. config_div.style.display = "none";
  1484. }
  1485. }
  1486.  
  1487. function createConfigPage(){
  1488. let config_div = document.createElement("div");
  1489.  
  1490. function createNewConfig(text, type, value){
  1491. let p = document.createElement("p");
  1492. let input = document.createElement("input");
  1493. input.setAttribute("type", type);
  1494. if(type === "text"){
  1495. input.style.width = "100%";
  1496. input.value = value;
  1497. p.innerText = text;
  1498. config_div.appendChild(p);
  1499. config_div.appendChild(input);
  1500. }
  1501. else if(type === "number"){
  1502. // input.style.width =
  1503. input.value = value;
  1504. p.append(text);
  1505. p.appendChild(input);
  1506. config_div.appendChild(p);
  1507. }
  1508. else if(type === "checkbox"){
  1509. input.style.marginLeft = "10px";
  1510. input.checked = value;
  1511. p.appendChild(input);
  1512. p.append(text);
  1513. config_div.appendChild(p);
  1514. }
  1515. return input;
  1516. }
  1517. function createSelection(info, name, values, display_names, selected){
  1518. let div = document.createElement("div");
  1519. div.innerText = info;
  1520. let selection = document.createElement("select");
  1521. selection.name = name;
  1522. div.appendChild(selection);
  1523. // selectedIndex = 0;
  1524. for (let i in values){
  1525. let opt = document.createElement("option");
  1526. opt.value = values[i];
  1527. opt.innerText = display_names[i];
  1528. if(values[i] == selected){
  1529. // selection.selectedIndex = i;
  1530. opt.setAttribute("selected", "");
  1531. }
  1532. selection.appendChild(opt);
  1533. }
  1534. config_div.appendChild(div);
  1535. return selection;
  1536. }
  1537. // 布尔值
  1538. let saveTags_input = createNewConfig("是否保存标签", "checkbox", saveTags);
  1539. let tagAuthor_input = createNewConfig("是否将作者名加入标签", "checkbox", tagAuthor);
  1540. let addToFavor_input = createNewConfig("下载时是否同时加入收藏", "checkbox", addToFavor);
  1541. let useCheckbox_input = createNewConfig("使用复选框,而不是每张图添加下载按键", "checkbox", useCheckbox);
  1542. let DLMultiple_input = createNewConfig("批量下载时,下载多P", "checkbox", DLMultiple);
  1543. let createSubfolder_input = createNewConfig("多P时创建子文件夹", "checkbox", createSubfolder);
  1544. // 单选
  1545. let tagTranslation_input = createSelection("标签翻译处理方式", "tagTrans", [0, 1, 2], ["仅保存原文", "仅保存翻译", "保存原文与翻译"], tagTranslation);
  1546. // 整型
  1547. let waitTime_input = createNewConfig("批量下载时等待时间(单位:ms)", "number", waitTime);
  1548. // 文本
  1549. let patt_input = createNewConfig("正则表达式,处理作者名多余后缀,匹配到的内容将被删除:", "text", patt.source);
  1550. let searchDirName_input = createNewConfig("父文件夹名:\n(在需要创建新文件夹时,新建文件夹的父文件夹名,在引号内输入文件夹名。留空则直接创建)", "text", searchDirName);
  1551. let searchDirId_input = createNewConfig("父文件夹id:\n(一般无需填写,填写会忽略上一行设置,可用来设置新建文件夹创建到某个子文件夹中。)\n(eagle中选中文件夹右键复制链接,获得如‘eagle://folder/K4130PELEY5W9’字符串,文件夹id就是其中K4130PELEY5W9部分)", "text", searchDirId);
  1552. let dirNameFormater_input = createNewConfig("新建文件夹名格式化:\n默认为作者名,可用变量包括 ${authorName} 和 ${pid} ", "text", dirNameFormater);
  1553.  
  1554. let button_save = document.createElement("button");
  1555. let button_cancel = document.createElement("button");
  1556. button_save.innerText = "保存";
  1557. button_cancel.innerText = "取消";
  1558. button_save.style.margin = "20px";
  1559. button_cancel.style.margin = "20px";
  1560. button_save.addEventListener("click", ()=>{
  1561. saveTags = saveTags_input.checked;
  1562. tagAuthor = tagAuthor_input.checked;
  1563. addToFavor = addToFavor_input.checked;
  1564. useCheckbox = useCheckbox_input.checked;
  1565. DLMultiple = DLMultiple_input.checked;
  1566. createSubfolder = createSubfolder_input.checked;
  1567. patt = new RegExp(patt_input.value);
  1568. searchDirName = searchDirName_input.value;
  1569. searchDirId = searchDirId_input.value;
  1570. dirNameFormater = dirNameFormater_input.value;
  1571. tagTranslation = tagTranslation_input.selectedOptions[0].value;
  1572. waitTime = waitTime_input.value;
  1573. GM_setValue("patt", patt.source);
  1574. GM_setValue("saveTags", saveTags);
  1575. GM_setValue("tagAuthor", tagAuthor);
  1576. GM_setValue("tagTranslation", tagTranslation);
  1577. GM_setValue("addToFavor", addToFavor);
  1578. GM_setValue("searchDirName", searchDirName);
  1579. GM_setValue("searchDirId", searchDirId);
  1580. GM_setValue("dirNameFormater", dirNameFormater);
  1581. GM_setValue("useCheckbox", useCheckbox);
  1582. GM_setValue("DLMultiple", DLMultiple);
  1583. GM_setValue("waitTime", waitTime);
  1584. GM_setValue("createSubfolder", createSubfolder);
  1585. config_div.style.display = "none";
  1586. });
  1587. button_cancel.addEventListener("click",()=>{
  1588. config_div.style.display = "none";
  1589. });
  1590. config_div.appendChild(button_save);
  1591. config_div.appendChild(button_cancel);
  1592. config_div.style.position = "fixed";
  1593. config_div.style.width = "80%";
  1594. config_div.style.top = "15%";
  1595. config_div.style.left = "10%";
  1596. config_div.style.padding = "15px";
  1597. config_div.style.border = "1px solid #777777";
  1598. config_div.style.borderRadius = "5px";
  1599. config_div.style.boxShadow = "-5px 5px 10px rgb(0 0 0 / 50%)";
  1600. config_div.style.background = isDarkMode() ? "black" : "white";
  1601. document.body.appendChild(config_div);
  1602. config_div.style.display = "none";
  1603. return config_div;
  1604. }