silveroxides commited on
Commit
e07e9ee
·
verified ·
1 Parent(s): 1854695

Upload Booru_E621_tag_export_userscript.user.js

Browse files
Booru_E621_tag_export_userscript.user.js ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==UserScript==
2
+ // @name Danbooru Tags Select to Sort and Export
3
+ // @name:zh-TW Danbooru 標籤 選擇排序和匯出器
4
+ // @name:zh-HK Danbooru 標籤 選擇排序和匯出器
5
+ // @name:zh-CN Danbooru 标签 选择排序和导出器
6
+ // @name:ja Danbooru Tags Select to Sort and Export
7
+ // @namespace https://github.com/Takenoko3333/Danbooru-Tags-Sort-Exporter
8
+ // @supportURL https://github.com/Takenoko3333/Danbooru-Tags-Sort-Exporter/issues
9
+ // @homepageURL https://github.com/Takenoko3333/Danbooru-Tags-Sort-Exporter
10
+ // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
11
+ // @version 0.8.1
12
+ // @description Select specified tags and copy to clipboard, for Stable Diffusion WebUI or NovelAI to use. Tags can be sorted by tag order in NovelAI method.
13
+ // @author Takenoko3333
14
+ // @match https://danbooru.donmai.us/posts/*
15
+ // @match https://aibooru.online/posts/*
16
+ // @match https://betabooru.donmai.us/posts/*
17
+ // @match https://e621.net/posts/*
18
+ // @match https://gelbooru.com/index.php?page=post&s=view*
19
+ // @match https://safebooru.org/index.php?page=post&s=view*
20
+ // @match https://rule34.xxx/index.php?page=post&s=view*
21
+ // @match https://booru.allthefallen.moe/posts/*
22
+ // @icon https://www.google.com/s2/favicons?sz=64&domain=donmai.us
23
+ // @grant GM.setClipboard
24
+ // @grant GM.notification
25
+ // @grant GM.addStyle
26
+ // @license AGPL-3.0
27
+ // ==/UserScript==
28
+
29
+ // Forked from FSpark/Danbooru-Tags-Exporter(https://github.com/FSpark/Danbooru-Tags-Exporter)
30
+
31
+ (function () {
32
+ 'use strict';
33
+
34
+ // Settings changed on the web page are saved to local storage and applied when revisiting.
35
+ // ウェブページ上で変更した設定はローカルストレージに保存され、再訪時に適用されます。
36
+
37
+ // ==Settings==
38
+ const initialSettings = {
39
+ sort: true, // Sorted by tag order in NovelAI method. (タグをNovelAI方式で並び替える)
40
+ bracketEscape: false, // true: Stable Diffusion, false: NovelAI
41
+ setWeight: false, // true: Show and Activate weight inputs. (ウェイト入力の表示と有効化)
42
+ useBracket: 1, // 0: ( ) Stable Diffusion, 1: { } NovelAI
43
+ selections: {artist: true, copyright: true, character: true, species: true, general: true, meta:true} // Pre-checked state for each category. (カテゴリ毎の初期選択状態)
44
+ };
45
+ // ==/Settings==
46
+
47
+ let settings = {};
48
+ let localSettings = JSON.parse(localStorage.getItem("settings")) || {};
49
+ for (let key in initialSettings) {
50
+ if (localSettings[key] === undefined) {
51
+ settings[key] = initialSettings[key];
52
+ } else {
53
+ settings[key] = localSettings[key];
54
+ }
55
+ }
56
+ localStorage.setItem("settings", JSON.stringify(settings));
57
+ // console.log(settings)
58
+
59
+ let sortCheck = "";
60
+ let bracketEscapeCheck = "";
61
+ let setWeightCheck = "";
62
+ let roundBracketsRadio = "";
63
+ let curlyBracketsRadio = "";
64
+ let selectArtistCheck = "";
65
+ let selectCopyrightCheck = "";
66
+ let selectCharacterCheck = "";
67
+ let selectSpeciesCheck = "";
68
+ let selectGeneralCheck = "";
69
+ let selectMetaCheck = "";
70
+
71
+ if(settings.sort) {
72
+ sortCheck = "checked";
73
+ }
74
+ if(settings.bracketEscape) {
75
+ bracketEscapeCheck = "checked";
76
+ }
77
+ if(settings.setWeight) {
78
+ setWeightCheck = "checked";
79
+ }
80
+ if(settings.useBracket) {
81
+ curlyBracketsRadio = "checked";
82
+ } else {
83
+ roundBracketsRadio = "checked";
84
+ }
85
+ if(settings.selections) {
86
+ if(settings.selections.artist) {
87
+ selectArtistCheck = "checked";
88
+ }
89
+ if(settings.selections.copyright) {
90
+ selectCopyrightCheck = "checked";
91
+ }
92
+ if(settings.selections.character) {
93
+ selectCharacterCheck = "checked";
94
+ }
95
+ if(settings.selections.species) {
96
+ selectSpeciesCheck = "checked";
97
+ }
98
+ if(settings.selections.general) {
99
+ selectGeneralCheck = "checked";
100
+ }
101
+ if(settings.selections.meta) {
102
+ selectMetaCheck = "checked";
103
+ }
104
+ }
105
+
106
+ let isRule34 = location.hostname == "rule34.xxx";
107
+ let tagListSelector = isRule34 ? "#tag-sidebar" : "#tag-list";
108
+
109
+ GM.addStyle(`#tags-exporter-setting button, ${tagListSelector} button {margin: 0.25rem 0 0.5rem 0; padding: 0.25em 0.55em;}
110
+ #tags-exporter-setting button#reset-settings {margin-top: .5em}
111
+ #tags-exporter-setting label {display: inline-block; padding: .1em .25em; line-height: 1.5em; font-weight: normal;}
112
+ #tags-exporter-setting .heading {margin-top: .25em; line-height: 1.5em;}
113
+ #tags-exporter-setting .inline-checkbox {display: inline-block;}
114
+ #tags-exporter-setting .use-bracket {margin-left: 1.3em;}
115
+ ${tagListSelector} input[type='checkbox'] {margin-right: .4em; vertical-align: text-bottom;}
116
+ .tag-weight {width: 3em; margin-right: .4em}
117
+ `);
118
+
119
+ if (location.hostname == "gelbooru.com") {
120
+ GM.addStyle(`#tags-exporter-setting {margin: 0 10px 0 25px;}
121
+ #tags-exporter-setting h2 {font-size: 1.2em;}
122
+ #tags-exporter-setting .heading {font-weight: bold}
123
+ #tags-exporter-setting button, ${tagListSelector} button {padding: 0.25em 0.4em;}
124
+ [id$="-tag-buttons"] {margin: 0 4px 0 15px;}
125
+ .tag-weight {width: 2.5em;}
126
+ `);
127
+ }
128
+
129
+ if (location.hostname == "rule34.xxx") {
130
+ GM.addStyle(`#tags-exporter-setting {margin: 10px 0;}
131
+ #tags-exporter-setting h2 {font-size: 1.2em;}
132
+ #tags-exporter-setting .heading {font-weight: bold}
133
+ #tags-exporter-setting button, ${tagListSelector} button {padding: 0.25em 0.4em;}
134
+ #tags-exporter-setting input, [class^="tag-type-"] input {margin: 0;}
135
+ input.tag-weight {width: 2.5em; padding: 0 0 0 .1em;}
136
+ ${tagListSelector} h6 {margin-top: 13px;}
137
+ `);
138
+
139
+ for (let i = 0; i < document.styleSheets.length; i++) {
140
+ let href = document.styleSheets[i].href;
141
+ if (href) {
142
+ let fileName = href.split('/').pop();
143
+ if (fileName.startsWith('mobile.css')) {
144
+ GM.addStyle(`#tags-exporter-setting {text-align: center;}
145
+ #tags-exporter-setting input, ${tagListSelector} input[type='checkbox'] {height: 27px; vertical-align: middle; transform: scale(1.5);}
146
+ #tags-exporter-setting input {margin: 0 0.2em}
147
+ ${tagListSelector} input[type='checkbox'] {margin-right: 0.6em;}
148
+ #tags-exporter-setting .use-bracket {margin-left: 0;}
149
+ `);
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ if (location.hostname != "e621.net") {
156
+ GM.addStyle(`#tags-exporter-setting .show-e621 {display: none;}
157
+ `);
158
+ }
159
+
160
+ if (location.hostname == "e621.net") {
161
+ GM.addStyle(`#tags-exporter-setting h2 {font-size: 1.16667em;}
162
+ #tags-exporter-setting .show-e621 {display: inline-block;}
163
+ .tag-list-header {margin-bottom: 2px;}
164
+ ul + .tag-list-header {margin-top: 8px;}
165
+ .tag-weight {width: 3.5em; padding: 1px;}
166
+ `);
167
+ }
168
+
169
+ let SettingPanel = document.createElement('section');
170
+ SettingPanel.id = "tags-exporter-setting";
171
+ SettingPanel.innerHTML = `
172
+ <h2>Tags Export Settings</h2>
173
+ <input type="checkbox" id="sort" ${sortCheck}/><label for="sort">Sort by NovelAI method</label><br>
174
+ <input type="checkbox" id="bracket-escape" ${bracketEscapeCheck}/><label for="bracket-escape"><code>(</code> <code>)</code> -> <code>\\(</code> <code>\\)</code></label><br>
175
+ <input type="checkbox" id="set-weight" ${setWeightCheck}/><label for="set-weight">Setting weights</label><br>
176
+ <div class="use-bracket">
177
+ <input type="radio" name="use_bracket" id="round-brackets" value="0" ${roundBracketsRadio}/><label for="round-brackets">Using ( )</label>
178
+ <input type="radio" name="use_bracket" id="curly-brackets" value="1" ${curlyBracketsRadio}/><label for="curly-brackets">Using { }</label>
179
+ </div>
180
+ <div>
181
+ <div class="heading">Pre-checked</div>
182
+ <span class="inline-checkbox"><input type="checkbox" id="select-artist" ${selectArtistCheck}/><label for="select-artist">Artist</label></span>
183
+ <span class="inline-checkbox"><input type="checkbox" id="select-copyright" ${selectCopyrightCheck}/><label for="select-copyright">Copyright</label></span>
184
+ <span class="inline-checkbox"><input type="checkbox" id="select-character" ${selectCharacterCheck}/><label for="select-character">Character</label></span>
185
+ <span class="inline-checkbox show-e621"><input type="checkbox" id="select-species" ${selectGeneralCheck}/><label for="select-species">Species</label></span>
186
+ <span class="inline-checkbox"><input type="checkbox" id="select-general" ${selectGeneralCheck}/><label for="select-general">General</label></span>
187
+ <span class="inline-checkbox"><input type="checkbox" id="select-meta" ${selectMetaCheck}/><label for="select-meta">Meta</label></span>
188
+ </div>
189
+ <button name="reset_settings" id="reset-settings">Settings Reset</button>
190
+ `
191
+ let Container = document.createElement('div');
192
+ Container.id = "tags-exporter-container";
193
+ Container.innerHTML = `
194
+ <button name="select_all">All</button>
195
+ <button name="select_none">None</button>
196
+ <button name="invert_select">Invert</button>
197
+ <button name="export">Export</button>
198
+ `
199
+
200
+ function insertBefore(newNode, referenceNode) {
201
+ referenceNode.parentNode.insertBefore(newNode, referenceNode);
202
+ }
203
+
204
+ function insertAfter(newNode, referenceNode) {
205
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
206
+ }
207
+
208
+ if (location.hostname == "gelbooru.com" || location.hostname == "rule34.xxx") {
209
+ insertBefore(SettingPanel, document.querySelector(tagListSelector));
210
+ } else {
211
+ insertAfter(SettingPanel, document.querySelector("#search-box"));
212
+ }
213
+ insertAfter(Container, document.querySelector("#tags-exporter-setting > h2"));
214
+
215
+ function addBrackets(prompts,isRound,n){
216
+ let l,r;
217
+ if(n==0) {
218
+ return prompts
219
+ }
220
+ else if(n>0){
221
+ if(isRound){
222
+ l='('
223
+ r=')'}
224
+ else{
225
+ l='{'
226
+ r='}'
227
+ }
228
+ }else{
229
+ l='['
230
+ r=']'
231
+ }
232
+ n=Math.abs(n)
233
+ return l.repeat(n).concat(prompts,r.repeat(n))
234
+ }
235
+ function exportTags(target){
236
+ let tags = []
237
+ let reorderedTags = []
238
+ let sort = document.getElementById("sort").checked
239
+ let bracket_escape = document.getElementById("bracket-escape").checked
240
+ let set_weight = document.getElementById("set-weight").checked
241
+ let round_brackets = document.getElementById("round-brackets").checked
242
+ if(!target) {
243
+ if(sort) {
244
+ ["[name=character-tags]:checked", "[name=copyright-tags]:checked", "[name=artist-tags]:checked", "[name=species-tags]:checked", "[name=general-tags]:checked", "[name=meta-tags]:checked"].forEach((t)=>createTags(t));
245
+ } else {
246
+ createTags(`${tagListSelector} input[type='checkbox']:checked`);
247
+ }
248
+ } else {
249
+ createTags(target);
250
+ }
251
+ function createTags(target) {
252
+ document.querySelectorAll(target).forEach((e) => {
253
+ let prompts = e.value
254
+ if(bracket_escape) {
255
+ prompts = prompts.replaceAll(`(`,`\\(`).replaceAll(`)`,`\\)`)
256
+ }
257
+ if(set_weight) {
258
+ prompts = addBrackets(prompts,round_brackets,e.nextSibling.value, ":", nbr.value)
259
+ }
260
+ tags.push(prompts)
261
+ })
262
+ }
263
+
264
+ if(sort) {
265
+ const regexp = /[1-6]\+?(girl|boy)s?/;
266
+ const girlsTags = tags.filter(tag => tag.includes('girl') && regexp.test(tag));
267
+ const boysTags = tags.filter(tag => !tag.includes('girl') && regexp.test(tag));
268
+ const otherTags = tags.filter(tag => !regexp.test(tag));
269
+ reorderedTags = [...boysTags, ...girlsTags, ...otherTags];
270
+ } else {
271
+ reorderedTags = tags;
272
+ }
273
+ let res = reorderedTags.join(", ")
274
+
275
+ GM.setClipboard(res)
276
+ GM.notification(`${reorderedTags.length} tag(s) were copied.`, "Danbooru Tags Sort and Exporter")
277
+ }
278
+
279
+ function insertButtons(target){
280
+ let head = document.querySelector(`h3.${target}-tag-list`)
281
+ if (location.hostname == "gelbooru.com" || location.hostname == "rule34.xxx") {
282
+ head = document.querySelector(`.tag-type-${target}`)
283
+ }
284
+ if (location.hostname == "e621.net") {
285
+ head = document.querySelector(`.${target}-tag-list`)
286
+ }
287
+ if(!head) return;
288
+ let buttonContainer = Container.cloneNode(true)
289
+ buttonContainer.id = `${target}-tag-buttons`
290
+ if (location.hostname == "gelbooru.com" || location.hostname == "rule34.xxx" || location.hostname == "e621.net") {
291
+ insertBefore(buttonContainer, head)
292
+ } else {
293
+ insertAfter(buttonContainer, head)
294
+ }
295
+
296
+ let tagItem = `.${target}-tag-list>li`;
297
+ if (location.hostname == "gelbooru.com" || location.hostname == "rule34.xxx") {
298
+ tagItem = `.tag-type-${target}`;
299
+ }
300
+
301
+ document.querySelectorAll(tagItem).forEach((e) => {
302
+ let chk = document.createElement('input');
303
+ chk.type = "checkbox"
304
+ chk.name = `${target}-tags`
305
+ if (location.hostname == "gelbooru.com" || location.hostname == "rule34.xxx" || location.hostname == "e621.net") {
306
+ let aTags = e.querySelectorAll('a');
307
+ if (aTags.length > 0) {
308
+ let lastATag = aTags[aTags.length - 1];
309
+ chk.value = lastATag.textContent;
310
+ }
311
+ } else {
312
+ chk.value = e.dataset.tagName.replaceAll("_", " ")
313
+ }
314
+ if(settings.selections[target.replace("-tag", "")]) {
315
+ chk.checked = true
316
+ }
317
+ e.insertBefore(chk, e.firstChild)
318
+
319
+ let nbr = document.createElement('input');
320
+ nbr.type = "number"
321
+ nbr.name = `${target}-tags-weight`
322
+ nbr.className = "tag-weight"
323
+ nbr.value = 1.0
324
+ nbr.hidden = true
325
+ nbr.step = 0.01
326
+ nbr.min = -1
327
+ nbr.max = 2
328
+ insertAfter(nbr,chk)
329
+ })
330
+
331
+ buttonContainer.querySelector("[name='select_all']").onclick = function () {
332
+ var items = document.getElementsByName(`${target}-tags`);
333
+ for (var i = 0; i < items.length; i++) {
334
+ items[i].checked = true;
335
+
336
+ }
337
+ };
338
+ buttonContainer.querySelector("[name='select_none']").onclick = function () {
339
+ var items = document.getElementsByName(`${target}-tags`);
340
+ for (var i = 0; i < items.length; i++) {
341
+ items[i].checked = false;
342
+
343
+ }
344
+ };
345
+ buttonContainer.querySelector("[name='invert_select']").onclick = function () {
346
+ var items = document.getElementsByName(`${target}-tags`);
347
+ for (var i = 0; i < items.length; i++) {
348
+ items[i].checked == true ? items[i].checked = false : items[i].checked = true;
349
+
350
+ }
351
+ };
352
+ buttonContainer.querySelector("[name='export']").onclick = function () {
353
+ exportTags(`[name=${target}-tags]:checked`)
354
+ };
355
+ }
356
+
357
+ function setSettings() {
358
+ let sort = document.getElementById("sort").checked;
359
+ let bracketEscape = document.getElementById("bracket-escape").checked;
360
+ let setWeight = document.getElementById("set-weight").checked;
361
+ let roundBrackets = document.getElementById("round-brackets").checked;
362
+ let useBracket = roundBrackets ? 0 : 1;
363
+ let selections = {artist: document.getElementById("select-artist").checked,
364
+ copyright: document.getElementById("select-copyright").checked,
365
+ character: document.getElementById("select-character").checked,
366
+ species: document.getElementById("select-species").checked,
367
+ general: document.getElementById("select-general").checked,
368
+ meta: document.getElementById("select-meta").checked
369
+ };
370
+ localStorage.setItem("settings", JSON.stringify({sort: sort, bracketEscape: bracketEscape, setWeight: setWeight, useBracket: useBracket, selections: selections}));
371
+ // console.log(JSON.parse(localStorage.getItem("settings")));
372
+ }
373
+
374
+ function resetSettings() {
375
+ localStorage.removeItem("settings");
376
+ document.getElementById("sort").checked = initialSettings.sort;
377
+ document.getElementById("bracket-escape").checked = initialSettings.bracketEscape;
378
+ document.getElementById("set-weight").checked = initialSettings.setWeight;
379
+ if(initialSettings.useBracket) {
380
+ document.getElementById("curly-brackets").checked = true;
381
+ } else {
382
+ document.getElementById("round-brackets").checked = true;
383
+ }
384
+ toggleWeightInputs();
385
+ document.getElementById("select-artist").checked = initialSettings.selections.artist;
386
+ document.getElementById("select-copyright").checked = initialSettings.selections.copyright;
387
+ document.getElementById("select-character").checked = initialSettings.selections.character;
388
+ document.getElementById("select-species").checked = initialSettings.selections.species;
389
+ document.getElementById("select-general").checked = initialSettings.selections.general;
390
+ document.getElementById("select-meta").checked = initialSettings.selections.meta;
391
+ }
392
+
393
+ function toggleWeightInputs(event) {
394
+ const target = event ? event.target : document.getElementById('set-weight');
395
+ if (target && target.id === 'set-weight') {
396
+ const isSetWeightChecked = target.checked;
397
+ document.querySelectorAll(`${tagListSelector} input[type='number']`).forEach(e => { e.hidden = !isSetWeightChecked; });
398
+ }
399
+ }
400
+
401
+ ["artist","character","copyright", "species", "general", "meta"].forEach((t)=>insertButtons(t))
402
+
403
+ toggleWeightInputs();
404
+
405
+ Container.querySelector("[name='select_all']").onclick = function () {
406
+ var items = document.querySelectorAll(`${tagListSelector} input[type='checkbox']`)
407
+ for (var i = 0; i < items.length; i++) {
408
+ items[i].checked = true;
409
+ }
410
+ };
411
+ Container.querySelector("[name='select_none']").onclick = function () {
412
+ var items = document.querySelectorAll(`${tagListSelector} input[type='checkbox']`)
413
+ for (var i = 0; i < items.length; i++) {
414
+ items[i].checked = false;
415
+ }
416
+ };
417
+ Container.querySelector("[name='invert_select']").onclick = function () {
418
+ var items = document.querySelectorAll(`${tagListSelector} input[type='checkbox']`)
419
+ for (var i = 0; i < items.length; i++) {
420
+ items[i].checked == true ? items[i].checked = false : items[i].checked = true;
421
+ }
422
+ };
423
+ Container.querySelector("[name='export']").onclick = function () {
424
+ exportTags()
425
+ };
426
+ SettingPanel.querySelector("[name='reset_settings']").onclick = function () {
427
+ resetSettings()
428
+ };
429
+ SettingPanel.querySelectorAll("input[type='radio'], input[type='checkbox']").forEach(e => {
430
+ e.onchange = function (event) {
431
+ setSettings();
432
+ toggleWeightInputs(event);
433
+ };
434
+ });
435
+
436
+ })();