Test / app.py
redstonehero's picture
Update app.py
d4aa2c1 verified
import gradio as gr
import requests
import random
import time
import html
import os
# --- CONFIGURATION ---
API_KEY = os.environ.get("GELBOORU_API_KEY")
USER_ID = os.environ.get("GELBOORU_USER_ID")
HEADERS = {"User-Agent": "GelbooruTagFetcher/10.0 (Python)"}
ENDPOINT = "https://gelbooru.com/index.php"
# --- DATA LISTS (RAW VALUES) ---
FRANCHISES_RAW =[
"genshin_impact", "pokemon", "blue_archive", "arknights",
"kancolle", "fire_emblem", "azur_lane", "honkai", "umamusume",
"girls_und_panzer", "girls'_frontline", "zenless_zone_zero",
"granblue_fantasy", "kemono_friends", "nikke", "project_moon",
"wuthering_waves", "league_of_legends", "princess_connect!",
"chainsaw_man", "guilty_gear", "reverse:1999", "mega_man", "dq", "tales"
]
FRANCHISE_SUBSTITUTIONS = {
"dq": "dragon quest",
"tales": "tales of (series)"
}
FRANCHISE_EXCEPTIONS = {
"fire_emblem": ["falchion_(fire_emblem)"],
"arknights":["infection_monitor_(arknights)", "oripathy_lesion_(arknights)"],
"project_moon":["e.g.o_(project_moon)", "(identity)"],
"zenless_zone_zero":["bangboo_(zenless_zone_zero)"],
"wuthering_waves":["pangu_terminal_(wuthering_waves)", "tacet_discord_(wuthering_waves)", "blake_bloom_(wuthering_waves)", "tacet_mark_(wuthering_waves)"],
"league_of_legends":["spirit_blossom_(league_of_legends)", "k/da_(league_of_legends)", "star_guardian_(league_of_legends)"],
"princess_connect!": ["gourmet_guild_(princess_connect!)"],
"chainsaw_man":["i'll_teach_you_everything_(chainsaw_man)"],
"dq":["dqn_(dqnww)", "dancer's_costume_(dq)"],
"tales":["momdy_(talesshinja)", "normin_(tales)"],
"genshin_impact":["vision_(genshin_impact)"]
}
UNWANTED = ["costume"]
HAIR_COLORS_RAW =[
"Any",
"white_hair", "red_hair", "purple_hair", "pink_hair",
"orange_hair", "grey_hair", "green_hair", "brown_hair", "blue_hair",
"blonde_hair", "black_hair", "aqua_hair"
]
# --- UI FORMATTERS ---
FRANCHISE_CHOICES =[
(FRANCHISE_SUBSTITUTIONS.get(f, f).replace("_", " ").title(), f)
for f in FRANCHISES_RAW
]
HAIR_CHOICES =[
(h.replace("_", " ").title(), h) for h in HAIR_COLORS_RAW
]
# --- BACKEND FUNCTIONS ---
def get_tag_count(tag_name):
"""Checks how many posts a specific tag has globally."""
params = {
"page": "dapi", "s": "tag", "q": "index",
"json": 1, "name": tag_name
}
if API_KEY and USER_ID:
params["api_key"] = API_KEY
params["user_id"] = USER_ID
try:
response = requests.get(ENDPOINT, params=params, headers=HEADERS)
response.raise_for_status()
data = response.json()
tags = data.get("tag",[]) if isinstance(data, dict) else data
return int(tags[0].get("count", 0)) if tags else 0
except Exception:
return 0
def find_character_tag(selected_franchises, selected_hair):
if not selected_franchises:
return "⚠️ Error: Select a franchise", "Please select at least one franchise checkbox."
chosen_franchise = random.choice(selected_franchises)
tag_modifier = f"({chosen_franchise}"
search_tags = f"1girl solo -alternate_hair_color -animated -1boy -*cosplay* *{tag_modifier}* score:>10 sort:random"
if selected_hair != "Any":
search_tags += f" {selected_hair}"
log_buffer = f"🎲 Randomly selected franchise: {chosen_franchise}\n"
log_buffer += f"πŸ”Ž Searching: {search_tags}\n"
params = {
"page": "dapi", "s": "post", "q": "index",
"json": 1, "limit": 50, "tags": search_tags
}
if API_KEY and USER_ID:
params["api_key"] = API_KEY
params["user_id"] = USER_ID
try:
response = requests.get(ENDPOINT, params=params, headers=HEADERS)
response.raise_for_status()
data = response.json()
posts = data.get("post",[]) if isinstance(data, dict) else data
if not posts:
log_buffer += "❌ No results found. Try selecting 'Any' hair color."
return "No Match Found", log_buffer
franchise_exceptions = FRANCHISE_EXCEPTIONS.get(chosen_franchise,[])
for post in posts:
raw_tags = html.unescape(post.get("tags", ""))
tag_list = raw_tags.split()
franchise_tags =[
t for t in tag_list
if tag_modifier in t
and not any(u in t for u in UNWANTED)
and not any(exc in t for exc in franchise_exceptions)
]
if len(franchise_tags) == 1:
candidate = franchise_tags[0]
# Check popularity
count = get_tag_count(candidate)
time.sleep(0.2)
if count >= 10:
log_buffer += f"βœ… Match found on Post ID {post.get('id')}\n"
log_buffer += f"🏷️ Tag '{candidate}' has {count} posts."
return candidate.replace('_', ' '), log_buffer
else:
log_buffer += f"⚠️ Candidate '{candidate}' only has {count} posts. Skipping.\n"
log_buffer += "❌ Checked 50 posts but none matched criteria."
return "No Match Found", log_buffer
except Exception as e:
return "API Error", f"An error occurred: {str(e)}"
# --- UI HELPER FUNCTIONS ---
def select_all():
return FRANCHISES_RAW
def select_none():
return[]
def invert_selection(current_selection):
return[item for item in FRANCHISES_RAW if item not in current_selection]
def copy_notification(x):
if x and "Error" not in x and "No Match" not in x:
gr.Info(f"Copied '{x}' to clipboard!")
return x
# --- GRADIO LAYOUT ---
JS_COPY_LOGIC = """
(x) => {
if (x && x !== "Result will appear here..." && !x.includes("Error")) {
navigator.clipboard.writeText(x);
}
return x;
}
"""
with gr.Blocks(theme=gr.themes.Soft()) as app:
gr.Markdown("## 🏷️ Gelbooru Character Tag Finder")
gr.Markdown("Pick your franchises and a hair color. The script will return **one specific character tag**.")
with gr.Row():
# --- Left Column: Inputs ---
with gr.Column(scale=1):
franchise_input = gr.CheckboxGroup(
choices=FRANCHISE_CHOICES,
value=FRANCHISES_RAW,
label="1. Select Franchises"
)
with gr.Row():
btn_all = gr.Button("Select All", size="sm")
btn_none = gr.Button("Clear All", size="sm")
btn_invert = gr.Button("Invert Selection", size="sm")
hair_input = gr.Radio(
choices=HAIR_CHOICES,
value="Any",
label="2. Select Hair Color"
)
submit_btn = gr.Button("Find Character Tag", variant="primary")
# --- Right Column: Outputs ---
with gr.Column(scale=1):
gr.Markdown("### πŸ‘‡ Click the button below to copy the tag")
result_btn = gr.Button(
value="Result will appear here...",
variant="secondary",
size="lg",
interactive=True
)
log_output = gr.TextArea(
label="Process Logs",
lines=10,
max_lines=15,
interactive=False
)
# --- EVENT LISTENERS ---
btn_all.click(fn=select_all, outputs=franchise_input)
btn_none.click(fn=select_none, outputs=franchise_input)
btn_invert.click(fn=invert_selection, inputs=franchise_input, outputs=franchise_input)
submit_btn.click(
fn=find_character_tag,
inputs=[franchise_input, hair_input],
outputs=[result_btn, log_output]
)
result_btn.click(
fn=copy_notification,
inputs=result_btn,
outputs=result_btn,
js=JS_COPY_LOGIC
)
if __name__ == "__main__":
app.launch()