Werli commited on
Commit
83782c8
·
verified ·
1 Parent(s): d3427d7

Update modules/booru.py

Browse files

As Gelbooru, Rule34 suddenly decided to stop working due to 'API authentication', so I decided to remove them temporarily (or permanently). Either way Xbooru is working, but I don't know for how long. If it fails, I'll try something else. Either way I recommend searching tags using their website instead of API.

Files changed (1) hide show
  1. modules/booru.py +141 -161
modules/booru.py CHANGED
@@ -1,161 +1,141 @@
1
- import requests,re,base64,io,numpy as np
2
- from PIL import Image,ImageOps
3
- import torch,gradio as gr
4
-
5
- # Custom CSS for gallery styling
6
- css = """
7
- #custom-gallery {--row-height: 180px;display: grid;grid-auto-rows: min-content;gap: 10px;}
8
- #custom-gallery .thumbnail-item {height: var(--row-height);width: 100%;position: relative;overflow: hidden;border-radius: 8px;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);transition: transform 0.2s ease, box-shadow 0.2s ease;}
9
- #custom-gallery .thumbnail-item:hover {transform: translateY(-3px);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);}
10
- #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: contain;margin: 0 auto;display: block;}
11
- #custom-gallery .thumbnail-item img.portrait {max-width: 100%;}
12
- #custom-gallery .thumbnail-item img.landscape {max-height: 100%;}
13
- .gallery-container {max-height: 500px;overflow-y: auto;padding-right: 0px;--size-80: 500px;}
14
- .thumbnails {display: flex;position: absolute;bottom: 0;width: 120px;overflow-x: scroll;padding-top: 320px;padding-bottom: 280px;padding-left: 4px;flex-wrap: wrap;}
15
- #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: initial;width: fit-content;margin: 0px auto;display: block;}
16
- """
17
-
18
- # Helper to load image from URL
19
- def loadImageFromUrl(url):
20
- response = requests.get(url, timeout=10)
21
- if response.status_code != 200:
22
- raise Exception(f"Failed to load image from {url}")
23
- i = Image.open(io.BytesIO(response.content))
24
- i = ImageOps.exif_transpose(i)
25
- if i.mode != "RGBA":
26
- i = i.convert("RGBA")
27
- alpha = i.split()[-1]
28
- image = Image.new("RGB", i.size, (0, 0, 0))
29
- image.paste(i, mask=alpha)
30
- image = np.array(image).astype(np.float32) / 255.0
31
- image = torch.from_numpy(image)[None,]
32
- return image
33
-
34
- # Fetch data from multiple booru platforms
35
- def fetch_booru_images(site, Tags, exclude_tags, score, count, Safe, Questionable, Explicit):
36
- # Clean and format tags
37
- def clean_tag_list(tags):
38
- return [item.strip().replace(' ', '_') for item in tags.split(',') if item.strip()]
39
-
40
- Tags = '+'.join(clean_tag_list(Tags)) if Tags else ''
41
- exclude_tags = '+'.join('-' + tag for tag in clean_tag_list(exclude_tags))
42
-
43
- rating_filters = []
44
- if not Safe:
45
- rating_filters.extend(["rating:safe", "rating:general"])
46
- if not Questionable:
47
- rating_filters.extend(["rating:questionable", "rating:sensitive"])
48
- if not Explicit:
49
- rating_filters.append("rating:explicit")
50
- rating_filters = '+'.join(f'-{r}' for r in rating_filters)
51
-
52
- score_filter = f"score:>{score}"
53
-
54
- # Build query
55
- base_query = f"tags=sort:random+{Tags}+{exclude_tags}+{score_filter}+{rating_filters}&limit={count}&json=1"
56
- base_query = re.sub(r"\++", "+", base_query)
57
-
58
- # Fetch data based on site
59
- if site == "Gelbooru":
60
- url = f"https://gelbooru.com/index.php?page=dapi&s=post&q=index&{base_query}"
61
- response = requests.get(url).json()
62
- posts = response.get("post", [])
63
- elif site == "Rule34":
64
- url = f"https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&{base_query}"
65
- response = requests.get(url).json()
66
- posts = response
67
- elif site == "Xbooru":
68
- url = f"https://xbooru.com/index.php?page=dapi&s=post&q=index&{base_query}"
69
- response = requests.get(url).json()
70
- posts = response
71
- else:
72
- return [], [], []
73
-
74
- # Extract image URLs, tags, and post URLs
75
- image_urls = []
76
- tags_list = [post.get("tags", "").replace(" ", ", ").replace("_", " ").replace("(", "\\(").replace(")", "\\)").strip() for post in posts]
77
- post_urls = []
78
-
79
- for post in posts:
80
- if site in ["Gelbooru", "Rule34", "Xbooru"]:
81
- file_url = post.get("file_url")
82
- tags = post.get("tags", "").replace(" ", ", ").strip()
83
- post_id = post.get("id", "")
84
- else:
85
- continue
86
-
87
- if file_url:
88
- image_urls.append(file_url)
89
- tags_list.append(tags)
90
- if site == "Gelbooru":
91
- post_urls.append(f"https://gelbooru.com/index.php?page=post&s=view&id={post_id}")
92
- elif site == "Rule34":
93
- post_urls.append(f"https://rule34.xxx/index.php?page=post&s=view&id={post_id}")
94
- elif site == "Xbooru":
95
- post_urls.append(f"https://xbooru.com/index.php?page=post&s=view&id={post_id}")
96
-
97
- return image_urls, tags_list, post_urls
98
-
99
- # Main function to fetch and return processed images
100
- def booru_gradio(Tags, exclude_tags, score, count, Safe, Questionable, Explicit, site):
101
- image_urls, tags_list, post_urls = fetch_booru_images(site, Tags, exclude_tags, score, count, Safe, Questionable, Explicit)
102
-
103
- if not image_urls:
104
- return [], [], [], []
105
-
106
- image_data = []
107
- for url in image_urls:
108
- try:
109
- image = loadImageFromUrl(url)
110
- image = (image * 255).clamp(0, 255).cpu().numpy().astype(np.uint8)[0]
111
- image = Image.fromarray(image)
112
- image_data.append(image)
113
- except Exception as e:
114
- print(f"Error loading image from {url}: {e}")
115
- continue
116
-
117
- return image_data, tags_list, post_urls, image_urls
118
-
119
- # Update UI on image click
120
- def on_select(evt: gr.SelectData, tags_list, post_url_list, image_url_list):
121
- idx = evt.index
122
- if idx < len(tags_list):
123
- return tags_list[idx], post_url_list[idx], image_url_list[idx]
124
- return "No tags", "", ""
125
-
126
- def create_booru_interface():
127
- with gr.Blocks(css=css, fill_width=True) as demo:
128
- with gr.Row():
129
- with gr.Column():
130
- gr.Markdown("### ⚙️ Search Parameters")
131
- site = gr.Dropdown(label="Select Source", choices=["Gelbooru (Not working)", "Rule34", "Xbooru"], value="Xbooru")
132
- Tags = gr.Textbox(label="Tags (comma-separated)", placeholder="e.g. solo, 1girl, 1boy, artist name, character, black hair, granblue fantasy, ...", lines=3)
133
- exclude_tags = gr.Textbox(label="Exclude Tags (comma-separated)", placeholder="e.g. animated, watermark, username, ...", lines=3)
134
- score = gr.Number(label="Minimum Score", value=0)
135
- count = gr.Slider(label="Number of Images", minimum=1, maximum=20, step=1, value=1)
136
- Safe = gr.Checkbox(label="Include Safe", value=True)
137
- Questionable = gr.Checkbox(label="Include Questionable", value=True)
138
- Explicit = gr.Checkbox(label="Include Explicit (18+)", value=False)
139
- submit_btn = gr.Button("Fetch Images", variant="primary")
140
- with gr.Column():
141
- gr.Markdown("### 📄 Results")
142
- images_output = gr.Gallery(
143
- columns=2,
144
- show_share_button=False,
145
- interactive=True,
146
- height='auto',
147
- label='Grid of images',
148
- preview=False,
149
- elem_id='custom-gallery'
150
- )
151
- tags_output = gr.Textbox(label="Tags", placeholder="Select an image to display tags", lines=6, show_copy_button=True)
152
- post_url_output = gr.Textbox(label="Post URL", lines=2, show_copy_button=True)
153
- image_url_output = gr.Textbox(label="Image URL", lines=2, show_copy_button=True)
154
- # State to store tags, URLs
155
- tags_state = gr.State([])
156
- post_url_state = gr.State([])
157
- image_url_state = gr.State([])
158
- submit_btn.click(fn=booru_gradio, inputs=[Tags, exclude_tags, score, count, Safe, Questionable, Explicit, site], outputs=[images_output, tags_state, post_url_state, image_url_state], )
159
- images_output.select(fn=on_select, inputs=[tags_state, post_url_state, image_url_state], outputs=[tags_output, post_url_output, image_url_output], )
160
-
161
- return demo
 
1
+ import requests,re,base64,io,numpy as np
2
+ from PIL import Image,ImageOps
3
+ import torch,gradio as gr
4
+
5
+ # Custom CSS for gallery styling
6
+ css = """
7
+ #custom-gallery {--row-height: 180px;display: grid;grid-auto-rows: min-content;gap: 10px;}
8
+ #custom-gallery .thumbnail-item {height: var(--row-height);width: 100%;position: relative;overflow: hidden;border-radius: 8px;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);transition: transform 0.2s ease, box-shadow 0.2s ease;}
9
+ #custom-gallery .thumbnail-item:hover {transform: translateY(-3px);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);}
10
+ #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: contain;margin: 0 auto;display: block;}
11
+ #custom-gallery .thumbnail-item img.portrait {max-width: 100%;}
12
+ #custom-gallery .thumbnail-item img.landscape {max-height: 100%;}
13
+ .gallery-container {max-height: 500px;overflow-y: auto;padding-right: 0px;--size-80: 500px;}
14
+ .thumbnails {display: flex;position: absolute;bottom: 0;width: 120px;overflow-x: scroll;padding-top: 320px;padding-bottom: 280px;padding-left: 4px;flex-wrap: wrap;}
15
+ #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: initial;width: fit-content;margin: 0px auto;display: block;}
16
+ """
17
+
18
+ # Helper to load image from URL
19
+ def loadImageFromUrl(url):
20
+ response = requests.get(url, timeout=10)
21
+ if response.status_code != 200:
22
+ raise Exception(f"Failed to load image from {url}")
23
+ i = Image.open(io.BytesIO(response.content))
24
+ i = ImageOps.exif_transpose(i)
25
+ if i.mode != "RGBA":
26
+ i = i.convert("RGBA")
27
+ alpha = i.split()[-1]
28
+ image = Image.new("RGB", i.size, (0, 0, 0))
29
+ image.paste(i, mask=alpha)
30
+ image = np.array(image).astype(np.float32) / 255.0
31
+ image = torch.from_numpy(image)[None,]
32
+ return image
33
+
34
+ # Fetch data from Xbooru platform only
35
+ def fetch_booru_images(Tags, exclude_tags, score, count, Safe, Questionable, Explicit):
36
+ # Clean and format tags
37
+ def clean_tag_list(tags):
38
+ return [item.strip().replace(' ', '_') for item in tags.split(',') if item.strip()]
39
+
40
+ Tags = '+'.join(clean_tag_list(Tags)) if Tags else ''
41
+ exclude_tags = '+'.join('-' + tag for tag in clean_tag_list(exclude_tags))
42
+
43
+ rating_filters = []
44
+ if not Safe:
45
+ rating_filters.extend(["rating:safe", "rating:general"])
46
+ if not Questionable:
47
+ rating_filters.extend(["rating:questionable", "rating:sensitive"])
48
+ if not Explicit:
49
+ rating_filters.append("rating:explicit")
50
+ rating_filters = '+'.join(f'-{r}' for r in rating_filters)
51
+
52
+ score_filter = f"score:>{score}"
53
+
54
+ # Build query
55
+ base_query = f"tags=sort:random+{Tags}+{exclude_tags}+{score_filter}+{rating_filters}&limit={count}&json=1"
56
+ base_query = re.sub(r"\++", "+", base_query)
57
+
58
+ # Fetch data from Xbooru only atm
59
+ url = f"https://xbooru.com/index.php?page=dapi&s=post&q=index&{base_query}"
60
+ response = requests.get(url).json()
61
+ posts = response
62
+
63
+ # Extract image URLs, tags, and post URLs
64
+ image_urls = []
65
+ tags_list = [post.get("tags", "").replace(" ", ", ").replace("_", " ").replace("(", "\\(").replace(")", "\\)").strip() for post in posts]
66
+ post_urls = []
67
+
68
+ for post in posts:
69
+ file_url = post.get("file_url")
70
+ tags = post.get("tags", "").replace(" ", ", ").strip()
71
+ post_id = post.get("id", "")
72
+
73
+ if file_url:
74
+ image_urls.append(file_url)
75
+ tags_list.append(tags)
76
+ post_urls.append(f"https://xbooru.com/index.php?page=post&s=view&id={post_id}")
77
+
78
+ return image_urls, tags_list, post_urls
79
+
80
+ # Main function to fetch and return processed images
81
+ def booru_gradio(Tags, exclude_tags, score, count, Safe, Questionable, Explicit):
82
+ image_urls, tags_list, post_urls = fetch_booru_images(Tags, exclude_tags, score, count, Safe, Questionable, Explicit)
83
+
84
+ if not image_urls:
85
+ return [], [], [], []
86
+
87
+ image_data = []
88
+ for url in image_urls:
89
+ try:
90
+ image = loadImageFromUrl(url)
91
+ image = (image * 255).clamp(0, 255).cpu().numpy().astype(np.uint8)[0]
92
+ image = Image.fromarray(image)
93
+ image_data.append(image)
94
+ except Exception as e:
95
+ print(f"Error loading image from {url}: {e}")
96
+ continue
97
+
98
+ return image_data, tags_list, post_urls, image_urls
99
+
100
+ # Update UI on image click
101
+ def on_select(evt: gr.SelectData, tags_list, post_url_list, image_url_list):
102
+ idx = evt.index
103
+ if idx < len(tags_list):
104
+ return tags_list[idx], post_url_list[idx], image_url_list[idx]
105
+ return "No tags", "", ""
106
+
107
+ def create_booru_interface():
108
+ with gr.Blocks(css=css, fill_width=True) as demo:
109
+ with gr.Row():
110
+ with gr.Column():
111
+ gr.Markdown("### ⚙️ Search Parameters")
112
+ Tags = gr.Textbox(label="Tags (comma-separated)", placeholder="e.g. solo, 1girl, 1boy, artist name, character, black hair, granblue fantasy, ...", lines=3)
113
+ exclude_tags = gr.Textbox(label="Exclude Tags (comma-separated)", placeholder="e.g. animated, watermark, username, ...", lines=3)
114
+ score = gr.Number(label="Minimum Score", value=0)
115
+ count = gr.Slider(label="Number of Images", minimum=1, maximum=10, step=1, value=1)
116
+ Safe = gr.Checkbox(label="Include Safe", value=True)
117
+ Questionable = gr.Checkbox(label="Include Questionable", value=True)
118
+ Explicit = gr.Checkbox(label="Include Explicit (18+)", value=False)
119
+ submit_btn = gr.Button("Fetch Images", variant="primary")
120
+ with gr.Column():
121
+ gr.Markdown("### 📄 Results")
122
+ images_output = gr.Gallery(
123
+ columns=2,
124
+ show_share_button=False,
125
+ interactive=True,
126
+ height='auto',
127
+ label='Grid of images',
128
+ preview=False,
129
+ elem_id='custom-gallery'
130
+ )
131
+ tags_output = gr.Textbox(label="Tags", placeholder="Select an image to display tags", lines=6, show_copy_button=True)
132
+ post_url_output = gr.Textbox(label="Post URL", lines=2, show_copy_button=True)
133
+ image_url_output = gr.Textbox(label="Image URL", lines=2, show_copy_button=True)
134
+ # State to store tags, URLs
135
+ tags_state = gr.State([])
136
+ post_url_state = gr.State([])
137
+ image_url_state = gr.State([])
138
+ submit_btn.click(fn=booru_gradio, inputs=[Tags, exclude_tags, score, count, Safe, Questionable, Explicit], outputs=[images_output, tags_state, post_url_state, image_url_state], )
139
+ images_output.select(fn=on_select, inputs=[tags_state, post_url_state, image_url_state], outputs=[tags_output, post_url_output, image_url_output], )
140
+
141
+ return demo