// ==UserScript==
// @name Pixiv Direct Links
// @namespace https://greasyfork.dpdns.org/scripts/4555
// @description Turns thumbnail titles into direct or mode=manga links, adds direct image links on mode=manga pages, replaces the medium thumbnail on mode=medium pages with the full size, and disables lazy-loading images.
// @include http://www.pixiv.net*
// @grant GM_xmlhttpRequest
// @version 2015.03.25
// ==/UserScript==
//Turn thumbnail titles into direct links (single images) or mode=manga links. May or may not work for a given thumbnail if GM_xmlhttpRequest is not available, depending on which layout Pixiv feels like using...
var directTitles = ( typeof(GM_xmlhttpRequest) != "undefined" );
//Append direct links below images on mode=manga pages
var directManga = true;
//Replace the medium thumbnail on mode=medium pages with (and link it to) the full size.
var fullSizeMedium = true;
//Disable lazy loading images. These appear on mode=manga pages, rankings, and the "Recommended" section of the bookmarks page.
var dontSayLazy = true;
//----------------------------------------------------------------//
var fullSizeWidth = "740px";
if( typeof(custom) != "undefined" )
custom();
if( location.search.indexOf("mode=manga_big") > 0 || location.search.indexOf("mode=big") > 0 )
{
//Make the 'big'/'manga_big' image link to itself instead of closing the window
var image = document.getElementsByTagName("img")[0];
if( image )
{
var link = document.createElement("a");
link.href = image.src;
link.appendChild( document.createElement("img") ).src = image.src;
document.body.innerHTML = "";
document.body.appendChild( link );
}
}
else if( location.search.indexOf("mode=manga") > 0 )
{
var container = document.getElementsByClassName("full-size-container");
if( directManga && container.length )
{
//Check the mode=manga_big page for the first page to determine if the "_big_p" size exists.
var req = new XMLHttpRequest();
req.open( "GET", location.href.replace(/page=\d+&?/,'').replace('mode=manga','mode=manga_big&page=0'), true );
req.onload = function()
{
var firstImage = req.responseXML.querySelector("img[src*='_p0.']").src;
for( var i = 0; i < container.length; i++ )
{
//Add direct link below each page
var link = document.createElement("a");
link.textContent = "direct link";
link.style.display = "block";
link.href = firstImage.replace( "_p0.", "_p"+i+"." );
container[i].parentNode.appendChild( link );
}
}
req.responseType = "document";
req.send(null);
}
}
else if( window == window.top )//not inside iframe
{
if( directTitles )
{
//Link dem titles.
linkThumbTitles([document]);
new MutationObserver( function(mutationSet)
{
mutationSet.forEach( function(mutation){ linkThumbTitles( mutation.addedNodes ); } );
}).observe( document.body, { childList:true, subtree:true } );
}
var worksDisplay = document.getElementsByClassName("works_display")[0];
if( worksDisplay )
{
var mainImage, mainLink = worksDisplay.querySelector("a[href*='mode=']");
if( mainLink )
mainLink.removeAttribute('target');//Make link open in same window
var oClass = document.getElementsByClassName("original-image");
if( oClass.length == 1 )//47235071
{
var worksDiv = worksDisplay.getElementsByTagName("div")[0];
worksDisplay.removeChild( worksDiv );//Need to remove instead of hide to prevent double source search links in other script
var link = worksDisplay.insertBefore( document.createElement("a"), worksDisplay.firstChild );
mainImage = link.appendChild( fullSizeMedium ? document.createElement("img") : worksDiv.getElementsByTagName("img")[0] );
fullsizeSrc = link.href = oClass[0].getAttribute("data-src");
}
else if( mainLink && mainLink.href.indexOf("mode=big") > 0 && (mainImage = mainLink.getElementsByTagName("img")[0]) != null )//17099702
{
if( mainImage.src.indexOf("_m.") > 0 )
mainLink.href = fullsizeSrc = mainImage.src.replace("_m.",".");
else if( typeof(GM_xmlhttpRequest) != "undefined" )
queryAPI( [{ "id": mainLink.href.match(/illust_id=(\d+)/)[1], "link": mainLink, "fullsize": ( fullSizeMedium ? mainImage : null ) }] );
else
{
//New thumbnails are always jpg, need to query mode=big page to get the right file extension.
var req = new XMLHttpRequest();
req.open( "GET", mainLink.href, true );
req.onload = function()
{
mainLink.href = req.responseXML.getElementsByTagName("img")[0].src;
if( fullSizeMedium )
mainImage.src = mainLink.href;
}
req.responseType = "document";
req.send(null);
}
}
if( mainImage && fullSizeMedium )
{
if( fullsizeSrc )
mainImage.src = fullsizeSrc;
mainImage.setAttribute("style", "max-width: "+fullSizeWidth+"; height: auto; width: auto;");
worksDisplay.style.width = fullSizeWidth;
}
}
}
if( dontSayLazy && unlazyImage() && window == window.top )
{
//Initial page has lazy images; listen for more images added later
new MutationObserver( function(mutationSet)
{
mutationSet.forEach( function(mutation)
{
for( var i = 0; i < mutation.addedNodes; i++ )
unlazyImage( mutation.addedNodes[i] );
} );
}).observe( document.body, { childList:true, subtree:true } );
}
//----------------------------------------------------------------//
function unlazyImage(target)
{
var images = ( target || document ).querySelectorAll("img[data-src]");
for( var i = 0; i < images.length; i++ )
images[i].src = images[i].getAttribute("data-src");
return images.length;
}
function pushTitleLink(list, link)
{
var matcher;
if( link && link.href && (matcher = link.href.match(/illust_id=(\d+)/)) && matcher[1] > 0 )
list.push({ "id": matcher[1], "link": link });
}
function linkThumbTitles(targets)
{
var titleList = [];
for( var i = 0; i < targets.length; i++ )
{
//search.php, bookmark.php, member_illust.php, new_illust.php, member.php (uploads), mypage.php (new works)
var foundTitle = targets[i].querySelectorAll("a[href*='mode=medium'][href*='illust_id='] > .title");
for( var j = 0; j < foundTitle.length; j++ )
pushTitleLink( titleList, foundTitle[j].parentNode );
//ranking.php
foundTitle = targets[i].querySelectorAll(".ranking-item a.title[href*='mode=medium'][href*='illust_id=']");
for( var j = 0; j < foundTitle.length; j++ )
pushTitleLink( titleList, foundTitle[j] );
//member_illust.php (what image was responding to)
foundTitle = targets[i].querySelector(".worksImageresponseInfo a.response-out-work[href*='mode=medium'][href*='illust_id=']");
if( foundTitle )
pushTitleLink( titleList, foundTitle );
//response.php, member_illust.php (before/after thumbnails), ?member.php (bookmarks)?
var image = targets[i].querySelectorAll("li a[href*='mode=medium'][href*='illust_id='] img");
for( var j = 0; j < image.length; j++ )
{
var page, title;
for( page = image[j].parentNode; page.tagName != "A"; page = page.parentNode );
//The prev/next thumbnails on mode=medium pages have text before/after the image. Text also follows the image on image responses listings.
if( !(title = page.getElementsByClassName("title")[0]) && (title = page.lastChild).nodeName != '#text' && (title = page.firstChild).nodeName != '#text' )
continue;//Can't find title element
//Start title link at mode=medium and change later.
var titleLink = document.createElement("a");
titleLink.href = page.href;
titleLink.style.color = "#333333";//Style used on some pages
//Move the title out of the thumbnail link
page.removeChild(title);
titleLink.appendChild(title);
page.parentNode.insertBefore( titleLink, page.nextSibling );
if( typeof(GM_xmlhttpRequest) != "undefined" || image[j].src.indexOf("img-master") < 0 )
pushTitleLink( titleList, titleLink );
}
}
if( titleList.length )
{
if( typeof(GM_xmlhttpRequest) != "undefined" )
queryAPI( titleList );
else for( var i = 0; i < titleList.length; i++ )
directLinkSingle( titleList[i] );
}
}
function queryAPI(titleList)
{
var query = "http://spapi.pixiv.net/iphone/illust.php?illust_id="+titleList[0].id;
for( var i = 1; i < titleList.length; i++ )
query += ","+titleList[i].id;
//Session ID is needed for R-18 images
var sessionID = document.cookie.match(/PHPSESSID=[^;]+/)[0];
if( sessionID )
query += "&"+sessionID;
GM_xmlhttpRequest(
{
method: "GET",
url: query,
onload: function(responseDetails)
{
var result = [], quote = false;
//Strip quotes and commas inside of quotes, for easier parsing later
for( var i = 0; i < responseDetails.responseText.length; i++ )
{
if( responseDetails.responseText[i] == '"' )
quote = !quote;
else if( !quote || responseDetails.responseText[i] != ',' )
result.push( responseDetails.responseText[i] );
}
result = result.join("").split("\n");
for( var i = 0; i < result.length && result[i].length; i++ )
{
var matcher, parr = result[i].split(",");
for( var j = 0; j < titleList.length; j++ )
if( parr[0] == titleList[j].id )
{
//[ 0]: illust_id
//[ 2]: extension
//[ 9]: thumbnail (always jpg)
//[19]: # pages (empty if not manga)
if( /^\d+$/.test(parr[19]) )
{
titleList[j].link.href = titleList[j].link.href.replace("mode=medium","mode=manga");
( titleList[j].link.firstChild.nodeName == '#text' ? titleList[j].link : titleList[j].link.firstChild ).title += " ("+parr[19]+"p)";
}
else if( (matcher = parr[9].match( /(.*\/img\d+\/img\/[^\/]+\/)mobile\/[^?]+(.*)/ )) != null )
{
//http://i2.pixiv.net/img04/img/aenobas/mobile/30095810_480mw.jpg?1348946035
// --> http://i2.pixiv.net/img04/img/aenobas/30095810.jpg?1348946035
titleList[j].link.href = matcher[1]+parr[0]+"."+parr[2]+matcher[2];//baseURL + illustID + "." + extension + revision
if( titleList[j].fullsize )
titleList[j].fullsize.src = titleList[j].link.href;
}
else if( (matcher = parr[9].match( /(.*\/)c\/480x960\/img-master([^_]+)_480mw.jpg/ )) != null )
{
//http://i1.pixiv.net/c/480x960/img-master/img/2014/09/24/23/49/09/46169016_480mw.jpg
// --> http://i1.pixiv.net/img-original/img/2014/09/24/23/49/09/46169016_p0.jpg
titleList[j].link.href = matcher[1]+"img-original"+matcher[2]+"_p0."+parr[2];
if( titleList[j].fullsize )
titleList[j].fullsize.src = titleList[j].link.href;
}
//Remove title from list
titleList.splice( j, 1 );
break;
}
}
//Individually query whatever didn't get matched above
for( var i = 0; i < titleList.length; i++ )
directLinkSingle( titleList[i] );
}
});
}
//Fallback method to query each image's mode=medium page.
function directLinkSingle(title)
{
var req = new XMLHttpRequest();
req.open( "GET", "http://www.pixiv.net/member_illust.php?mode=medium&illust_id="+title.id, true );
req.onload = function()
{
var select = req.responseXML.getElementsByClassName("original-image");
if( select.length == 1 )
title.link.href = select[0].getAttribute("data-src");
else if( (select = req.responseXML.querySelector(".works_display a[href*='mode=big'] img[src*='_m.']")) != null )
title.link.href = select.src.replace("_m.",".");
else if( (select = req.responseXML.querySelector(".works_display a[href*='mode=manga']")) != null )
title.link.href = select.href;
}
req.responseType = "document";
req.send(null);
}