Cleared Pixiv Artists

Keeps track of Pixiv artists' works and image responses and adds a random link to one of those pages.

  1. // ==UserScript==
  2. // @name Cleared Pixiv Artists
  3. // @namespace https://greasyfork.org/scripts/3634
  4. // @description Keeps track of Pixiv artists' works and image responses and adds a random link to one of those pages.
  5. // @include http://danbooru.donmai.us/*
  6. // @include https://danbooru.donmai.us/*
  7. // @include http://hijiribe.donmai.us/*
  8. // @include http://sonohara.donmai.us/*
  9. // @include http://www.donmai.us/*
  10. // @include http://donmai.us/*
  11. // @include http://www.pixiv.net/*
  12. // @include https://www.pixiv.net/*
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @version 2017.06.08
  16. // ==/UserScript==
  17.  
  18. var waitingDays = 365;//Days since a page was cleared before the Random link will include it again (when all artists are cleared)
  19. var worksWeight = 3;//Number of chances to give each member_illust link of being selected for the random link (response links get 1)
  20.  
  21. //////////////////////////////////////////////////////////////////////////////////////
  22.  
  23. if( window == window.top )
  24. addLinks();
  25.  
  26. function addLinks()
  27. {
  28. if( typeof(GM_getValue) != "undefined" && GM_getValue('a', 'b') )
  29. {
  30. getList = function(name){ try{ return JSON.parse( GM_getValue(name,"[]") ); }catch(e){ return []; } }
  31. setList = function(name,list) { GM_setValue(name,JSON.stringify(list)); }
  32. }
  33. else if( location.host.indexOf("donmai.us") >= 0 )
  34. {
  35. return;//GM is needed to maintain a single list across both Pixiv and Danbooru, so Danbooru is disabled when that isn't available
  36. }
  37. else
  38. {
  39. //Use local storage instead.
  40. getList = function(name)
  41. {
  42. var value = localStorage.getItem("cleared_pixiv."+name);
  43. if( value )
  44. try{ return JSON.parse(value); } catch(e){}
  45. return [];
  46. }
  47. setList = function(name,list) { localStorage.setItem("cleared_pixiv."+name, JSON.stringify(list)); }
  48. }
  49. //Add random link
  50. var randomLink;
  51. if( location.hostname.indexOf("donmai") >= 0 )
  52. {
  53. //Danbooru: Add to right end of subnavbar
  54. randomLink = document.evaluate("//header/nav/menu[not(contains(@class,'main'))]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  55. if( !randomLink )
  56. return;
  57. randomLink = randomLink.appendChild( document.createElement("li") ).appendChild( document.createElement("a") );
  58. }
  59. else
  60. {
  61. //Pixiv: Add to right of whatever
  62. randomLink = document.getElementsByTagName("header")[0];
  63. if( !randomLink )
  64. return;
  65. randomLink = randomLink.appendChild( document.createElement("a") );
  66. randomLink.style.cssFloat = "right";
  67. randomLink.style.margin = "3px";
  68. }
  69. randomLink.id = "random_pixiv_link";
  70. setRandomLink();
  71. if( location.href.indexOf("//www.pixiv.net/member.php?id=") > 0 )
  72. {
  73. //Member profile
  74. addListListener( document.evaluate("//div[@class='worksListOthers']//p/a[contains(@href,'member_illust.php?id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "worksList" );
  75. addListListener( document.evaluate("//div[@class='worksListOthers']//p/a[contains(@href,'response.php?mode=all&id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "responseList" );
  76. }
  77. else if( location.href.indexOf("//www.pixiv.net/member_illust.php?id=") > 0 )
  78. {
  79. //Member's works
  80. addListListener( document.evaluate("//div/h1/a[contains(@href,'member_illust.php?id=')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, "worksList" );
  81. }
  82. else if( location.href.indexOf("//www.pixiv.net/response.php?mode=all&id=") > 0 )
  83. {
  84. //Image responses
  85. var header = document.evaluate("//div[@class='search_top_result']/h2", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  86. if( !header )
  87. addListListener( null, "responseList" );//deleted user
  88. else
  89. {
  90. var div = document.createElement("div");
  91. var link = div.appendChild( document.createElement("a") );
  92. link.textContent = header.textContent;
  93. link.href = location.href;
  94. addListListener(link, "responseList");
  95. header.innerHTML = "";
  96. header.appendChild(div);
  97. }
  98. }
  99. else if( location.href.indexOf("donmai.us/artist") >= 0 )
  100. {
  101. //Danbooru artist
  102. var links = document.evaluate('//ul/ul/li/a[contains(@href,"//www.pixiv.net/member.php?id=")]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  103. for( var i = 0; i < links.snapshotLength; i++ )
  104. addListListener( links.snapshotItem(i), "worksList" );
  105. }
  106. }
  107.  
  108. function setRandomLink()
  109. {
  110. var randomLink = document.getElementById("random_pixiv_link");
  111. if( !randomLink )
  112. return;
  113. var worksList = getList("worksList");
  114. var responseList = getList("responseList");
  115. var found = 0, i, pending = [];
  116. var responseWeight = (worksWeight < 0 ? -worksWeight : 1);
  117. worksWeight = (worksWeight > 0 ? worksWeight : 1);
  118. function addToList(maxDate)
  119. {
  120. for( var i = 0; i < worksList.length; i++ )
  121. if( worksList[i].date < maxDate )
  122. {
  123. if( location.href.indexOf( "//www.pixiv.net/member_illust.php?id="+worksList[i].id ) < 0 )
  124. for( j = 0; j < worksWeight; j++ )
  125. pending.push( location.protocol+"//www.pixiv.net/member_illust.php?id="+worksList[i].id );
  126. found++;
  127. }
  128. for( var i = 0; i < responseList.length; i++ )
  129. if( responseList[i].date < maxDate )
  130. {
  131. if( location.href.indexOf( "//www.pixiv.net/response.php?mode=all&id="+responseList[i].id ) < 0 )
  132. for( j = 0; j < responseWeight; j++ )
  133. pending.push( location.protocol+"//www.pixiv.net/response.php?mode=all&id="+responseList[i].id );
  134. found++;
  135. }
  136. }
  137. addToList(1);
  138. if( !found && waitingDays > 1 )
  139. addToList( new Date().getTime() - waitingDays*24*3600*1000 );
  140. randomLink.title = found+" / "+(worksList.length+responseList.length)+" links pending";
  141. if( found > 0 )
  142. {
  143. randomLink.textContent = "Random pixiv link ("+found+")";
  144. randomLink.style.textDecoration = "";
  145. if( pending.length > 0 )
  146. randomLink.href = pending[Math.floor(Math.random()*pending.length)];
  147. else
  148. randomLink.removeAttribute("href");
  149. }
  150. else
  151. {
  152. randomLink.textContent = "Random pixiv link";
  153. randomLink.style.textDecoration = "line-through";
  154. randomLink.removeAttribute("href");
  155. }
  156. }
  157.  
  158. function addListListener(curLink, listName)
  159. {
  160. var thisList = getList(listName);
  161. var userID = (curLink ? curLink : location).href.replace(/.*id=(\d+).*/,"$1");
  162. if( /^\d+$/.test(userID) && ( listName == "worksList" || listName == "responseList" ) )
  163. {
  164. if( !curLink )
  165. {
  166. for( var index = thisList.length - 1; index >= 0; index-- )
  167. if( thisList[index].id == userID )
  168. {
  169. thisList.splice( index, 1 );
  170. setList(listName,thisList);
  171. setRandomLink();
  172. alert("Removed: "+userID+" from "+listName);
  173. }
  174. return;
  175. }
  176. }
  177. else return;
  178. curLink.parentNode.appendChild( document.createTextNode(" ") );
  179. var statusLink = curLink.parentNode.appendChild( document.createElement("a") );// Add / Remove
  180. statusLink.style.cursor = "pointer";
  181. statusLink.onclick = "return false;";
  182. var clearLink = curLink.parentNode.appendChild( document.createElement("a") ); // Clear
  183. clearLink.style.cursor = "pointer";
  184. clearLink.onclick = "return false;";
  185. var found = false;
  186. for( var i = thisList.length; i-- > 0; )
  187. if( userID == thisList[i].id )
  188. {
  189. if( found )
  190. {
  191. //Remove older duplicates that somehow managed to sneak in.
  192. thisList.splice( i, 1 );
  193. setList(listName,thisList);
  194. }
  195. else
  196. {
  197. if( thisList[i].date == 0 )
  198. clearLink.textContent = "/[Clear]";
  199. else
  200. {
  201. var days = Math.round( (new Date().getTime() - thisList[i].date)/(24*3600*1000) );
  202. clearLink.textContent = "/[Cleared "+days+" day"+(days != 1 ? "s" : "")+" ago]";
  203. }
  204. statusLink.textContent = "[Remove]";
  205. found = true;
  206. }
  207. }
  208. if( !found )
  209. statusLink.textContent = "[Add]";
  210.  
  211. //Switch between "Add" and "Remove", modifying the list accordingly.
  212. statusLink.addEventListener("click", function()
  213. {
  214. var list = getList(listName);//Reload list in case it is being modified across multiple tabs
  215. for( var index = list.length - 1; index >= 0 && list[index].id != userID; index-- );
  216. if( !found )
  217. {
  218. //Add to list if it isn't there already.
  219. if( index < 0 )
  220. list.push( { id: userID, date: 0 } );
  221. found = true;
  222. statusLink.textContent = "[Remove]";
  223. clearLink.textContent = "/[Clear]";
  224. }
  225. else
  226. {
  227. //Remove from list if it's really there
  228. if( index >= 0 )
  229. list.splice( index, 1 );
  230. found = false;
  231. curLink.textContent = curLink.textContent.replace(/ \[.*/g,'');
  232. statusLink.textContent = "[Add]";
  233. clearLink.textContent = "";
  234. }
  235. setList(listName,list);
  236. setRandomLink();
  237. }, true );
  238. //Set link as cleared today
  239. clearLink.addEventListener("click", function()
  240. {
  241. var list = getList(listName);
  242. for( var index = 0; index < list.length && list[index].id != userID; index++ );
  243. list[index].id = userID;
  244. list[index].date = new Date().getTime();
  245. setList(listName,list);
  246. statusLink.textContent = "[Remove]";
  247. clearLink.textContent = "/[Cleared 0 days ago]";
  248. setRandomLink();
  249. }, true );
  250. }
  251.  
  252. //document.body.innerHTML = "<b>Cleared Pixiv Artists</b><br><b>worksList</b> = "+JSON.stringify(getList("worksList"))+"<br><b>responseList</b> = "+JSON.stringify(getList("responseList"));