Donmai

Danbooru (etc...) userscripts

Posted under General

Easier 1up b37

Forked from https://gist.github.com/TypeA2/bff1474c0f4ca2188cf21897d4e4b2dd

This is a userscript written for users below level 37. Additionally, it allows you to 1up related posts from the same source.

Show
// ==UserScript==
// @name        Easier 1up b37
// @namespace   https://danbooru.donmai.us/forum_topics/8502
// @match       *://*.donmai.us/uploads/*
// @grant       none
// @version     0.0.1-fork
// @author      Sibyl
// @description 1.0.3 2024-06-19 (upstream), 2024-12-10 (current)
//              Forked from https://gist.github.com/TypeA2/bff1474c0f4ca2188cf21897d4e4b2dd
// @run-at      document-end
// ==/UserScript==

const CUSTOM_THUMBNAIL = 23609685; // Custom thumbnail for banned posts with media asset ID

const easier1Up = {
  iconHash: document.querySelector("a#close-notice-link use").href.baseVal.split(/-|\./)[1],
  tagsField: document.querySelector("#post_tag_string"),
  async init() {
    let thumbnailData = JSON.parse(localStorage.getItem("easier_1up_b37"));
    if (!thumbnailData || thumbnailData.id !== CUSTOM_THUMBNAIL) {
      let resp = await (await fetch(`/media_assets/${CUSTOM_THUMBNAIL}.json`)).json();
      if (!resp || resp.error) {
        thumbnailData = {
          id: 23609685,
          url: "https://cdn.donmai.us/180x180/3e/3c/3e3c7baac2a12a0936ba1f62a46a3478.jpg",
          width: 180,
          height: 135
        };
      } else {
        thumbnailData = resp.variants.filter(v => v.type === "180x180")[0];
        thumbnailData.id = resp.id;
      }
      localStorage.setItem("easier_1up_b37", JSON.stringify(thumbnailData));
    }
    this.thumbnail = thumbnailData;

    const relatedPosts = document.querySelector("#related-posts-by-source p.fineprint a");
    if (relatedPosts) {
      const shownCount = Number(relatedPosts.innerText.split(" ")[0]);
      let articles = document.querySelectorAll("#related-posts-by-source article");
      if ((articles.length === 5 && shownCount > 5) || articles.length === shownCount);
      else {
        const url = new URL(relatedPosts.href);
        url.pathname = "/posts.json";
        url.searchParams.append("limit", 5);
        fetch(url)
          .then(resp => resp.json())
          .then(json => {
            articles = this.updateArticles(json, articles, true);
            articles.forEach(el => {
              const div = document.createElement("div");
              this.addButton(el, div);
              el.querySelector(".post-preview-container").nextElementSibling.appendChild(div);
            });
          });
      }
    }

    const similar = document.getElementById("iqdb-similar");
    this.observer = new MutationObserver(ms => ms.forEach(m => m.addedNodes.forEach(this.process.bind(this))));
    this.observer.observe(similar, {
      subtree: true,
      childList: true
    });
  },
  async process(node) {
    if (node.className !== "iqdb-posts") return;
    let articles = node.querySelectorAll("#iqdb-similar article");
    let shownCount = articles.length;
    let iqdbNoPostFound = shownCount === 0 && document.querySelector(".post-gallery-grid > p:only-child");
    if (!iqdbNoPostFound && shownCount !== 5) {
      let iqdbResults = await this.iqdbReq();
      if (iqdbResults.length !== shownCount) articles = this.updateArticles(iqdbResults, articles);
    }
    for (const post of articles) {
      const div = post.querySelector(".iqdb-similarity-score").parentElement;
      this.addButton(post, div);
    }
    this.observer?.disconnect();
  },
  copyTags(post, isParent) {
    const tags = post.dataset.tags.split(" ").filter(t => t === "social_commentary" || t.indexOf("commentary") == -1);
    document.querySelector(`input.radio_buttons[value='${post.dataset.rating}']`).checked = true;
    if (isParent) {
      document.getElementById("post_parent_id").value = post.dataset.id;
    } else tags.push("child:" + post.dataset.id);
    this.tagsField.value = tags.join(" ") + " ";
    this.tagsField.dispatchEvent(new InputEvent("input", { bubbles: true }));
    document.querySelector(".source-tab").click();
    Danbooru.Utility.notice("Successfully copied tags. Please check the commentary tags.");
  },
  addButton(post, div) {
    const setParent = document.createElement("a");
    setParent.classList.add("inactive-link");
    setParent.href = "#";
    setParent.innerText = "parent";
    setParent.addEventListener("click", e => {
      e.preventDefault();
      this.copyTags(post, true);
    });
    const setChild = document.createElement("a");
    setChild.classList.add("inactive-link");
    setChild.href = "#";
    setChild.innerText = "child";
    setChild.addEventListener("click", e => {
      e.preventDefault();
      this.copyTags(post, false);
    });
    div.children.length && div.appendChild(document.createTextNode(" | "));
    div.appendChild(setParent);
    div.appendChild(document.createTextNode(" | "));
    div.appendChild(setChild);
  },
  async iqdbReq() {
    try {
      let mid = document.getElementById("media_asset_id").value;
      let resp = await (
        await fetch(`/iqdb_queries.json?limit=5&search%5Bmedia_asset_id%5D=${mid}&search%5Bsimilarity%5D=50&search%5Bhigh_similarity%5D=70`)
      ).json();
      if (Array.isArray(resp)) return resp;
      else throw new Error(JSON.stringify(resp));
    } catch (e) {
      console.error("Error:", e);
    }
  },
  updateArticles(posts, currentPosts, relatedSection = false) {
    currentPosts = Array.from(currentPosts);
    const currentPostIds = currentPosts.map(el => {
      return Number(el.getAttribute("data-id"));
    });
    currentPostIds.push(0);
    let idx = 0,
      postsLength = posts.length;
    currentPostIds.forEach((pid, index) => {
      let htmlToInsert = "";
      for (; idx < postsLength; idx++) {
        let post = relatedSection ? posts[idx] : posts[idx].post;
        if (post.id !== pid) {
          if (post.is_banned) htmlToInsert += relatedSection ? this.render(post) : this.render(post, posts[idx].score);
        } else break;
      }
      if (htmlToInsert) {
        if (pid === 0) {
          const prefix = relatedSection ? "#related-posts-by-source" : ".iqdb-posts";
          document.querySelector(prefix + " .posts-container").insertAdjacentHTML("beforeend", htmlToInsert);
        } else currentPosts[index].insertAdjacentHTML("beforebegin", htmlToInsert);
      }
    });
    const prefix = relatedSection ? "#related-posts-by-source" : "#iqdb-similar";
    return document.querySelectorAll(prefix + " article");
  },
  render(
    {
      id,
      uploader_id,
      score,
      rating,
      tag_string,
      is_pending,
      is_flagged,
      is_deleted,
      has_children,
      parent_id,
      source,
      media_asset: { id: mid, image_width, image_height, file_size, file_ext }
    },
    similarity
  ) {
    const dataFlag = is_pending ? "pending" : is_flagged ? "flagged" : is_deleted ? "deleted" : "";

    const classList = ["post-preview", "post-preview-fit-compact", "post-preview-180"];
    is_pending && classList.push("post-status-pending");
    is_flagged && classList.push("post-status-flagged");
    is_deleted && classList.push("post-status-deleted");
    has_children && classList.push("post-status-has-children");
    parent_id && classList.push("post-status-has-parent");
    similarity && classList.push(similarity < 70 ? "iqdb-low-similarity hidden" : "iqdb-high-similarity");

    const { url, width, height } = this.thumbnail;
    const similarityHtml = similarity
      ? `<div><a class="inactive-link iqdb-similarity-score" href="/iqdb_queries?post_id=${id}">${similarity.toFixed(0)}% similar</a></div>`
      : "";

    return `<article id="post_${id}" class="${classList.join(
      " "
    )}" data-id="${id}" data-tags="${tag_string}" data-rating="${rating}" data-flags="${dataFlag}" data-score="${score}" data-uploader-id="${uploader_id}">
<div class="post-preview-container">
<a class="post-preview-link" draggable="false" href="/posts/${id}">
<picture><img src="${url}" width="${width}" height="${height}" class="post-preview-image" title="" alt="post #${id}"draggable="false" aria-expanded="false" data-title="${tag_string} rating:${rating} score:${score}"></picture>
</a></div><div class="text-xs text-center mt-1"><div>
<a rel="external noreferrer nofollow" title="${source}" class="inline-block align-top" href="${source}">
<svg class="icon svg-icon globe-icon h-4" viewBox="0 0 512 512"><use fill="currentColor" href="/packs/static/icons-${this.iconHash}.svg#globe"></use></svg>
</a>
<a href="/media_assets/${mid}">${this.formatBytes(file_size)} .${file_ext}, ${image_width}×${image_height}</a></div>${similarityHtml}</div>
</article>`;
  },
  formatBytes(bytes) {
    if (bytes === 0) return "0 Bytes";
    const units = ["Bytes", "KB", "MB"];
    const k = 1024;
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    const value = bytes / Math.pow(k, i);
    const formattedValue = value % 1 === 0 ? value.toFixed(0) : value.toFixed(2);
    return `${formattedValue} ${units[i]}`;
  }
};
easier1Up.init();

Get a better experience by using it with the script in forum #312212.

Updated

SpikySquid said:

Easy Pre-tagging manager

  • "Save tags" button on the upload page for saving tags for unposted uploads
  • Posts => Uploads => Pre-tagged Uploads - Browse pre-tagged uploads
  • The userscript saves all of the data locally on your browser's config folder by using JavaScript's localStorage variable
Show
// ==UserScript==
// @name         Danbooru - Easy Pre-tagging manager
// @namespace    http://tampermonkey.net/
// @version      2024-10-29
// @description  Save and manage tags on unposted uploads
// @author       yyk
// @match        https://danbooru.donmai.us/*
// @match        https://aibooru.online/*
// @match        https://gaybooru.app/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=donmai.us
// @grant        none
// ==/UserScript==

// Before using the plugin type:
// localStorage.setItem("danbooru_savedtags_json", `[]`);
// into your JavaScript console
// created at 2024-09-02

(function() {
    'use strict';

    if(!JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`))) {localStorage.setItem(`danbooru_savedtags_json_(${window.location.host})`, `[]`);}

    if(window.location.href.startsWith(`https://${window.location.host}/uploads/new`) || /\/users\/\d+\/uploads/.test(window.location.href) || /\/uploads\/\d+\/assets/.test(window.location.href) ) {
        document.querySelector("#subnav-all-uploads").outerHTML+=`
        <li id="subnav-all-uploads"><a id="subnav-all-uploads-link" href="/pretagged_uploads">Pre-tagged uploads</a></li>
        `
    }

    if(  /\/users\/\d+\/uploads/.test(window.location.href) || /\/uploads\/\d+\/assets/.test(window.location.href) ) {
        var danbooru_savedtags_json=JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`));
        (document.querySelectorAll(".media-asset-preview a")).forEach(item => {
            var existing_upload=danbooru_savedtags_json.find((element) => element.loc == `${item.href.replace(`https://${window.location.host}/uploads/`,"")}`);
            //console.log( existing_upload===undefined )
            if(existing_upload!=undefined) {
                item.style.display="relative";
                item.innerHTML+="<div style='overflow:hidden; color:white; background:green; opacity:0.7; width:100%; height:15px; bottom:0; right:0; position:absolute; font-size:14px'>Pretagged</div>"
            }
            else if(item.href.endsWith("assets")) {
                var existing_upload=danbooru_savedtags_json.find((element) => element.loc.split("/")[0] == item.href.split("/")[4]);
                if(existing_upload!=undefined) {
                    item.style.display="relative";
                    item.innerHTML+="<div style='overflow:hidden; color:white; background:green; opacity:0.7; width:100%; height:15px; bottom:0; right:0; position:absolute; font-size:14px'>Contains pretagged</div>"
                }
            }

        })
    }

    if(window.location.href.startsWith(`https://${window.location.host}/pretagged_uploads`)) {
        var myFunctions = window.myFunctions = {};
        document.title="DB | Pretagged uploads"

        myFunctions.pasteDBSTJ = function() {
            danbooru_savedtags_json=JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`));
            console.log(
                JSON.stringify(danbooru_savedtags_json).split("").reverse().join("")
            )
        }

        myFunctions.removePretagsFor = function(locix) {
            console.log(locix);
            if (confirm(`Are you sure about deleting your saved tags for ${locix}? Only delete saved tags if the asset was already uploaded or is a duplicate.`) == true) {
                var existing_upload=danbooru_savedtags_json.find((element) => element.loc == `${locix}`);
                let index = danbooru_savedtags_json.indexOf(existing_upload);
                if(index!=-1) {
                    danbooru_savedtags_json.splice(index, 1);
                    localStorage.setItem(`danbooru_savedtags_json_(${window.location.host})`, JSON.stringify(danbooru_savedtags_json));
                }
                document.querySelector(`#pretag-item-${locix.replaceAll("/","-")}`).remove();
            } else {
                text = "You canceled!";
            }
        }

        myFunctions.getColorFromScore = function(score) {
            var maxVal=30;
            score = Math.max(0, Math.min(maxVal, score));

            const percentage = (score / maxVal) * 100;

            const red = Math.floor(255 - (percentage / 100) * 255);
            const green = Math.floor((percentage / 100) * 255);

            const hex = `#${red.toString(16).padStart(2, '0')}${green.toString(16).padStart(2, '0')}00`;

            return hex;
        }

        var danbooru_savedtags_json=JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`));
        document.querySelector("#a-not-found").innerHTML=`
           <div id="pretagged-posts" class="user-favorites recent-posts">
   <h2 onclick="myFunctions.pasteDBSTJ()" class="recent-posts-header">Pre-tagged posts</h2>`;

        var timeout_slp=50;
        danbooru_savedtags_json=danbooru_savedtags_json.sort((a, b) => b.loc.localeCompare(a.loc));
        danbooru_savedtags_json.forEach(item => {
              var tag_count=item.tagstr.replaceAll("\n"," ").split(" ").length;
              var tag_count_bg=myFunctions.getColorFromScore(tag_count);
              document.querySelector("#pretagged-posts").innerHTML+=`<div id="pretag-item-${item.loc.replaceAll("/","-")}" style="width:150px; height:190px; float:left; margin:3px; position:relative;">
              <div onclick="myFunctions.removePretagsFor('${item.loc}')" class="delete-butt" style="background-color:red; border-radius:3px; width:25px; height:25px; position:absolute; right:0; opacity:0.5; z-index:90"></div>
      <a class="post-preview-link" draggable="false" href="/uploads/${item.loc}">
         <picture>
            <img src="https://cdn.donmai.us/180x180/41/55/41553d2ffa946482b91fb0cd51bc59ab.jpg" style="width:145px;height:145px;object-fit:contain;" class="post-preview-image">
         </picture>
      </a>

      <div class="post-preview-score text-sm text-center mt-1">
         <span onclick="console.log(\`${item.tagstr}\`)" class="post-score inline-block text-center whitespace-nowrap align-middle min-w-4">
           ${item.loc}</span><br>
           <span style="background: linear-gradient(rgba(0,0,0,0.4),rgba(0,0,0,0.4)), linear-gradient(${tag_count_bg},${tag_count_bg}); padding: 0px 4px 1px 4px; border-radius:2px; border:1px solid white;">${tag_count} tags</span>
      </div>

   </div>

  </div>`;

            setTimeout(function() {
            fetch(`/uploads/${item.loc}.json`,{
                credentials: "same-origin"
            })
            .then(response => response.json())
            .then(
                json => {
                    //console.log(JSON.stringify(json))
                    if(item.loc.includes("/assets/")) {
                         fetch(`/media_assets/${json.media_asset_id}.json`,{
                             credentials: "same-origin"
                         })
                         .then(response => response.json())
                         .then(json2 => {
                             //console.log(JSON.stringify(json2))
                             document.querySelector(`#pretag-item-${item.loc.replaceAll("/","-")} img`).src=json2.variants[0].url;
                         })
                    }
                    else {
                         document.querySelector(`#pretag-item-${item.loc.replaceAll("/","-")} img`).src=json.upload_media_assets[0].media_asset.variants[0].url;
                    }
                }
            )
            }, timeout_slp);
            timeout_slp+=200;

        })
    }

    if(/(\/uploads\/\d+$)|(\/uploads\/\d+\/assets\/\d+$)/.test(window.location.href)) {
        var myFunctions = window.myFunctions = {};

        myFunctions.getUploadItemLocationPath = function() {
            return window.location.href.replace(`https://${window.location.host}/uploads/`,"").replaceAll(/\?.*/g,'');
        }

        myFunctions.loadSavedTagstr = function() {
            var upload_loc=myFunctions.getUploadItemLocationPath();
            var danbooru_savedtags_json=JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`));
            var existing_upload=danbooru_savedtags_json.find((element) => element.loc == `${upload_loc}`);
            if(existing_upload!=undefined) {
                document.querySelector("#post_tag_string").value=existing_upload.tagstr;
            }
        }

        myFunctions.saveTagString = function() {
            var upload_loc=myFunctions.getUploadItemLocationPath();
            var danbooru_savedtags_json=JSON.parse(localStorage.getItem(`danbooru_savedtags_json_(${window.location.host})`));
            var existing_upload=danbooru_savedtags_json.find((element) => element.loc == `${upload_loc}`);
            console.log(upload_loc)
            if(existing_upload!=undefined) {
                existing_upload.tagstr=document.querySelector("#post_tag_string").value;
            }
            else {
                existing_upload={loc:`${upload_loc}`, tagstr:document.querySelector("#post_tag_string").value};
                danbooru_savedtags_json.push(existing_upload);
            }
            localStorage.setItem(`danbooru_savedtags_json_(${window.location.host})`, JSON.stringify(danbooru_savedtags_json));
            console.log(danbooru_savedtags_json);
        }

        myFunctions.updateGUI = function () { console.log("hi") };

        document.querySelector(".mb-4").outerHTML='<input type="submit" onclick="preventDefault();" id="btn-pass-tags" style="float: left; margin-right: 6px;" type="button" value="Save tags" class="button-primary button-sm">'+document.querySelector(".mb-4").outerHTML;
        document.querySelector("#btn-pass-tags").addEventListener("click", function(ev) {
            ev.preventDefault();
            myFunctions.saveTagString();
            console.log(myFunctions.getUploadItemLocationPath());
            localStorage.setItem("somecuterandomstring", myFunctions.getUploadItemLocationPath());
        });

        myFunctions.loadSavedTagstr();

        const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        const pretagged_tags = urlParams.get('pretagged_tags')
        if(pretagged_tags!=null) {
            document.querySelector("#post_tag_string").value+=" "+pretagged_tags
        }

    }

})();

I modified this to allow it to work alongside "Validate Tag Input".

https://gist.github.com/Shinjidude/d8b815519a2a07ca82d25c66a213a06b/raw/DanbooruEasyPretaggingManager.user.js

Use source img to preview image when uploading

The image server is having hiccups right now, so I made this script to use image previews from the original source.

  • This will not work with assets that weren't uploaded by pasting the source link.
  • This will not work with pixiv images and it simply won't replace the img.src (image source).
  • This will not work if the image was deleted from it's original source or if the link is private or inaccessible.
Show
// ==UserScript==
// @name        Danbooru - Use source img to preview image when uploading
// @namespace   Violentmonkey Scripts
// @match       https://danbooru.donmai.us/uploads/*
// @grant       none
// @version     1.0
// @author      YYK
// @description 12/13/2024, 2:05:20 PM
// ==/UserScript==

(function() {

    fetch(window.location.href+".json")
    .then(response=>response.json())
    .then(data=>{
        console.log(data)
        var new_src_link=""
        if(data.upload_media_assets) new_src_link=(data.upload_media_assets[0]['source_url'])
        if(data.source_url) new_src_link=data.source_url
        img_link_valid=(new_src_link.includes("https://") && !new_src_link.includes("/i.pximg.net/"))
        if(img_link_valid) document.querySelector(".media-asset-component img").src=new_src_link
    });

})()

Updated

1 7 8 9 10 11