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.2-fork
// @author      Sibyl
// @description 1.0.3 2024-06-19 (upstream), 2024-12-31 (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");
      const addButton = articles =>
        articles.forEach(el => {
          const div = document.createElement("div");
          this.addButton(el, div);
          el.querySelector(".post-preview-container").nextElementSibling.appendChild(div);
        });
      if ((articles.length === 5 && shownCount > 5) || articles.length === shownCount) addButton(articles);
      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);
            addButton(articles);
          });
      }
    }

    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

Probably not the right thread to ask this but I used to have a bookmarklet that would grab pixiv/bsky/twitter user IDs. I lost it when upgrading to a new version of Firefox and can't find it anymore. Is this one still around or does an userscript port of it exist?. Would love to have it again.

Asht said:

Probably not the right thread to ask this but I used to have a bookmarklet that would grab pixiv/bsky/twitter user IDs. I lost it when upgrading to a new version of Firefox and can't find it anymore. Is this one still around or does an userscript port of it exist?. Would love to have it again.

This one generally works. Copy the text starting from javascript:

I had plans for writing something like this for a bit but was a bit lazy. I saw UnderCurve metion it on discord so I threw something together. I don't see any glaring issues with the brief use so far so I thought I'd share it.

The script checks the artist of an upload for deleted posts/ai tagged art and presents a color accordingly.

Green - Nothing deleted
Yellow - More than 1 deleted image (on the first page)
Orange - More than half deleted images (on the first page)
Red - Post found with "ai-generated"

Code
// ==UserScript==
// @name         AI Check
// @namespace    http://tampermonkey.net/
// @version      2024-12-31
// @description  Spy check!
// @author       waterflame
// @match        https://danbooru.donmai.us/uploads/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=donmai.us
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    async function process() {
        const tag = document.querySelector("#post_tag_string").value;
        if (tag == "") return;
        document.querySelector("#aic").innerText = " - Loading...";
        const resp = await fetch(`https://danbooru.donmai.us/posts.json?tags=${encodeURIComponent(tag)}`, {
            method: 'get',
            headers: {
                'Content-Type': 'application/json'
            }
        });

        if (!resp.ok) {
            document.querySelector("#aic").innerText = ` - ${resp.status} ${resp.statusText}`;
            return;
        }

        const posts = await resp.json();
        var deleted = 0;
        var ai = 0;
        for (const post of posts) {
            if (post.is_deleted) {
                deleted += 1;
            }
            if (post.tag_string.includes("ai-generated")) {
                ai += 1;
            }
        }

        if (ai > 0) {
            document.querySelector("#aic").innerText = " - Red";
            document.querySelector("#aic").parentElement.style.color = "red";
        } else if (deleted >= (Math.round(posts.length/2))) {
            document.querySelector("#aic").innerText = ` - Orange(${deleted}/${posts.length})`;
            document.querySelector("#aic").parentElement.style.color = "orange";
        } else if (deleted > 0) {
            document.querySelector("#aic").innerText = ` - Yellow(${deleted}/${posts.length})`;
            document.querySelector("#aic").parentElement.style.color = "#dad55e";
        } else {
           document.querySelector("#aic").parentElement.style.color = "green";
           document.querySelector("#aic").innerText = " - Green";
        }
    }

    const span = document.createElement("span");
    span.innerText = "Artist Check";
    const s2 = document.createElement("span");
    s2.id = "aic";
    span.append(s2);
    document.querySelector("#related-tags-container").insertAdjacentElement("beforeBegin",span);
    document.querySelector("#related-tags-container").insertAdjacentHTML("beforeBegin","<br><br>");
    process();
})();
1 7 8 9 10 11