Greasy Fork is available in English.
Makes Pixiv searches "bottomless", adds IQDB image search links, searches for existing posts on Danbooru, and filters out images on search pages with less than a set number of favorites
当前为
// ==UserScript==
// @name Endless Pixiv Pages
// @namespace http://userscripts.org/scripts/show/57567
// @include http://www.pixiv.net*
// @description Makes Pixiv searches "bottomless", adds IQDB image search links, searches for existing posts on Danbooru, and filters out images on search pages with less than a set number of favorites
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @version 2014.07.12
// ==/UserScript==
//Main features
var makeEndless = true;
var addIQDB = false;//Add links to IQDB image searches (Danbooru)
var addSourceSearch = false;//Danbooru post search (looks for matching source)
//Endless settings
var scrollBuffer = 500;//Minimum height remaining to scroll before loading the next page.
var timeToFailure = 30;//Seconds to wait for a response from the next page before attempting to fetch the page again.
//Default minimum number of favorites for a thumb to be displayed; only affects pages that display favorite counts.
var minFavs = 0;
//Source search options; login info is required
var danbooruLogin = "";//e.g. "UserName"
var danbooruPassHash = "";//e.g. "ab3718d912849aff02482dbadf00d00ab391283a"
var styleSourceFound = "color:green; font-weight: bold;";
var styleSourceMissing = "color:red;";
var sourceTimeout = 20;//seconds to wait before retrying query
var maxAttempts = 20;//# of times to try a query before completely giving up on source searches
//////////////////////////////////////////////////////////////////////////////////////
//Stop script if this page is inside an iframe.
if( window != window.top ) return;
//Remove Premium-only options
var premium = document.getElementsByClassName("require-premium");
for( var i = premium.length - 1; i >= 0; i-- )
premium[i].parentNode.removeChild( premium[i] );
setTimeout( function()
{
var filter = document.getElementById("thumbnail-filter-container");
if( filter )
filter.parentNode.removeChild(filter);
}, 300 );
if( typeof(GM_getValue) == "undefined" || !GM_getValue('a', 'b') )
{
GM_getValue = function(name,defV){ var value = localStorage.getItem("endless_pixiv."+name); return( value ? value : defV ); };
GM_setValue = function(name,value) { localStorage.setItem("endless_pixiv."+name, value ); }
}
if( typeof(custom) != "undefined" )
custom();
//Source search requires GM_xmlhttpRequest()
addSourceSearch = ( addSourceSearch && typeof(GM_xmlhttpRequest) != "undefined" );
//Startup variables.
var nextPage = null, timeout, pending = false, anyBookmarks = false, sourceTimer;
//Manga images have to be handled specially
if( location.search.indexOf("mode=manga") >= 0 )
{
if( addSourceSearch )
{
var thumbList = [];
var images = document.evaluate("//div[contains(@class,'item-container')]/img", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for( var i = 0; i < images.snapshotLength; i++ )
{
var thisImage = images.snapshotItem(i);
thumbList.push({ link: thisImage.parentNode.appendChild( document.createElement("div") ).appendChild( document.createElement("a") ),
pixiv_id: pixivIllustID( thisImage.getAttribute("data-src") || thisImage.src ), page: i });
}
sourceSearch( thumbList );
}
return;
}
//Add ability to set minFavs inside Search Options
var addSearch = document.getElementById("word-and");
if( addSearch )
{
anyBookmarks = true;
//Load "minFavs" setting
if( GM_getValue("minFavs") )
minFavs = parseInt( GM_getValue("minFavs") );
//Set option
addSearch = addSearch.parentNode.parentNode;
var favTr = document.createElement("tr");
favTr.appendChild( document.createElement("th") ).textContent = "Minimum favorites";
favInput = favTr.appendChild( document.createElement("td") ).appendChild( document.createElement("input") );
favInput.type = "text";
favInput.value = ""+minFavs;
favInput.addEventListener("input", function()
{
if( /^ *\d+ *$/.test(this.value) )
{
GM_setValue("minFavs", this.value.replace(/ +/g,''));
minFavs = parseInt( this.value );
}
}, true);
addSearch.parentNode.insertBefore( favTr, addSearch );
}
//Fix thumbs and add links if requested
processThumbs();
if( ( addIQDB || addSourceSearch ) && location.href.indexOf("mode=medium") > 0 )
processThumbs(null,true);
window.addEventListener( "DOMNodeInserted", function(e) { setTimeout( function() { processThumbs(e) }, 1 ) }, true );
//Prevent page from being cut off after ~35 added pages
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '#pixiv { overflow:visible; ! important } ul.images li.image, li.image-item{ height:auto !important; min-height:250px !important; padding:5px 0px !important } ';
document.getElementsByTagName('head')[0].appendChild(style);
//Stop script if there are no thumbnails.
mainTable = makeEndless && getMainTable(document);
if( !mainTable )
return;
var bottomDoc = getBottomPager(document);
if( bottomDoc == null )
return;
bottomDoc.parentNode.removeChild(bottomDoc);
mainTable.parentNode.parentNode.appendChild(bottomDoc);
//Stop script if there are no more pages.
if( (nextPage = getNextPage(bottomDoc)) == null )
return;
//Hide the showcase on search pages
var showcase = document.getElementsByClassName("user-ad-container")[0];
if( showcase )
showcase.style.display = "none";
//iframe to hold new pages
var iframe = document.createElement("iframe");
iframe.addEventListener("load", pullingMore, false);
iframe.width = 0;
iframe.height = 0;
iframe.style.visibility = "hidden";
document.body.appendChild(iframe);
//Adjust buffer height
scrollBuffer += window.innerHeight;
//Watch scrolling
window.addEventListener("scroll", testScrollPosition, false);
testScrollPosition();
//====================================== Functions ======================================
function processThumbs(e,modeMedium)
{
if( !addIQDB && !addSourceSearch && !minFavs )
return;
var thumbSearch = null, thumbList = [];
if( e && e.target.tagName != "LI" && e.target.tagName != "UL" && e.target.tagName != "DIV" )
return;
if( modeMedium )
thumbSearch = document.evaluate("//div[@class = 'works_display']/a/img[not(@endless)]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
else
{
//Member profiles have both a/img and a/div/img
thumbSearch = document.evaluate("descendant-or-self::li/a[contains(@href,'mode=medium') or contains(@href,'/novel/show.php')]/img[not(@endless)] | "+
"descendant-or-self::div/a[contains(@href,'mode=medium') or contains(@href,'/novel/show.php')]/img[not(@endless)] | "+
"descendant-or-self::li[@class='image-item']/a/node()/img[not(@endless)]",
(e ? e.target : document), null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
for( var i = 0; i < thumbSearch.snapshotLength; i++ )
{
var thumbImg = thumbSearch.snapshotItem(i);
var thumbPage = ( thumbImg.parentNode.tagName == "A" ? thumbImg : thumbImg.parentNode ).parentNode;
var thumbDiv = thumbPage.parentNode;
var bookmarkCount = 0, bookmarkLink = document.evaluate( ".//a[contains(@href,'/bookmark_detail.php')]", thumbDiv, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
var sourceContainer = thumbDiv;
//Mark thumb so it gets skipped when the next page loads.
thumbImg.setAttribute("endless","done");
//Skip generic restricted thumbs
if( thumbImg.src.indexOf("http://source.pixiv.net/") == 0 )
continue;
//Skip special thumbs except on image pages
if( thumbImg.src.indexOf("_100.") > 0 && location.search.indexOf("mode=") < 0 )
continue;
if( bookmarkLink )
{
//Thumb has bookmark info
bookmarkCount = parseInt( bookmarkLink.getAttribute("data-tooltip","x").replace(/([^\d]+)/g,'') ) || 1;
sourceContainer = bookmarkLink.parentNode;
}
else if( addIQDB )
{
//Thumb doesn't have bookmark info. Add a fake bookmark link to link with the IQDB.
bookmarkLink = document.createElement("a");
bookmarkLink.className = "bookmark-count";
if( anyBookmarks )
{
bookmarkLink.className += " ui-tooltip";
bookmarkLink.setAttribute("data-tooltip", "0 bookmarks");
}
//Dummy div to force new line when needed
thumbDiv.appendChild( document.createElement("div") );
thumbDiv.appendChild( bookmarkLink );
}
else
{
//Dummy div to force new line when needed
thumbDiv.appendChild( document.createElement("div") );
}
if( anyBookmarks && bookmarkCount < minFavs )
{
thumbDiv.parentNode.removeChild(thumbDiv);
continue;
}
if( addIQDB )
{
bookmarkLink.href = "http://danbooru.iqdb.org/?url="+thumbImg.src+"&fullimage="+thumbPage.href;
bookmarkLink.innerHTML = "(IQDB)";
}
if( addSourceSearch )
{
sourceContainer.appendChild( document.createTextNode(" ") );
thumbList.push({ link: sourceContainer.appendChild( document.createElement("a") ), pixiv_id: pixivIllustID(thumbImg.src), page: -1 });
}
}
sourceSearch( thumbList );
}
function pixivIllustID(url) { return url.replace( /.*\/(\d+)(_|\.)[^\/]+$/g, '$1' ); }
function pixivPageNumber(url) { return /_p\d+\./.test(url) ? url.replace( /.*_p(\d+)\..*/g, '$1' ) : "x" }
function sourceSearch( thumbList, attempt, page )
{
//Login info is required.
if( danbooruPassHash.length == 0 || danbooruLogin.length == 0 )
return;
//thumbList[index] = { link, id, page? }
if( page === undefined )
{
//First call. Finish initialization
attempt = page = 1;
for( var i = 0; i < thumbList.length; i++ )
{
if( !thumbList[i].status )
thumbList[i].status = thumbList[i].link.parentNode.appendChild( document.createElement("span") );
thumbList[i].link.textContent = "Searching...";
thumbList[i].posts = [];
}
}
if( attempt >= maxAttempts )
{
//Too many failures (or Downbooru); give up. :(
for( var i = 0; i < thumbList.length; i++ )
{
thumbList[i].status.style.display = "none";
if( thumbList[i].link.textContent[0] != '(' )
thumbList[i].link.textContent = "(error)";
thumbList[i].link.setAttribute("style","color:blue; font-weight: bold;");
}
return;
}
//Is there actually anything to process?
if( thumbList.length == 0 )
return;
//Retry this call if timeout
var retry = (function(a,b,c){ return function(){ setTimeout( function(){ sourceSearch(a,b,c); }, 1000 ); }; })( thumbList, attempt + 1, page );
//var retry = (function(a,b,c){ return function(){ sourceSearch(a,b,c); }; })( thumbList, attempt + 1, page );
sourceTimer = setTimeout( retry, sourceTimeout*1000 );
// Combine the IDs from the thumbList into a single search string
var query = "status:any+pixiv:";
for( var i = 0; i < thumbList.length; i++ )
{
thumbList[i].status.textContent = " ["+attempt+"]";
query += thumbList[i].pixiv_id+",";
}
GM_xmlhttpRequest(
{
method: "GET",
url: 'http://danbooru.donmai.us/posts.json?limit=100&tags='+query+'0&login='+danbooruLogin+'&password_hash='+danbooruPassHash+'&page='+page,
onload: function(responseDetails)
{
clearTimeout(sourceTimer);
//Check server response for errors
var result = false, status = null;
if( /^ *$/.test(responseDetails.responseText) )
status = "(error)";//No content
else if( responseDetails.responseText.indexOf("<title>Downbooru</title>") > 0 )
{
maxAttempts = 0;//Give up
status = "(Downbooru)";
}
else if( responseDetails.responseText.indexOf("<title>Failbooru</title>") > 0 )
status = "(Failbooru)";
else try {
result = JSON.parse(responseDetails.responseText);
status = "Searching...";
}
catch(err) {
result = false;
status = "(error)";
}
//Update thumbnail messages
for( var i = 0; i < thumbList.length; i++ )
thumbList[i].link.textContent = status;
if( result === false )
return retry();//Hit an error; try again?
for( var i = 0; i < thumbList.length; i++ )
{
//Collect the IDs of every post with the same pixiv_id/page as the pixiv image
for( var j = 0; j < result.length; j++ )
if( thumbList[i].pixiv_id == result[j].pixiv_id && thumbList[i].posts.indexOf( result[j].id ) < 0 && ( thumbList[i].page < 0 || thumbList[i].page == pixivPageNumber( result[j].source ) ) )
{
thumbList[i].link.title = result[j].tag_string+" user:"+result[j].uploader_name+" rating:"+result[j].rating+" score:"+result[j].score;
thumbList[i].posts.push( result[j].id );
}
if( thumbList[i].posts.length == 1 )
{
//Found one post; link directly to it
thumbList[i].link.textContent = "post #"+thumbList[i].posts[0];
thumbList[i].link.href = "http://danbooru.donmai.us/posts/"+thumbList[i].posts[0];
thumbList[i].link.setAttribute("style",styleSourceFound);
}
else if( thumbList[i].posts.length > 1 )
{
//Found multiple posts; link to tag search
thumbList[i].link.textContent = "("+thumbList[i].posts.length+" sources)";
thumbList[i].link.href = "http://danbooru.donmai.us/posts?tags=status:any+pixiv:"+thumbList[i].pixiv_id;
thumbList[i].link.setAttribute("style",styleSourceFound);
thumbList[i].link.removeAttribute("title");
}
}
if( result.length == 100 )
sourceSearch( thumbList, attempt + 1, page + 1 );//Max results returned, so fetch the next page
else for( var i = 0; i < thumbList.length; i++ )
{
//No more results will be forthcoming; hide the status counter and set the links for the images without any posts
thumbList[i].status.style.display = "none";
if( thumbList[i].posts.length == 0 )
{
thumbList[i].link.textContent = "(no sources)";
thumbList[i].link.setAttribute("style",styleSourceMissing);
}
}
},
onerror: retry,
onabort: retry
});
}
function getMainTable(source)
{
var result = null, tableFun =
[
//bookmarks: user
function(src){ src = src.getElementById("search-result"); return src ? src.getElementsByTagName("ul")[0] : null; }
,
//search
//new_illust
//member_illust.php?id=####
function(src){ src = src.getElementsByClassName("image-item")[0]; return src ? src.parentNode : null; }
,
//default
function(src){ src = src.getElementsByClassName("linkStyleWorks")[0]; return src ? src.getElementsByTagName("ul")[0] : null; }
];
for( var i = 0; i < tableFun.length; i++ )
{
getMainTable = tableFun[i];
if( (result = getMainTable(source)) != null )
return result;
}
return null;
}
function getBottomPager(source)
{
var result = null, pagerFun =
[
//search
function(src){ src = src.getElementsByClassName("pager-container"); return src.length ? src[src.length - 1].parentNode : null; },
//default
function(src){ src = src.getElementsByClassName("pages"); return src.length ? src[src.length - 1] : null; }
];
for( var i = 0; i < pagerFun.length; i++ )
{
getBottomPager = pagerFun[i];
if( (result = getBottomPager(source)) != null )
return result;
}
return null;
}
function getNextPage(pager)
{
var links = pager.getElementsByTagName("a");
if( links.length == 0 || links[links.length-1].getAttribute("rel") != "next" )
return null;//No more pages
else if( links[links.length-1].href.indexOf("http://www.pixiv.net/") < 0 )
return "http://www.pixiv.net/"+links[links.length-1].href;
else
return links[links.length-1].href;
}
function testScrollPosition()
{
if( !pending && window.pageYOffset + scrollBuffer > bottomDoc.offsetTop )
{
pending = true;
timeout = setTimeout(function(){pending=false;testScrollPosition();},timeToFailure*1000);
iframe.contentDocument.location.replace(nextPage);
}
}
function pullingMore(responseDetails)
{
var bottomPage, newTable, nextElem, pageLink;
if( timeout != undefined )
clearTimeout(timeout);
//Make sure page loaded properly
if( (newTable = getMainTable(iframe.contentDocument)) == null )
{
pending = false;
return;
}
//Add page link
pageLink = mainTable.parentNode.appendChild( document.createElement("div") );
pageLink.setAttribute("style","font-size:large; text-align:left; margin-left: 10px");
pageLink.setAttribute("class","clear");
pageLink.innerHTML = '<hr style="clear: both;"><a href="'+nextPage+'">Page '+nextPage.replace(/.*(\?|&)p=([0-9]+).*/,'$2')+'</a>';
//Remove the paginator in case it's in mainTable, then refresh the visible bottom paginator.
bottomPage = getBottomPager(iframe.contentDocument);
bottomPage.parentNode.removeChild(bottomPage);
bottomDoc.innerHTML = bottomPage.innerHTML;
mainTable.parentNode.appendChild( document.adoptNode(newTable) );
//Check for the next page, and disable the script if there isn't one.
if( nextPage = getNextPage(bottomPage) )
{
pending = false;
testScrollPosition();
}
else
{
pageLink = mainTable.parentNode.appendChild( document.createElement("div") );
pageLink.setAttribute("class","clear");
testScrollPosition = function() { }
}
}
if( typeof GM_deleteValue != "undefined" )
GM_deleteValue("last_check");
//So as I pray, Unlimited Pixiv Works.