Sleazy Fork is available in English.

Gelbooru Tag Copyer

复制 Gelbooru 的 tags,支持自定义选择 tag 类型 / Quickly copy tags from gelbooru's post so you can use thoes tags to generate AI images.

  1. // ==UserScript==
  2. // @name Gelbooru Tag Copyer
  3. // @namespace https://greasyfork.org/zh-CN/scripts/439308
  4. // @version 1.5
  5. // @description 复制 Gelbooru 的 tags,支持自定义选择 tag 类型 / Quickly copy tags from gelbooru's post so you can use thoes tags to generate AI images.
  6. // @author 3989364
  7. // @include *://gelbooru.com/index.php*
  8. // @icon https://gelbooru.com/favicon.ico
  9. // @grant none
  10. // @license MIT
  11.  
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. "use strict";
  16. const tagListEle = document.querySelector("#tag-list");
  17.  
  18. const DEFAULT_CHECKED_TAGS = ["general", "copyright", "character"];
  19. const DEFAULT_QUALITY_TAG = ''; // `((masterpiece)), (((best quality))), ((ultra-detailed)), ((illustration)), (an extremely delicate and beautiful), `;
  20.  
  21. const attriTags = ["hair", "eyes", "dress", "sleeves", "bow"];
  22.  
  23. const EXCLUDED_TAGS = ["censor", "out-of-frame", "speech bubble"];
  24.  
  25. function initUI(tagListEle, defultCheckedTags) {
  26. /**
  27. * @param tagListEle
  28. * @returns [String, ...]
  29. */
  30. function parseTags(tagListEle) {
  31. const tags = Object.create(null);
  32.  
  33. tagListEle
  34. .querySelectorAll('li[class^="tag-type"]')
  35. .forEach((tagItem) => {
  36. const tagEle = tagItem.querySelector(".sm-hidden + a");
  37. const tag = tagEle.textContent;
  38.  
  39. const tagCount = parseInt(tagEle.nextElementSibling.textContent) || 0;
  40.  
  41. const tagType = tagItem.className.replace("tag-type-", "");
  42. if (!tags[tagType]) {
  43. tags[tagType] = [];
  44. }
  45.  
  46. tags[tagType].push({ tag, tagCount });
  47. });
  48.  
  49. // sort general tags by count
  50. if ("general" in tags) {
  51. tags["general"].sort((a, b) => b.tagCount - a.tagCount);
  52. }
  53.  
  54. for (const key in tags) {
  55. tags[key] = tags[key].map((item) => item.tag);
  56. }
  57.  
  58. // add attr tag
  59. tags["attrTag"] = [];
  60. return tags;
  61. }
  62.  
  63. function createTagCheckbox(tagType, checked = false) {
  64. const el = document.createElement("div");
  65.  
  66. el.innerHTML = `
  67. <input
  68. type="checkbox"
  69. name="${tagType}"
  70. ${checked ? "checked" : ""}>${tagType}
  71. `;
  72.  
  73. el.style.marginBottom = "0.15rem";
  74. el.style.display = "flex";
  75. el.style.alignItems = "center";
  76.  
  77. return [el, el.querySelector("input")];
  78. }
  79.  
  80. const tagCheckboxList = [];
  81. const tagsObj = parseTags(tagListEle);
  82.  
  83. // emphasis character
  84. if ("character" in tagsObj) {
  85. tagsObj["character"] = tagsObj["character"].map(
  86. (character) => `${character}`
  87. );
  88. }
  89.  
  90. // exclude special tags & resort tag
  91. if ("general" in tagsObj) {
  92. tagsObj["attrTag"] = tagsObj["general"].filter((tag) =>
  93. attriTags.some((attrTag) => tag.includes(attrTag))
  94. );
  95.  
  96. tagsObj["general"] = tagsObj["general"]
  97. .filter(
  98. (name) => !EXCLUDED_TAGS.some((exTags) => name.includes(exTags))
  99. )
  100. .filter((tag) => !attriTags.some((attrTag) => tag.includes(attrTag)));
  101. }
  102.  
  103. const tagCheckboxContainer = document.createElement("li");
  104.  
  105. for (const tagType in tagsObj) {
  106. if (tagType === "attrTag") {
  107. continue;
  108. }
  109.  
  110. tagsObj[tagType] = tagsObj[tagType].map(item => item.replaceAll('(', '\\(').replaceAll(')', '\\)'))
  111. // console.log(tagsObj)
  112. const [wrapper, ckbox] = createTagCheckbox(
  113. tagType,
  114. defultCheckedTags.includes(tagType)
  115. );
  116.  
  117. tagCheckboxContainer.appendChild(wrapper);
  118. tagCheckboxList.push(ckbox);
  119. }
  120.  
  121. const copyBtn = document.createElement("button");
  122.  
  123. copyBtn.innerText = "Copy";
  124. tagCheckboxContainer.appendChild(copyBtn);
  125. tagListEle.insertBefore(tagCheckboxContainer, tagListEle.firstChild);
  126.  
  127. return {
  128. copyBtn,
  129. tagCheckboxList,
  130. tagsObj,
  131. };
  132. }
  133.  
  134. const ui = initUI(tagListEle, DEFAULT_CHECKED_TAGS);
  135.  
  136. const tagTypeOrderMap = {
  137. character: 1,
  138. copyright: 1,
  139. artist: 1,
  140. attrTag: 1,
  141. general: 2,
  142. };
  143.  
  144. ui.copyBtn.addEventListener("click", () => {
  145. const checkedTagsType = ui.tagCheckboxList
  146. .filter((item) => item.checked)
  147. .map((item) => item.name);
  148.  
  149. if(checkedTagsType.includes('character')) {
  150. checkedTagsType.push("attrTag");
  151. }
  152.  
  153. // sort tags by type
  154. // checkedTagsType.sort((a, b) => {
  155. // return tagTypeOrderMap[a] - tagTypeOrderMap[b];
  156. // });
  157.  
  158. const tags =
  159. DEFAULT_QUALITY_TAG +
  160. "\n" +
  161. checkedTagsType
  162. .map((type) => {
  163. return ui.tagsObj[type].join(", ");
  164. })
  165. .join(",\n");
  166.  
  167. navigator.clipboard.writeText(tags).then(() => {
  168. window.notice && window.notice('Tags copied.')
  169. }).catch((e) => {
  170. // alert('failed copy tags:', e)
  171. prompt("copied tags:", tags);
  172. })
  173. });
  174. })();