readme
[4Free-FSE.git] / Danbooru-Image-Adder.user.js
blob44cbb517989d8b69452cee80001ddf32abdda553
1 // ==UserScript==
2 // @name         Danbooru-Image-Adder
3 // @namespace    http://tampermonkey.net/
4 // @version      1.0
5 // @description  Add images to posts
6 // @author       ECHibiki /qa/
7 // @match *://boards.4chan.org/*
8 // @grant         GM_xmlhttpRequest
9 // @updateURL    https://github.com/ECHibiki/4chan-UserScripts/raw/master/Danbooru-Image-Adder.user.js
10 // @downloadURL  https://github.com/ECHibiki/4chan-UserScripts/raw/master/Danbooru-Image-Adder.user.js
11 // @run-at document-end
12 // ==/UserScript==
15 I(CREATING THE POSTS)
16      1) DO JSON SEARCH FOR TAGS:
17             search[name]=TAG1,TAG2 &&&&&& search[order]=count
18      2) PICK THE SMALLEST ONES AS BASE
19      3) GENERATE A RANDOM NUMBER BETWEEN 0 AND FINAL_PAGE-1
20      4) ITTERATE THROUGH POSTS WITH TAGS UNTIL:
21             A) IT TIMESOUT
22             B) IT GOES FROM START TO END AND NOT FOUND
23      5) DO A 4CHANX CreateNotification***
25 II(AUTO COMPLETE)
26     1) ON FIELD CHANGE READ THE CURSOR LEFT AND DO A  search[name_matches]=n* & search[order]=count
27     2) PHONE STYLED AUTO COMPLETE
28     3) CLICK ON THE GIVEN ITEM TO ADD
29     4) GETS PLACED IN THE GIVEN FIELD
31 III(CHECKBOXES)
32    1) CHECKS FOR RATINGS
33    2) DROPDOWN FOR ORDER
35    3)***   (TODO)
38 //update 0.8.9 single tag warning and number_of_posts bug.
41 function alert4ChanX(message, type){
42     var detail = {type: type, content: message, lifetime: 10};
43     if (typeof cloneInto === 'function') {
44         detail = cloneInto(detail, document.defaultView);
45     }
46     var event = new CustomEvent('CreateNotification', {bubbles: true, detail: detail});
47     document.dispatchEvent(event);
50 var number_of_posts = 0;
51 var pageNo;
52 var JSONPage;
53 var JSONTag;
54 var pagesLoaded = 0;
55 var smallestTag;
57 var top_page_max =  10000000;
58 var top_page = top_page_max;
59 var attemptMax = 20;
60 var attemptCounter = attemptMax ;
62 var imgURL = "";
63 var sendURL = "";
64 var oldVal = "";
66 var timeout = false;
67 var fail_state = false;
68 var tag_incorrect_state = false;
69 var time_max = 10;
70 var time = time_max;
71 var intervalFunction;
72 var timeout_functions = [];
73 var tries = Array();
75 var taggingFunction;
77 var interfaceSet = false;
79 //set listener to build interface in 4chanX
80 window.onload = function(){
81     var len = document.links.length;
82     for(var i = 0 ; i < len ; i++){
83         document.links[i].addEventListener("click", enhance4ChanX);
84     }
86     //ENHANCE DUMP TABS (COVER, 482PX - 482PX)
87     //DUMP LIST MAX-HEIGHT TO 490
89     document.getElementById("fourchanx-css").textContent += ".qr-preview { height: 482px; width: 482px; background-size: cover;}";
90     document.getElementById("fourchanx-css").textContent += "#dump-list { min-height: 400px; width: 509px;}";
94 //Alter 4chanX
95 var enhance4ChanX = function(){
96     var qrWindow = document.getElementById("qr");
97     //check if elements already made upon opening a qr window
98     if(document.getElementById("qrImages") !== null){
99         qrWindow.removeChild(document.getElementById("qrImages"));
100         clearInterval(taggingFunction);
101         //4chanx autodeletes images
102         clearImage();
103     }
104     var dButton = document.getElementById("dump-button");
105     if(dButton !== null){dButton.click();}
106     else{return;}
108     var dList = document.getElementById("dump-list");
109     var filenamecontainer = document.getElementById("qr-filename-container");
111     //used for setting and unsetting high resolution thumbs for dump list.
112     var BGImg = "";
113     var oldBGImg = "";
114     var observer = new MutationObserver(function(mutate){
115         BGImg = dList.firstChild.style.backgroundImage;
116         if(BGImg !== oldBGImg && imgURL !== ""){
117             dList.firstChild.style.backgroundImage = "url(" + imgURL + ")";
118             oldBGImg = dList.firstChild.style.backgroundImage;
119         }
120         else if (imgURL == ""){
121         }
122     });
123     observer.observe(dList , {attributes: true,subtree:true, childList: true, characterData: true });
124     //make the image clear button clear images;
125     document.getElementById("qr-filerm").addEventListener("click", clearImage);
127     //image setting html elements.
128     var qrTable = document.createElement("TABLE");
129     qrTable.setAttribute("id", "qrImages");
130     qrTable.setAttribute("style", "text-align:center");
131     qrWindow.appendChild(qrTable);
132     //qrWindow.appendChild(document.createElement("BR"));
134     var instructionRow = document.createElement("TR");
135     var topRowNodes = [document.createElement("BR"),
136                        document.createTextNode("Insert Tags to search from danbooru bellow."),
137                        document.createElement("BR"),
138                        document.createTextNode("Do Not Use \"order:\" tags"),
139                        document.createElement("BR"),
140                        document.createTextNode("Do Not Use \"rating:\" tags"),
141                        document.createElement("BR"),
142                        document.createTextNode("For more speed uncheck all boxes!"),
143                       ];
144     topRowNodes.forEach(
145         function(node){
146             instructionRow.appendChild(node);
147         });
148     qrTable.appendChild(instructionRow);
150     var optionsRow = document.createElement("TR");
151     optionsRow.setAttribute("ID", "or");
152     optionsRow.setAttribute("style", "margin:5px;");
153     qrTable.appendChild(optionsRow);
154     var checkSafe = document.createElement("INPUT");
155     checkSafe.setAttribute("id", "safe");
156     checkSafe.setAttribute("type", "checkbox");
157     var safeText  = document.createTextNode("Safe");
158     var checkQuest= document.createElement("INPUT");
159     checkQuest.setAttribute("id", "questionable");
160     checkQuest.setAttribute("type", "checkbox");
161     var questText= document.createTextNode("Questionable");
162     var checkExplicit = document.createElement("INPUT");
163     checkExplicit.setAttribute("id", "explicit");
164     checkExplicit.setAttribute("type", "checkbox");
165     var explText = document.createTextNode("Explicit");
167     optionsRow.appendChild(safeText);
168     optionsRow.appendChild(checkSafe);
169     optionsRow.appendChild(questText);
170     optionsRow.appendChild(checkQuest);
171     optionsRow.appendChild(explText);
172     optionsRow.appendChild(checkExplicit);
174     var tagRow = document.createElement("TR");
175     var secondRowNodes = [
176         document.createTextNode("Tags: "),
177         document.createElement("INPUT"),
178         document.createElement("INPUT"),
179         document.createElement("A"),
180         document.createElement("INPUT"),
181     ];
182     secondRowNodes.forEach(
183         function(node){
184             tagRow.appendChild(node);
185         });
186     qrTable.appendChild(tagRow);
188     var autoCompleteRow = document.createElement("TR");
189     autoCompleteRow.setAttribute("ID", "acr");
190     autoCompleteRow.setAttribute("style", "margin:5px;");
191     qrTable.appendChild(autoCompleteRow);
193     secondRowNodes[1].setAttribute("ID", "tags");
194     secondRowNodes[1].setAttribute("style", "width:44.9%");
195     secondRowNodes[3].setAttribute("ID", "timer");
196     secondRowNodes[3].setAttribute("style", "width:20%;margin:0 5px");
197     secondRowNodes[4].setAttribute("ID", "urlContainer");
198     secondRowNodes[4].setAttribute("style", "width:20%;margin:0 5px");
199     secondRowNodes[4].setAttribute("disabled", "");
200     var tags = "";
201     var tagNode = document.getElementById("tags");
202     var rightMost;
203     var leftMost;
205     secondRowNodes[2].setAttribute("ID", "imageButton");
206     secondRowNodes[2].setAttribute("type", "button");
207     secondRowNodes[2].setAttribute("value", "Set Image");
210     //event listener logic
211     secondRowNodes[2].addEventListener("click", buttonClickFunction);
213     //ping ever 0.5s for changes
214     taggingFunction = setInterval(
215         function(){setTagInterface(tagNode, autoCompleteRow, secondRowNodes);},
216         500);
220 function buttonClickFunction(){
221         tries = Array();
222         primed_for_fail = false;
223         for(var i = 0 ; i < timeout_functions.length; i++){
224                 clearInterval(timeout_functions[i]);
225         }
226         tag_incorrect_state = false;
227         timeout = false;
228         document.getElementById("tags").setAttribute("disabled", 1);
229         document.getElementById("imageButton").setAttribute("disabled", 1);
230         time = time_max;
231         timeout_functions.push(setInterval(counterFunction, 1000));
232         setImage();
235 function clearImage(){
236     var dList = document.getElementById("dump-list");
237     dList.firstChild.style.backgroundImage = "url()";//trigger mutation event
238     imgURL = ""; //get mutation to set to dead
241 var setTagInterface =  function(tagNode, autoCompleteRow, secondRowNodes){
242     tags = tagNode.value;
243     if(oldVal !== tags){
244         var cursorPos = tagNode.selectionStart - 1;
245         var currentTag =  (function(){
246             var currentChar = tags.charAt(cursorPos);
247             var i = 0;
248             rightMost = cursorPos;
249             while(currentChar != " " && currentChar != "" && currentChar !== undefined){
250                 i++;
251                 currentChar = tags.charAt(cursorPos + i);
252                 if(currentChar != " " && currentChar != "") rightMost = cursorPos + i;
253             }
254             rightMost += 1;
255             currentChar = tags.charAt(cursorPos);
256             i = 0;
257             leftMost = cursorPos;
258             while(currentChar != " " && currentChar != ""  && currentChar !== undefined){
259                 i++;
260                 currentChar = tags.charAt(cursorPos - i);
261                 if(currentChar != " " && currentChar != "") leftMost = cursorPos - i;
262             }
263             return tags.substring(leftMost, rightMost);
264         })();
265         var xhr = new GM_xmlhttpRequest(({
266             method: "GET",
267             url: "https://danbooru.donmai.us/tags.json?search[name_matches]=" + currentTag + "*&search[order]=count",
268             responseType : "json",
269             onload: function(data){
270                 data = data.response;
271                 var tagArray = tags.split(" ");
272                 while (autoCompleteRow.hasChildNodes()) {
273                     autoCompleteRow.removeChild(autoCompleteRow.lastChild);
274                 }
275                 for (var i = 0 ; i < 5 ; i++){
276                     var a  = document.createElement("A");
277                     a.setAttribute("style", "padding:5px;padding-top:0px;font-size:15px;font-weight:bold;border:1px solid black;");
278                     var tagText = data["" + i];
279                     if(tagText == "" || tagText === undefined) break;
280                     tagText = tagText["name"];
282                     var aTxt  = document.createTextNode(data[i]["name"]);
284                     autoCompleteRow.appendChild(a);
285                     a.appendChild(aTxt);
286                     a.addEventListener("click", function(evt){
287                         tagArray[tagArray.indexOf(currentTag)] = this.textContent;
288                         secondRowNodes[1].value = tagArray.join(" ");
289                     });
290                 }
291             }}));
292     }
293     oldVal =  tagNode.value;
296 var setImage = function(){
297     //Set image tags.
298     var tags = document.getElementById("tags").value;
300     //TODO 4cx notification of warning(no error)
301     if(tags.indexOf(":") > -1) {
302         alert4ChanX("Character ':' not used for file characteristic searches", "warning");
303     }
304     tags = tags.split(" ");
306     var xhr_image_load = new GM_xmlhttpRequest(({
307         method: "GET",
308         //returns a list of all tags and their properties
309         url: "https://danbooru.donmai.us/tags.json?search[name]=" + tags.join() + "&search[order]=count",
310         responseType : "json",
311         onload: function(data)
312         {
313             verifyTags(data, tags);
314                         if(fail_state) return;
315                         
316             //set the end
317             var endURL = ratingURL(tags, JSONTag);
319             var URL = setPostAndPage(endURL, tags);
320             sendURL = URL;
321             //final check, sends final request after function or calls this function again
322             getJSON(URL, checkPageFromDanbooru, tags);
323         }}));
326 function verifyTags(data, tags){
327     data = data.response;
328     if(tags.length == 1 && tags[0] == "") JSONTag = [{"name":""}];
329     else JSONTag = data;
330         fail_state = false;
331     if(data.length == 0){
332         //TODO 4cx notification of error)
333         alert4ChanX("All tags incorrect", "error");
334                 fail_state = true;
335                 document.getElementById("timer").textContent = "";
336                 document.getElementById("tags").removeAttribute("disabled");
337                 document.getElementById("imageButton").removeAttribute("disabled");
338         return;
339     }
340         else if(data.length != tags.length && !tag_incorrect_state){
341                 tag_incorrect_state = true;
342                 if(document.getElementById("tags").value.trim() == "") alert4ChanX("No Tags", "info");
343                 else alert4ChanX("One Tag Incorrect", "warning");
344         }
345     //tag size. Smallest tag is placed at bottom of JSON
346     smallestTag = parseInt(data[data.length-1]["post_count"]);
349 var setPostAndPage = function(endURL, tags){
350         //posts
351         if(number_of_posts > 0)
352     number_of_posts = 0;
353    //page
354         if(top_page != top_page_max) smallestTag = top_page * 20;
355     if(smallestTag == 0) smallestTag = 100;
356         do{     
357                 escape_cond = true;
358                 pageNo = ((Math.floor(Math.random() * 10000)) % Math.ceil(smallestTag / 20)) % 1000;    //1000 is max page search limit
359                 tries.forEach(function(page){
360                         if(page == 0){
361                                 primed_for_fail = true;
362                                 escape_cond = true;
363                                 return;
364                         }
365                         else if(page == pageNo){
366                                 escape_cond = false;
367                                 return;
368                         }
369                 });
370         } while(!escape_cond);
371         tries.push(pageNo);
373     var URL = "https://danbooru.donmai.us/posts.json?page=" + pageNo + endURL;
375     loopOne = false;
376     loopPage = pageNo;
377     loopPost = number_of_posts;
378     sameTrigger = 0;
379     return URL;
382 var ratingURL = function(tags, data){
383     var URL = "";
384     if(document.getElementById("safe").checked){
385         if(document.getElementById("questionable").checked){
386             if(document.getElementById("explicit").checked){
387                 if(data.length > 1)  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-2]["name"] + "+" + data[data.length-1]["name"];
388                 else  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-1]["name"];
389             }
390             else{
391                 URL =  "&utf8=%E2%9C%93&tags=" + "-rating%3Aexplicit" + "+" + data[data.length-1]["name"];
392             }
393         }
394         else if(document.getElementById("explicit").checked){
395             URL = "&utf8=%E2%9C%93&tags=" + "-rating%3Aquestionable" + "+" + data[data.length-1]["name"];
396         }
397         else{
398             URL = "&utf8=%E2%9C%93&tags=" + "rating%3Asafe" + "+" + data[data.length-1]["name"];
399         }
400     }
401     else if(document.getElementById("questionable").checked){
402         if(document.getElementById("explicit").checked){
403             URL =  "&utf8=%E2%9C%93&tags=" + "-rating%3Asafe" + "+" + data[data.length-1]["name"];
404         }
405         else{
406             URL =  "&utf8=%E2%9C%93&tags=" + "rating%3Aquestionable" + "+" + data[data.length-1]["name"];
407         }
408     }
409     else if(document.getElementById("explicit").checked){
410         URL =  "&utf8=%E2%9C%93&tags=" + "rating%3Aexplicit" + "+" + data[data.length-1]["name"];
411     }
412     else{
413         if(data.length > 1)  URL =  "&utf8=%E2%9C%93&tags=" + data[data.length-2]["name"] + "+" + data[data.length-1]["name"];
414         else  URL = "&utf8=%E2%9C%93&tags=" + data[data.length-1]["name"];
415     }
416         console.log(URL);
417     return URL;
420 //check if valid url location
421 var primed_for_fail = false;
422 var checkPageFromDanbooru = function(err, data, tags){
423         if (err != null) {
424                 console.log('Something went wrong: ' + err);
425                 alert4ChanX("Danbooru Server Did Not Perform request -- Error: "  + err, "error");
426                 top_page = top_page_max;
427                 attemptCounter = attemptMax;
428                 document.getElementById("timer").textContent = "";
429                 document.getElementById("tags").removeAttribute("disabled");
430                 document.getElementById("imageButton").removeAttribute("disabled");
431                 pageNo = 0;
432                 //number_of_posts = 0;
433         }
434         else {
435                 if(primed_for_fail){
436                         alert4ChanX("No Results", "error");
437                         top_page = top_page_max;
438                         attemptCounter = attemptMax;
439                         document.getElementById("timer").textContent = "";
440                         document.getElementById("tags").removeAttribute("disabled");
441                         document.getElementById("imageButton").removeAttribute("disabled");
442                         return;
443                 }
444                 //redo
445                 else if(data.length < number_of_posts+1 && attemptCounter > 0) {
446                         if(top_page > pageNo){
447                                 top_page = pageNo + number_of_posts / 20;
448                         }
449                         attemptCounter--;
450                         document.getElementById("timer").textContent = attemptCounter + "|" + time;
451                         setImage();
452                 }
453                 //process page
454                 else if (attemptCounter > 0){
455                         //ALL PARAMETERS WILL BE RESET INSIDE JSON
456                         document.getElementById("timer").textContent =  attemptCounter + "|" + time;
457                         getJSON(sendURL, setImageFromDanbooru, tags);
458                 }
459                 else{
460                         alert4ChanX("Not found", "error");
461                         top_page = top_page_max;
462                         attemptCounter = attemptMax;
463                         document.getElementById("timer").textContent = "";
464                         document.getElementById("tags").removeAttribute("disabled");
465                         document.getElementById("imageButton").removeAttribute("disabled");     
466                         return;
467                 }
468         }
471 var setImageFromDanbooru = function(err, data, tags){
472     if (err != null) {
473         console.log('Something went wrong: ' + err);
474         alert4ChanX("Danbooru Server Did Not Perform request -- Error: "  + err, "error");
475         top_page = top_page_max;
476         attemptCounter = attemptMax;
477         document.getElementById("timer").textContent = "";
478         document.getElementById("tags").removeAttribute("disabled");
479         document.getElementById("imageButton").removeAttribute("disabled");
481     }
482     else {
483                 JSONPage = data;
484                 var image_found = false;
485                 for (number_of_posts = 0; number_of_posts < 20 ; number_of_posts++){
486                         if(timeout){
487                                 alert4ChanX("timeout after " + time +" seconds", "error");
488                                 clearInterval(counterFunction);
489                                 document.getElementById("timer").textContent = "";
490                                 document.getElementById("tags").removeAttribute("disabled");
491                                 document.getElementById("imageButton").removeAttribute("disabled");
492                                 top_page = top_page_max;
493                                 attemptCounter = attemptMax;
494                                 return;
495                         }
496                         else if(JSONPage["" + number_of_posts] == undefined){
497                                 top_page = pageNo;
498                                 attemptCounter--;
499                                 setImage();
500                                 return;
501                         }
503                         var endURL = JSONPage["" + number_of_posts].file_url;
504                         var URL = "https://danbooru.donmai.us" + endURL;
506                         urlContainterFunction(URL);
508                         var fail = false;
510                         if(endURL === undefined ||
511                            endURL.indexOf(".mp4") > -1 || endURL.indexOf(".webm") > -1 || endURL.indexOf(".swf") > -1 || endURL.indexOf(".zip") > -1){
512                                 // top_page = pageNo;
513                                 // attemptCounter--;
514                                 // setImage();
515                                 // return;
516                                 continue;
517                         }
518                         else{
519                                 tags.forEach(function(tag){
520                                         if(tag.indexOf("order:") > -1);
521                                         else if(tag.indexOf("rating:") > -1){
522                                                 if(tag.charAt(7) !== JSONPage["" + number_of_posts]["rating"]){
523                                                         fail = true;
524                                                         return;
525                                                 }
526                                         }
527                                         else if(JSONPage["" + number_of_posts]["tag_string"].indexOf(tag) == -1){
528                                                 fail = true;
529                                                 return;
530                                         }
531                                 });
532                         }
533                         if(fail){
534                                 // top_page = pageNo;
535                                 // attemptCounter--;
536                                 // setImage();
537                                 // return;
538                                 continue;
539                         }
540                         else{
541                                 if(JSONPage["" + number_of_posts].file_size >= 4000000){
542                                         var endURL = JSONPage["" + number_of_posts].large_file_url;
543                                         var URL = "https://danbooru.donmai.us" + endURL;
544                                 }
545                                 document.getElementById("timer").textContent = "...";
546                                 imgURL = URL;
547                                 var xhr = new GM_xmlhttpRequest(({
548                                         method: "GET",
549                                         url: URL,
550                                         responseType : "arraybuffer",
551                                         onload: function(response)
552                                         {
553                                                 top_page = top_page_max;
554                                                 attemptCounter = attemptMax;
555                                                 document.getElementById("tags").removeAttribute("disabled");
556                                                 document.getElementById("imageButton").removeAttribute("disabled");
557                                                 loopOne = false;
558                                                 clearInterval(intervalFunction);
559                                                 time = time_max;
560                                                 var counter = document.getElementById("timer");
561                                                 while(counter.hasChildNodes())
562                                                         document.getElementById("timer").removeChild(document.getElementById("timer").lastChild);
564                                                 var blob;
565                                                 if(endURL.indexOf(".jpg") > -1)
566                                                         blob = new Blob([response.response], {type:"image/jpeg"});
567                                                 else if(endURL.indexOf(".png") > -1)
568                                                         blob = new Blob([response.response], {type:"image/png"});
569                                                 else if(endURL.indexOf(".gif") > -1)
570                                                         blob = new Blob([response.response], {type:"image/gif"});
573                                                 var name = endURL.replace(/(data|cached)/g, "");
574                                                 name = name.replace(/\//g, "");
576                                                 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
577                                                 var detail = {file:blob, name:name};
578                                                 if (typeof cloneInto === 'function') {
579                                                         detail  = cloneInto(detail , document.defaultView);
580                                                 }
581                                                 document.getElementById("dump-list").firstChild.click();
582                                                 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
583                                         }
584                                 }));
585                                                                                                 //end function;
586                                 image_found = true;
587                                 number_of_posts = 9001;
588                         }
589                 }
590                 if(!image_found){
591                         top_page = pageNo;
592                         attemptCounter--;
593                         setImage();
594                 }
595     }
598 var urlContainterFunction = function(url){
599     var urlBox = document.getElementById("urlContainer");
600     urlBox.value = url;
603 var counterFunction  = function(){
604     if(!timeout){
605         time--;
606         if(time < 0){
607             timeout = true;
608             time = time_max;
609         }
610     }
613 var getJSON = function(url, callback, extra) {
614     var xhr = new XMLHttpRequest();
615     xhr.open('GET', url, true);
616     xhr.responseType = 'json';
617     xhr.onload = function() {
618         var status = xhr.status;
619         if (status == 200) {
620             callback(null, xhr.response, extra);
621         } else {
622             callback(status);
623         }
624     };
625     xhr.send();