Guest User

Untitled

a guest
Jul 20th, 2016
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Booru Tag Parser
  3. // @namespace    http://average.website
  4. // @version      1.1.2derpibooru
  5. // @description  Copy current post tags and rating on boorus and illustration2vec in to the clipboard for easy import in to a program or another booru.
  6. // @author       William Moodhe
  7. // @downloadURL  https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js
  8. // @updateURL    https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js
  9.  
  10. // Illustration2Vec
  11. // @include      *demo.illustration2vec.net*
  12.  
  13. // Catch-all for boorus
  14. // @include      *booru*/post*
  15. // @include      *booru*/*?page=post*
  16. // @include      *booru*/?page=post
  17.  
  18. // Boorus with weird names
  19. // @include      *rule34.xxx/index.php?page=post*
  20. // @include      *chan.sankakucomplex.com/post/show/*
  21. // @include      *chan.sankakucomplex.com/?tags=*
  22. // @include      *idol.sankakucomplex.com/post/show/*
  23. // @include      *idol.sankakucomplex.com/?tags=*
  24. // @include      *behoimi.org/post/show/*
  25. // @include      *e621.net/post/show/*
  26. // @include      *konachan.com/post/*
  27. // @include      *konachan.net/post/*
  28. // @include      *shimmie.katawa-shoujo.com/post/*
  29. // @include      *rule34.paheal.net/post/*
  30. // @include      *rule34hentai.net/post/*
  31. // @include      *tbib.org/index.php?page=post*
  32. // @include      *yande.re/post/*
  33. // @include      /^https?://derpiboo\.ru/[1-9]+/
  34. // @include      /^https?://derpibooru\.org/[1-9]+/
  35. // @include      /^https?://trixiebooru\.org/[1-9]+/
  36.  
  37. // nhentai
  38. // @include      *nhentai.net/g/*
  39.  
  40. // @run-at       document-end
  41. // @grant        GM_setClipboard
  42. // @grant        GM_setValue
  43. // @grant        GM_getValue
  44. // @grant        GM_xmlhttpRequest
  45.  
  46. // ==/UserScript==
  47. /* jshint -W097 */
  48. 'use strict';
  49.  
  50. ///////
  51.  
  52. var copy_key_code = GM_getValue('copy_key_code', 221); // ] key
  53. var download_key_code = GM_getValue('download_key_code', 220); // \ key
  54. var copy_sound = GM_getValue('copy_sound', 'http://heavy.noxiousnet.com/boorucopy.ogg');
  55. var iv2_confidence_rating = GM_getValue('iv2_confidence_rating', 20.0);
  56. var attach_explicit = GM_getValue('attach_explicit', true);
  57. var attach_gid = GM_getValue('attach_gid', true);
  58. var div_top = GM_getValue('div_top', false);
  59.  
  60. ///////
  61.  
  62. var tags_selector = 'h5, b';
  63. var button_style = 'font:11px monospace;font-weight:0;border:1px solid black;background:#eee;color:#000;display:block;width:90%;margin:2px auto;z-index:9999;';
  64. var div_style = 'opacity:0.2;position:fixed;width:240px;right:2px;top:2px;text-align:center;font:11px monospace;font-weight:0;border:1px solid black;background:rgba(255,255,255,0.8);z-index:9999;';
  65.  
  66. function replaceAll(str, originalstr, newstr)
  67. {
  68.     return str.split(originalstr).join(newstr);
  69. }
  70.  
  71. function insertTags(tags, selector, prefix, stripns)
  72. {
  73.     if (typeof stripns === "undefined") {
  74.         stripns = false;
  75.     }
  76.  
  77.     var elements = document.querySelectorAll(selector);
  78.  
  79.     for (var i=0; i < elements.length; i++)
  80.     {
  81.         var element = elements[i];
  82.         var text = element.innerHTML;
  83.         if (text.length > 1) // Don't copy - + or ?
  84.         {
  85.             text = replaceAll(text, '_', ' ');
  86.             text = replaceAll(text, '&gt;', '>');
  87.             text = replaceAll(text, '&lt;', '<');
  88.  
  89.             if (stripns) {
  90.                 text = text.match(".*?:(.*)")[1];
  91.             }
  92.  
  93.             tags[tags.length] = prefix+text;
  94.         }
  95.     }
  96. }
  97.  
  98. function insertRating(tags, selector)
  99. {
  100.     var elements = document.querySelectorAll(selector);
  101.  
  102.     for (var i=0; i < elements.length; i++)
  103.     {
  104.         var element = elements[i];
  105.         var text = element.innerHTML;
  106.         text = replaceAll(text.toLowerCase().trim(), ' ', '');
  107.  
  108.         if (text.indexOf('rating:explicit') >= 0)
  109.         {
  110.             tags[tags.length] = 'rating:explicit';
  111.             break;
  112.         }
  113.         else if (text.indexOf('rating:questionable') >= 0)
  114.         {
  115.             tags[tags.length] = 'rating:questionable';
  116.             break;
  117.         }
  118.         else if (text.indexOf('rating:safe') >= 0)
  119.         {
  120.             tags[tags.length] = 'rating:safe';
  121.             break;
  122.         }
  123.     }
  124. }
  125.  
  126. function copyNHentaiTags(noRating, callback)
  127. {
  128.     // nhentai has a json output we can use.
  129.     // Which is nice because the tags are available even if viewing an individual file.
  130.  
  131.     var id = window.location.href.match('/g/(\\d+)/*')[1];
  132.     if (!id)
  133.         return;
  134.  
  135.     id = Number(id);
  136.  
  137.     fetch('http://nhentai.net/g/' + id + '/json').then(function(response) {
  138.         return response.json();
  139.     }).then(function(json) {
  140.         var tags = [];
  141.  
  142.         if (json.tags)
  143.         {
  144.             for (var i=0; i < json.tags.length; i++)
  145.             {
  146.                 var tagtype = json.tags[i][1];
  147.                 var tag = json.tags[i][2];
  148.  
  149.                 // maintain schema consistency
  150.                 if (tagtype == 'group')
  151.                     tagtype = 'studio';
  152.                 else if (tagtype == 'parody')
  153.                     tagtype = 'series';
  154.                 else if (tagtype == 'artist')
  155.                     tagtype = 'creator';
  156.  
  157.                 if (tagtype != 'tag')
  158.                     tag = tagtype + ':' + tag;
  159.  
  160.                 tags[tags.length] = tag;
  161.             }
  162.         }
  163.  
  164.         if (attach_explicit)
  165.             tags[tags.length] = 'rating:explicit';
  166.  
  167.         if (attach_gid && json.id)
  168.             tags[tags.length] = 'gallery:' + json.id;
  169.  
  170.         copyTagsToClipboard(tags);
  171.  
  172.         if (callback)
  173.             callback(tags);
  174.     }).catch(function(err) {
  175.         console.log('couldn\'t get json!');
  176.     });
  177. }
  178.  
  179. function copyBooruTags(noRating)
  180. {
  181.     var tags = [];
  182.     // Instead of having a list of boorus and their tags and tag structures I just make a big catch-all.
  183.  
  184.     // danbooru-like
  185.     insertTags(tags, '#tag-list li.category-3 > a.search-tag', 'series:');
  186.     insertTags(tags, '#tag-list li.category-1 > a.search-tag', 'creator:');
  187.     insertTags(tags, '#tag-list li.category-4 > a.search-tag', 'character:');
  188.     insertTags(tags, '#tag-list li.category-0 > a.search-tag', '');
  189.  
  190.     // lolibooru-like
  191.     insertTags(tags, 'li.tag-type-copyright > a', 'series:');
  192.     insertTags(tags, 'li.tag-type-author > a', 'creator:');
  193.     insertTags(tags, 'li.tag-type-artist > a', 'creator:');
  194.     insertTags(tags, 'li.tag-type-character > a', 'character:');
  195.     insertTags(tags, 'li.tag-type-model > a', 'model:');
  196.     insertTags(tags, 'li.tag-type-general > a', '');
  197.     insertTags(tags, 'li.tag-type-studio > a', 'studio:');
  198.     insertTags(tags, 'li.tag-type-circle > a', 'studio:');
  199.     insertTags(tags, 'li.tag-type-medium > a', 'medium:');
  200.     insertTags(tags, 'li.tag-type-style > a', 'medium:');
  201.     insertTags(tags, 'li.tag-type-meta > a', 'meta:');
  202.     insertTags(tags, 'li.tag-type-species > a', 'species:');
  203.     insertTags(tags, 'li.tag-type-faults > a', 'fault:');
  204.  
  205.     // derpibooru-like
  206.     insertTags(tags, '.tag-list .tag.tag-ns-artist > a', 'creator:', true);
  207.     insertTags(tags, '.tag-list .tag.tag-ns-oc > a', 'character:', true);
  208.     insertTags(tags, '.tag-list .tag.tag-system > a', 'rating:');
  209.     insertTags(tags, '.tag-list [class="dropdown tag"] > a', ''); // generic tags on derpibooru do not have a "namespace" class of their own, this seems to be the best way to match generic tags
  210.  
  211.     // booru.org-like
  212.     insertTags(tags, '#tag_list li a', '');
  213.  
  214.     // paheal-like
  215.     insertTags(tags, 'a.tag_name', '');
  216.  
  217.     if (!noRating)
  218.     {
  219.         // danbooru-like
  220.         insertRating(tags, '#post-information > ul li');
  221.  
  222.         // lolibooru-like
  223.         insertRating(tags, '#stats > ul li');
  224.  
  225.         // booru.org-like
  226.         insertRating(tags, '#tag_list ul');
  227.     }
  228.  
  229.     copyTagsToClipboard(tags);
  230.  
  231.     return tags;
  232. }
  233.  
  234. function insertI2VTags(tags, selector, prefix, confidenceRequired)
  235. {
  236.     var elements = document.querySelectorAll(selector);
  237.     for (var i=1; i < elements.length; i++)
  238.     {
  239.         var element = elements[i];
  240.  
  241.         if (confidenceRequired > 0)
  242.         {
  243.             var confidence = element.children[3].children[0].innerHTML;
  244.             confidence = confidence.substr(0, confidence.length - 1);
  245.             confidence = Number(confidence);
  246.  
  247.             if (confidence < confidenceRequired)
  248.                 continue;
  249.         }
  250.  
  251.         var tag = element.children[1].innerHTML;
  252.  
  253.         tag = replaceAll(tag, '_', ' ');
  254.  
  255.         if (prefix)
  256.             tag = prefix + tag;
  257.  
  258.         tags[tags.length] = tag;
  259.  
  260.         if (prefix == 'rating') // only add one rating
  261.             break;
  262.     }
  263. }
  264.  
  265. function copyI2VTags(confidenceRequired, noGeneral, noRating)
  266. {
  267.     var tags = [];
  268.  
  269.     insertI2VTags(tags, 'table#copyright_root tr', 'series:', confidenceRequired);
  270.     insertI2VTags(tags, 'table#character_root tr', 'character:', confidenceRequired);
  271.  
  272.     if (!noGeneral)
  273.         insertI2VTags(tags, 'table#general_root tr', '', confidenceRequired);
  274.  
  275.     if (!noRating)
  276.         insertI2VTags(tags, 'table#rating_root tr', 'rating:', confidenceRequired);
  277.  
  278.     copyTagsToClipboard(tags);
  279.  
  280.     return tags;
  281. }
  282.  
  283. function doCopyAll(callback)
  284. {
  285.     control.style.opacity = '1';
  286.  
  287.     if (window.location.href.indexOf('nhentai.net') >= 0)
  288.         copyNHentaiTags(null, callback);
  289.     else if (window.location.href.indexOf('illustration2vec.net') >= 0)
  290.         callback(copyI2VTags(iv2_confidence_rating, false));
  291.     else
  292.         callback(copyBooruTags());
  293. }
  294.  
  295. function copyTagsToClipboard(tags)
  296. {
  297.     GM_setClipboard(tags.join('\n'));
  298.  
  299.     var buttontext = '';
  300.  
  301.     if (tags.length > 0)
  302.     {
  303.         playCopySound();
  304.         buttontext = 'copied ' +  tags.length +' tag(s)';
  305.     }
  306.     else
  307.         buttontext = 'nothing to copy!';
  308.  
  309.     var button = document.querySelector('button#copytagsbutton');
  310.     if (button)
  311.     {
  312.         button.innerHTML = buttontext;
  313.         setTimeout(function(){ button.innerHTML = 'copy tags'; }, 3000);
  314.     }
  315. }
  316.  
  317. function doOptions()
  318. {
  319.     optionsArea.style.display = optionsArea.style.display == 'none' ? 'block' : 'none';
  320. }
  321.  
  322. function makeDownloadRequest(href, tags)
  323. {
  324.     if (!tags)
  325.         tags = [];
  326.  
  327.     GM_xmlhttpRequest({
  328.         'method':'POST',
  329.         'url':'http://localhost:14007/download?' + href,
  330.         'data':tags.join(','),
  331.         'anonymous':true,
  332.         'timeout':12500,
  333.         'onerror':function() { alert('Error downloading to your local server. Is boorutagparser-server running?\nGet it at github.com/JetBoom/boorutagparser-server if you do not have it.'); }
  334.     });
  335. }
  336.  
  337. function doDownload()
  338. {
  339.     var a = document.querySelector('a#highres, a[itemprop="contentSize"], a.original-file-unchanged, li > a[href*="/images/"], section#image-container > a > img, img#image, img[src*="/_images/"], a[href*="/img/download/"]');
  340.     if (!a)
  341.         return;
  342.  
  343.     var href = a.src || a.href;
  344.  
  345.     doCopyAll(function(tags) { makeDownloadRequest(href, tags); } );
  346. }
  347.  
  348. function doc_keyUp(e)
  349. {
  350.     if (e.keyCode == copy_key_code)
  351.         doCopyAll();
  352.     else if (e.keyCode == download_key_code)
  353.         doDownload();
  354. }
  355. document.addEventListener('keyup', doc_keyUp, false);
  356.  
  357. var elements = document.querySelectorAll(tags_selector);
  358. for (var i=0; i < elements.length; i++)
  359. {
  360.     var element = elements[i];
  361.     if (element.innerHTML == 'Tags')
  362.     {
  363.         element.onclick = copyBooruTags;
  364.         break;
  365.     }
  366. }
  367.  
  368. if (copy_sound.length > 0)
  369. {
  370.     var audio = document.createElement("audio");
  371.     audio.src = copy_sound;
  372.     audio.preload = false;
  373. }
  374.  
  375. function playCopySound()
  376. {
  377.     if (audio && copy_sound.length > 0)
  378.         audio.play();
  379. }
  380.  
  381. function doChangeConfidence(e)
  382. {
  383.     iv2_confidence_rating = Number(optionForConfidence.value);
  384.     captionForConfidence.innerHTML = 'iv2 min confidence: ' + iv2_confidence_rating + '%';
  385.     GM_setValue('iv2_confidence_rating', iv2_confidence_rating);
  386. }
  387.  
  388. function doChangeCopySound(e)
  389. {
  390.     copy_sound = String(optionForCopySound.value);
  391.     GM_setValue('copy_sound', copy_sound);
  392.  
  393.     if (audio)
  394.         audio.src = copy_sound;
  395. }
  396.  
  397. function doChangeAttachExplicit(e)
  398. {
  399.     attach_explicit = optionForAttachExplicit.checked;
  400.     GM_setValue('attach_explicit', attach_explicit);
  401. }
  402.  
  403. function doChangeAttachGID(e)
  404. {
  405.     attach_gid = optionForAttachGID.checked;
  406.     GM_setValue('attach_gid', attach_gid);
  407. }
  408.  
  409. function doChangeDivTop(e)
  410. {
  411.     div_top = optionForDivTop.checked;
  412.     GM_setValue('div_top', div_top);
  413.  
  414.     if (div_top)
  415.     {
  416.         control.style.top = '2px';
  417.         control.style.bottom = '';
  418.     }
  419.     else
  420.     {
  421.         control.style.top = '';
  422.         control.style.bottom = '2px';
  423.     }
  424. }
  425.  
  426. var control = document.createElement('div');
  427. control.id = 'boorutagparser';
  428. control.setAttribute('style', div_style);
  429. control.onmouseenter = function(e) { this.style.opacity = 1; };
  430. control.onmouseleave = function(e) { this.style.opacity = 0.2; };
  431. document.body.appendChild(control);
  432.  
  433. var copyButton = document.createElement('button');
  434. copyButton.innerHTML = 'copy tags';
  435. copyButton.id = 'copytagsbutton';
  436. copyButton.setAttribute('style', button_style);
  437. copyButton.onclick = doCopyAll;
  438. control.appendChild(copyButton);
  439.  
  440. var optionsButton = document.createElement('button');
  441. optionsButton.innerHTML = 'options';
  442. optionsButton.id = 'optionsbutton';
  443. optionsButton.setAttribute('style', button_style);
  444. optionsButton.onclick = doOptions;
  445. control.appendChild(optionsButton);
  446.  
  447. var downloadButton = document.createElement('button');
  448. downloadButton.innerHTML = 'download with tags';
  449. downloadButton.id = 'downloadbutton';
  450. downloadButton.setAttribute('style', button_style);
  451. downloadButton.onclick = doDownload;
  452. control.appendChild(downloadButton);
  453.  
  454. var optionsArea = document.createElement('div');
  455. optionsArea.id = 'optionsarea';
  456. optionsArea.setAttribute('style', 'display:none;');
  457. control.appendChild(optionsArea);
  458.  
  459. if (window.location.href.indexOf('illustration2vec.net') >= 0)
  460. {
  461.     var captionForConfidence = document.createElement('span');
  462.     captionForConfidence.id = 'captionconfidence';
  463.     captionForConfidence.innerHTML = 'iv2 min confidence: ' + iv2_confidence_rating + '%';
  464.     optionsArea.appendChild(captionForConfidence);
  465.  
  466.     var optionForConfidence = document.createElement('input');
  467.     optionForConfidence.id = 'optionsconfidence';
  468.     optionForConfidence.setAttribute('type', 'range');
  469.     optionForConfidence.setAttribute('value', iv2_confidence_rating);
  470.     optionForConfidence.setAttribute('min', 0);
  471.     optionForConfidence.setAttribute('max', 100);
  472.     optionForConfidence.onchange = doChangeConfidence;
  473.     optionsArea.appendChild(optionForConfidence);
  474. }
  475.  
  476. if (window.location.href.indexOf('nhentai.net/g/') >= 0)
  477. {
  478.     var optionForAttachExplicit = document.createElement('input');
  479.     optionForAttachExplicit.setAttribute('type', 'checkbox');
  480.     optionForAttachExplicit.checked = attach_explicit;
  481.     optionForAttachExplicit.onchange = doChangeAttachExplicit;
  482.     optionsArea.appendChild(optionForAttachExplicit);
  483.  
  484.     var captionForAttachExplicit = document.createElement('span');
  485.     captionForAttachExplicit.innerHTML = 'add rating:explicit<br>';
  486.     optionsArea.appendChild(captionForAttachExplicit);
  487.  
  488.     var optionForAttachGID = document.createElement('input');
  489.     optionForAttachGID.setAttribute('type', 'checkbox');
  490.     optionForAttachGID.checked = attach_gid;
  491.     optionForAttachGID.onchange = doChangeAttachGID;
  492.     optionsArea.appendChild(optionForAttachGID);
  493.  
  494.     var captionForAttachGID = document.createElement('span');
  495.     captionForAttachGID.innerHTML = 'add gallery:id#<br>';
  496.     optionsArea.appendChild(captionForAttachGID);
  497. }
  498.  
  499. var captionForCopySound = document.createElement('span');
  500. captionForCopySound.id = 'captioncopysound';
  501. captionForCopySound.innerHTML = 'copy sound url';
  502. optionsArea.appendChild(captionForCopySound);
  503.  
  504. var optionForCopySound = document.createElement('input');
  505. optionForCopySound.id = 'optionssound';
  506. optionForCopySound.setAttribute('value', copy_sound);
  507. optionForCopySound.setAttribute('style', 'display:block;font-size:10px;width:90%;margin:1px auto;');
  508. optionForCopySound.onchange = doChangeCopySound;
  509. optionsArea.appendChild(optionForCopySound);
  510.  
  511. var optionForDivTop = document.createElement('input');
  512. optionForDivTop.setAttribute('type', 'checkbox');
  513. optionForDivTop.checked = div_top;
  514. optionForDivTop.onchange = doChangeDivTop;
  515. optionsArea.appendChild(optionForDivTop);
  516.  
  517. var captionForDivTop = document.createElement('span');
  518. captionForDivTop.innerHTML = 'attach to top of page<br>';
  519. optionsArea.appendChild(captionForDivTop);
  520.  
  521. var version = document.createElement('div');
  522. version.innerHTML = GM_info.script.version;
  523. version.setAttribute('style', 'font-size:8px;');
  524. optionsArea.appendChild(version);
  525.  
  526. doChangeDivTop();
Advertisement
Add Comment
Please, Sign In to add comment