pixiv_sort_by_popularity

non premium menber use "Sort by popularity"

当前为 2020-02-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name pixiv_sort_by_popularity
  3. // @name:zh-CN pixiv_sort_by_popularity
  4. // @name:zh-TW pixiv_sort_by_popularity
  5. // @name:ja pixiv_sort_by_popularity
  6. // @namespace pixiv_sort_by_popularity
  7. // @supportURL https://github.com/zhuzemin
  8. // @description non premium menber use "Sort by popularity"
  9. // @description:zh-CN non premium menber use "Sort by popularity"
  10. // @description:zh-TW non premium menber use "Sort by popularity"
  11. // @description:ja non premium menber use "Sort by popularity"
  12. // @include https://www.pixiv.net/*/tags/*
  13. // @include https://www.pixiv.net/tags/*
  14. // @version 1.0
  15. // @run-at document-start
  16. // @author zhuzemin
  17. // @license Mozilla Public License 2.0; http://www.mozilla.org/MPL/2.0/
  18. // @license CC Attribution-ShareAlike 4.0 International; http://creativecommons.org/licenses/by-sa/4.0/
  19. // @grant GM_xmlhttpRequest
  20. // @grant GM_registerMenuCommand
  21. // @grant GM_setValue
  22. // @grant GM_getValue
  23. // @connect-src workers.dev
  24. // ==/UserScript==
  25. var config = {
  26. 'debug': false
  27. }
  28. var debug = config.debug ? console.log.bind(console) : function () {
  29. };
  30.  
  31. //this userscript desire for free member use "Sort by popularity"
  32.  
  33. //default
  34. //pixiv search request through this url will use my cookie.
  35. var cloudFlareUrl='https://proud-surf-e590.zhuzemin.workers.dev/ajax/';
  36.  
  37. //Obejct use for xmlHttpRequest
  38. class requestObject{
  39. constructor(keyword,page,order,mode) {
  40. this.method = 'GET';
  41. this.url = cloudFlareUrl+'https://www.pixiv.net/ajax/search/artworks/'+keyword+'?word='+keyword+'&order='+order+'&mode='+mode+'&p='+page+'&s_mode=s_tag&type=all';
  42. this.data=null,
  43. this.headers = {
  44. 'User-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0',
  45. 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
  46. //'Accept': 'application/atom+xml,application/xml,text/xml',
  47. //'Referer': window.location.href,
  48. };
  49. this.charset = 'text/plain;charset=utf8';
  50. this.package=null;
  51. }
  52. }
  53.  
  54. var btn;
  55. var page;
  56. var select;
  57.  
  58. // prepare UserPrefs
  59. setUserPref(
  60. 'cloudFlareUrl',
  61. cloudFlareUrl,
  62. 'Set cloudFlareUrl',
  63. `cloudFlareUrl only working on "Sort by popularity"`,
  64. ','
  65. );
  66.  
  67.  
  68. //for override fetch, I think override function sure insert to page, otherwise userscript don't have permission modified fetch in page?
  69. function addJS_Node (text)
  70. {
  71. var scriptNode = document.createElement ('script');
  72. scriptNode.type = "text/javascript";
  73. if (text) scriptNode.textContent = text;
  74.  
  75. var targ = document.getElementsByTagName('head')[0] || d.body || d.documentElement;
  76. targ.appendChild (scriptNode);
  77. }
  78.  
  79.  
  80.  
  81. //override fetch
  82. function intercept(){
  83. //insert override function to page
  84. addJS_Node(`
  85. var newData;
  86. var interceptEnable;
  87. var constantMock = window.fetch;
  88. window.fetch = function() {
  89. console.log(arguments);
  90.  
  91. return new Promise((resolve, reject) => {
  92. constantMock.apply(this, arguments)
  93. .then((response) => {
  94. if(interceptEnable&&/https:\\/\\/www\\.pixiv\\.net\\/ajax\\/search\\/artworks\\/[^\\?&]*\\?word=[^\\?&]*&order=date(_d)?/.test(response.url)){
  95. var blob = new Blob([newData], {type : 'application/json'});
  96. console.log(newData);
  97.  
  98. var newResponse=new Response(
  99. blob, {
  100. status: response.status,
  101. statusText: response.statusText,
  102. headers: response.headers
  103. });
  104. console.log(newResponse);
  105. response=newResponse;
  106. interceptEnable=false;
  107. }
  108. resolve(response);
  109. })
  110. .catch((error) => {
  111. reject(response);
  112. })
  113. });
  114. }
  115. `);
  116. //here is script end,
  117. //in console ,log show fetch response body has been changed <--- not very sure
  118. //and page have react ---> stay blank for ever
  119. //my confuse is: even comment "return data" (line:93), page still return blank,
  120. //that makes me wonder: maybe this override function miss something.
  121. //if my terrible code can be understanding somehow,
  122. //and knoa san have nothing else todo in leisure time,
  123. //knoa san can you take while, look my newbie problem?
  124. //of cource if too painful read my code, I totally understand!
  125. //knoa san can read to here already be my greatest honor, and I'm very happy!
  126. }
  127.  
  128. //userscript entry
  129. var init=function(){
  130. //create button
  131. if(window.self === window.top){
  132. debug("init");
  133. cloudFlareUrl=GM_getValue('cloudFlareUrl')||cloudFlareUrl;
  134. intercept();
  135. var div=document.querySelectorAll('div.sc-LzMhL.krKUte')[1];
  136. btn =document.createElement('button');
  137. btn.innerHTML='Sort by popularity';
  138. btn.addEventListener('click',sortByPopularity);
  139. div.insertBefore(btn,null);
  140. select=document.createElement('select');
  141. select.id='sortByPopularity';
  142. var optionObj={
  143. 'Popular with all':'popular_d',
  144. 'Popular (male)':'popular_male_d',
  145. 'Popular (female)':'popular_female_d'
  146. }
  147. for(var key of Object.keys(optionObj)){
  148. var option=document.createElement('option');
  149. option.innerHTML=key;
  150. option.value=optionObj[key];
  151. select.insertBefore(option,null);
  152. }
  153. div.insertBefore(select,null);
  154. }
  155. }
  156.  
  157. window.addEventListener('load', init);
  158.  
  159. //get current search word, then use xmlHttpRequest get response(from my server)
  160. function sortByPopularity(e) {
  161. btn.innerHTML='Searching...'
  162. var keyword;
  163. var matching=window.location.href.match(/https:\/\/www\.pixiv\.net\/(\w*\/)?tags\/(.*)\/artworks\?(order=[^\?&]*)?&?(mode=(\w\d*))?&?(p=(\d*))?/);
  164. keyword=matching[2]
  165. debug(e.target.tagName)
  166. if(/(\d*)/.test(e.target.textContent)&&(e.target.tagName=='SPAN'||e.target.tagName=="A")){
  167. page=e.target.textContent.match(/(\d*)/)[1];
  168. }
  169. else if(e.target.tagName=='svg'||e.target.tagName=='polyline'){
  170. if(e.target.parentElement.tagName=='a'){
  171. page=e.target.parentElement.href.match(/p=(\d*)/)[1];
  172.  
  173. }
  174. else {
  175. page=e.target.parentElement.parentElement.href.match(/p=(\d*)/)[1];
  176.  
  177. }
  178. }
  179. else if(/p=(\d*)/.test(window.location.href)){
  180. page=window.location.href.match(/p=(\d*)/)[1];
  181.  
  182. }
  183. else{
  184. page=1;
  185. }
  186. page=parseInt(page);
  187. debug(keyword);
  188. debug('page: '+page);
  189. var order=document.querySelector('#sortByPopularity').value;
  190. var mode;
  191. if(/(&|\?)mode=([\d\w]*)/.test(window.location.href)){
  192. mode=window.location.href.match(/(&|\?)mode=([\d\w]*)/)[2];
  193. }
  194. else {
  195. mode='all';
  196. }
  197. var obj=new requestObject(keyword,page,order,mode);
  198. request(obj,function (responseDetails) {
  199.  
  200. unsafeWindow.newData=JSON.stringify(responseDetails.response,null,2);
  201. unsafeWindow.interceptEnable=true;
  202. //trigger fetch by click "Newest" or "Oldest"
  203. var div=document.querySelectorAll('div.sc-LzMhL.krKUte')[1];
  204. div.querySelector('a').click();
  205. var interval=setInterval(function () {
  206. var nav=document.querySelector('nav.sc-LzNRw.qhAyw');
  207. if(nav!=null){
  208. nav.addEventListener('click',sortByPopularity);
  209. nav.childNodes[1].childNodes[0].innerText=page;
  210. if(page<=7&&page>1){
  211. nav.childNodes[1].href=nav.childNodes[page].href;
  212. nav.childNodes[page].innerText=1;
  213. nav.childNodes[page].href=nav.childNodes[0].href;
  214. nav.insertBefore(nav.childNodes[1],nav.childNodes[page]);
  215. nav.insertBefore(nav.childNodes[page],nav.childNodes[1]);
  216.  
  217. }
  218. else if(page>7){
  219. //nav.insertBefore(nav.childNodes[1],nav.childNodes[8]);
  220. for(var i=2;i<8;i++){
  221. nav.childNodes[i].childNodes[0].innerText=page+i-1;
  222. nav.childNodes[i].href=nav.childNodes[i].href.replace(/p=\d*/,'p='+(page+i-1));
  223. }
  224. }
  225. if(page!=1){
  226. nav.childNodes[0].style='opacity:1!important;';
  227. nav.childNodes[0].href=nav.childNodes[8].href.replace(/p=\d*/,'p='+(page-1));
  228. nav.childNodes[8].href=nav.childNodes[8].href.replace(/p=\d*/,'p='+(page+1));
  229.  
  230. }
  231. btn.innerHTML='Sort by popularity';
  232. clearInterval(interval);
  233.  
  234. }
  235. },2000);
  236. });
  237.  
  238. }
  239. function request(object,func) {
  240. GM_xmlhttpRequest({
  241. method: object.method,
  242. url: object.url,
  243. headers: object.headers,
  244. responseType: 'json',
  245. overrideMimeType: object.charset,
  246. timeout: 60000,
  247. //synchronous: true
  248. onload: function (responseDetails) {
  249. debug(responseDetails);
  250. //Dowork
  251. func(responseDetails);
  252. },
  253. ontimeout: function (responseDetails) {
  254. //Dowork
  255. func(responseDetails);
  256.  
  257. },
  258. ononerror: function (responseDetails) {
  259. debug(responseDetails);
  260. //Dowork
  261. func(responseDetails);
  262.  
  263. }
  264. });
  265. }
  266. function setUserPref(varName, defaultVal, menuText, promtText, sep){
  267. GM_registerMenuCommand(menuText, function() {
  268. var val = prompt(promtText, GM_getValue(varName, defaultVal));
  269. if (val === null) { return; } // end execution if clicked CANCEL
  270. // prepare string of variables separated by the separator
  271. if (sep && val){
  272. var pat1 = new RegExp('\\s*' + sep + '+\\s*', 'g'); // trim space/s around separator & trim repeated separator
  273. var pat2 = new RegExp('(?:^' + sep + '+|' + sep + '+$)', 'g'); // trim starting & trailing separator
  274. //val = val.replace(pat1, sep).replace(pat2, '');
  275. }
  276. //val = val.replace(/\s{2,}/g, ' ').trim(); // remove multiple spaces and trim
  277. GM_setValue(varName, val);
  278. // Apply changes (immediately if there are no existing highlights, or upon reload to clear the old ones)
  279. //if(!document.body.querySelector(".THmo")) THmo_doHighlight(document.body);
  280. //else location.reload();
  281. });
  282. }