// ==UserScript==
// @name Danbooru - Yooyooko's extra features
// @namespace http://tampermonkey.net/
// @version 2024-10-13
// @description Useful functions for user analitics for Danbooru and other stuff
// @author yooyooko
// @match https://danbooru.donmai.us*
// @match https://danbooru.donmai.us/*
// @match https://aibooru.online*
// @match https://aibooru.online/*
// @match https://gaybooru.app*
// @match https://gaybooru.app/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=donmai.us
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Yooyooko's Extra Features for Danbooru (+ sister sites)
// - Show deletion percentage on /users/* by default
// - Fetch more info: You get a button on /users/* that fetches more information on users like: Uploads per day, Deletion percentage (last 60 days), Flagged, pending, appealed posts, Related tags of user's uploads, Rating percentages of user's uploads
// - Use raw files for transparent and animated posts: Uses raw files for posts tagged with transparent_background or animated
// - Use raw files for all thumbnails: Same as above, but without tag discrimination
// - Hot Garbage posts: You get a button in the toolbar on /posts that takes you to {{(age:<2d order:score_asc status:any)}} posts
// - Artists on Danbooru: You get a button on /artists that shows you the list of artists who have an account on Danbooru
// - Searching feedbacks and bans by account level, user upload count, user post edit rendition count
// - [BUILDERS+ ONLY] Button for applying tagscript to all visible posts
// - Button on /dmails/new that gives you a set of useful pre-written responses containing links to useful help: and howto: pages
// - /users/* favgroups link - Sort favorite groups alphabetically by default
// - Yooyooko's extra features settings tab that lets you enable/disable some of the features (active-id: 100)
//configuration
if(localStorage.getItem("yooyooko-extrafeatures-settings-hotgarbagepostslink-enable")==null) localStorage.setItem("yooyooko-extrafeatures-settings-hotgarbagepostslink-enable",1)
if(localStorage.getItem("yooyooko-extrafeatures-settings-artistsondanboorulink-enable")==null) localStorage.setItem("yooyooko-extrafeatures-settings-artistsondanboorulink-enable",1)
if(localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesfortransparentandanimatedposts-enable")==null) localStorage.setItem("yooyooko-extrafeatures-settings-userawfilesfortransparentandanimatedposts-enable",0)
if(localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesforallthumbnails-enable")==null) localStorage.setItem("yooyooko-extrafeatures-settings-userawfilesforallthumbnails-enable",0)
var hot_garbage_posts_link_e=localStorage.getItem("yooyooko-extrafeatures-settings-hotgarbagepostslink-enable")
var artists_on_danbooru_link_e=localStorage.getItem("yooyooko-extrafeatures-settings-artistsondanboorulink-enable")
var use_raw_files_for_transparent_and_animated_posts=localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesfortransparentandanimatedposts-enable")
var use_raw_files_for_all_thumbnails=localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesforallthumbnails-enable")
var my_html_elem=null
if(!myFunctionsYYEF) var myFunctionsYYEF = window.myFunctionsYYEF = {};
myFunctionsYYEF.htmlToNode = function(html) {
const template = document.createElement('template');
template.innerHTML = html;
const nNodes = template.content.childNodes.length;
if (nNodes !== 1) {
throw new Error(
`html parameter must represent a single node; got ${nNodes}. ` +
'Note that leading or trailing spaces around an element in your ' +
'HTML, like " <img/> ", get parsed as text nodes neighbouring ' +
'the element; call .trim() on your input to avoid this.'
);
}
return template.content.firstChild;
}
myFunctionsYYEF.applySettings = function() {
localStorage.setItem("yooyooko-extrafeatures-settings-hotgarbagepostslink-enable", document.querySelector("#yooyooko-ef-selectinput-hotgarbagepostslink-enable").value );
localStorage.setItem("yooyooko-extrafeatures-settings-artistsondanboorulink-enable", document.querySelector("#yooyooko-ef-selectinput-artistsondanboorulink-enable").value );
localStorage.setItem("yooyooko-extrafeatures-settings-userawfilesfortransparentandanimatedposts-enable", document.querySelector("#yooyooko-ef-selectinput-userawfilesfortransparentandanimatedposts-enable").value );
localStorage.setItem("yooyooko-extrafeatures-settings-userawfilesforallthumbnails-enable", document.querySelector("#yooyooko-ef-selectinput-userawfilesforallthumbnails-enable").value );
}
// link for browsing hot garbage posts
if(hot_garbage_posts_link_e==1) if(document.querySelector("#subnav-hot")) document.querySelector("#subnav-hot").outerHTML+=`<li id="subnav-hotgarbage"><a id="subnav-hotgarbage-link" href="/posts?tags=%28age%3A<2d+order%3Ascore_asc+status%3Aany%29&z=5">Hot Garbage</a></li>`;
// link for browsing artists with a danbooru account
if(artists_on_danbooru_link_e==1) if(document.querySelector("#subnav-artists")) document.querySelector("#subnav-artists").outerHTML+=`<li id="subnav-hotgarbage"><a id="subnav-hotgarbage-link" href="/artists?commit=Search&search%5Border%5D=post_count&search%5Burl_matches%5D=%2A${document.querySelector("#app-name").innerHTML.toLowerCase()}%2A">Artists on ${document.querySelector('#app-name').innerHTML}</a></li>`;
// button for applying tagscript to all visible posts
if(document.querySelector("#mode-box")) {
var my_add_button=`<input type="submit" onclick='Array.from(document.querySelectorAll(".post-preview")).forEach(item => item.querySelector("img").click())' id="btn-applytagscripttoallvisible" style="margin-top:5px;" value="Apply to all visible" class="button-primary button-sm">`;
//document.querySelector("#mode-box h2").onclick=function() {document.querySelector("#mode-box").innerHTML+=`${my_add_button}`;};
document.querySelector("#mode-box h2").onclick=function() {if(document.querySelector("#btn-applytagscripttoallvisible")==null) document.querySelector("#mode-box").appendChild(myFunctionsYYEF.htmlToNode(my_add_button)) }
}
// enable transparent backgrounds for thumbnails
if(use_raw_files_for_transparent_and_animated_posts==1)
Array.from(document.querySelectorAll("article.post-preview")).filter( item => (item.dataset.tags.split(" ").includes("transparent_background") || item.dataset.tags.split(" ").includes("animated")) ).forEach(item=>{
console.log(item.dataset.id)
fetch(`/posts/${item.dataset.id}.json`)
.then(response => response.json())
.then(json => {
console.log(item.querySelector("img"))
var selected_asset_variant=json.media_asset.variants[json.media_asset.variants.length-1]
if(selected_asset_variant.url.endsWith(".zip")) {
selected_asset_variant=json.media_asset.variants[json.media_asset.variants.length-2]
}
item.querySelector("img").src=selected_asset_variant.url
item.querySelector("img").style=`background-image: linear-gradient(45deg, #1d1d1d 25%, transparent 25%), linear-gradient(-45deg, #1d1d1d 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #1d1d1d 75%), linear-gradient(-45deg, transparent 75%, #1d1d1d 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px;`
item.querySelector("source").srcset=selected_asset_variant.url;
if(selected_asset_variant.url.endsWith(".mp4") || selected_asset_variant.url.endsWith(".webm") ) {
//item.querySelector("img").src+="#t=0,5"
item.querySelector("img").outerHTML=item.querySelector("img").outerHTML.replace("<img","<video autoplay muted loop")
}
//console.log(json.media_asset.variants)
})
})
if(use_raw_files_for_all_thumbnails==1)
Array.from(document.querySelectorAll("article.post-preview")).forEach(item=>{
console.log(item.dataset.id)
fetch(`/posts/${item.dataset.id}.json`)
.then(response => response.json())
.then(json => {
console.log(item.querySelector("img"))
var selected_asset_variant=json.media_asset.variants[json.media_asset.variants.length-1]
if(selected_asset_variant.url.endsWith(".zip")) {
selected_asset_variant=json.media_asset.variants[json.media_asset.variants.length-2]
}
item.querySelector("img").src=selected_asset_variant.url
item.querySelector("img").style=`background-image: linear-gradient(45deg, #1d1d1d 25%, transparent 25%), linear-gradient(-45deg, #1d1d1d 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #1d1d1d 75%), linear-gradient(-45deg, transparent 75%, #1d1d1d 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px;`
item.querySelector("source").srcset=selected_asset_variant.url;
if(selected_asset_variant.url.endsWith(".mp4") || selected_asset_variant.url.endsWith(".webm") ) {
//item.querySelector("img").src+="#t=0,5"
item.querySelector("img").outerHTML=item.querySelector("img").outerHTML.replace("<img","<video autoplay muted loop")
}
//console.log(json.media_asset.variants)
})
})
if(window.location.href.endsWith("/settings")) {
document.querySelector(".tab-list").innerHTML+=`
<a class="tab security-yooyooko-ef" x-on:click.prevent="active = 100" x-bind:class="{ 'active-tab': active === 100 }" href="#">Yooyooko's extra features</a>
`;
document.querySelector(".tab-panels").innerHTML+=`
<div class="tab-panel advanced-tab active-tab" x-bind:class="{ 'active-tab': active === 100 }">
<form class="simple_form edit_user" id="edit_user_1211591" autocomplete="off" novalidate="novalidate" action="/users/1211591" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch" autocomplete="off"><input type="hidden" autocomplete="off">
<div class="input select optional user_new_post_navigation_layout field_with_hint">
<label class="select optional" for="user_new_post_navigation_layout">Hot Garbage Posts link</label>
<select class="select optional" id="yooyooko-ef-selectinput-hotgarbagepostslink-enable">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="hint">Show a button that takes you to recently uploaded posts (age:<2d) ordered by their score from lowest to highest</span>
</div>
<div class="input select optional user_new_post_navigation_layout field_with_hint">
<label class="select optional" for="user_new_post_navigation_layout">Artists on Danbooru link</label>
<select class="select optional" id="yooyooko-ef-selectinput-artistsondanboorulink-enable">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="hint">Show a button that takes you to the page that shows the list of artists who have a Danbooru account</span>
</div>
<div class="input select optional user_new_post_navigation_layout field_with_hint">
<label class="select optional" for="user_new_post_navigation_layout">Show animated and transparent thumbnails</label>
<select class="select optional" id="yooyooko-ef-selectinput-userawfilesfortransparentandanimatedposts-enable">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="hint">Enabling this will make Danbooru use raw files as thumbnails for posts tagged as transparent or animated. Enabling this could also cause lags, sending too many web requests, excessive cache and RAM memory build-up and high CPU usage.</span>
</div>
<div class="input select optional user_new_post_navigation_layout field_with_hint">
<label class="select optional" for="user_new_post_navigation_layout">Use raw files for all thumbnails</label>
<select class="select optional" id="yooyooko-ef-selectinput-userawfilesforallthumbnails-enable">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="hint">Enabling this will do the same as above, but it will do so for all posts.</span>
</div>
</form>
<input type="submit" class="btn" data-disable-with="Submit" onclick="myFunctionsYYEF.applySettings()">
</div>
`
document.querySelector("#yooyooko-ef-selectinput-hotgarbagepostslink-enable").value=localStorage.getItem("yooyooko-extrafeatures-settings-hotgarbagepostslink-enable");
document.querySelector("#yooyooko-ef-selectinput-artistsondanboorulink-enable").value=localStorage.getItem("yooyooko-extrafeatures-settings-artistsondanboorulink-enable");
document.querySelector("#yooyooko-ef-selectinput-userawfilesfortransparentandanimatedposts-enable").value=localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesfortransparentandanimatedposts-enable");
document.querySelector("#yooyooko-ef-selectinput-userawfilesforallthumbnails-enable").value=localStorage.getItem("yooyooko-extrafeatures-settings-userawfilesforallthumbnails-enable");
}
if(/https:.*\/(user_feedbacks|bans).*/.test(window.location.href)) {
document.querySelectorAll("#a-index form div")[document.querySelectorAll("#a-index form div").length-1].outerHTML+=`
<div class="input string optional search_reason_matches"><label class="string optional" for="search_reason_matches">User upload count</label><input class="string optional" type="text" name="search[user][post_upload_count]" id="search_upload_count"></div>
<div class="input string optional search_reason_matches"><label class="string optional" for="search_reason_matches">User note update count</label><input class="string optional" type="text" name="search[user][note_update_count]" id="search_note_update_count"></div>
<div class="input string optional search_reason_matches"><label class="string optional" for="search_reason_matches">User post update count</label><input class="string optional" type="text" name="search[user][post_update_count]" id="search_post_update_count"></div>
<div class="input select optional search_level"><label class="select optional" for="search_level">Level</label><select class="select optional" name="search[user][level]" id="search_level"><option value="" label=" "></option>
<option value="10">Restricted</option>
<option value="20">Member</option>
<option value="30">Gold</option>
<option value="31">Platinum</option>
<option value="32">Builder</option>
<option value="35">Contributor</option>
<option value="37">Approver</option>
<option value="40">Moderator</option>
<option value="50">Admin</option>
<option value="60">Owner</option></select></div>
`
document.querySelector("#search_level").value=(new URLSearchParams(window.location.search)).get('search[user][level]');
document.querySelector("#search_note_update_count").value=(new URLSearchParams(window.location.search)).get('search[user][note_update_count]');
document.querySelector("#search_post_update_count").value=(new URLSearchParams(window.location.search)).get('search[user][post_update_count]');
document.querySelector("#search_upload_count").value=(new URLSearchParams(window.location.search)).get('search[user][post_upload_count]');
}
if(/https:\/\/.*\/dmails\/new.*/.test(window.location.href)) {
var pre_written_responses=[
{
name:"New user uploading and unaware of rules",
title:"Please read the upload rules",
message:
`Hello, I see that you’re new to uploading.
Please read [[help:upload rules|The Upload Rules]] before uploading more posts.
`
},
{
name:"User unresponsive to DMails",
title:"Unresponsive to DMails",
message:
`Hello, you are being unresponsive to my DMails.
Please take the critic and don’t be ignorant about it.
Especially if it’s because of you breaking the rules or me telling you how not to do things.
`
},
{
name:"User is incorrectly rating posts",
title:"Incorrect rating",
message:
`Hello, you’re not rating your posts correctly.
Please take a look at asset #22830992 which gives you great examples of post ratings.
Please do also read the wiki page [[howto:rate]] which gives you a more in depth explanation of post ratings.
`
},
{
name:"User not adding enough tags",
title:"Improper tagging",
message:
`Hello, you’re not tagging your posts properly.
Please take a look at [[howto:tag]].
`
},
{
name:"Self-uploader uploading their art regardless of quality",
title:"Self-uploading",
message:
`Hello, [[self-upload|uploading your art]] on Danbooru is not against the rules, however the quality of the art needs to follow Danbooru's standards like any other post here.
[[Danbooru_(site)|Danbooru]] is not made for artists. [[Danbooru_(site)|Danbooru]]'s primary purpose is archiving high-quality artwork.
If you want to upload your own artwork, please go to [[DeviantART]], [[Pixiv]], [[Twitter]] or any other website that allows artists to upload their works.
Please take a look at the wiki for more information:
* [[help:upload]]
* [[help:upload rules|upload rules]]
`
},
{
name:"User is a minor",
title:"Please leave this site",
message:
`Hello, please do not use Danbooru in any way if you’re under 18 years old.
This website contains explicit imagery and is not suited for such people.
Your account will simply be banned.
`
},
{
name:"Blatant mistagging",
title:"Blatant mistagging",
message:
`Hello, please do not put blatantly wrong tags where they do not belong.
Please take a look at [[howto:tag]].
`
},
{
name:"Comment spamming",
title:"Comment spamming",
message:
`Hello, please do not write unnecessary stuff in the comments.
This includes “lol”, “wow”, anything too simple or anything that doesn’t add much value to Danbooru.
Stuff like this will either be seen as spam or trolling.
Please check [[help:comments]] and [[help:community_rules]] for more information.
`
},
{
name:"Comment hostility",
title:"Comment hostility",
message:
`Hello, please do not write hostile and demeaning comments on Danbooru.
Comments like that are very likely to be deleted and attract negative feedback.
Please check [[help:comments]] and [[help:community_rules]] for more information.
`
},
{
name:"User uploading AI art",
title:"You're uploading AI generated imagery",
message:
`Hello, please do not upload fully [[ai-generated]] imagery.
It is against the [[help:upload_rules|upload rules]] to upload [[ai-generated]] images to Danbooru.
There is a “sister”-site to Danbooru called [https://aibooru.online/](AIBooru), which was created only for [[ai-generated]] images and artwork, so please upload [[ai-generated|AI]] images there instead.
There are many people who post [[self-upload]]s there, so if you’re an AI creator yourself you can get decent feedback on your generations and improve them.
`
},
{
name:"User uploading duplicates",
title:"You're uploading duplicates",
message:
`Hello, please do not upload [[duplicate|duplicates]].
`
},
{
name:"User uploading real life images",
title:"You're uploading real life images",
message:
`Hello, please do not upload [[photo_(medium)|real life photographs]] that have nothing to do with anime, manga or video games.
Stuff like that is blatantly [[off-topic]], therefore against the [[help:upload rules|upload rules]].
`
},
{
name:"User uploading off-topic content",
title:"You're uploading off-topic content",
message:
`Hello, you’re uploading [[off-topic]] content which is against the [[help:upload rules|upload rules]].
[u][b]Please check the following wiki pages[/b][/u]:
* [u][b][[help:upload]][/b][/u]
* [[help:upload rules|upload rules]]
* [[off-topic]]
`
},
{
name:"User uploading low quality content",
title:"You're uploading low quality content",
message:
`Hello, please do not upload low quality content to [[danbooru_(site)|Danbooru]].
Please check the [[help:upload]] wiki page.
`
},
{
name:"User uploading blatantly low quality content",
title:"You're uploading blatantly low quality content",
message:
`Hello, please do not upload blatantly low quality content to Danbooru.
Please check the [[help:upload]] wiki page.
If you do not wish to contribute to this site, please log off now and do not cause further trouble.
`
},
{
name:"User writing bad wikis",
title:"You're writing bad wikis",
message:
`Hello, you are not making or editing wiki pages properly.
Wiki pages are supposed to be objectively provided information about a tag.
Please check [[howto:wiki]] for more information.
`
},
{
name:"User continuously trolls and behaves bad",
title:"You're continuously trolling and behaving badly",
message:
`Hello, it seems that you are repeatedly breaking the rules after the members, mods and admins told you many times not to break them.
You should leave the site for a bit and reflect upon yourself.
Trolling and behaving like this is unhealthy.
Come back once you actually want to contribute to the site,
unless your account has already been permanently banned.
`
},
]
document.querySelector("div.dmail_title").innerHTML+=`<br><input value="Quick response" id='temp-to-remove' onclick='myFunctionsYYEF.showResponseButtons()' type="button" style='margin-top:5px;'>`
myFunctionsYYEF.showResponseButtons = function() {
document.querySelector("#temp-to-remove").remove();
pre_written_responses.forEach(response => {
var my_html_elem_tmp = myFunctionsYYEF.htmlToNode(`
<input value="${response.name}" onclick='document.querySelector(".dmail_title input").value=\`${response.title.replaceAll("'","’")}\`; document.querySelector("textarea.dtext").value=\`${response.message.replaceAll("'","’")}\`' type="button" style='margin-top:5px;'>
`.trim());
document.querySelector("div.dmail_title").appendChild(my_html_elem_tmp)
my_html_elem_tmp = myFunctionsYYEF.htmlToNode('<br>');
document.querySelector("div.dmail_title").appendChild(my_html_elem_tmp);
})
}
}
// show total delete ratio on profile page
if(/https:\/\/.*\/profile$/.test(window.location.href) || /https:\/\/.*\/users\/\d+$/.test(window.location.href)) {
var danbooru_username=document.querySelector("#c-users a.user").innerHTML.replaceAll(' ','_')
var deleted_upload_count=parseInt(Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deleted Uploads').parentElement.querySelector("td a").innerHTML);
var total_upload_count=parseInt(Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Uploads').parentElement.querySelector("td a").innerHTML)
var deletion_ratio=0;
if (!(deleted_upload_count==0 || total_upload_count==0)) deletion_ratio=Math.round((deleted_upload_count / total_upload_count)*10000)/100;
Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deleted Uploads').parentElement.outerHTML+=
`
<tr>
<th>Deletion percentage</th>
<td>
${deletion_ratio}%
</td>
</tr>
`;
// fetch more info about a user (with a button)
document.querySelector("#a-show h1 a").outerHTML+=` <a class="dtext-link" style="font-size:16px; font-style: italic; cursor:pointer" onclick="myFunctionsYYEF.showMoreUserInfo();">Fetch more info</a>`
myFunctionsYYEF.showMoreUserInfo = function() {
document.querySelector("#a-show h1 a.dtext-link").remove()
// posts per day statistic
var dateEnd = new Date(new Date().setDate(new Date().getDate() - 2));
var dateStart = new Date(new Date().setDate(new Date().getDate() - 32));
var days_counted=0;
var posts_per_day=0;
console.log(`${dateStart.toISOString().slice(0, 10)}`);
fetch(`/reports/posts.json?commit=Search&id=posts&search%5Bfrom%5D=${dateStart.toISOString().slice(0, 10)}&search%5Bperiod%5D=day&search%5Btags%5D=user%3A${danbooru_username}&search%5Bto%5D=${dateEnd.toISOString().slice(0, 10)}`)
.then(response => response.json())
.then(json => {
json.forEach(item => {
posts_per_day+=item.posts;
})
posts_per_day=Math.round((posts_per_day/json.length)*100)/100
console.log(posts_per_day)
Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Uploads').parentElement.outerHTML+=
`
<tr>
<th>Uploads per day<br>(last 30 days)</th>
<td>
<a href="/reports/posts?commit=Search&id=posts&search%5Bperiod%5D=day&search%5Btags%5D=user%3A${danbooru_username}">${posts_per_day}</a>
</td>
</tr>
`;
})
// deletion ratio from last 60days
fetch(`/counts/posts.json?tags=user:${danbooru_username} age:<=60d`)
.then(response => response.json())
.then(json => {
fetch(`/counts/posts.json?tags=user:${danbooru_username} age:<=60d status:deleted`)
.then(response => response.json())
.then(json2 => {
var deletion_ratio_60days=0;
if(json.counts.posts!=0)
deletion_ratio_60days=Math.round((json2.counts.posts/json.counts.posts)*10000) / 100
var total_upload_count_60days=json.counts.posts;
Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deletion percentage').parentElement.outerHTML+=
`
<tr>
<th>Deletion percentage <br>(last 60 days)</th>
<td>
<a href="/posts?tags=user:${danbooru_username} status:deleted age:<=60d">${deletion_ratio_60days}% (${json2.counts.posts})</a> of <a href="/posts?tags=user:${danbooru_username} age:<=60d">${total_upload_count_60days} posts</a>
</td>
</tr>
`;
})
})
// show flagged post count
var my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deletion percentage');
if(my_html_elem_flgps===null) my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deleted Uploads')
my_html_elem_flgps=my_html_elem_flgps.parentElement;
fetch(`/counts/posts.json?tags=user%3A${danbooru_username}+status%3Aflagged`)
.then(response => response.json())
.then(json => {
my_html_elem_flgps.outerHTML+=
`
<tr>
<th>Flagged Posts</th>
<td>
<a href="/posts?tags=status%3Aflagged+user%3A${danbooru_username}">${json.counts.posts}</a>
</td>
</tr>
`;
fetch(`/counts/posts.json?tags=user%3A${danbooru_username}+status%3Apending`)
.then(response => response.json())
.then(json => {
my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Flagged Posts');
if(my_html_elem_flgps===null) my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deleted Uploads')
my_html_elem_flgps=my_html_elem_flgps.parentElement;
my_html_elem_flgps.outerHTML+=
`
<tr>
<th>Pending Posts</th>
<td>
<a href="/posts?tags=status%3Apending+user%3A${danbooru_username}">${json.counts.posts}</a>
</td>
</tr>
`;
fetch(`/counts/posts.json?tags=is%3Aappealed+appealer%3A${danbooru_username}`)
.then(response => response.json())
.then(json => {
my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Pending Posts');
if(my_html_elem_flgps===null) my_html_elem_flgps=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Deleted Uploads')
my_html_elem_flgps=my_html_elem_flgps.parentElement;
my_html_elem_flgps.outerHTML+=
`
<tr>
<th>Appealed Posts</th>
<td>
<a href="/posts?tags=is%3Aappealed+appealer%3A${danbooru_username}">${json.counts.posts}</a>
</td>
</tr>
`;
})
})
})
// redirect saved search links to post search query links
fetch('/saved_searches.json')
.then(response => response.json())
.then(json => {
json.forEach(item => {
var my_html_elem_temp1=document.querySelector(`a[href='/posts?tags=search%3A${encodeURIComponent(item.labels[0]).replaceAll("(","%28").replaceAll(")","%29")}']`)
console.log(my_html_elem_temp1)
my_html_elem_temp1.href=`/posts?tags=${item.query}`
})
})
// rating percentages
var html_elem_tmp=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Favorites').parentElement
if(html_elem_tmp) {
fetch('/counts/posts.json?tags=rating:g+user:'+danbooru_username)
.then(response => response.json())
.then(counts_general => {
fetch('/counts/posts.json?tags=rating:s+user:'+danbooru_username)
.then(response => response.json())
.then(counts_sensitive => {
fetch('/counts/posts.json?tags=rating:q+user:'+danbooru_username)
.then(response => response.json())
.then(counts_questionable => {
fetch('/counts/posts.json?tags=rating:e+user:'+danbooru_username)
.then(response => response.json())
.then(counts_explicit => {
var pperc_g=0, pperc_s=0, pperc_q=0, pperc_e=0
if(total_upload_count>0) {
pperc_g=Math.round((counts_general.counts.posts / total_upload_count) * 10000)/100;
pperc_s=Math.round((counts_sensitive.counts.posts / total_upload_count) * 10000)/100;
pperc_q=Math.round((counts_questionable.counts.posts / total_upload_count) * 10000)/100;
pperc_e=Math.round((counts_explicit.counts.posts / total_upload_count) * 10000)/100;
}
html_elem_tmp.outerHTML+=
`<tr>
<th>Rating percentages</th>
<td>
<span><a href='/posts?tags=rating%3Ag+user%3A${danbooru_username}'>General (${pperc_g}%)</a>,
<a href='/posts?tags=rating%3As+user%3A${danbooru_username}'>Sensitive (${pperc_s}%)</a>,
<a href='/posts?tags=rating%3Aq+user%3A${danbooru_username}'>Questionable (${pperc_q}%)</a>,
<a href='/posts?tags=rating%3Ae+user%3A${danbooru_username}'>Explicit (${pperc_e}%)</a> </span>
</td>
</tr>`
})
})
})
})
}
// related tags
// danbooru_username=document.querySelector("#c-users a.user").innerHTML.replaceAll(' ','_')
if(total_upload_count>0)
fetch('/related_tag.json?commit=Search&search%5Bcategory%5D=General&search%5Border%5D=Frequency&search%5Bquery%5D=user%3A'+danbooru_username)
.then(response => response.json())
.then(reltags_general => {
fetch('/related_tag.json?commit=Search&search%5Bcategory%5D=Copyright&search%5Border%5D=Frequency&search%5Bquery%5D=user%3A'+danbooru_username)
.then(response => response.json())
.then(reltags_copyrights => {
fetch('/related_tag.json?commit=Search&search%5Bcategory%5D=Character&search%5Border%5D=Frequency&search%5Bquery%5D=user%3A'+danbooru_username)
.then(response => response.json())
.then(reltags_characters => {
fetch('/related_tag.json?commit=Search&search%5Bcategory%5D=Artist&search%5Border%5D=Frequency&search%5Bquery%5D=user%3A'+danbooru_username)
.then(response => response.json())
.then(reltags_artists => {
fetch('/related_tag.json?commit=Search&search%5Bcategory%5D=Meta&search%5Border%5D=Frequency&search%5Bquery%5D=user%3A'+danbooru_username)
.then(response => response.json())
.then(reltags_meta => {
console.log(reltags_meta)
var html_related_tags_str="<span>"
var excluded_general_tags="shirt teeth 2boys 3boys long_sleeves closed_mouth open_mouth pants"
var amount_of_tags=20
excluded_general_tags.split(" ").forEach(taggy =>{
reltags_general.related_tags.splice( reltags_general.related_tags.indexOf(reltags_general.related_tags.find((item) => item.tag.name==taggy)),1 )
})
reltags_general.related_tags.slice(0,amount_of_tags*2).forEach(item => {
html_related_tags_str+=`<a class="tag-type-${item.tag.category}" href="/posts?tags=${item.tag.name} user:${danbooru_username}">${item.tag.name}</a> (${Math.round(item.frequency*10000)/100}%), `
})
html_related_tags_str+="<br>"
reltags_artists.related_tags.slice(0,amount_of_tags).forEach(item => {
html_related_tags_str+=`<a class="tag-type-${item.tag.category}" href="/posts?tags=${item.tag.name} user:${danbooru_username}">${item.tag.name}</a> (${Math.round(item.frequency*10000)/100}%), `
})
html_related_tags_str+="<br>"
reltags_copyrights.related_tags.slice(0,amount_of_tags).forEach(item => {
html_related_tags_str+=`<a class="tag-type-${item.tag.category}" href="/posts?tags=${item.tag.name} user:${danbooru_username}">${item.tag.name}</a> (${Math.round(item.frequency*10000)/100}%), `
})
html_related_tags_str+="<br>"
reltags_characters.related_tags.slice(0,amount_of_tags).forEach(item => {
html_related_tags_str+=`<a class="tag-type-${item.tag.category}" href="/posts?tags=${item.tag.name} user:${danbooru_username}">${item.tag.name}</a> (${Math.round(item.frequency*10000)/100}%), `
})
html_related_tags_str+="<br>"
reltags_meta.related_tags.slice(0,amount_of_tags).forEach(item => {
html_related_tags_str+=`<a class="tag-type-${item.tag.category}" href="/posts?tags=${item.tag.name} user:${danbooru_username}">${item.tag.name}</a> (${Math.round(item.frequency*10000)/100}%), `
})
html_related_tags_str+="<br>"
html_related_tags_str+="</span>"
console.log(html_related_tags_str)
var tb_fav_row=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Favorites').parentElement
tb_fav_row.outerHTML=
`
<tr>
<th>Related tags</th>
<td>
${html_related_tags_str}
</td>
</tr>
`+tb_fav_row.outerHTML;
})
})
})
})
})
};
// favorite groups sort alphabetically by default
var myelem=Array.from(document.querySelectorAll('th')).find(item => item.innerHTML == 'Favorite Groups').parentElement.querySelector("td a")
myelem.href+="&limit=200&search%5Border%5D=name";
if (Danbooru.PostTooltip.instance) Danbooru.PostTooltip.instance[0].destroy();
Danbooru.PostTooltip.SHOW_DELAY = 0;
Danbooru.PostTooltip.initialize();
}
})();