Donmai

Danbooru (etc...) userscripts

Posted under General

incubatedveg said:

I have some questions about scripting (Js) on the website, is anyone knowledgeable about that? Specifically Image Board Enhancer greasyfork userscript. I've made some modifications that seems to work almost perfectly in chrome, but when I switch to firefox, the addon doesnt work at all. My changes are here, i would love if someone who knows Js better could take a look at why it might work in Chrome but not Firefox. <3 thanks

Also, even though it works in chrome, i do have this tiny issue with one of the icons being way too small, which i have no idea why. https://github.com/vscum/Image-Board-Enhancer-Plus/blob/main/IBE%20Problem.PNG?raw=true

There's a wrong quote after the comma at the end of line 22 of IB-Icons.js. If you remove it the script works.

I can't reproduce the second issue.

ref: forum #220473

There was a request for a way to quickly switch the safe mode on and off. It was also mentioned on Discord that having the same for dark/light mode would be useful. The following bookmarklets can be used to achieve that.

Bookmarklets

Safe mode

Switches safe mode on and off.

javascript:Danbooru.CurrentUser.update({enable_safe_mode:!Danbooru.CurrentUser.data('enable-safe-mode')}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;});
Theme
Auto
javascript:Danbooru.CurrentUser.update({theme:'auto'}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;});
Light
javascript:Danbooru.CurrentUser.update({theme:'light'}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;});
Dark
javascript:Danbooru.CurrentUser.update({theme:'dark'}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;});

Setup

1. Right-click on Bookmarks Bar
2a. (Chrome) Add page
2b. (Firefox) Add bookmark
3. Add description to Name field
4. Copy bookmarklet text and add to URL field

To activate, just click the bookmark link.

I've developed a lexer (tokeniser) for tag expressions, supporting all the current syntax features. It's a Python module, plus an extensive set of test cases.

Documentation and source code:
https://bipface.gitlab.io/taglex/taglex.html
https://gitlab.com/bipface/taglex/-/tree/master

I recommend any new projects which need to deal with tag expressions should consider using this module as a basis or as a reference, since it deals with many of the intricacies present in the language which require a great deal of care to handle all cases correctly.

Feedback welcome -- please report any issues/proposals at the project page or to me directly.

I used the My Uploads feature a lot since it was implemented, essentially polluting my page. Some uploads I didn't post have later been uploaded by someone else with equivalent pictures, some I don't feel like posting anymore and others were sourced with multiple pictures in which I left cropped and sketch versions, so they stay in the page as "1/2 posted". I've been thinking, would there be any way to filter them at least?

EDIT: Thanks, it helps soo much. I added an * at the end of the fourth line to filter the posted uploads easily.

Updated

Nameless_Contributor said:

I made this script a while ago. It shows a checkbox to hide an upload when hovering over the thumbnail. You can use the "show hidden" toggle in the top right to see previously hidden uploads.

Shouldn't the @match lines have an * at the end so the userscript isn't suppressed when there's a query string from the "posted"/"unposted" filters or paginator?

Modified version of the theme switcher from forum #220485 that lets you toggle dark/light and doesn't add [object Object] to the page. (doesn't do auto mode)

javascript:void(Danbooru.CurrentUser.update({theme:document.body.dataset["currentUserTheme"]==="light"?"dark":"light"}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;}));

Talulah said:

Modified version of the theme switcher from forum #220485 that lets you toggle dark/light and doesn't add [object Object] to the page. (doesn't do auto mode)

javascript:void(Danbooru.CurrentUser.update({theme:document.body.dataset["currentUserTheme"]==="light"?"dark":"light"}).then(()=>{Danbooru.Utility.notice("Settings updated.");window.location=window.location;}));

A script that toggles modes on sunset/sunrise:

// ==UserScript==
// @name         Danbooru theme auto toggle
// @version      1
// @description  Changes theme automatically based on sunrise/sunset time
// @author       Dramorian, fredgido
// @match        https://danbooru.donmai.us/*
// @run-at       document-start
// ==/UserScript==

// Replace the following values with your local sunset and sunrise times in hh:mm format
const sunset = "18:00";
const sunrise = "06:00";

// Get the current time in hh:mm format
const currentTime = new Date().toLocaleTimeString([], {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false
});

// Check if the current time is between sunset and sunrise
const isNightTime = (currentTime >= sunset || currentTime < sunrise);

// Update the theme based on the time
const currentThemeIsDark = Danbooru.CurrentUser.darkMode();
if ((isNightTime && !currentThemeIsDark) || (!isNightTime && currentThemeIsDark)) {
    Danbooru.CurrentUser.update({
        theme: isNightTime ? "dark" : "light"
    }).then(() => {
        Danbooru.Utility.notice("Theme updated.");
        window.location = window.location;
    });
}

Thanks to @fredgido for helping in discord DMs.

alternative with using sunrise API:

// Type your latitude/longitude after "="
// E.g, for New York it is 40.7127281, -74.0060152
const apiUrl = "https://api.sunrise-sunset.org/json?lat=&lng=&formatted=0";

// Fetch the sunrise and sunset times from the API
fetch(apiUrl)
  .then(response => response.json())
  .then(data => {
    // Extract the sunrise and sunset times from the API response
    const sunrise = new Date(data.results.sunrise).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', hour12: false});
    const sunset = new Date(data.results.sunset).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', hour12: false});

    // Get the current time in hh:mm format
    const currentTime = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', hour12: false});

    // Check if the current time is between sunset and sunrise
    const isNightTime = (currentTime >= sunset || currentTime < sunrise);

    // Update the theme based on the time
    const currentThemeIsDark = Danbooru.CurrentUser.darkMode();
    if((isNightTime && !currentThemeIsDark) || (!isNightTime && currentThemeIsDark) ){
      Danbooru.CurrentUser.update({theme: isNightTime ? "dark" : "light"}).then(() => {
        Danbooru.Utility.notice("Theme updated.");
        window.location = window.location;
      });
    }
  })
  .catch(error => console.error(error));

Updated

Make image draggable like yande.re. Drag function modified from Moebooru.
You can prevent the image from moving by pressing the Ctrl or Alt key while adding a note box.

Show
// ==UserScript==
// @name         Danbooru - Drag Image
// @version      1.0
// @description  Drag image to view
// @author       Sibyl, Moebooru
// @match        https://danbooru.donmai.us/posts/*
// @run-at       document-end
// ==/UserScript==

(() => {
  let image = document.querySelector("picture > img#image");
  if (image) {
    dragElement(image);
    image.style.paddingRight = "10px";
  }
  document.querySelector("div#a-show")?.addEventListener("click", e => {
    if (e.target.classList.contains("image-view-original-link")) {
      document
        .querySelector("picture > img#image")
        .classList.remove("fit-width");
    }
  });

  function dragElement(el) {
    let prevPos = [];

    const current = (x, y) => {
      const windowOffset = [
        window.pageXOffset ||
          document.documentElement.scrollLeft ||
          document.body.scrollLeft,
        window.pageYOffset ||
          document.documentElement.scrollTop ||
          document.body.scrollTop,
      ];
      const offset = [
        windowOffset[0] + prevPos[0] - x,
        windowOffset[1] + prevPos[1] - y,
      ];
      prevPos[0] = x;
      prevPos[1] = y;
      return offset;
    };

    el.addEventListener("dragstart", () => false);

    return el.addEventListener("mousedown", e => {
      if (e.button !== 0 || e.altKey || e.ctrlKey) {
        return;
      }

      e.preventDefault();
      const pageScroller = function (e) {
        const scroll = current(e.clientX, e.clientY);
        window.scrollTo(scroll[0], scroll[1]);
        el.setAttribute("data-drag-element", "1");
        return false;
      };

      const unsetAttr = () => el.removeAttribute("data-drag-element");

      el.style.cursor = "grabbing";
      prevPos = [e.clientX, e.clientY];

      document.addEventListener("mousemove", pageScroller);

      document.addEventListener(
        "mouseup",
        () => {
          document.removeEventListener("mousemove", pageScroller);
          setTimeout(unsetAttr, 0);
          el.style.cursor = "auto";
          return false;
        },
        {
          once: true,
        }
      );
      return false;
    });
  }
})();
Favorite group enhance
  • Hover to show a button to remove current post from favorite group at the end of favorite group navigation bar.
Show
// ==UserScript==
// @name         Danbooru - Remove post from fav group
// @version      1.0
// @description  Add a remove button to the end of favorite group navigation bar
// @match        https://danbooru.donmai.us/posts/*
// @grant        none
// ==/UserScript==

(() => {
  let noticeSearchBar = document.querySelector(".post-notice-search"),
    favBars = noticeSearchBar?.querySelectorAll(".favgroup-navbar") || [],
    postId = document.querySelector('meta[name="post-id"]').content,
    headers = {
      "X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content,
    };
  document.head.insertAdjacentHTML(
    "beforeend",
    `<style>.post-notice-search > .favgroup-navbar {display: flex;align-items: center;}.favgroup-navbar > .favgroup-name {white-space: normal !important;}.favgroup-navbar:hover .fav-remove-link {opacity: 1;}.favgroup-navbar .fav-remove-link {opacity: 0;}.fav-remove-link {color: var(--button-danger-background-color);}.fav-remove-link:hover {color: var(--button-danger-hover-background-color);}</style>`
  );
  favBars.forEach(fav => {
    let favName = fav.querySelector(".favgroup-name");
    let pre = favName.children[0].href;
    favName.insertAdjacentHTML(
      "beforeend",
      '&nbsp;<a class="fav-remove-link text-lg" title="Remove from this group"><svg class="icon svg-icon close-icon" viewBox="0 0 320 512"><use fill="currentColor" href="/packs/static/images/icons-f4ca0cd60cf43cc54f9a.svg#xmark"></use></svg></a>'
    );
    favName.lastElementChild.addEventListener("click", () => {
      fetch(`${pre}/remove_post.js?post_id=${postId}`, {
        method: "PUT",
        headers,
      })
        .then(resp => resp.text())
        .then(text => {
          if (/"(Removed post from favorite group.+?)"\);/.test(text)) {
            window.Danbooru.notice(RegExp.$1);
            fav.remove();
            if (noticeSearchBar.children.length === 0) noticeSearchBar.remove();
          }
        });
    });
  });
})();
  • Add ascending & descending button to fav group edit page.
Show
// ==UserScript==
// @name         Danbooru - Fav group in ascending or descending order
// @version      1.0
// @description  Add ascending & descending button to fav group edit page
// @match        https://danbooru.donmai.us/favorite_groups/*/edit
// @grant        none
// ==/UserScript==

(() => {
  let textAreaLabel = document.querySelector(
    ".favorite_group_post_ids_string > label"
  );
  textAreaLabel.insertAdjacentHTML(
    "beforeend",
    `<span class="text-xxs text-center" style="font-weight:normal;">&nbsp;&nbsp;<a class="ids_ascending">Ascending</a>&nbsp;|&nbsp;<a class="ids_descending">Descending</a></span>`
  );
  textAreaLabel
    .querySelector("a.ids_ascending")
    .addEventListener("click", () => sortIds());
  textAreaLabel
    .querySelector("a.ids_descending")
    .addEventListener("click", () => sortIds(false));
  function sortIds(ascending = true) {
    let tArea = document.querySelector("#favorite_group_post_ids_string"),
      ids = tArea.value.trim(),
      idsArr = ids.split(/\s+/).filter(id => /^\d+$/.test(id));
    idsArr = [...new Set(idsArr)];
    idsArr.sort((a, b) => (ascending ? a - b : b - a));
    tArea.value = idsArr.join(" ");
    window.Danbooru.notice(
      `Sort in ${ascending ? "ascending" : "descending"} order.`
    );
  }
})();

Updated

skb044 said:

Nice, super convenient to check if I'm correctly using a tag, Thank you! Now I just need to find something similar for post editing and I'll be golden.

You can add the following two lines at the top of the script after // @exclude https://*.donmai.us/uploads/:

// @match       https://*.donmai.us/posts/*
// @exclude     https://*.donmai.us/posts/

Nameless_Contributor said:

You can add the following two lines at the top of the script after // @exclude https://*.donmai.us/uploads/:

// @match       https://*.donmai.us/posts/*
// @exclude     https://*.donmai.us/posts/

I've done similar edits to Danbooru EX because it had display issues on certain pages (overlapping header) but I didn't know this script code would be compatible on the edit page. Thank you for the tip! I don't know about others but personally, I would even suggest adding this bit to the original script, very compact and useful feature for new members who don't always have the tag definitions drilled in our mind, saves me having a couple of tabs open dedicated to checking wiki definitions.

skb044 said:

I've done similar edits to Danbooru EX because it had display issues on certain pages (overlapping header) but I didn't know this script code would be compatible on the edit page. Thank you for the tip! I don't know about others but personally, I would even suggest adding this bit to the original script, very compact and useful feature for new members who don't always have the tag definitions drilled in our mind, saves me having a couple of tabs open dedicated to checking wiki definitions.

So you have the same display issues as me? what was the edit you did on the Db EX script?

Mayhem-Chan said:

So you have the same display issues as me? what was the edit you did on the Db EX script?

My fix is probably going to disappoint you, it works well enough for me but it's no complete fix, I'm no web dev unfortunately.

I assume the original script was supposed to insert itself as part of the original site header just above it, but since I couldn't find a way to make it "push" the original navigation bar under, I made a dirty fix to force it into the compact mode (the one you get when scrolling down) at all time. In the compact mode it at least doesn't overlap with the navbar, and so can still be enabled without getting in the way on pages which do not support scrolling (ex: site_map, etc.).

line 1457:

<h1 class="ex-small-header"><a href="/">Danbooru</a></h1>

I think it's the only edit I did, please tell me if I missed anything else required to make it work, not lying when I said it's a really low tech janky fix.

skb044 said:

My fix is probably going to disappoint you, it works well enough for me but it's no complete fix, I'm no web dev unfortunately.

I assume the original script was supposed to insert itself as part of the original site header just above it, but since I couldn't find a way to make it "push" the original navigation bar under, I made a dirty fix to force it into the compact mode (the one you get when scrolling down) at all time. In the compact mode it at least doesn't overlap with the navbar, and so can still be enabled without getting in the way on pages which do not support scrolling (ex: site_map, etc.).

line 1457:

<h1 class="ex-small-header"><a href="/">Danbooru</a></h1>

I think it's the only edit I did, please tell me if I missed anything else required to make it work, not lying when I said it's a really low tech janky fix.

Seems to work exactly like it should so far, thanks

hdk5 said:

Image pan & zoom for new upload page, replaces the current two-state zoom toggle

https://gist.github.com/hdk5/b6261471096dc0c45a3563dc9af58949

I had some issues with it not centering the image correctly when creating a new upload, because the image was still loading, but I managed to fix it by replacing the current this.$panzoom.moveTo() call on line 40 with the following:

Show
const img = this.$image.get(0);
const move_func = () => {
    this.$panzoom.moveTo(
        this.$container.width() / 2) - (this.$image.width() / 2),
        this.$container.height() / 2) - (this.$image.height() / 2),
    );
}
if (img.complete) {
    move_func();
} else {
    img.addEventListener("load", move_func);
}
1 3 4 5 6 7 8 9 10 11