// ==UserScript== // @name pixiv_sort_by_popularity // @name:zh-CN pixiv_sort_by_popularity // @name:zh-TW pixiv_sort_by_popularity // @name:ja pixiv_sort_by_popularity // @namespace pixiv_sort_by_popularity // @supportURL https://github.com/zhuzemin // @description non premium menber use "Sort by popularity" // @description:zh-CN non premium menber use "Sort by popularity" // @description:zh-TW non premium menber use "Sort by popularity" // @description:ja non premium menber use "Sort by popularity" // @include https://www.pixiv.net/*/tags/* // @include https://www.pixiv.net/tags/* // @version 1.08 // @run-at document-start // @author zhuzemin // @license Mozilla Public License 2.0; http://www.mozilla.org/MPL/2.0/ // @license CC Attribution-ShareAlike 4.0 International; http://creativecommons.org/licenses/by-sa/4.0/ // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @connect-src workers.dev // @downloadURL none // ==/UserScript== var config = { 'debug': false } var debug = config.debug ? console.log.bind(console) : function () { }; //this userscript desire for free member use "Sort by popularity" //default //pixiv search request through this url will use my cookie. var cloudFlareUrl='https://proud-surf-e590.zhuzemin.workers.dev/ajax/'; //Obejct use for xmlHttpRequest class requestObject{ constructor(originUrl,page,order) { this.method = 'GET'; this.url = cloudFlareUrl+originUrl .replace(/(https:\/\/www\.pixiv\.net\/)(\w*)?\/tags\/([^\/]*)\/(\w*)\?([^\/\?]*)/, function(match, $1, $2,$3,$4,$5, offset, original){ return $1+'ajax/search/'+$4+'/'+$3+'?'+$5;}) .replace(/p=\d*/,'').replace(/order=[_\w]*/,'')+'&p='+page+'&order='+order; this.data=null, this.headers = { 'User-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0', 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8' //'Accept': 'application/atom+xml,application/xml,text/xml', //'Referer': window.location.href, }; this.charset = 'text/plain;charset=utf8'; this.package=null; } } var btn; // prepare UserPrefs setUserPref( 'cloudFlareUrl', cloudFlareUrl, 'Set cloudFlareUrl', `cloudFlareUrl only working on "Sort by popularity"`, ',' ); //for override fetch, I think override function sure insert to page, otherwise userscript don't have permission modified fetch in page? function addJS_Node (text) { var scriptNode = document.createElement ('script'); scriptNode.type = "text/javascript"; if (text) scriptNode.textContent = text; var targ = document.getElementsByTagName('head')[0] || d.body || d.documentElement; targ.appendChild (scriptNode); } //override fetch function intercept(){ //insert override function to page addJS_Node(` var newData; var interceptEnable; var constantMock = window.fetch; window.fetch = function() { //console.log(arguments); return new Promise((resolve, reject) => { constantMock.apply(this, arguments) .then((response) => { if(interceptEnable&&/https:\\/\\/www\\.pixiv\\.net\\/ajax\\/search\\//.test(response.url)){ var blob = new Blob([newData], {type : 'application/json'}); //console.log(newData); var newResponse=new Response( blob, { status: response.status, statusText: response.statusText, headers: response.headers }); //console.log(newResponse); response=newResponse; interceptEnable=false; } resolve(response); }) .catch((error) => { reject(response); }) }); } `); //here is script end, //in console ,log show fetch response body has been changed <--- not very sure //and page have react ---> stay blank for ever //my confuse is: even comment "return data" (line:93), page still return blank, //that makes me wonder: maybe this override function miss something. //if my terrible code can be understanding somehow, //and knoa san have nothing else todo in leisure time, //knoa san can you take while, look my newbie problem? //of cource if too painful read my code, I totally understand! //knoa san can read to here already be my greatest honor, and I'm very happy! } //userscript entry var init=function(){ //create button if(window.self === window.top){ debug("init"); cloudFlareUrl=GM_getValue('cloudFlareUrl')||cloudFlareUrl; intercept(); var div=document.querySelector('div.sc-LzNRw.qhAyw'); btn =document.createElement('button'); btn.innerHTML='Sort by popularity'; btn.addEventListener('click',sortByPopularity); div.insertBefore(btn,null); select=document.createElement('select'); select.id='sortByPopularity'; var optionObj={ 'Popular with all':'popular_d', 'Popular (male)':'popular_male_d', 'Popular (female)':'popular_female_d' } for(var key of Object.keys(optionObj)){ var option=document.createElement('option'); option.innerHTML=key; option.value=optionObj[key]; select.insertBefore(option,null); } div.insertBefore(select,null); } } window.addEventListener('load', init); //get current search word, then use xmlHttpRequest get response(from my server) function sortByPopularity(e) { btn.innerHTML='Searching...' try{ var page; //var matching=window.location.href.match(/https:\/\/www\.pixiv\.net\/(\w*\/)?tags\/(.*)\/\w*\?(order=[^\?&]*)?&?(mode=(\w\d*))?&?(p=(\d*))?/); debug(e.target.tagName); if(/(\d*)/.test(e.target.textContent)&&(e.target.tagName.toLowerCase()=='span'||e.target.tagName.toLowerCase()=="a")){ page=e.target.textContent.match(/(\d*)/)[1]; } else if(e.target.tagName.toLowerCase()=='svg'||e.target.tagName.toLowerCase()=='polyline'){ //debug('e.target.parentElement.tagName: '+e.target.parentElement.tagName); if(e.target.parentElement.tagName.toLowerCase()=='a'){ page=e.target.parentElement.href.match(/p=(\d*)/)[1]; } else { page=e.target.parentElement.parentElement.href.match(/p=(\d*)/)[1]; } } //for test /*else if(matching[7]!=null){ page=matching[7]; }*/ else{ page=1; } page=parseInt(page); debug('page: '+page); var order=document.querySelector('#sortByPopularity').value; var obj=new requestObject(window.location.href,page,order); debug('JSON.stringify(obj): '+JSON.stringify(obj)); request(obj,function (responseDetails) { debug("responseDetails.response: "+JSON.stringify(responseDetails.response)); unsafeWindow.newData=JSON.stringify(responseDetails.response,null,2); unsafeWindow.interceptEnable=true; //trigger fetch by click "Newest" or "Oldest" var divList=document.querySelectorAll('div.sc-LzMhM.krTqXn'); var div=divList[divList.length-1]; div.querySelector('a').click(); var interval=setInterval(function () { var nav=document.querySelector('nav.sc-LzNRx.qpXcF'); if(nav!=null){ nav.addEventListener('click',sortByPopularity); if(page<=7&&page>1){ //nav button "1" text -> current page number nav.childNodes[1].childNodes[0].innerText=page; //nav button "1" href -> current page href nav.childNodes[1].href=nav.childNodes[page].href; //current page button text -> "1" nav.childNodes[page].innerText=1; //current page button href -> origin nav button "1" href nav.childNodes[page].href=nav.childNodes[0].href; //switch two button positon nav.insertBefore(nav.childNodes[1],nav.childNodes[page]); nav.insertBefore(nav.childNodes[page],nav.childNodes[1]); } else if(page>7){ var currentPositionInNav=page%7; debug("currentPositionInNav: "+currentPositionInNav); var buttonStartNumber=page-currentPositionInNav; debug("buttonStartNumber: "+buttonStartNumber); var navButtonCount=1; //switch two button positon nav.insertBefore(nav.childNodes[1],nav.childNodes[currentPositionInNav+1]); nav.insertBefore(nav.childNodes[currentPositionInNav+1],nav.childNodes[1]); for(var i=buttonStartNumber;i<=(buttonStartNumber+6);i++){ debug("navButtonCount: "+navButtonCount); debug("i: "+i); nav.childNodes[navButtonCount].childNodes[0].innerText=i; nav.childNodes[navButtonCount].href=nav.childNodes[8].href.replace(/p=\d*/,'p='+(i)); navButtonCount++; } } if(page!=1){ //display previous button nav.childNodes[0].style='opacity:1!important;'; //previous button href nav.childNodes[0].href=nav.childNodes[8].href.replace(/p=\d*/,'p='+(page-1)); //next button href nav.childNodes[8].href=nav.childNodes[8].href.replace(/p=\d*/,'p='+(page+1)); } btn.innerHTML='Sort by popularity'; clearInterval(interval); } },2000); }); } catch (e) { debug('[Error]: '+e) } } function request(object,func) { GM_xmlhttpRequest({ method: object.method, url: object.url, headers: object.headers, responseType: 'json', overrideMimeType: object.charset, timeout: 60000, //synchronous: true onload: function (responseDetails) { debug(responseDetails); //Dowork func(responseDetails); }, ontimeout: function (responseDetails) { //Dowork func(responseDetails); }, ononerror: function (responseDetails) { debug(responseDetails); //Dowork func(responseDetails); } }); } function setUserPref(varName, defaultVal, menuText, promtText, sep){ GM_registerMenuCommand(menuText, function() { var val = prompt(promtText, GM_getValue(varName, defaultVal)); if (val === null) { return; } // end execution if clicked CANCEL // prepare string of variables separated by the separator if (sep && val){ var pat1 = new RegExp('\\s*' + sep + '+\\s*', 'g'); // trim space/s around separator & trim repeated separator var pat2 = new RegExp('(?:^' + sep + '+|' + sep + '+$)', 'g'); // trim starting & trailing separator //val = val.replace(pat1, sep).replace(pat2, ''); } //val = val.replace(/\s{2,}/g, ' ').trim(); // remove multiple spaces and trim GM_setValue(varName, val); // Apply changes (immediately if there are no existing highlights, or upon reload to clear the old ones) //if(!document.body.querySelector(".THmo")) THmo_doHighlight(document.body); //else location.reload(); }); }