Pixiv Lazy plus

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

当前为 2015-05-12 提交的版本,查看 最新版本

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