Pixiv Lazy plus

provide a direct link to original image ([s] link).

当前为 2015-10-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Pixiv Lazy plus
  3. // @namespace pixivlazyplus
  4. // @description provide a direct link to original image ([s] link).
  5. // @version 0.9.0.9
  6. // @include http://www.pixiv.net/*
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_log
  11. // @grant GM_registerMenuCommand
  12. // ==/UserScript==
  13. // version 0.9.0.9 - fix for elimination of public API me.json
  14. // version 0.9.0.8 - add Auto Login menu item
  15. // version 0.9.0.7 - more fix of cookie issue
  16. // version 0.9.0.6 - fix cookie issue
  17. // version 0.9.0.5 - fix ugoira link again
  18. // version 0.9.0.4 - restore MangaFull function
  19. // version 0.9.0.3 - fix clearing accessToken logic, add function to check if accessToken is still valid
  20. // version 0.9.0.2 - fix ugoira link
  21. // version 0.9.0.1 - disable MangaFull since pixiv checks referer for full size manga pages now.
  22. // version 0.9 - upgrade to OAuth API + Pixiv Public API
  23. // version 0.8.14.1 - fix new medium page layout
  24. // version 0.8.13 - fix image response lazy link position
  25. // version 0.8.12 - fix mypage right side lazy links
  26. // version 0.8.11 - fix double links in medium page
  27. // version 0.8.10 - fix double links in medium page
  28. // version 0.8.9 - append [U] link after canvas
  29. // version 0.8.8 - fix loading big manga URL bug introduced in 0.8.7
  30. // version 0.8.7 - fix loading big manga URL in with PNG format
  31. // version 0.8.6 - fix modifying big manga URL in manga page
  32. // version 0.8.5 - fix preloading new manga URL format
  33. // version 0.8.4 - fix detecting URL new format
  34. // version 0.8.3 - fix detecting URL new format
  35. // version 0.8.2 - fix detecting URL new format
  36. // version 0.8.1 - fix detecting ugoira in prev/next
  37. // version 0.8.0 - add ability for downloading ugoira as zip
  38. // version 0.7.9 - fix for no profile image
  39. // version 0.7.8 - fix for spapi return value changes
  40. // version 0.7.7 - change [s] to [M](go directly to manga page) for manga links
  41. // version 0.7.6 - fix prev/next [s] link
  42. // version 0.7.5 - fix new manga page
  43. // version 0.7.4 - fix modified images
  44. // version 0.7.3 - fix missing session ID when it is not the end of cookie
  45. // version 0.7.2 - fix comma in tags breaking parsing logic
  46. // version 0.7.1 - fix iPhone API by supplying session ID
  47. // version 0.7 - work with new sample images with iPhone API, fix old manga
  48. // version 0.6.1 - preload manga images
  49. // version 0.6 - change manga mode to big images
  50. // version 0.5 - remove [b] link, add stylish style class
  51. // version 0.4 - updated to filter new thumbnails
  52. // version 0.3 - fix a bug, hat-tip to syosyo
  53. // version 0.2 - updated on 2008-06-25
  54. var pixivlink_run = 0;
  55. var isNewManga = 1;
  56. var postProcImg = new Array();
  57. var preloadImg = new Array();
  58. var Imgs = new Array();
  59. var sessID1 = (/PHPSESSID=[0-9a-f]*?(?=;|$)/.exec(document.cookie) || "");
  60. var sessID2 = (/PHPSESSID=[^;]*?(?=;|$)/.exec(document.cookie) || "");
  61. var accessToken = GM_getValue("accessToken","");
  62. var oauthUserName = GM_getValue("userName","");
  63. var oauthPassword = GM_getValue("passWord","");
  64. var autoLogin = GM_getValue("autoLogin",false);
  65. var mangaFormat = 'jpg';
  66.  
  67. /*
  68. String.prototype.splitCSV = function(sep) {
  69. for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
  70. if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
  71. if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
  72. foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
  73. } else if (x) {
  74. foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
  75. } else foo = foo.shift().split(sep).concat(foo);
  76. } else foo[x].replace(/""/g, '"');
  77. } return foo;
  78. };*/
  79.  
  80. function _e(id) { return document.getElementById(id); }
  81.  
  82. function getElementsByClassName(matchClass) {
  83. var clselems = new Array();
  84. var elems = document.getElementsByTagName('*'), i;
  85. for (i in elems) {
  86. if((' ' + elems[i].className + ' ').indexOf(' ' + matchClass + ' ')
  87. > -1) {
  88. clselems.push(elems[i]);
  89. }
  90. }
  91. return clselems;
  92. }
  93.  
  94. function createCookie(cookietext,days, domain) {
  95. if (days) {
  96. var date = new Date();
  97. date.setTime(date.getTime()+(days*24*60*60*1000));
  98. var expires = "; expires="+date.toGMTString();
  99. }
  100. else var expires = "";
  101. if (domain) domain = "; domain="+domain;
  102. else domain = "";
  103. document.cookie = cookietext+expires+domain+"; path=/";
  104. }
  105.  
  106. function doOAuth(obj) {
  107. var oauthUser, oauthPass, returnData;
  108. oauthUser = _e("oauthUsername").value;
  109. oauthPass = _e("oauthPassword").value;
  110. createCookie("PHPSESSID=",-1,""); // erase cookie first
  111. createCookie("PHPSESSID=",-1,".pixxiv.net"); // erase cookie first
  112. if(oauthUser && oauthPass) {
  113. GM_setValue("userName",oauthUser);
  114. GM_setValue("passWord",oauthPass);
  115. GM_xmlhttpRequest({
  116. url: 'https://oauth.secure.pixiv.net/auth/token',
  117. method: "POST",
  118. headers: {
  119. Referer: "http://www.pixiv.net",
  120. "Content-Type": "application/x-www-form-urlencoded"
  121. },
  122. data: "username="+oauthUser+"&password="+oauthPass+"&grant_type=password&client_id=bYGKuGVw91e0NMfPGp44euvGt59s&client_secret=HP3RmkgAmEGro0gn1x9ioawQE8WMfvLXDz3ZqxpK",
  123. onload: function (response) {
  124. if (response.status == 200) {
  125. returnData = JSON.parse(response.responseText);
  126. accessToken = returnData.response.access_token;
  127. GM_setValue("accessToken",accessToken);
  128. if(!autoLogin) alert("Login Sucessfully.");
  129. //createCookie(sessID1,0,""); // restore cookie first
  130. createCookie(sessID2,0,".pixiv.net"); // restore cookie first
  131. location.reload();
  132. } else {
  133. //createCookie(sessID1,0,""); // restore cookie first
  134. createCookie(sessID2,0,".pixiv.net"); // restore cookie first
  135. GM_setValue("autoLogin",false); // clear autologin flag
  136. alert("Login Failed!");
  137. }
  138. }
  139. });
  140. }
  141. }
  142.  
  143. function createOAuthLoginForm() {
  144. var tmp, newHTML = document.createElement ('div');
  145. newHTML.id = 'gmOAuthLoginFrm';
  146. newHTML.style.position='absolute';
  147. newHTML.style.backgroundColor='#fff';
  148. newHTML.style.top='0';
  149. newHTML.style.left='0';
  150. newHTML.style.zIndex='100';
  151. newHTML.appendChild(document.createTextNode('OAuth Login User: '));
  152. tmp = document.createElement ('input');
  153. tmp.id = 'oauthUsername';
  154. tmp.type = 'text';
  155. tmp.value = oauthUserName;
  156. newHTML.appendChild(tmp);
  157. newHTML.appendChild(document.createTextNode('Pass: '));
  158. tmp = document.createElement ('input');
  159. tmp.id = 'oauthPassword';
  160. tmp.type = 'password';
  161. tmp.value = oauthPassword;
  162. newHTML.appendChild(tmp);
  163. tmp = document.createElement ('input');
  164. tmp.id = 'oauthSubmit';
  165. tmp.type = 'button';
  166. tmp.value='Login';
  167. tmp.addEventListener("click", function(e){return doOAuth(e);}, false);
  168. newHTML.appendChild(tmp);
  169.  
  170. document.body.appendChild (newHTML);
  171. if(autoLogin) doOAuth(null);
  172. }
  173.  
  174. function checkTokenValid() {
  175. if(getElementsByClassName('not-logged-in').length) {
  176. createCookie("PHPSESSID=",-1,""); // erase oauth cookie
  177. _e('login_pixiv_id').value = oauthUserName;
  178. _e('login_password').value = oauthPassword;
  179. if(autoLogin) {
  180. autoLogin = false;
  181. var loginForm = _e('login_password').parentNode;
  182. do {
  183. if(loginForm.nodeName == 'FORM') {
  184. loginForm.submit();
  185. break;
  186. }
  187. } while(loginForm = loginForm.parentNode);
  188. }
  189. }
  190. GM_xmlhttpRequest({
  191. url: 'https://public-api.secure.pixiv.net/v1/me/feeds.json',
  192. method: "GET",
  193. headers: {
  194. Referer: "http://www.pixiv.net",
  195. "Authorization": "Bearer " + accessToken,
  196. "Cookie": sessID1
  197. },
  198. onload: function (response) {
  199. if (response.status == 200) {
  200. //GM_log(response.responseText);
  201. /* var vals = JSON.parse(response.responseText);
  202. if(vals.status == "success") {
  203. }*/
  204. } else /*if (response.status == 400)*/ {
  205. accessToken ="";
  206. GM_setValue("accessToken",accessToken);
  207. createOAuthLoginForm();
  208. }
  209. }
  210. });
  211. }
  212.  
  213. function GetImageIDFromLink(imageLink) {
  214. var imgID = 0; // If lower 11319936 it means Manga does not have Big version
  215.  
  216. var re = /\d+([_a-z0-9]+)?\.(jpe?g|gif|png)\??.*$|id=[0-9]+$/;
  217. var s = re.exec(imageLink);
  218. if (s && s.length > 0) {
  219. re = /\d+/;
  220. imgID = re.exec(s[0])[0];
  221. }
  222. return imgID;
  223. }
  224.  
  225. function pixivlink() {
  226. //alert(pixivlink_run);
  227. if (!pixivlink_run) pixivlink_run = 1;
  228. else return;
  229. checkTokenValid();
  230. var Items = document.getElementsByTagName('img');
  231. var rexa = /\?mode\=medium\&illust_id|\?mode\=big\&illust_id/;
  232. var rexb = /source.pixiv.net/;
  233. var rexc = /\/img-inf\//;
  234. var rexd = /\/mobile\//;
  235. var rexe = /\/c\//;
  236. for (var i = 0; i < Items.length; i++) {
  237. var imgR = Items[i];
  238. var aR = imgR.parentNode.parentNode;
  239. var aR2 = imgR.parentNode;
  240. var isThumbnail = ((' ' + aR2.className + ' ').indexOf(' _layout-thumbnail ') > -1) //&& ((' ' + aR2.className + ' ').indexOf(' ui-modal-trigger ') > -1)
  241. //GM_log('aR2.className = "'+aR2.className+'", imgR.src='+imgR.src);
  242. //GM_log('isThumbnail = '+isThumbnail);
  243. if (rexa.test(aR2.href)/* || isThumbnail*/) {
  244. aR = aR2;
  245. }
  246. if (rexa.test(aR.href) || isThumbnail) {
  247. var imgID = GetImageIDFromLink(imgR.src);
  248. var srcR = imgR.src.replace(/_s\.|_m\.|_100\.|_64x64\./i, ".");
  249. // var hrefR = aR.href.replace(/medium/i, "big");
  250. var tdR = aR.parentNode;
  251. /* var linkB = document.createElement('a');
  252. linkB.href = hrefR;
  253. linkB.target = '_blank';
  254. linkB.style.padding = '0 2px';
  255. linkB.className = '_pxlazy';
  256. linkB.appendChild(document.createTextNode('[b]'));
  257. tdR.appendChild(linkB);*/
  258. // tdR.appendChild(document.createTextNode(' '));
  259. if (!rexb.test(srcR) && Imgs.indexOf(imgID) == -1) {
  260. var linkS = document.createElement('a');
  261. linkS.href = srcR;
  262. linkS.target = '_blank';
  263. linkS.className = '_pxlazy _pxlazy_s';
  264. linkS.setAttribute('id', 'ill_' + imgID);
  265. linkS.appendChild(document.createTextNode('[s]'));
  266. if (aR2.tagName.toUpperCase() == 'DIV' && imgR.className.toLowerCase().indexOf('thumbnail') == -1) {
  267. var targetelem = getElementsByClassName('works_display');
  268. if(targetelem.length) {
  269. targetelem[0].appendChild(linkS);
  270. }
  271. } else {
  272. tdR.appendChild(linkS);
  273. }
  274. if (rexc.test(imgR.src)||rexd.test(imgR.src)||rexe.test(imgR.src)) {
  275. //GM_log("postProcImg.push("+imgID+")"+imgR.src);
  276. postProcImg.push(imgID);
  277. }
  278. Imgs.push(imgID);
  279. }
  280. }
  281. }
  282.  
  283. if (postProcImg.length > 0) {
  284. for (var x = 0; x < postProcImg.length; x++) {
  285. if(accessToken) GM_xmlhttpRequest({
  286. url: 'https://public-api.secure.pixiv.net/v1/works/' + postProcImg[x] + '.json?image_sizes=large',
  287. method: "GET",
  288. headers: {
  289. Referer: "http://www.pixiv.net",
  290. "Authorization": "Bearer " + accessToken,
  291. "Cookie": sessID1
  292. },
  293. onload: function (response) {
  294. if (response.status == 200) {
  295. var rexb = /source.pixiv.net/;
  296. // var rexU = /\/c\//;
  297. var rexU = /_ugoira/;
  298. var rexe = /\/c\//;
  299. var vals = JSON.parse(response.responseText);
  300. /*var vtxt = '';
  301. for(var x=0;x < vals.length;x++)
  302. vtxt=vtxt+x+':'+vals[x]+"\n";
  303. GM_log(vtxt);*/
  304. if (vals.status == "success") {
  305. var slnk, imgID, isRestricted;
  306. isRestricted = rexb.test(vals.response[0].image_urls.large);
  307. isUgoira = rexU.test(vals.response[0].image_urls.large);
  308. // if (!isRestricted) {
  309. //GM_log("imgID = vals[0]");
  310. imgID = vals.response[0].id;
  311. /* } else {
  312. //GM_log("GetImageIDFromLink("+response.finalUrl+")");
  313. imgID = GetImageIDFromLink(response.finalUrl);
  314. }*/
  315. slnk = _e('ill_' + imgID);
  316. if (slnk) {
  317. var goodSlink;
  318. if (vals.response[0].is_manga) {
  319. goodSlink = 'http://www.pixiv.net/member_illust.php?mode=manga&illust_id=' + imgID;
  320. slnk.innerHTML = '[M]';
  321. } else {
  322. var re = new RegExp('/' + imgID + '_.*$');
  323. if (isUgoira) {// grab zip!
  324. //GM_log("isUgoira");
  325. slnk.innerHTML = '[U]';
  326. goodSlink = vals.response[0].image_urls.large.replace(/img-original/, 'img-zip-ugoira').replace(re, '/' + imgID + '_ugoira1920x1080.zip');
  327. /* } else if (rexe.test(vals[9])) {// new 480mw URL
  328. //GM_log("480mw");
  329. goodSlink = vals[9].replace(/c\/480x960\//, '').replace(/img-master/, 'img-original').replace(re, '/' + vals[0] + '_p0.' + vals[2]);
  330. } else if (!isRestricted && !rexe.test(vals[9])) {// use 480mw instead
  331. //GM_log("480mw");
  332. goodSlink = vals[9].replace(/mobile\//, '').replace(re, '/' + vals[0] + '.' + vals[2]);
  333. } else { //salvage from profile image
  334. //GM_log("salvage");
  335. re = /\/[0-9_]+\..*$/;
  336. goodSlink = vals[29].replace(/mobile\//, '').replace(/profile\//, 'img/').replace(re, '/' + imgID + '.' + vals[2]);
  337. */
  338. } else {
  339. goodSlink = vals.response[0].image_urls.large;
  340. }
  341. }
  342. slnk.href = goodSlink;
  343. slnk.title=vals.response[0].reuploaded_time;
  344. slnk.className = '_pxlazy _pxlazy_s _pxlazy_s_new';
  345. }
  346. }
  347. } else if (response.status == 400) {
  348. accessToken ="";
  349. GM_setValue("accessToken",accessToken);
  350. }
  351. }
  352. });
  353. }
  354. }
  355.  
  356. var links = document.getElementsByTagName('a');
  357. var tagslink = /tags\.php\?tag=/;
  358. for (var i = 0; i < links.length; i++) {
  359. if (tagslink.test(links[i].href))
  360. links[i].href = links[i].href.replace("tags.php?tag=", "search.php?s_mode=s_tag_full&word=");
  361. }
  362.  
  363. if (unsafeWindow.pixiv && unsafeWindow.pixiv.context.images) {
  364. var illustID = GetImageIDFromLink(unsafeWindow.pixiv.context.images[0]/*[0]*/);
  365. isNewManga = (illustID >= 11319936);
  366. //GM_log("illustID="+illustID);
  367. GM_xmlhttpRequest({
  368. url: 'https://public-api.secure.pixiv.net/v1/works/' + illustID + '.json?image_sizes=large',
  369. method: "GET",
  370. headers: {
  371. Referer: "http://www.pixiv.net",
  372. "Authorization": "Bearer " + accessToken,
  373. "Cookie": sessID1
  374. },
  375. onload: function (response) {
  376. if (response.status == 200) {
  377. GM_log(response.responseText);
  378. var vals = JSON.parse(response.responseText);
  379. var rexe = /\/c\//;
  380. if(vals.status == "success") {
  381. mangaFormat = vals.response[0].metadata.pages[0].image_urls.large.split('.').pop();
  382. GM_log(mangaFormat);
  383. /*var vtxt = '';
  384. for(var x=0;x < vals.length;x++)
  385. vtxt=vtxt+x+':'+vals[x]+"\n";
  386. GM_log(vtxt);*/
  387. //GM_log("2nd");
  388. /*if (rexe.test(vals[9]))*/ mangaFull();
  389. }
  390. } else if (response.status == 400) {
  391. accessToken ="";
  392. GM_setValue("accessToken",accessToken);
  393. }
  394. }
  395. });
  396. var rexe = /\/c\//;
  397. //GM_log("isNewManga");
  398. //setTimeout(mangaFull,250);
  399. //GM_log("2nd");
  400. // mangaFull();
  401. }
  402. }
  403.  
  404. function mangaFull() {
  405. Items = document.getElementsByTagName('img');
  406. var rexe = /\/img-/;
  407. for (var x = 0; x < unsafeWindow.pixiv.context.images.length; x++) {
  408. if (isNewManga) {
  409. if(rexe.test(unsafeWindow.pixiv.context.images[x]))
  410. unsafeWindow.pixiv.context.images[x]/*[0]*/ = unsafeWindow.pixiv.context.images[x]/*[0]*/.replace(/c\/1200x1200\//, '').replace(/img-master/, 'img-original').replace(/_p(\d+).*(\.[a-zA-Z\?\d]+)$/, "_p$1."+mangaFormat);
  411. else
  412. unsafeWindow.pixiv.context.images[x]/*[0]*/ = unsafeWindow.pixiv.context.images[x]/*[0]*/.replace(/_p(\d+\.[a-zA-Z\?\d]+)$/, "_big_p$1");
  413. }
  414. preloadImg.push(new Image());
  415. preloadImg[preloadImg.length - 1].src = unsafeWindow.pixiv.context.images[x]/*[0]*/;
  416. }
  417.  
  418. for (var x = 0; x < Items.length; x++) {
  419. var datasrc = Items[x].getAttribute("data-src");
  420. if (datasrc) {
  421. //GM_log("original-datasrc="+datasrc+" ,mangaFormat="+mangaFormat);
  422. if(rexe.test(datasrc))
  423. datasrc = datasrc.replace(/c\/1200x1200\//, '').replace(/img-master/, 'img-original').replace(/_p(\d+).*(\.[a-zA-Z\?\d]+)$/, "_p$1."+mangaFormat);
  424. else
  425. datasrc = datasrc.replace(/_p(\d+\.[a-zA-Z\?\d]+)$/, "_big_p$1");
  426. //GM_log("new-datasrc="+datasrc+" ,mangaFormat="+mangaFormat);
  427. }
  428. Items[x].setAttribute("data-src", datasrc);
  429. if (isNewManga) {
  430. //GM_log(x+".src="+Items[x].src);
  431. if(datasrc)
  432. Items[x].src = datasrc;
  433. else if(rexe.test(Items[x].src))
  434. Items[x].src = Items[x].src.replace(/c\/1200x1200\//, '').replace(/img-master/, 'img-original').replace(/_p(\d+).*(\.[a-zA-Z\?\d]+)$/, "_p$1$2");
  435. else
  436. Items[x].src = datasrc ? datasrc : Items[x].src.replace(/_p(\d+\.[a-zA-Z\?\d]+)$/, "_big_p$1");
  437. }
  438. }
  439. }
  440.  
  441. if(!autoLogin) {
  442. GM_registerMenuCommand("Enable Auto Login", function(){
  443. GM_setValue("autoLogin",true);
  444. });
  445. } else {
  446. GM_registerMenuCommand("Disable Auto Login", function(){
  447. GM_setValue("autoLogin",false);
  448. });
  449. }
  450. window.addEventListener("load", pixivlink, true);