Extract images for pixiv

Adds a button that get all attached images as original size to every post.

  1. // ==UserScript==
  2. // @name Extract images for pixiv
  3. // @name:zh P站原图收割机
  4. // @namespace https://github.com/cmheia/extract-images-for-pixiv
  5. // @description Adds a button that get all attached images as original size to every post.
  6. // @include http://www.pixiv.net/member_illust.php*
  7. // @include https://www.pixiv.net/member_illust.php*
  8. // @author cmheia
  9. // @version 1.3.2
  10. // @icon http://www.pixiv.net/favicon.ico
  11. // @grant GM_setClipboard
  12. // @grant GM_xmlhttpRequest
  13. // @license MPL
  14. // ==/UserScript==
  15. (function () {
  16. 'use strict';
  17. /**********************************************************************
  18. * 长得像库
  19. **********************************************************************/
  20. var $id = function (o) {
  21. // return document.getElementById(o);
  22. return document.querySelector(`#${o}`);
  23. };
  24.  
  25. var $class = function (o) {
  26. // return document.getElementsByClassName(o);
  27. return document.querySelector(`.${o}`);
  28. };
  29.  
  30. // 去重
  31. var unique = function (arr) {
  32. var result = [],
  33. hash = {};
  34. for (let i = 0, elem; (elem = arr[i]) !== undefined; i++) {
  35. if (!hash[elem]) {
  36. result.push(elem);
  37. hash[elem] = true;
  38. }
  39. }
  40. return result;
  41. };
  42.  
  43. // 插入样式表
  44. var apendStyle = function (cssText) {
  45. var head = document.head || document.getElementsByTagName('head')[0];
  46. var style = document.createElement('style');
  47. style.type = 'text/css';
  48. var textNode = document.createTextNode(cssText);
  49. style.appendChild(textNode);
  50. head.appendChild(style);
  51. };
  52.  
  53. // 增加 class
  54. var addClassName = function (elem, clas) {
  55. var current = elem.className;
  56. if (current) {
  57. current += " ";
  58. current += clas;
  59. current = current.split(' ').filter(function (v, i) {
  60. if (v) {
  61. return v;
  62. }
  63. });
  64. current = unique(current);
  65. elem.className = current.join(" ");
  66. } else {
  67. elem.className = clas;
  68. }
  69. };
  70.  
  71. // 移除 class
  72. var removeClassName = function (elem, clas) {
  73. var current = elem.className;
  74. if (current) {
  75. current = current.split(' ').filter(function (v, i) {
  76. if (clas != v) {
  77. return v;
  78. }
  79. });
  80. current = unique(current);
  81. elem.className = current.join(" ");
  82. }
  83. };
  84.  
  85. // 增加/移除 class
  86. var toggleClassName = function (elem, clas) {
  87. var current = elem.className;
  88. if (current) {
  89. if (-1 === current.split(' ').indexOf(clas)) {
  90. addClassName(elem, clas);
  91. } else {
  92. removeClassName(elem, clas);
  93. }
  94. } else {
  95. elem.className = clas;
  96. }
  97. };
  98.  
  99. // 伤脑筋!
  100. function illustCollector() {
  101.  
  102. function tergetContainer() {
  103. // illust_id
  104. this.id = "-1";
  105. // 取得的原图链接
  106. this.result = [];
  107. // 最终的原图链接,1 -> yes,0 -> no,-1 -> failed
  108. this.final = [];
  109. }
  110.  
  111. this.illust = [];
  112.  
  113. // 删除重复目标
  114. this.shrinkTarget = function () {
  115. var elem,
  116. hash = {},
  117. duplicate = [];
  118. // 第一步:找出需要删除的重复 id
  119. for (let i = 0; (elem = this.illust[i]) !== undefined; i++) {
  120. if (hash[elem.id]) {
  121. duplicate.push(i); // 重复
  122. } else {
  123. hash[elem.id] = true;
  124. }
  125. }
  126. // 第二步:删除的重复 id
  127. for (let i = duplicate.length - 1; i >= 0; i--) {
  128. this.illust.splice(duplicate[i], 1);
  129. }
  130. // console.log("删除重复 id", duplicate.length, "个");
  131. return duplicate.length;
  132. };
  133.  
  134. // 增加新目标
  135. this.addTarget = function (illust_id) {
  136. // console.group("addTarget", illust_id, this.illust.length);
  137. var i,
  138. index;
  139. for (i = 0; i < this.illust.length; i++) {
  140. if (illust_id === this.illust[i].id) {
  141. // console.log("目标重复了");
  142. index = -1;
  143. break;
  144. }
  145. }
  146. if (-1 !== index) {
  147. this.shrinkTarget();
  148. index = this.illust.length;
  149. // console.log("新增目标", index, this.illust.length);
  150. this.illust.push(new tergetContainer());
  151. // illust_id
  152. this.illust[index].id = illust_id;
  153. }
  154. // console.log(index, this.illust);
  155. // console.groupEnd();
  156. return index;
  157. };
  158.  
  159. // 删除目标
  160. // type: true -> target is id; false -> target is index (default)
  161. this.removeTarget = function (target, type) {
  162. // console.group("removeTarget", target, type);
  163. var index = -1;
  164.  
  165. if (type) {
  166. for (let i = 0; i < this.illust.length; i++) {
  167. if (target === this.illust[i].id) {
  168. index = i;
  169. break;
  170. }
  171. }
  172. } else {
  173. index = target;
  174. }
  175. if (index > -1) {
  176. this.illust.splice(index, 1);
  177. }
  178. // console.groupEnd();
  179. };
  180.  
  181. // 记录指定 illust_id 包含的图片数量(取得目标 html 后调用)
  182. // count: -1 -> 记录为失败
  183. // type: true -> target is index; false -> target is id (default)
  184. this.recordTargetLength = function (target, count, type) {
  185. // console.group("recordTargetLength", target, count, type);
  186. var index = -1;
  187.  
  188. if (type && this.illust[target] && this.illust[target].id) {
  189. index = target;
  190. } else {
  191. for (let i = 0; i < this.illust.length; i++) {
  192. if (target === this.illust[i].id) {
  193. index = i;
  194. break;
  195. }
  196. }
  197. }
  198. if (index > -1) {
  199. if (0 > count) {
  200. // 记录为失败
  201. // 取得的原图链接
  202. // this.illust[index].result[0] = "";
  203. // 最终的原图链接,1 -> yes,0 -> no,-1 -> failed
  204. this.illust[index].final[0] = -1;
  205. // console.log(target, "被标记为获取失败,index =", index);
  206. } else {
  207. // 取得的原图链接
  208. this.illust[index].result = new Array(count);
  209. // 最终的原图链接,1 -> yes,0 -> no,-1 -> failed
  210. this.illust[index].final = new Array(count);
  211. // console.log("初始化 illust[", index, "] 为", count, "个原图存放区");
  212. }
  213. }
  214. // console.groupEnd();
  215. };
  216.  
  217. // 记录指定 illust_id 的原图 URL (取得目标的原图后调用, 每次调用添加一个 URL, 多图多调)
  218. // type: true -> target is index; false -> target is id (default)
  219. this.setTarget = function (target, content, offset, status, type) {
  220. // console.group("setTarget", target, content, offset, status, type);
  221. var index = -1,
  222. result = false;
  223.  
  224. if (type && this.illust[target] && this.illust[target].id) {
  225. index = target;
  226. } else {
  227. for (let i = 0; i < this.illust.length; i++) {
  228. if (target === this.illust[i].id) {
  229. index = i;
  230. break;
  231. }
  232. }
  233. }
  234. if (index > -1) {
  235. if (offset < this.illust[index].final.length) {
  236. // console.log("记录第", offset, "个原图", content, "到", index);
  237. // 取得的原图链接
  238. this.illust[index].result[offset] = content;
  239. // 最终的原图链接,1 -> yes,0 -> no,-1 -> failed
  240. this.illust[index].final[offset] = parseInt(status);
  241. result = true;
  242. } else {
  243. // console.log(offset, "已越界");
  244. }
  245. }
  246. // console.groupEnd();
  247. return result;
  248. };
  249.  
  250. // 完工?
  251. // final[],1 -> yes,0 -> no,-1 -> failed
  252. // 遍历所有 final, 发现 0 即为未完成
  253. this.isAllDone = function () {
  254. // console.group("isAllDone", this.illust.length);
  255. var working = false;
  256.  
  257. // console.group("loop illust[]");
  258. for (let i = 0; i < this.illust.length && !working; i++) {
  259. // console.log("illust[", i, "]: id =", this.illust[i].id, ", final.length =", this.illust[i].final.length);
  260. if (0 === this.illust[i].final.length) {
  261. working = true;
  262. // console.warn("final.length=0, 即还未记录结果, 属未完成");
  263. break;
  264. }
  265. for (let j = 0; j < this.illust[i].final.length && !working; j++) {
  266. // console.log("\tfinal[", j, "] =", this.illust[i].final[j]);
  267. if (0 === this.illust[i].final[j]) {
  268. working = true;
  269. // console.warn("illust[", i, "].final[", j, "] = 0, 还未完成");
  270. break;
  271. }
  272. }
  273. }
  274. // console.groupEnd();
  275. if (working) {
  276. // console.warn("在忙");
  277. } else {
  278. // console.warn("完工!!!");
  279. }
  280. // console.groupEnd();
  281. return !working;
  282. };
  283.  
  284. // 导出结果
  285. this.exportAll = function () {
  286. // console.group("exportAll");
  287. var j,
  288. k,
  289. total = 0,
  290. failed = new Array(this.illust.length),
  291. src = [],
  292. result = {};
  293.  
  294. for (let i = 0; i < this.illust.length; i++) {
  295. for (j = 0, k = 0; j < this.illust[i].final.length; j++) {
  296. if (1 === this.illust[i].final[j]) {
  297. src[total++] = this.illust[i].result[j];
  298. k++;
  299. }
  300. }
  301. failed[i] = j - k;
  302. // console.log("illust[", i, "]导出", k, "个,失败", failed, "个");
  303. }
  304. // console.log("共导出", total, "个");
  305. result.fail = failed;
  306. result.done = src;
  307. // console.groupEnd();
  308. return result;
  309. };
  310.  
  311. // 导出 ID
  312. this.getID = function () {
  313. // console.group("getID");
  314. var result = [];
  315.  
  316. for (let i = 0; i < this.illust.length; i++) {
  317. result[i] = this.illust[i].id;
  318. }
  319. // console.groupEnd();
  320. return result;
  321. };
  322. }
  323.  
  324. /**********************************************************************
  325. * 基础设施
  326. **********************************************************************/
  327. // 页面显示信息
  328. var msg = function (msg) {
  329. $id("extracted").innerHTML = msg;
  330. };
  331.  
  332. // 创建样式表
  333. var addStyle = function () {
  334. apendStyle(".cmheia_checkbox {position:absolute;left:0;} .cmheia_item {padding:1px;} .cmheia_item_unselect {background-color:pink;}");
  335. };
  336.  
  337. // 作品目录?
  338. // 综合
  339. // http://www.pixiv.net/member_illust.php?id=xxxxxxxx
  340. // 插画
  341. // http://www.pixiv.net/member_illust.php?type=illust&id=xxxxxxxx
  342. // 漫画
  343. // http://www.pixiv.net/member_illust.php?type=manga&id=xxxxxxxx
  344. // 动图
  345. // http://www.pixiv.net/member_illust.php?type=ugoira&id=xxxxxxxx
  346. // 小说
  347. // http://www.pixiv.net/novel/member.php?id=xxxxxxxx
  348. var isWorksList = function () {
  349. // console.group('页面类型');
  350. var userId,
  351. workId;
  352.  
  353. userId = window.location.search.match(/[^_]id=(\d+)/);
  354. workId = window.location.search.match(/illust_id=(\d+)/);
  355. if (userId) {
  356. // console.log("作品目录,USER ID:", userId[1]);
  357. }
  358. if (workId) {
  359. // console.log("作品页面,WORK ID:", workId[1]);
  360. }
  361. // console.groupEnd();
  362. return null !== userId && null === workId;
  363. };
  364.  
  365. // 匹配单个图片链接
  366. var parseImageUrl = function (src) {
  367. var result = src.match(/((http|https):\/\/)+(\w+\.)+(\w+)[\w\/\.\-]*(jpg|jpeg|gif|png|webp)/gi);
  368. if (null === result || 1 !== result.length) {
  369. return null;
  370. }
  371. return result[0];
  372. };
  373.  
  374. // 提取多图页面原图链接
  375. var parseMultiImageUrl = function (target, callback) {
  376. // console.group("parseMultiImageUrl", target);
  377. var num = target.length,
  378. parsed = 0,
  379. result = {};
  380. var referer = target[0].replace(/big/, "medium");
  381. // console.warn('Referer :', referer);
  382.  
  383. result.done = new Array(num);
  384. result.fail = new Array(num);
  385. for (let i = 0; i < num; i++) {
  386. // console.log(target[i]);
  387. // 下面闭包的 index 无实际必要,
  388. // xhr.finalUrl.replace(/.*(page=\d+)/, "$1") 可取得相同的值,
  389. // 然而
  390. // 听说闭包很深奥,那就多练练
  391. GM_xmlhttpRequest({
  392. method : 'GET',
  393. url : target[i],
  394. headers: {
  395. 'Referer': referer
  396. },
  397. onload : (function (xhr) {
  398. var index = i;
  399. return function (xhr) {
  400. var src;
  401. if (200 === xhr.status) {
  402. src = parseImageUrl(xhr.response);
  403. if (null !== src) {
  404. result.done[index] = src;
  405. }
  406. }
  407. // console.log("parseMultiImageUrl:onload", xhr.finalUrl.replace(/.*(page=\d+)/, "$1"), parsed, src, result);
  408. if (++parsed === num) {
  409. callback(result);
  410. }
  411. };
  412. })(),
  413. onerror: (function (xhr) {
  414. var index = i;
  415. return function (xhr) {
  416. // console.log("parseMultiImageUrl:onerror", xhr.finalUrl.replace(/.*(page=\d+)/, "$1"));
  417. result.fail[index] = xhr.finalUrl;
  418. if (++parsed === num) {
  419. callback(result);
  420. }
  421. };
  422. })()
  423. });
  424. }
  425. // console.groupEnd();
  426. return num;
  427. };
  428.  
  429. /**********************************************************************
  430. * 作品目录页面功能
  431. **********************************************************************/
  432. // 解析详情页链接
  433. var extractIllustUrl = function () {
  434. // console.group("extractIllustUrl");
  435. var id = [],
  436. itemList = $class('_image-items').children;
  437.  
  438. if (itemList) {
  439. for (let i = 0; i < itemList.length; i++) {
  440. let cmheia_checkbox = itemList[i].children[0].children[0].getElementsByTagName('input')[0];
  441. // console.log(cmheia_checkbox);
  442. if (cmheia_checkbox && cmheia_checkbox.checked) {
  443. let href = itemList[i].children[1].getAttribute('href');
  444. if (href && href.match(/.*illust_id=(\d+).*/)) {
  445. // id.push(href.replace(/.*illust_id=(\d+).*/, "$1") || "");
  446. id.push(href);
  447. }
  448. }
  449. }
  450. }
  451. // console.log(id);
  452. // console.groupEnd();
  453. return id;
  454. };
  455.  
  456. // 选中全部图片
  457. var ctrlSelectAll = function () {
  458. // console.group("ctrlSelectAll");
  459. var itemList = $class('_image-items').children;
  460.  
  461. if (itemList) {
  462. for (let i = 0; i < itemList.length; i++) {
  463. let index = itemList[i].children[0].children[0].children.length - 1;
  464. let bt = itemList[i].children[0].children[0].children[index];
  465. bt.checked = true;
  466. removeClassName(itemList[i].children[0], 'cmheia_item_unselect');
  467. }
  468. }
  469. // console.groupEnd();
  470. };
  471.  
  472. // 反选
  473. var ctrlSelectInvert = function () {
  474. // console.group("ctrlSelectInvert");
  475. var itemList = $class('_image-items').children;
  476.  
  477. if (itemList) {
  478. for (let i = 0; i < itemList.length; i++) {
  479. let index = itemList[i].children[0].children[0].children.length - 1;
  480. let bt = itemList[i].children[0].children[0].children[index];
  481. let x = bt.checked;
  482. bt.checked = !x;
  483. toggleClassName(itemList[i].children[0], 'cmheia_item_unselect');
  484. }
  485. }
  486. // console.groupEnd();
  487. };
  488.  
  489. // 提取指定页面
  490. var fetchPageContent = function (arr, prefix, onload, onerror, referer) {
  491. // console.group('fetchPageContent');
  492. // console.warn('Referer :', referer);
  493.  
  494. for (let i of arr) {
  495. // 听说闭包很深奥,那就多练练
  496. var target = i.replace(/.*illust_id=(\d+).*/, "$1");
  497. // console.log(prefix + i);
  498. GM_xmlhttpRequest({
  499. method : 'GET',
  500. url : prefix + i,
  501. headers: {
  502. 'Referer': referer
  503. },
  504. onload : (function (xhr) {
  505. var id = target;
  506. return function (xhr) {
  507. onload(id, xhr);
  508. };
  509. })(),
  510. onerror: (function (xhr) {
  511. var id = target;
  512. return function (xhr) {
  513. onerror(id, xhr);
  514. };
  515. })()
  516. });
  517. }
  518. // console.groupEnd();
  519. };
  520.  
  521. // 从 html 源码提取原图链接
  522. // 先尝试作为单图解析,解析失败再作为图集解析,解析再次失败再作为动图解析
  523. // 返回:
  524. // 单图 -> 原图链接(57565823);
  525. // -> html 中包含字符串 "original-image"
  526. // 多图 -> 包含原图的目标页面链接(第二个参数为此而生)(56207143);
  527. // -> html 中包含字符串 "multiple"
  528. // 动图 -> 原图压缩包链接(44588377,56083603)(动图仅包含单个 zip , 使用与单图相同的方法处理)
  529. // -> html 中包含字符串 "ugoira_view"
  530. var parseWorkPage = function (html, url) {
  531. // console.group("parseWorkPage");
  532. // 2016-07-18 更新特征:
  533. // 单图 -> 原图链接(57565823);
  534. // -> html 中包含字符串 "original-image"
  535. // -> document.querySelector('.works_display').innerHTML.indexOf('manga') === -1
  536. // -> html 中仅字符串 "manga" 仅出现一次 <meta name="keywords" content="pixiv,插画,漫画,manga,社区,SNS投稿,比赛">
  537. // -> 即 XMLHttpRequest.responseText.match(/manga/gi).length === 1
  538. // 多图 -> 包含原图的目标页面链接(第二个参数为此而生)(56207143);
  539. // -> html 中包含字符串 "multiple"
  540. // -> html 中仅字符串 "manga" 仅出现一次 <meta name="keywords" content="pixiv,插画,漫画,manga,社区,SNS投稿,比赛">
  541. // -> document.querySelector('.works_display').innerHTML.indexOf('manga') !== -1
  542. // -> 即 XMLHttpRequest.responseText.match(/manga/gi).length > 1
  543. // 动图 -> 原图压缩包链接(44588377,56083603)(动图仅包含单个 zip , 使用与单图相同的方法处理)
  544. // -> html 中包含字符串 "ugoira_view"
  545. // -> document.querySelector('.works_display').innerHTML.indexOf('manga') === -1
  546. // -> html 中仅字符串 "manga" 仅出现一次 <meta name="keywords" content="pixiv,插画,漫画,manga,社区,SNS投稿,比赛">
  547. // -> 即 XMLHttpRequest.responseText.match(/manga/gi).length === 1
  548.  
  549. // 实例:
  550. // 单图
  551. // <div class="works_display"><div class="_layout-thumbnail ui-modal-trigger"><img src="http://i1.pixiv.net/c/600x600/img-master/img/2015/01/23/12/29/40/xxxxxxxx_p0_master1200.jpg" alt="Верный"></div></div>
  552. // 多图(伪)
  553. // <div class="works_display"><a href="member_illust.php?mode=big&amp;illust_id=xxxxxxxx" target="_blank" class=" _work manga "><div class="_layout-thumbnail"><img src="http://i3.pixiv.net/c/600x600/img-master/img/2015/11/13/20/05/08/xxxxxxxx_p0_master1200.jpg" alt="COMITIA114"></div></a></div>
  554. // 多图(真)
  555. // <div class="works_display"><a href="member_illust.php?mode=manga&amp;illust_id=xxxxxxxx" target="_blank" class=" _work multiple "><div class="_layout-thumbnail"><div class="multiple"><i class="_icon-20 _icon-files"></i></div><img src="http://i3.pixiv.net/c/600x600/img-master/img/2016/07/15/20/47/58/xxxxxxxx_p0_master1200.jpg" alt="シャロ生誕祭"></div></a></div>
  556. // 动图
  557. // <div class="works_display"><div class="_ugoku-illust-player-container ready playing"><div class="wrapper"><div class="_spinner"></div><div class="player toggle"><canvas width="477.326968973747" height="600" style="width: 477.327px; height: 600px;"></canvas></div><a href="/member_illust.php?mode=ugoira_view&amp;illust_id=xxxxxxxx" target="_blank" class="full-screen _ui-tooltip" data-tooltip="全屏显示"><img src="http://source.pixiv.net/www/images/ugoku-illust/full-screen.png?2" width="20" height="20"></a></div></div><div class="_full-screen-container"><div class="_ugoku-illust-player-container"><div class="wrapper toggle"><div class="_spinner"></div><div class="player"></div></div><div class="exit-full-screen"><img src="http://source.pixiv.net/www/images/ugoku-illust/exit-full-screen.png" width="30" height="30"></div></div></div></div>
  558.  
  559. // 对应正则:
  560. // /<div[^<>]*class=\"works_display\">[^<>]*<(\w*)[^<>]*class=\"([\w\s\-\_]*)\"[^<>]*>/
  561. // /<div[^<>]*class[^<>]*=[^<>]*\"[^<>]*works_display[^<>]*\">[^<>]*<(\w*)[^<>]*class[^<>]*=[^<>]*\"([\w\s\-\_]*)\"[^<>]*>/
  562.  
  563. // 上述实例 match 结果:
  564. // 单图
  565. // ["<div class="works_display"><div class="_layout-thumbnail ui-modal-trigger">", "div", "_layout-thumbnail ui-modal-trigger"]
  566. // 多图(伪)
  567. // ["<div class="works_display"><a href="member_illust.php?mode=big&amp;illust_id=xxxxxxxx" target="_blank" class=" _work manga ">", "a", " _work manga "]
  568. // 多图(真)
  569. // ["<div class="works_display"><a href="member_illust.php?mode=manga&amp;illust_id=xxxxxxxx" target="_blank" class=" _work multiple ">", "a", " _work multiple "]
  570. // 动图
  571. // ["<div class="works_display"><div class="_ugoku-illust-player-container ready playing">", "div", "_ugoku-illust-player-container ready playing"]
  572. var result = [],
  573. target = html.match(/<div[^<>]*class=\"works_display\">[^<>]*<(\w*)[^<>]*class=\"([\w\s\-\_]*)\"[^<>]*>/);
  574.  
  575. if (target && 3 === target.length) {
  576. // 先尝试用类名判断
  577. if (-1 !== target[2].indexOf("trigger")) {
  578. // 单图
  579. target = html.match(/<img\s+alt=\"[^\"]*\".*data-src=\"([^\"]*)\".*class=\"original-image\">/);
  580. if (target && target[1]) {
  581. result.push(target[1]);
  582. }
  583. // console.log("单图", result);
  584. // console.log(target);
  585. } else if (-1 !== target[2].indexOf("multiple")) {
  586. // 多图(真)
  587. target = html.match(/<ul class=\"meta\"><li>[^<>]*<\/li><li>[^<>\d]*(\d+)P<\/li>/);
  588. if (target && target[1]) {
  589. let count = parseInt(target[1]);
  590. result.push(count);
  591. for (let i = 0; i < count; i++) {
  592. let link = url.replace(/medium/, "manga_big");
  593. link = `${link}&page=${i}`;
  594. result.push(link);
  595. }
  596. }
  597. // console.log("多图(真)", result);
  598. // console.log(target);
  599. } else if (-1 !== target[2].indexOf("_ugoku")) {
  600. // 动图
  601. target = html.match(/pixiv\.context\.ugokuIllustFullscreenData[\s]*=[\s]*\{[^}]*\"src\"[\s]*:[\s]*\"((http|https):[\\\/]*[\w\d\.]*pximg\.net(.*)\/(\d+)_ugoira(\d+)x(\d+)\.zip)\"/);
  602. if (target && target[1]) {
  603. result[0] = target[1].replace(/\\(.)/gi, '$1');
  604. }
  605. // console.log("动图", result);
  606. } else if (-1 !== target[2].indexOf("manga")) {
  607. // 多图(伪)
  608. result.push(1);
  609. result.push(url.replace(/medium/, "big"));
  610. // http://www.pixiv.net/member_illust.php?mode=big&illust_id=53517282
  611. // 这个链接直接打开会被302导致失败
  612. // 需要设置 Referer
  613. // console.log("多图(伪)", result);
  614. // console.log(target);
  615. } else {
  616. // 未知
  617. // console.error("错误:未知类型", target);
  618. }
  619. } else {
  620. // 不行再靠老一套
  621. target = html.match(/<img\s+alt=\"[^\"]*\".*data-src=\"([^\"]*)\".*class=\"original-image\">/);
  622. if (target && target[1]) {
  623. // 单图
  624. result[0] = target[1];
  625. // console.log("单图", result);
  626. } else if (-1 !== html.indexOf("multiple") && (target = html.match(/<ul class=\"meta\"><li>[^<>]*<\/li><li>[^<>\d]*(\d+)P<\/li>/)) && target && target[1]) {
  627. // 根据 meta 判断遇到作者使用多图模式发表单张图片会失败
  628. // meta === "一次性投稿多张作品 "(\d+)"P"
  629. // 多图
  630. // http://www.pixiv.net/member_illust.php?mode=manga_big&illust_id=xxxxxxxx&page=0
  631. let count = parseInt(target[1]);
  632. result.push(count);
  633. for (let i = 0; i < count; i++) {
  634. let link = url.replace(/medium/, "manga_big");
  635. link = `${link}&page=${i}`;
  636. result.push(link);
  637. }
  638. // console.log("多图", result, target);
  639. } else if (html.match(/manga/gi).length > 1) {
  640. // 多图模式的单图
  641. result.push(1);
  642. result.push(url.replace(/medium/, "big"));
  643. // http://www.pixiv.net/member_illust.php?mode=big&illust_id=53517282
  644. // 这个链接直接打开会被302导致失败
  645. // 需要设置 Referer
  646. // console.log("多图模式的单图", result, target);
  647. } else if (-1 !==html.indexOf("ugoira_view") && (target = html.match(/pixiv\.context\.ugokuIllustFullscreenData[\s]*=[\s]*\{[^}]*\"src\"[\s]*:[\s]*\"((http|https):[\\\/]*[\w\d\.]*pixiv\.net(.*)\/(\d+)_ugoira(\d+)x(\d+)\.zip)\"/)) && target && target[1]) {
  648. // 动图
  649. // http://www.pixiv.net/member_illust.php?mode=medium&illust_id=xxxxxxxx
  650. result[0] = target[1].replace(/\\(.)/gi, '$1');
  651. // console.log("动图", result[0]);
  652. } else {
  653. // console.error("错误:未知类型", target);
  654. }
  655. }
  656.  
  657. // console.groupEnd();
  658. return result;
  659. };
  660.  
  661. // 提取选定的原图
  662. var extractWorkList = function (url) {
  663. // console.group("开始提取");
  664. var exportImages = function () {
  665. if (result.isAllDone()) {
  666. var info,
  667. arr,
  668. res = result.exportAll();
  669.  
  670. // console.log("已采集原图:", res.done);
  671. // console.log("提取失败: ", res.fail);
  672. info = "搞到 " + res.done.length + " 张图啦 (⺻▽⺻ )";
  673. arr = result.getID();
  674. for (let i = res.fail.length - 1; i >= 0; i--) {
  675. if (0 === res.fail[i]) {
  676. arr.splice(i, 1);
  677. }
  678. }
  679. if (arr.length) {
  680. info += " 然而" + arr.toString() + "提取失败 (ಥ_ಥ)";
  681. }
  682. msg(info);
  683. GM_setClipboard(res.done.join("\r\n"));
  684. }
  685. };
  686.  
  687. var recordFails = function (illustId, status) {
  688. // console.error(illustId, "提取失败", status);
  689. msg(illustId + "提取失败 (ಥ_ಥ) [http " + status + "]");
  690. result.recordTargetLength(illustId, -1);
  691. result.setTarget(illustId, null, 0, -1);
  692. };
  693.  
  694. var progress = 0,
  695. result;
  696.  
  697. if (0 === url.length) {
  698. msg("至少选择一张图吧 ◔ ‸◔?");
  699. // console.groupEnd();
  700. return;
  701. }
  702. msg("正在赶工 (๑•̀_•́๑)");
  703. // console.log("添加目标", url);
  704. result = new illustCollector();
  705. for (let i = 0; i < url.length; i++) {
  706. result.addTarget(url[i].replace(/.*illust_id=(\d+).*/, "$1"));
  707. }
  708. fetchPageContent(url,
  709. window.location.origin,
  710. function (illustId, xhr) {
  711. // console.group("得到页面", illustId, ", 开始解析", illustId == xhr.finalUrl.replace(/.*illust_id=(\d+).*/, "$1"));
  712. progress++;
  713. msg("进度" + progress + "/" + url.length + " (ฅ´ω`ฅ)");
  714. // console.warn("进度" + progress + "/" + url.length + " (ฅ´ω`ฅ)");
  715.  
  716. if (200 === xhr.status) {
  717. // 解析页面取得原图链接(单图和动图)或新的目标页面链接(多图)
  718. let target = parseWorkPage(xhr.responseText, xhr.finalUrl);
  719. if (target) {
  720. // 记录原图数量
  721. if (1 === target.length) {
  722. result.recordTargetLength(illustId, 1);
  723. // 单图和动图可立即取得原图链接,那就顺手录入,并标记为已完成
  724. let i = result.setTarget(illustId, target[0], 0, 1);
  725. // console.log("记录单图或动图", i);
  726. // msg("到手" + parsed + "页,就剩" + (url.length - parsed) + "页啦 (ฅ´ω`ฅ)");
  727. } else {
  728. var count = target.shift();
  729. result.recordTargetLength(illustId, count);
  730. // 多图需要再次解析链接
  731. // console.warn("多图需要再次解析链接", target);
  732. if (1 === count && 1 === target.length) { // 伪多图
  733. result.setTarget(illustId, target[0], 0, 0);
  734. // console.warn("伪多图", target[0]);
  735. } else {
  736. for (let i = 0; i < count; i++) {
  737. result.setTarget(illustId, target[i], i, 0);
  738. // console.log(target[i]);
  739. }
  740. }
  741. parseMultiImageUrl(target, function (obj) {
  742. // console.warn("n:callback", obj);
  743. // console.log("搞完这 ", count, " 张图啦 (⺻▽⺻ )");
  744. for (let i = 0; i < count; i++) {
  745. let status = (undefined !== obj.done[i] && undefined === obj.fail[i]) ? 1 : -1;
  746. result.setTarget(illustId, obj.done[i], i, status);
  747. }
  748. exportImages();
  749. });
  750. }
  751. } else {
  752. recordFails(illustId, xhr.status);
  753. }
  754. } else {
  755. recordFails(illustId, xhr.status);
  756. }
  757. exportImages();
  758. // console.groupEnd();
  759. },
  760. function (illustId, xhr) {
  761. // console.group("页面", illustId, ", 获取失败", illustId == xhr.finalUrl.replace(/.*illust_id=(\d+).*/, "$1"));
  762. progress++;
  763. msg("进度" + progress + "/" + url.length + " (ฅ´ω`ฅ)");
  764.  
  765. recordFails(illustId, xhr.status);
  766. exportImages();
  767. // console.groupEnd();
  768. },
  769. window.location.href
  770. );
  771. // console.groupEnd();
  772. };
  773.  
  774. // 添加按钮
  775. var addButtonWorkList = function () {
  776. // console.group("addButtonWorkList");
  777. var itemList = $class("_image-items");
  778.  
  779. if (itemList) {
  780. let button,
  781. menu = $class('menu-items');
  782.  
  783. // 全选按钮
  784. button = document.createElement('li');
  785. button.innerHTML="<a href='javascript:;'>全选</a>";
  786. button.addEventListener("click", function () {
  787. ctrlSelectAll();
  788. });
  789. menu.appendChild(button);
  790.  
  791. // 反选按钮
  792. button = document.createElement('li');
  793. button.innerHTML="<a href='javascript:;'>反选</a>";
  794. button.addEventListener("click", function () {
  795. ctrlSelectInvert();
  796. });
  797. menu.appendChild(button);
  798.  
  799. // 导出按钮
  800. button = document.createElement('li');
  801. button.innerHTML="<a href='javascript:;'>收割 ๑乛◡乛๑ (●´∀`●)</a>";
  802. button.addEventListener("click", function () {
  803. extractWorkList(extractIllustUrl());
  804. });
  805. menu.appendChild(button);
  806.  
  807. // 消息显示区域
  808. button = document.createElement('li');
  809. button.innerHTML="<span id='extracted'></span>";
  810. menu.appendChild(button);
  811.  
  812. // 添加复选框
  813. addStyle();
  814. for (let i = 0; i < itemList.children.length; i++) {
  815. button = document.createElement('input');
  816. button.type = "checkbox";
  817. button.className = "cmheia_checkbox";
  818. button.checked = true;
  819. button.setAttribute('data-index', i);
  820. // a
  821. // 删除原先的链接
  822. // console.log(itemList.children[i].children[0]);
  823. itemList.children[i].children[0].setAttribute('href', '#');
  824. // 增加背景
  825. itemList.children[i].children[0].setAttribute('style', 'margin-bottom:0;');
  826. addClassName(itemList.children[i].children[0], 'cmheia_item');
  827. // addClassName(itemList.children[i].children[0], 'cmheia_item_unselect');
  828. // div
  829. // 增加点击事件
  830. itemList.children[i].children[0].children[0].appendChild(button);
  831. itemList.children[i].children[0].addEventListener("click", function (e) {
  832. // 点击图片切换选中状态
  833. let index = this.children[0].children.length - 1;
  834. let bt = this.children[0].children[index];
  835. // console.log(index);
  836. // console.log(bt);
  837. // console.log('点击图片切换' + (bt.checked ? '未' : '') + '选中状态');
  838. bt.checked = !bt.checked;
  839. toggleClassName(this, 'cmheia_item_unselect');
  840. });
  841. }
  842. }
  843. // console.groupEnd();
  844. };
  845.  
  846. /**********************************************************************
  847. * 作品页面功能
  848. **********************************************************************/
  849. // 移除分享按钮
  850. var removeShareButton = function () {
  851. var shareButton = $class('share-link-container'),
  852. count = shareButton.children.length;
  853. for (let i = 0; i < count; i++) {
  854. shareButton.removeChild(shareButton.children[0]);
  855. }
  856. };
  857.  
  858. // 添加导出按钮
  859. var addButtonWorkPage = function () {
  860. var button = document.createElement('li');
  861. button.innerHTML="<a href='javascript:;' style='margin:0 8px;'>收割 ๑乛◡乛๑ (●´∀`●)</a>";
  862. button.addEventListener("click", function () {
  863. extractWorkList([window.location.pathname + window.location.search]);
  864. });
  865. $class('share-link-container').appendChild(button);
  866.  
  867. button = document.createElement('li');
  868. button.innerHTML="<span id='extracted'></span>";
  869. $class('share-link-container').appendChild(button);
  870. };
  871.  
  872. // 初始化作品列表界面
  873. var postInitWorksListUI = function () {
  874. let itemList = $class("_image-items");
  875.  
  876. if (itemList) {
  877. if (1 === itemList.children.length && "" === itemList.children[0].className) {
  878. // <li>未找到任何相关结果</li>
  879. // console.log("未找到任何相关结果");
  880. } else {
  881. addButtonWorkList();
  882. document.addEventListener("keyup", function (event) {
  883. // F9 = 120
  884. if (120 === event.keyCode) {
  885. extractWorkList(extractIllustUrl());
  886. }
  887. }, true);
  888. }
  889. }
  890. console.warn("inited");
  891. };
  892.  
  893. // 初始化作品列表界面
  894. var initWorksListUI = function () {
  895. var DOMObserverTimer = false;
  896. var DOMObserverConfig = {
  897. childList : true,
  898. subtree : true,
  899. };
  900. var DOMObserver = new MutationObserver(function () {
  901. if (DOMObserverTimer !== 'false') {
  902. clearTimeout(DOMObserverTimer);
  903. }
  904. DOMObserverTimer = setTimeout(function () {
  905. if (!$id("extracted")) {
  906. DOMObserver.disconnect();
  907. postInitWorksListUI();
  908. }
  909. }, 100);
  910. });
  911. DOMObserver.observe(document.querySelector('.image-item'), DOMObserverConfig);
  912. };
  913.  
  914. // 初始化作品界面
  915. var initWorkPageUI = function () {
  916. removeShareButton();
  917. addButtonWorkPage();
  918. document.addEventListener("keyup", function (event) {
  919. // F9 = 120
  920. if (120 === event.keyCode) {
  921. extractWorkList([window.location.pathname + window.location.search]);
  922. }
  923. }, true);
  924. console.warn("inited");
  925. };
  926.  
  927. var initPEUI = function () {
  928. if (isWorksList()) {
  929. initWorksListUI();
  930. } else {
  931. initWorkPageUI();
  932. }
  933. };
  934.  
  935. // 运行
  936. console.warn("P站原图收割机:开始");
  937. initPEUI();
  938. }) ();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址