Pixiv Direct Links

Turns thumbnail titles into direct links and disables lazy-loading on ranking pages.

  1. // ==UserScript==
  2. // @name Pixiv Direct Links
  3. // @namespace https://greasyfork.org/scripts/4555
  4. // @description Turns thumbnail titles into direct links and disables lazy-loading on ranking pages.
  5. // @include *://www.pixiv.net/*
  6. // @grant none
  7. // @version 2019.10.13
  8. // ==/UserScript==
  9.  
  10. //Disable lazy loading images. These appear on ranking pages and the "Recommended" section of the bookmarks page.
  11. var dontSayLazy = true;
  12.  
  13. //----------------------------------------------------------------//
  14.  
  15. if( window == window.top )//not inside iframe
  16. {
  17. //Link dem titles.
  18. linkThumbTitles(document);
  19. new MutationObserver( function(mutationSet)
  20. {
  21. mutationSet.forEach( function(mutation)
  22. {
  23. for( let i = 0; i < mutation.addedNodes.length; i++ )
  24. if( mutation.addedNodes[i].nodeType == Node.ELEMENT_NODE )
  25. linkThumbTitles( mutation.addedNodes[i] );
  26. } );
  27. }).observe( document.body, { childList:true, subtree:true } );
  28.  
  29. if( dontSayLazy && unlazyImage() )
  30. {
  31. //Initial page has lazy images; listen for more images added later
  32. new MutationObserver( function(mutationSet)
  33. {
  34. mutationSet.forEach( function(mutation)
  35. {
  36. for( let i = 0; i < mutation.addedNodes; i++ )
  37. if( mutation.addedNodes[i].nodeType == Node.ELEMENT_NODE )
  38. unlazyImage( mutation.addedNodes[i] );
  39. } );
  40. }).observe( document.body, { childList:true, subtree:true } );
  41. }
  42. }
  43.  
  44. //----------------------------------------------------------------//
  45.  
  46. function unlazyImage(target)
  47. {
  48. let images = ( target || document ).querySelectorAll("img[data-src]");
  49. for( let i = 0; i < images.length; i++ )
  50. images[i].src = images[i].getAttribute("data-src");
  51. return images.length;
  52. }
  53.  
  54. function linkThumbTitles(target)
  55. {
  56. //bookmark_new_illust.php, discovery, search.php
  57. let foundTitle = target.querySelectorAll("li a[href*='/artworks/'][title]");
  58. for( let j = 0; j < foundTitle.length; j++ )
  59. directLinkSingle( foundTitle[j] );
  60. //member.php, member_illust.php, new_illust.php, artworks (related works)
  61. foundTitle = target.querySelectorAll("li > div > a[href*='/artworks/']");
  62. for( let j = 0; j < foundTitle.length; j++ )
  63. directLinkSingle( foundTitle[j] );
  64. //main page
  65. foundTitle = target.querySelectorAll("li > a[href*='/artworks/'] > .title");
  66. for( let j = 0; j < foundTitle.length; j++ )
  67. directLinkSingle( foundTitle[j].parentNode );
  68. //bookmark.php
  69. foundTitle = target.querySelectorAll("li > a[href*='mode=medium'][href*='illust_id='] > .title");
  70. for( let j = 0; j < foundTitle.length; j++ )
  71. directLinkSingle( foundTitle[j].parentNode );
  72.  
  73. //ranking.php
  74. foundTitle = target.querySelectorAll("a.title[href*='/artworks/']");
  75. for( let j = 0; j < foundTitle.length; j++ )
  76. directLinkSingle( foundTitle[j] );
  77. //response.php
  78. let image = target.querySelectorAll("li a[href*='mode=medium'][href*='illust_id='] img");
  79. for( let j = 0; j < image.length; j++ )
  80. {
  81. let page, title;
  82. for( page = image[j].parentNode; page.tagName != "A"; page = page.parentNode );
  83.  
  84. //The prev/next thumbnails on mode=medium pages have text before/after the image. Text also follows the image on image responses listings.
  85. if( !(title = page.getElementsByClassName("title")[0]) && (title = page.lastChild).nodeName != '#text' && (title = page.firstChild).nodeName != '#text' )
  86. continue;//Can't find title element
  87.  
  88. //Start title link at mode=medium and change later.
  89. let titleLink = document.createElement("a");
  90. titleLink.href = page.href;
  91. titleLink.style.color = "#333333";//Style used on some pages
  92.  
  93. //Move the title out of the thumbnail link
  94. page.removeChild(title);
  95. titleLink.appendChild(title);
  96. page.parentNode.insertBefore( titleLink, page.nextSibling );
  97.  
  98. directLinkSingle( titleLink );
  99. }
  100. }
  101.  
  102. function directLinkSingle(titleLink)
  103. {
  104. let illustID;
  105. if( !titleLink || !titleLink.href )
  106. return;
  107. if( !(illustID = titleLink.href.match(/\/artworks\/(\d+)/)) && !(illustID = titleLink.href.match(/illust_id=(\d+)/)) )
  108. return;
  109. let req = new XMLHttpRequest();
  110. req.open( "GET", location.protocol+"//www.pixiv.net/artworks/"+illustID[1], true );
  111. req.onload = function()
  112. {
  113. let scripts = req.responseXML.head.getElementsByTagName("script");
  114. for( let i = 0; i < scripts.length; i++ )
  115. {
  116. //JSON requires double quotes around property names, forbids trailing commas, etc... The illust info can't be simply parsed as a raw JSON object, so just grab the values we need.
  117. let originalURL = scripts[i].textContent.match(/"original":"(http[^"]+)"/);
  118. if( !originalURL )
  119. continue;
  120. //Do nothing for ugoira (animated) images
  121. if( originalURL.indexOf("ugoira") > 0 )
  122. return;
  123. //There are several pageCount properties; we just want the last one.
  124. let pageCount = 0, rCount = RegExp( '"pageCount": *(\\d+)', 'g' ), search;
  125. while( (search = rCount.exec( scripts[i].textContent )) !== null )
  126. pageCount = parseInt(search[1]);
  127.  
  128. //Only link single images
  129. if( pageCount <= 1 )
  130. titleLink.href = originalURL[1].replace(/\\\//g,'/');
  131.  
  132. return;
  133. }
  134. //console.log("Unable to find direct image link for illust #"+illustID[1]);
  135. };
  136. req.responseType = "document";
  137. req.send(null);
  138. }