ginipick commited on
Commit
9ac4609
ยท
verified ยท
1 Parent(s): 4b12e77

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +558 -480
app.py CHANGED
@@ -1,484 +1,562 @@
1
- import subprocess
2
- import os
3
-
4
- # ํ•„์ˆ˜ ํŒจํ‚ค์ง€ ์„ค์น˜ (์ด๋ฏธ ์„ค์น˜๋˜์–ด ์žˆ๋‹ค๋ฉด ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค)
5
- subprocess.run(
6
- "pip install flash-attn --no-build-isolation",
7
- env={"FLASH_ATTENTION_SKIP_CUDA_BUILD": "TRUE"},
8
- shell=True,
9
- )
10
- subprocess.run("pip install huggingface_hub==0.25.0", shell=True)
11
- subprocess.run("pip install numpy==1.26.4 sentencepiece sacremoses transformers gradio safetensors torchvision diffusers", shell=True)
12
-
13
- # ์ฒดํฌํฌ์ธํŠธ ํด๋” ์ƒ์„ฑ ๋ฐ ๋ชจ๋ธ ์Šค๋ƒ…์ƒท ๋‹ค์šด๋กœ๋“œ
14
- os.makedirs("/home/user/app/checkpoints", exist_ok=True)
15
- from huggingface_hub import snapshot_download
16
- snapshot_download(repo_id="Alpha-VLLM/Lumina-Image-2.0", local_dir="/home/user/app/checkpoints")
17
-
18
- hf_token = os.environ["HF_TOKEN"]
19
-
20
- # โ˜… ์ค‘์š”: CUDA ์ดˆ๊ธฐํ™” ์ „์— spaces ํŒจํ‚ค์ง€๋ฅผ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
21
- import spaces
22
-
23
- # ์ด์ œ CUDA์™€ ๊ด€๋ จ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
24
- import argparse
25
- import builtins
26
- import json
27
- import math
28
- import multiprocessing as mp
29
- import random
30
- import socket
31
- import traceback
32
-
33
- import torch
34
- import gradio as gr
35
- import numpy as np
36
- from safetensors.torch import load_file
37
- from torchvision.transforms.functional import to_pil_image
38
-
39
- # ๋ฒˆ์—ญ ํŒŒ์ดํ”„๋ผ์ธ (ํ•œ๊ธ€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์˜์–ด๋กœ ๋ฒˆ์—ญ)
40
- from transformers import pipeline
41
- translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en")
42
-
43
- from imgproc import generate_crop_size_list
44
- import models
45
- from transport import Sampler, create_transport
46
-
47
- from multiprocessing import Process, Queue, set_start_method, get_context
48
-
49
- class ModelFailure:
50
- pass
51
-
52
- # Adapted from pipelines.StableDiffusionXLPipeline.encode_prompt
53
- def encode_prompt(prompt_batch, text_encoder, tokenizer, proportion_empty_prompts, is_train=True):
54
- captions = []
55
- for caption in prompt_batch:
56
- if random.random() < proportion_empty_prompts:
57
- captions.append("")
58
- elif isinstance(caption, str):
59
- captions.append(caption)
60
- elif isinstance(caption, (list, np.ndarray)):
61
- captions.append(random.choice(caption) if is_train else caption[0])
62
-
63
- with torch.no_grad():
64
- text_inputs = tokenizer(
65
- captions,
66
- padding=True,
67
- pad_to_multiple_of=8,
68
- max_length=256,
69
- truncation=True,
70
- return_tensors="pt",
71
- )
72
-
73
- print(f"Text Encoder Device: {text_encoder.device}")
74
- text_input_ids = text_inputs.input_ids.cuda()
75
- prompt_masks = text_inputs.attention_mask.cuda()
76
- print(f"Text Input Ids Device: {text_input_ids.device}")
77
- print(f"Prompt Masks Device: {prompt_masks.device}")
78
-
79
- prompt_embeds = text_encoder(
80
- input_ids=text_input_ids,
81
- attention_mask=prompt_masks,
82
- output_hidden_states=True,
83
- ).hidden_states[-2]
84
- text_encoder.cpu()
85
-
86
- return prompt_embeds, prompt_masks
87
-
88
- @torch.no_grad()
89
- def model_main(args, master_port, rank):
90
- # diffusers, transformers ๋“ฑ์˜ ๋‚ด๋ถ€ ์ž„ํฌํŠธ๋ฅผ ์œ„ํ•ด ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
91
- from diffusers.models import AutoencoderKL
92
- from transformers import AutoModel, AutoTokenizer
93
-
94
- # ๊ธฐ๋ณธ print ํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ ์ถœ๋ ฅ ์ง€์—ฐ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
95
- original_print = builtins.print
96
- def print(*args, **kwargs):
97
- kwargs.setdefault("flush", True)
98
- original_print(*args, **kwargs)
99
- builtins.print = print
100
-
101
- train_args = torch.load(os.path.join(args.ckpt, "model_args.pth"))
102
- print("Loaded model arguments:", json.dumps(train_args.__dict__, indent=2))
103
-
104
- print(f"Creating lm: Gemma-2-2B")
105
- dtype = {"bf16": torch.bfloat16, "fp16": torch.float16, "fp32": torch.float32}[args.precision]
106
-
107
- text_encoder = AutoModel.from_pretrained("google/gemma-2-2b", torch_dtype=dtype, token=hf_token).eval().to("cuda")
108
- cap_feat_dim = text_encoder.config.hidden_size
109
- if args.num_gpus > 1:
110
- raise NotImplementedError("Inference with >1 GPUs not yet supported")
111
-
112
- tokenizer = AutoTokenizer.from_pretrained("google/gemma-2-2b", token=hf_token)
113
- tokenizer.padding_side = "right"
114
-
115
- vae = AutoencoderKL.from_pretrained("black-forest-labs/FLUX.1-dev", subfolder="vae", token=hf_token).cuda()
116
-
117
- print(f"Creating DiT: {train_args.model}")
118
- model = models.__dict__[train_args.model](
119
- in_channels=16,
120
- qk_norm=train_args.qk_norm,
121
- cap_feat_dim=cap_feat_dim,
122
- )
123
- model.eval().to("cuda", dtype=dtype)
124
-
125
- assert train_args.model_parallel_size == args.num_gpus
126
- if args.ema:
127
- print("Loading EMA model.")
128
- print('Loading model weights...')
129
- ckpt_path = os.path.join(
130
- args.ckpt,
131
- f"consolidated{'_ema' if args.ema else ''}.{rank:02d}-of-{args.num_gpus:02d}.safetensors",
132
- )
133
- if os.path.exists(ckpt_path):
134
- ckpt = load_file(ckpt_path)
135
- else:
136
- ckpt_path = os.path.join(
137
- args.ckpt,
138
- f"consolidated{'_ema' if args.ema else ''}.{rank:02d}-of-{args.num_gpus:02d}.pth",
139
- )
140
- assert os.path.exists(ckpt_path)
141
- ckpt = torch.load(ckpt_path, map_location="cuda")
142
- model.load_state_dict(ckpt, strict=True)
143
- print('Model weights loaded.')
 
 
 
 
 
 
 
 
 
 
144
 
145
- return text_encoder, tokenizer, vae, model
146
-
147
- @torch.no_grad()
148
- def inference(args, infer_args, text_encoder, tokenizer, vae, model):
149
- dtype = {"bf16": torch.bfloat16, "fp16": torch.float16, "fp32": torch.float32}[args.precision]
150
- train_args = torch.load(os.path.join(args.ckpt, "model_args.pth"))
151
- torch.cuda.set_device(0)
152
- with torch.autocast("cuda", dtype):
153
- (
154
- cap,
155
- neg_cap,
156
- system_type,
157
- resolution,
158
- num_sampling_steps,
159
- cfg_scale,
160
- cfg_trunc,
161
- renorm_cfg,
162
- solver,
163
- t_shift,
164
- seed,
165
- scaling_method,
166
- scaling_watershed,
167
- proportional_attn,
168
- ) = infer_args
169
-
170
- system_prompt = system_type
171
- cap = system_prompt + cap
172
- if neg_cap != "":
173
- neg_cap = system_prompt + neg_cap
174
-
175
- metadata = dict(
176
- real_cap=cap,
177
- real_neg_cap=neg_cap,
178
- system_type=system_type,
179
- resolution=resolution,
180
- num_sampling_steps=num_sampling_steps,
181
- cfg_scale=cfg_scale,
182
- cfg_trunc=cfg_trunc,
183
- renorm_cfg=renorm_cfg,
184
- solver=solver,
185
- t_shift=t_shift,
186
- seed=seed,
187
- scaling_method=scaling_method,
188
- scaling_watershed=scaling_watershed,
189
- proportional_attn=proportional_attn,
190
- )
191
- print("> Parameters:", json.dumps(metadata, indent=2))
192
-
193
- try:
194
- # ์ƒ˜ํ”Œ๋Ÿฌ ์„ค์ •
195
- if solver == "dpm":
196
- transport = create_transport("Linear", "velocity")
197
- sampler = Sampler(transport)
198
- sample_fn = sampler.sample_dpm(
199
- model.forward_with_cfg,
200
- model_kwargs=model_kwargs,
201
- )
202
- else:
203
- transport = create_transport(
204
- args.path_type,
205
- args.prediction,
206
- args.loss_weight,
207
- args.train_eps,
208
- args.sample_eps,
209
- )
210
- sampler = Sampler(transport)
211
- sample_fn = sampler.sample_ode(
212
- sampling_method=solver,
213
- num_steps=num_sampling_steps,
214
- atol=args.atol,
215
- rtol=args.rtol,
216
- reverse=args.reverse,
217
- time_shifting_factor=t_shift,
218
- )
219
- # ํ•ด์ƒ๋„ ๋ฐ latent ๊ณต๊ฐ„ ํฌ๊ธฐ ๊ณ„์‚ฐ
220
- resolution = resolution.split(" ")[-1]
221
- w, h = resolution.split("x")
222
- w, h = int(w), int(h)
223
- latent_w, latent_h = w // 8, h // 8
224
- if int(seed) != 0:
225
- torch.random.manual_seed(int(seed))
226
- z = torch.randn([1, 16, latent_h, latent_w], device="cuda").to(dtype)
227
- z = z.repeat(2, 1, 1, 1)
228
-
229
- with torch.no_grad():
230
- if neg_cap != "":
231
- cap_feats, cap_mask = encode_prompt([cap] + [neg_cap], text_encoder, tokenizer, 0.0)
232
- else:
233
- cap_feats, cap_mask = encode_prompt([cap] + [""], text_encoder, tokenizer, 0.0)
234
-
235
- cap_mask = cap_mask.to(cap_feats.device)
236
-
237
- model_kwargs = dict(
238
- cap_feats=cap_feats,
239
- cap_mask=cap_mask,
240
- cfg_scale=cfg_scale,
241
- cfg_trunc=1 - cfg_trunc,
242
- renorm_cfg=renorm_cfg,
243
- )
244
-
245
- print(f"> Caption: {cap}")
246
- print(f"> Number of sampling steps: {num_sampling_steps}")
247
- print(f"> CFG scale: {cfg_scale}")
248
- print("> Starting sampling...")
249
- if solver == "dpm":
250
- samples = sample_fn(z, steps=num_sampling_steps, order=2, skip_type="time_uniform_flow", method="multistep", flow_shift=t_shift)
251
- else:
252
- samples = sample_fn(z, model.forward_with_cfg, **model_kwargs)[-1]
253
- samples = samples[:1]
254
- print("Sample dtype:", samples.dtype)
255
-
256
- vae_scale = {
257
- "sdxl": 0.13025,
258
- "sd3": 1.5305,
259
- "ema": 0.18215,
260
- "mse": 0.18215,
261
- "cogvideox": 1.15258426,
262
- "flux": 0.3611,
263
- }["flux"]
264
- vae_shift = {
265
- "sdxl": 0.0,
266
- "sd3": 0.0609,
267
- "ema": 0.0,
268
- "mse": 0.0,
269
- "cogvideox": 0.0,
270
- "flux": 0.1159,
271
- }["flux"]
272
- print(f"> VAE scale: {vae_scale}, shift: {vae_shift}")
273
- print("Samples shape:", samples.shape)
274
- samples = vae.decode(samples / vae_scale + vae_shift).sample
275
- samples = (samples + 1.0) / 2.0
276
- samples.clamp_(0.0, 1.0)
277
-
278
- img = to_pil_image(samples[0].float())
279
- print("> Generated image successfully.")
280
-
281
- return img, metadata
282
- except Exception:
283
- print(traceback.format_exc())
284
- return ModelFailure()
285
-
286
- def none_or_str(value):
287
- if value == "None":
288
- return None
289
- return value
290
-
291
- def parse_transport_args(parser):
292
- group = parser.add_argument_group("Transport arguments")
293
- group.add_argument(
294
- "--path-type",
295
- type=str,
296
- default="Linear",
297
- choices=["Linear", "GVP", "VP"],
298
- help="Type of path for transport: 'Linear', 'GVP' (Geodesic Vector Pursuit), or 'VP' (Vector Pursuit).",
299
- )
300
- group.add_argument(
301
- "--prediction",
302
- type=str,
303
- default="velocity",
304
- choices=["velocity", "score", "noise"],
305
- help="Prediction model for the transport dynamics.",
306
- )
307
- group.add_argument(
308
- "--loss-weight",
309
- type=none_or_str,
310
- default=None,
311
- choices=[None, "velocity", "likelihood"],
312
- help="Weighting of different loss components: 'velocity', 'likelihood', or None.",
313
- )
314
- group.add_argument("--sample-eps", type=float, help="Sampling parameter in the transport model.")
315
- group.add_argument("--train-eps", type=float, help="Training epsilon to stabilize learning.")
316
-
317
- def parse_ode_args(parser):
318
- group = parser.add_argument_group("ODE arguments")
319
- group.add_argument(
320
- "--atol",
321
- type=float,
322
- default=1e-6,
323
- help="Absolute tolerance for the ODE solver.",
324
- )
325
- group.add_argument(
326
- "--rtol",
327
- type=float,
328
- default=1e-3,
329
- help="Relative tolerance for the ODE solver.",
330
- )
331
- group.add_argument("--reverse", action="store_true", help="Run the ODE solver in reverse.")
332
- group.add_argument(
333
- "--likelihood",
334
- action="store_true",
335
- help="Enable likelihood calculation during the ODE solving process.",
336
- )
337
-
338
- def find_free_port() -> int:
339
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
340
- sock.bind(("", 0))
341
- port = sock.getsockname()[1]
342
- sock.close()
343
- return port
344
-
345
- # ํ•œ๊ธ€ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ์˜์–ด๋กœ ๋ฒˆ์—ญํ•˜๋Š” ํ•จ์ˆ˜
346
- def translate_if_korean(text: str) -> str:
347
- import re
348
- if re.search(r"[ใ„ฑ-ใ…Žใ…-ใ…ฃ๊ฐ€-ํžฃ]", text):
349
- print("Translating Korean prompt to English...")
350
- translation = translator(text)
351
- return translation[0]["translation_text"]
352
- return text
353
-
354
- def main():
355
- parser = argparse.ArgumentParser()
356
- parser.add_argument("--num_gpus", type=int, default=1)
357
- parser.add_argument("--ckpt", type=str, default='/home/user/app/checkpoints', required=False)
358
- parser.add_argument("--ema", action="store_true")
359
- parser.add_argument("--precision", default="bf16", choices=["bf16", "fp32"])
360
- parser.add_argument("--hf_token", type=str, default=None, help="Hugging Face read token for accessing gated repo.")
361
- parser.add_argument("--res", type=int, default=1024, choices=[256, 512, 1024])
362
- parser.add_argument("--port", type=int, default=12123)
363
-
364
- parse_transport_args(parser)
365
- parse_ode_args(parser)
366
- args = parser.parse_known_args()[0]
367
-
368
- if args.num_gpus != 1:
369
- raise NotImplementedError("Multi-GPU Inference is not yet supported")
370
-
371
- master_port = find_free_port()
372
- text_encoder, tokenizer, vae, model = model_main(args, master_port, 0)
373
- description = "Lumina-Image 2.0 ([Github](https://github.com/Alpha-VLLM/Lumina-Image-2.0/tree/main))"
374
-
375
- # ์ปค์Šคํ…€ CSS: ๋ฉ”๋‰ด ์ปจํ…Œ์ด๋„ˆ์˜ ๋„ˆ๋น„๋ฅผ ์ค„์ด๊ณ , ๋ฐฐ๊ฒฝ์ด ์ž˜ ๋ณด์ด๋„๋ก ๋ฐ˜ํˆฌ๋ช… ๋ฐฐ๊ฒฝ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
376
- custom_css = """
377
- body {
378
- background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
379
- font-family: 'Helvetica', sans-serif;
380
- color: #333;
381
- }
382
- .gradio-container {
383
- background: rgba(255, 255, 255, 0.85); /* ๋ฐ˜ํˆฌ๋ช… ๋ฐฐ๊ฒฝ */
384
- max-width: 800px; /* ์ปจํ…Œ์ด๋„ˆ ์ตœ๋Œ€ ๋„ˆ๋น„ ์กฐ์ • */
385
- margin: 20px auto; /* ์ค‘์•™ ์ •๋ ฌ */
386
- border-radius: 15px;
387
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);
388
- padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  }
390
- .gradio-title {
391
- font-weight: bold;
392
- font-size: 1.5em;
393
- text-align: center;
394
- margin-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  }
396
- """
397
-
398
- with gr.Blocks(css=custom_css) as demo:
399
- with gr.Row():
400
- gr.Markdown(f"<div class='gradio-title'>{description}</div>")
401
- with gr.Row():
402
- with gr.Column():
403
- cap = gr.Textbox(
404
- lines=2,
405
- label="Caption",
406
- interactive=True,
407
- value="Majestic landscape photograph of snow-capped mountains under a dramatic sky at sunset. The mountains dominate the lower half of the image, with rugged peaks and deep crevasses visible. A glacier flows down the right side, partially illuminated by the warm light. The sky is filled with fiery orange and golden clouds, contrasting with the cool tones of the snow. The central peak is partially obscured by clouds, adding a sense of mystery. The foreground features dark, shadowed forested areas, enhancing the depth. High contrast, natural lighting, warm color palette, photorealistic, expansive, awe-inspiring, serene, visually balanced, dynamic composition.",
408
- placeholder="Enter a caption."
409
- )
410
- neg_cap = gr.Textbox(
411
- lines=2,
412
- label="Negative Caption",
413
- interactive=True,
414
- value="",
415
- placeholder="Enter a negative caption."
416
- )
417
- default_value = "You are an assistant designed to generate high-quality images with the highest degree of image-text alignment based on textual prompts."
418
- system_type = gr.Dropdown(
419
- value=default_value,
420
- choices=[
421
- "You are an assistant designed to generate high-quality images with the highest degree of image-text alignment based on textual prompts.",
422
- "You are an assistant designed to generate superior images with the superior degree of image-text alignment based on textual prompts or user prompts.",
423
- ""
424
- ],
425
- label="System Type"
426
- )
427
- with gr.Row():
428
- res_choices = [f"{w}x{h}" for w, h in generate_crop_size_list((args.res // 64) ** 2, 64)]
429
- default_value = "1024x1024"
430
- resolution = gr.Dropdown(value=default_value, choices=res_choices, label="Resolution")
431
- with gr.Row():
432
- num_sampling_steps = gr.Slider(minimum=1, maximum=70, value=40, step=1, interactive=True, label="Sampling Steps")
433
- seed = gr.Slider(minimum=0, maximum=int(1e5), value=0, step=1, interactive=True, label="Seed (0 for random)")
434
- cfg_trunc = gr.Slider(minimum=0, maximum=1, value=0, step=0.01, interactive=True, label="CFG Truncation")
435
- with gr.Row():
436
- solver = gr.Dropdown(value="euler", choices=["euler", "midpoint", "rk4"], label="Solver")
437
- t_shift = gr.Slider(minimum=1, maximum=20, value=6, step=1, interactive=True, label="Time Shift")
438
- cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, value=4.0, interactive=True, label="CFG Scale")
439
- with gr.Row():
440
- renorm_cfg = gr.Dropdown(value=True, choices=[True, False, 2.0], label="CFG Renorm")
441
- with gr.Accordion("Advanced Settings for Resolution Extrapolation", open=False):
442
- with gr.Row():
443
- scaling_method = gr.Dropdown(value="Time-aware", choices=["Time-aware", "None"], label="RoPE Scaling Method")
444
- scaling_watershed = gr.Slider(minimum=0.0, maximum=1.0, value=0.3, interactive=True, label="Linear/NTK Watershed")
445
- with gr.Row():
446
- proportional_attn = gr.Checkbox(value=True, interactive=True, label="Proportional Attention")
447
- with gr.Row():
448
- submit_btn = gr.Button("Submit", variant="primary")
449
- with gr.Column():
450
- output_img = gr.Image(label="Generated Image", interactive=False)
451
- with gr.Accordion(label="Generation Parameters", open=True):
452
- gr_metadata = gr.JSON(label="Metadata", show_label=False)
453
- with gr.Row():
454
- prompts = [
455
- "Close-up portrait of a young woman with light brown hair, looking to the right, illuminated by warm, golden sunlight. Her hair is gently tousled, catching the light and creating a halo effect around her head. She wears a white garment with a V-neck, visible in the lower left of the frame. The background is dark and out of focus, enhancing the contrast between her illuminated face and the shadows. Soft, ethereal lighting, high contrast, warm color palette, shallow depth of field, natural backlighting, serene and contemplative mood, cinematic quality, intimate and visually striking composition.",
456
- "ํ•˜๋Š˜์„ ๋‚˜๋Š” ์šฉ, ์‹ ๋น„๋กœ์šด ๋ถ„์œ„๊ธฐ, ๊ตฌ๋ฆ„ ์œ„๋ฅผ ๋‚ ๋ฉฐ ๋น›๋‚˜๋Š” ๋น„๋Š˜์„ ๊ฐ€์ง„, ์ „์„ค ์†์˜ ์กด์žฌ, ๊ฐ•๋ ฌํ•œ ์ƒ‰์ฑ„์™€ ๋””ํ…Œ์ผํ•œ ๋ฌ˜์‚ฌ.",
457
- "Aesthetic photograph of a bouquet of pink and white ranunculus flowers in a clear glass vase, centrally positioned on a wooden surface. The flowers are in full bloom, displaying intricate layers of petals with a soft gradient from pale pink to white. The vase is filled with water, visible through the clear glass, and the stems are submerged. In the background, a blurred vase with green stems is partially visible, adding depth to the composition. The lighting is warm and natural, casting soft shadows and highlighting the delicate textures of the petals. The scene is serene and intimate, with a focus on the organic beauty of the flowers. Photorealistic, shallow depth of field, soft natural lighting, warm color palette, high contrast, glossy texture, tranquil, visually balanced.",
458
- "ํ•œๅชไผ˜้›…็š„็™ฝ็Œซ็ฉฟ็€ไธ€ไปถ็ดซ่‰ฒ็š„ๆ——่ข๏ผŒๆ——่ขไธŠ็ปฃๆœ‰็ฒพ่‡ด็š„็‰กไธน่Šฑๅ›พๆกˆ๏ผŒๆ˜พๅพ—้ซ˜่ดตๅ…ธ้›…ใ€‚ๅฎƒๅคดไธŠๆˆด็€ไธ€ๆœต้‡‘่‰ฒ็š„ๅ‘้ฅฐ๏ผŒๅ˜ด้‡Œๅผ็€ไธ€ๆ น่ฑกๅพๅฅฝ่ฟ็š„็บข่‰ฒไธๅธฆใ€‚ๅ‘จๅ›ด็Žฏ็ป•็€่ฎธๅคš้ฃ˜ๅŠจ็š„็บธ้นคๅ’Œ้‡‘่‰ฒ็š„ๅ…‰็‚น๏ผŒ่ฅ้€ ๅ‡บไธ€็ง็ฅฅ็‘žๅ’Œๆขฆๅนป็š„ๆฐ›ๅ›ดใ€‚่ถ…ๅ†™ๅฎž้ฃŽๆ ผใ€‚"
459
- ]
460
- prompts = [[p] for p in prompts]
461
- gr.Examples(prompts, [cap], label="Examples")
462
- @spaces.GPU(duration=200)
463
- def on_submit(cap, neg_cap, system_type, resolution, num_sampling_steps, cfg_scale, cfg_trunc, renorm_cfg, solver, t_shift, seed, scaling_method, scaling_watershed, proportional_attn, progress=gr.Progress(track_tqdm=True)):
464
- # ํ•œ๊ธ€ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ์˜์–ด๋กœ ๋ฒˆ์—ญ
465
- cap = translate_if_korean(cap)
466
- if neg_cap and neg_cap.strip():
467
- neg_cap = translate_if_korean(neg_cap)
468
- infer_args = (cap, neg_cap, system_type, resolution, num_sampling_steps, cfg_scale, cfg_trunc, renorm_cfg, solver, t_shift, seed, scaling_method, scaling_watershed, proportional_attn)
469
- result = inference(args, infer_args, text_encoder, tokenizer, vae, model)
470
- if isinstance(result, ModelFailure):
471
- raise RuntimeError("Model failed to generate the image.")
472
- return result
473
- submit_btn.click(
474
- on_submit,
475
- [cap, neg_cap, system_type, resolution, num_sampling_steps, cfg_scale, cfg_trunc, renorm_cfg, solver, t_shift, seed, scaling_method, scaling_watershed, proportional_attn],
476
- [output_img, gr_metadata],
477
- )
478
- def show_scaling_watershed(scaling_m):
479
- return gr.update(visible=scaling_m == "Time-aware")
480
- scaling_method.change(show_scaling_watershed, scaling_method, scaling_watershed)
481
- demo.queue().launch(server_name="0.0.0.0")
482
 
483
- if __name__ == "__main__":
484
- main()
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import os, re, json
3
+
4
+ app = Flask(__name__)
5
+
6
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 1. CONFIGURATION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
7
+
8
+ # Domains that commonly block iframes
9
+ BLOCKED_DOMAINS = [
10
+ "naver.com", "daum.net", "google.com",
11
+ "facebook.com", "instagram.com", "kakao.com",
12
+ "ycombinator.com"
13
+ ]
14
+
15
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 2. CURATED CATEGORIES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
16
+ CATEGORIES = {
17
+ "Popular": [
18
+ "https://huggingface.co/spaces/openfree/AGI-Screenplay",
19
+ "https://huggingface.co/spaces/openfree/AGI-WebNovel",
20
+ "https://huggingface.co/spaces/openfree/AGI-NOVEL",
21
+ "https://huggingface.co/spaces/fantaxy/AGI-LEADERBOARD",
22
+ "https://cutechicken-3d-airforce-simulator.static.hf.space",
23
+ "https://huggingface.co/spaces/ginipick/Private-AI",
24
+ "https://huggingface.co/spaces/fantaxy/ofai-flx-logo",
25
+ "https://huggingface.co/spaces/aiqtech/FLUX-Ghibli-Studio-LoRA",
26
+ "https://huggingface.co/spaces/seawolf2357/REALVISXL-V5",
27
+ "https://huggingface.co/spaces/fantos/flx8lora",
28
+ "https://huggingface.co/spaces/ginipick/Realtime-FLUX",
29
+ "https://huggingface.co/spaces/fantaxy/flx-pulid",
30
+ "https://huggingface.co/spaces/ginipick/FLUX-Prompt-Generator",
31
+ "https://huggingface.co/spaces/aiqtech/kofaceid",
32
+ "https://huggingface.co/spaces/aiqtech/flxgif",
33
+ "https://huggingface.co/spaces/fantos/flxfashmodel",
34
+ "https://huggingface.co/spaces/fantos/flxcontrol",
35
+ "https://huggingface.co/spaces/fantos/textcutobject",
36
+ "https://huggingface.co/spaces/seawolf2357/flxloraexp",
37
+ "https://huggingface.co/spaces/fantaxy/flxloraexp",
38
+ "https://huggingface.co/spaces/aiqtech/imaginpaint",
39
+ "https://huggingface.co/spaces/ginipick/FLUXllama",
40
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
41
+ "https://huggingface.co/spaces/fantaxy/flx-upscale",
42
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video",
43
+ "https://huggingface.co/spaces/fantos/VoiceClone",
44
+ "https://huggingface.co/spaces/fantaxy/Rolls-Royce",
45
+ "https://huggingface.co/spaces/aiqtech/FLUX-military",
46
+ "https://huggingface.co/spaces/fantaxy/FLUX-Animations",
47
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2",
48
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
49
+ "https://huggingface.co/spaces/ginipick/Time-Stream",
50
+ "https://huggingface.co/spaces/seawolf2357/sd-prompt-gen",
51
+ "https://huggingface.co/spaces/openfree/MagicFace-V3",
52
+ "https://huggingface.co/spaces/Heartsync/adult",
53
+ "https://huggingface.co/spaces/Heartsync/wan2-1-fast-security",
54
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
55
+ "https://huggingface.co/spaces/seawolf2357/img2vid",
56
+ "https://huggingface.co/spaces/openfree/image-to-vector",
57
+ "https://huggingface.co/spaces/openfree/DreamO-video",
58
+ "https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape",
59
+ "https://huggingface.co/spaces/fantaxy/Sound-AI-SFX",
60
+ "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
61
+ "https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
62
+ "https://huggingface.co/spaces/Heartsync/NSFW-image",
63
+ "https://huggingface.co/spaces/Heartsync/NSFW-detection",
64
+ "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
65
+ "https://huggingface.co/spaces/ginigen/VEO3-Free",
66
+ "https://huggingface.co/spaces/ginigen/FLUX-Text-Tree-Image",
67
+ "https://huggingface.co/spaces/ginigen/text3d-r1",
68
+ "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
69
+
70
+ ],
71
+ "BEST": [
72
+ "https://huggingface.co/spaces/MaziyarPanahi/FACTS-Leaderboard",
73
+ "https://huggingface.co/spaces/ginigen/Flux-Kontext-Style",
74
+ "https://huggingface.co/spaces/openfree/Cycle-Navigator",
75
+ "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
76
+ "https://huggingface.co/spaces/ginigen/Seedance-Free",
77
+ "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
78
+ "https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
79
+ "https://huggingface.co/spaces/VIDraft/DNA-CASINO",
80
+ "https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
81
+ "https://huggingface.co/spaces/fantaxy/YTB-TEST",
82
+ "https://huggingface.co/spaces/aiqtech/Contributors-Leaderboard",
83
+ "https://huggingface.co/spaces/ginigen/text3d-r1",
84
+ "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
85
+ "https://huggingface.co/spaces/openfree/Korean-Leaderboard",
86
+ "https://huggingface.co/spaces/fantos/flxcontrol",
87
+ "https://huggingface.co/spaces/aiqtech/FLUX-Ghibli-Studio-LoRA",
88
+ "https://huggingface.co/spaces/openfree/AI-Podcast",
89
+ "https://huggingface.co/spaces/ginigen/Workflow-Canvas",
90
+ "https://huggingface.co/spaces/ginigen/3D-LLAMA",
91
+ "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
92
+ "https://huggingface.co/spaces/VIDraft/ACE-Singer",
93
+ "https://huggingface.co/spaces/ginipick/AI-BOOK",
94
+ "https://huggingface.co/spaces/immunobiotech/drug-discovery",
95
+ "https://huggingface.co/spaces/VIDraft/Robo-Beam",
96
+ "https://huggingface.co/spaces/fantaxy/fantasy-novel",
97
+ "https://huggingface.co/spaces/immunobiotech/Gemini-MICHELIN",
98
+ "https://huggingface.co/spaces/openfree/Chart-GPT",
99
+ "https://huggingface.co/spaces/ginipick/NH-Korea",
100
+ "https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast",
101
+ "https://huggingface.co/spaces/ginipick/Private-AI",
102
+ "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
103
+ "https://huggingface.co/spaces/openfree/open-GAMMA",
104
+ "https://huggingface.co/spaces/ginipick/PharmAI-Korea",
105
+ "https://huggingface.co/spaces/ginipick/Pharmacy",
106
+ "https://huggingface.co/spaces/ginipick/PDF-EXAM",
107
+ "https://huggingface.co/spaces/ginipick/IDEA-DESIGN",
108
+ "https://huggingface.co/spaces/openfree/DreamO-video",
109
+ "https://huggingface.co/spaces/ginipick/10m-marketing",
110
+ "https://huggingface.co/spaces/VIDraft/voice-trans",
111
+ "https://huggingface.co/spaces/VIDraft/NH-Prediction",
112
+ "https://huggingface.co/spaces/fantos/flx8lora",
113
+ "https://huggingface.co/spaces/ginigen/MagicFace-V3",
114
+ "https://huggingface.co/spaces/openfree/Live-Podcast",
115
+ "https://huggingface.co/spaces/seawolf2357/ocrlatex",
116
+ "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
117
+ "https://huggingface.co/spaces/ginigen/VEO3-Free",
118
+ "https://huggingface.co/spaces/openfree/MagicFace-V3",
119
+ "https://huggingface.co/spaces/aiqtech/FLUX-military",
120
+ "https://huggingface.co/spaces/fantaxy/flxloraexp",
121
+ "https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX",
122
+ "https://huggingface.co/spaces/ginigen/FLUXllama-Multilingual",
123
+ "https://huggingface.co/spaces/Heartsync/wan2-1-fast-security",
124
+ "https://huggingface.co/spaces/fantaxy/Rolls-Royce",
125
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
126
+ "https://huggingface.co/spaces/ginipick/Realtime-FLUX",
127
+ "https://huggingface.co/spaces/aiqtech/imaginpaint",
128
+ "https://huggingface.co/spaces/aiqtech/flxgif",
129
+ "https://huggingface.co/spaces/fantos/flxfashmodel",
130
+ "https://huggingface.co/spaces/aiqtech/kofaceid",
131
+ "https://huggingface.co/spaces/ginipick/FLUX-Prompt-Generator",
132
+ "https://huggingface.co/spaces/seawolf2357/REALVISXL-V5",
133
+ "https://huggingface.co/spaces/fantaxy/FLUX-Animations",
134
+ "https://huggingface.co/spaces/fantaxy/flx-pulid",
135
+ "https://huggingface.co/spaces/fantaxy/ofai-flx-logo",
136
+ "https://huggingface.co/spaces/openfree/image-to-vector",
137
+ "https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
138
+ "https://huggingface.co/spaces/seawolf2357/sd-prompt-gen",
139
+ "https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape",
140
+ "https://huggingface.co/spaces/ginipick/FLUXllama",
141
+ "https://huggingface.co/spaces/Heartsync/NSFW-image",
142
+ "https://huggingface.co/spaces/seawolf2357/img2vid",
143
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2",
144
+ "https://huggingface.co/spaces/Heartsync/NSFW-detection",
145
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video",
146
+ "https://huggingface.co/spaces/Heartsync/adult",
147
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
148
+ "https://huggingface.co/spaces/fantos/VoiceClone",
149
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
150
+ "https://huggingface.co/spaces/fantaxy/flx-upscale",
151
+ "https://huggingface.co/spaces/seawolf2357/flxloraexp",
152
+ "https://huggingface.co/spaces/ginipick/Time-Stream",
153
+ "https://huggingface.co/spaces/fantos/textcutobject",
154
 
155
+
156
+ ],
157
+ "NEW": [
158
+ "https://huggingface.co/spaces/ginigen/Flux-Kontext-Style",
159
+ "https://cutechicken-3d-airforce-simulator.static.hf.space",
160
+ "https://huggingface.co/spaces/ginipick/Private-AI",
161
+ "https://huggingface.co/spaces/VIDraft/ACE-Singer",
162
+ "https://huggingface.co/spaces/ginipick/AI-BOOK",
163
+ "https://huggingface.co/spaces/openfree/Best-AI",
164
+ "https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
165
+ "https://huggingface.co/spaces/VIDraft/DNA-CASINO",
166
+ "https://huggingface.co/spaces/openfree/AGI-Screenplay",
167
+ "https://huggingface.co/spaces/openfree/AGI-WebNovel",
168
+ "https://huggingface.co/spaces/openfree/AGI-NOVEL",
169
+ "https://huggingface.co/spaces/fantaxy/AGI-LEADERBOARD",
170
+ "https://huggingface.co/spaces/ginigen/Seedance-Free",
171
+ "https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
172
+ "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
173
+ "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
174
+ "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
175
+ "https://huggingface.co/spaces/openfree/Open-GAMMA",
176
+ "https://huggingface.co/spaces/ginigen/VEO3-Free",
177
+ "https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX",
178
+ "https://huggingface.co/spaces/VIDraft/voice-trans",
179
+ "https://huggingface.co/spaces/VIDraft/Robo-Beam",
180
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
181
+ "https://huggingface.co/spaces/fantaxy/fantasy-novel",
182
+ "https://huggingface.co/spaces/openfree/Chart-GPT",
183
+ "https://huggingface.co/spaces/Heartsync/Novel-NSFW",
184
+ "https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2",
185
+ "https://huggingface.co/spaces/Heartsync/WAN-VIDEO-AUDIO",
186
+ "https://huggingface.co/spaces/Heartsync/wan2-1-fast-security",
187
+ "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
188
+ "https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE",
189
+ "https://huggingface.co/spaces/VIDraft/Mistral-RAG-BitSix",
190
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2",
191
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video",
192
+ "https://huggingface.co/spaces/fantaxy/YTB-TEST",
193
+ "https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
194
+ "https://huggingface.co/spaces/Heartsync/adult",
195
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
196
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
197
+ "https://huggingface.co/spaces/openfree/Live-Podcast",
198
+ "https://huggingface.co/spaces/openfree/AI-Podcast",
199
+ "https://huggingface.co/spaces/ginipick/NH-Korea",
200
+ "https://huggingface.co/spaces/VIDraft/NH-Prediction",
201
+ "https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast",
202
+ "https://huggingface.co/spaces/ginipick/PDF-EXAM",
203
+ "https://huggingface.co/spaces/openfree/Game-Gallery",
204
+ "https://huggingface.co/spaces/openfree/Vibe-Game",
205
+ "https://huggingface.co/spaces/ginipick/IDEA-DESIGN",
206
+ "https://huggingface.co/spaces/openfree/Cycle-Navigator",
207
+ "https://huggingface.co/spaces/openfree/DreamO-video",
208
+ "https://huggingface.co/spaces/Heartsync/NSFW-detection",
209
+
210
+
211
+ ],
212
+ "Productivity": [
213
+ "https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
214
+ "https://huggingface.co/spaces/VIDraft/DNA-CASINO",
215
+ "https://huggingface.co/spaces/openfree/Open-GAMMA",
216
+ "https://huggingface.co/spaces/VIDraft/Robo-Beam",
217
+ "https://huggingface.co/spaces/VIDraft/voice-trans",
218
+ "https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
219
+ "https://huggingface.co/spaces/openfree/Chart-GPT",
220
+ "https://huggingface.co/spaces/ginipick/AI-BOOK",
221
+ "https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast",
222
+ "https://huggingface.co/spaces/ginipick/PDF-EXAM",
223
+ "https://huggingface.co/spaces/ginigen/perflexity-clone",
224
+ "https://huggingface.co/spaces/ginipick/IDEA-DESIGN",
225
+ "https://huggingface.co/spaces/ginipick/10m-marketing",
226
+ "https://huggingface.co/spaces/openfree/Live-Podcast",
227
+ "https://huggingface.co/spaces/openfree/AI-Podcast",
228
+ "https://huggingface.co/spaces/ginipick/QR-Canvas-plus",
229
+ "https://huggingface.co/spaces/openfree/Badge",
230
+ "https://huggingface.co/spaces/VIDraft/mouse-webgen",
231
+ "https://huggingface.co/spaces/openfree/Vibe-Game",
232
+ "https://huggingface.co/spaces/VIDraft/NH-Prediction",
233
+ "https://huggingface.co/spaces/ginipick/NH-Korea",
234
+ "https://huggingface.co/spaces/openfree/Naming",
235
+ "https://huggingface.co/spaces/ginipick/Change-Hair",
236
+ ],
237
+ "Multimodal": [
238
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
239
+ "https://huggingface.co/spaces/fantaxy/YTB-TEST",
240
+ "https://huggingface.co/spaces/ginigen/Seedance-Free",
241
+ "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
242
+ "https://huggingface.co/spaces/ginigen/VEO3-Free",
243
+ "https://huggingface.co/spaces/ginigen/VEO3-Directors",
244
+ "https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX",
245
+ "https://huggingface.co/spaces/Heartsync/adult",
246
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
247
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2",
248
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video",
249
+ "https://huggingface.co/spaces/Heartsync/WAN-VIDEO-AUDIO",
250
+ "https://huggingface.co/spaces/Heartsync/wan2-1-fast-security",
251
+ "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
252
+ "https://huggingface.co/spaces/ginigen/3D-LLAMA-V1",
253
+ "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
254
+ "https://huggingface.co/spaces/openfree/Multilingual-TTS",
255
+ "https://huggingface.co/spaces/VIDraft/ACE-Singer",
256
+ "https://huggingface.co/spaces/openfree/DreamO-video",
257
+ "https://huggingface.co/spaces/fantaxy/Sound-AI-SFX",
258
+ "https://huggingface.co/spaces/ginigen/SFX-Sound-magic",
259
+ "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
260
+ "https://huggingface.co/spaces/aiqcamp/ENGLISH-Speaking-Scoring",
261
+ "https://huggingface.co/spaces/fantaxy/Remove-Video-Background",
262
+ ],
263
+ "Professional": [
264
+ "https://huggingface.co/spaces/Heartsync/NSFW-novels",
265
+ "https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
266
+ "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
267
+ "https://huggingface.co/spaces/Heartsync/Novel-NSFW",
268
+ "https://huggingface.co/spaces/fantaxy/fantasy-novel",
269
+ "https://huggingface.co/spaces/VIDraft/money-radar",
270
+ "https://huggingface.co/spaces/immunobiotech/drug-discovery",
271
+ "https://huggingface.co/spaces/immunobiotech/Gemini-MICHELIN",
272
+ "https://huggingface.co/spaces/openfree/Cycle-Navigator",
273
+ "https://huggingface.co/spaces/VIDraft/Fashion-Fit",
274
+ "https://huggingface.co/spaces/openfree/Stock-Trading-Analysis",
275
+ "https://huggingface.co/spaces/ginipick/AgentX-Papers",
276
+ "https://huggingface.co/spaces/Heartsync/Papers-Leaderboard",
277
+ "https://huggingface.co/spaces/VIDraft/PapersImpact",
278
+ "https://huggingface.co/spaces/ginigen/multimodal-chat-mbti-korea",
279
+ ],
280
+ "Image": [
281
+ "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
282
+ "https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
283
+ "https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2",
284
+ "https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE",
285
+ "https://huggingface.co/spaces/VIDraft/BAGEL-Websearch",
286
+ "https://huggingface.co/spaces/ginigen/Every-Text",
287
+ "https://huggingface.co/spaces/ginigen/text3d-r1",
288
+ "https://huggingface.co/spaces/ginipick/FLUXllama",
289
+ "https://huggingface.co/spaces/ginigen/Workflow-Canvas",
290
+ "https://huggingface.co/spaces/ginigen/canvas-studio",
291
+ "https://huggingface.co/spaces/VIDraft/ReSize-Image-Outpainting",
292
+ "https://huggingface.co/spaces/Heartsync/FLUX-Vision",
293
+ "https://huggingface.co/spaces/fantos/textcutobject",
294
+ "https://huggingface.co/spaces/aiqtech/imaginpaint",
295
+ "https://huggingface.co/spaces/openfree/ColorRevive",
296
+ "https://huggingface.co/spaces/openfree/ultpixgen",
297
+ "https://huggingface.co/spaces/VIDraft/Polaroid-Style",
298
+ "https://huggingface.co/spaces/ginigen/VisualCloze",
299
+ "https://huggingface.co/spaces/fantaxy/ofai-flx-logo",
300
+ "https://huggingface.co/spaces/ginigen/interior-design",
301
+ "https://huggingface.co/spaces/ginigen/MagicFace-V3",
302
+ "https://huggingface.co/spaces/fantaxy/flx-pulid",
303
+ "https://huggingface.co/spaces/seawolf2357/Ghibli-Multilingual-Text-rendering",
304
+ "https://huggingface.co/spaces/VIDraft/Open-Meme-Studio",
305
+ "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
306
+ "https://huggingface.co/spaces/aiqtech/flxgif",
307
+ "https://huggingface.co/spaces/openfree/VectorFlow",
308
+ "https://huggingface.co/spaces/ginigen/3D-LLAMA",
309
+ "https://huggingface.co/spaces/ginigen/Multi-LoRAgen",
310
+ ],
311
+ "LLM / VLM": [
312
+ "https://huggingface.co/spaces/fantaxy/fantasy-novel",
313
+ "https://huggingface.co/spaces/ginigen/deepseek-r1-0528-API",
314
+ "https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API",
315
+ "https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
316
+ "https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528-qwen3-8b",
317
+ "https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
318
+ "https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API",
319
+ "https://huggingface.co/spaces/VIDraft/Mistral-RAG-BitSix",
320
+ "https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-4B",
321
+ "https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-12B",
322
+ "https://huggingface.co/spaces/ginigen/Mistral-Perflexity",
323
+ "https://huggingface.co/spaces/aiqcamp/gemini-2.5-flash-preview",
324
+ "https://huggingface.co/spaces/openfree/qwen3-30b-a3b-research",
325
+ "https://huggingface.co/spaces/openfree/qwen3-235b-a22b-research",
326
+ "https://huggingface.co/spaces/openfree/Llama-4-Maverick-17B-Research",
327
+ ],
328
+ }
329
+
330
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 3. URL HELPERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
331
+ def direct_url(hf_url):
332
+ m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url)
333
+ if not m:
334
+ return hf_url
335
+ owner, name = m.groups()
336
+ owner = owner.lower()
337
+ name = name.replace('.', '-').replace('_', '-').lower()
338
+ return f"https://{owner}-{name}.hf.space"
339
+
340
+ def screenshot_url(url):
341
+ return f"https://image.thum.io/get/fullpage/{url}"
342
+
343
+ def process_url_for_preview(url):
344
+ """Returns (preview_url, mode)"""
345
+ # Handle blocked domains first
346
+ if any(d for d in BLOCKED_DOMAINS if d in url):
347
+ return screenshot_url(url), "snapshot"
348
+
349
+ # Special case handling for problematic URLs
350
+ if "vibe-coding-tetris" in url or "World-of-Tank-GAME" in url or "Minesweeper-Game" in url:
351
+ return screenshot_url(url), "snapshot"
352
+
353
+ # General HF space handling
354
+ try:
355
+ if "huggingface.co/spaces" in url:
356
+ parts = url.rstrip("/").split("/")
357
+ if len(parts) >= 5:
358
+ owner = parts[-2]
359
+ name = parts[-1]
360
+ embed_url = f"https://huggingface.co/spaces/{owner}/{name}/embed"
361
+ return embed_url, "iframe"
362
+ except Exception:
363
+ return screenshot_url(url), "snapshot"
364
+
365
+ # Default handling
366
+ return url, "iframe"
367
+
368
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 4. API ROUTES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
369
+ @app.route('/api/category')
370
+ def api_category():
371
+ cat = request.args.get('name', '')
372
+ urls = CATEGORIES.get(cat, [])
373
+
374
+ # Add pagination for categories
375
+ page = int(request.args.get('page', 1))
376
+ per_page = int(request.args.get('per_page', 4))
377
+
378
+ total_pages = max(1, (len(urls) + per_page - 1) // per_page)
379
+ start = (page - 1) * per_page
380
+ end = min(start + per_page, len(urls))
381
+
382
+ urls_page = urls[start:end]
383
+
384
+ items = [
385
+ {
386
+ "title": url.split('/')[-1],
387
+ "owner": url.split('/')[-2] if '/spaces/' in url else '',
388
+ "iframe": direct_url(url),
389
+ "shot": screenshot_url(url),
390
+ "hf": url
391
+ } for url in urls_page
392
+ ]
393
+
394
+ return jsonify({
395
+ "items": items,
396
+ "page": page,
397
+ "total_pages": total_pages
398
+ })
399
+
400
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 5. MAIN ROUTES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
401
+ @app.route('/')
402
+ def home():
403
+ os.makedirs('templates', exist_ok=True)
404
+
405
+ with open('templates/index.html', 'w', encoding='utf-8') as fp:
406
+ fp.write(r'''<!DOCTYPE html>
407
+ <html>
408
+ <head>
409
+ <meta charset="utf-8">
410
+ <meta name="viewport" content="width=device-width, initial-scale=1">
411
+ <title>Web Gallery</title>
412
+ <style>
413
+ @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;600&display=swap');
414
+ body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;}
415
+ .tabs{display:flex;flex-wrap:wrap;gap:8px;padding:16px;}
416
+ .tab{padding:6px 14px;border:none;border-radius:18px;background:#e2e8f0;font-weight:600;cursor:pointer;}
417
+ .tab.active{background:#a78bfa;color:#1a202c;}
418
+ .tab.popular{background:#ff6b6b;color:white;}
419
+ .tab.popular.active{background:#fa5252;color:white;}
420
+ .tab.best{background:#4ecdc4;color:white;}
421
+ .tab.best.active{background:#38d9a9;color:white;}
422
+ .tab.new{background:#ffe066;color:#1a202c;}
423
+ .tab.new.active{background:#ffd43b;color:#1a202c;}
424
+ /* Updated grid to show 2x2 layout */
425
+ .grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:0 16px 60px;max-width:1200px;margin:0 auto;}
426
+ @media(max-width:800px){.grid{grid-template-columns:1fr;}}
427
+ /* Increased card height for larger display */
428
+ .card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.08);overflow:hidden;height:540px;display:flex;flex-direction:column;position:relative;}
429
+ .frame{flex:1;position:relative;overflow:hidden;}
430
+ .frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0;}
431
+ .frame img{width:100%;height:100%;object-fit:cover;}
432
+ .card-label{position:absolute;top:10px;left:10px;padding:4px 8px;border-radius:4px;font-size:11px;font-weight:bold;z-index:100;text-transform:uppercase;letter-spacing:0.5px;box-shadow:0 2px 4px rgba(0,0,0,0.2);}
433
+ .label-live{background:linear-gradient(135deg, #00c6ff, #0072ff);color:white;}
434
+ .label-static{background:linear-gradient(135deg, #ff9a9e, #fad0c4);color:#333;}
435
+ .foot{height:44px;background:#fafafa;display:flex;align-items:center;justify-content:center;border-top:1px solid #eee;}
436
+ .foot a{font-size:.82rem;font-weight:700;color:#4a6dd8;text-decoration:none;}
437
+ .pagination{display:flex;justify-content:center;margin:20px 0;gap:10px;}
438
+ .pagination button{padding:5px 15px;border:none;border-radius:20px;background:#e2e8f0;cursor:pointer;}
439
+ .pagination button:disabled{opacity:0.5;cursor:not-allowed;}
440
+ </style>
441
+ </head>
442
+ <body>
443
+ <header style="text-align: center; padding: 20px; background: linear-gradient(135deg, #f6f8fb, #e2e8f0); border-bottom: 1px solid #ddd;">
444
+ <h1 style="margin-bottom: 10px;">๐ŸŒŸOPEN & Free: BEST AI Playground</h1>
445
+ <p>
446
+ <a href="https://huggingface.co/OpenFreeAI" target="_blank"><img src="https://img.shields.io/static/v1?label=Community&message=OpenFree_AI&color=%23800080&labelColor=%23000080&logo=HUGGINGFACE&logoColor=%23ffa500&style=for-the-badge" alt="badge"></a>
447
+ <a href="https://discord.gg/openfreeai" target="_blank"><img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="badge"></a>
448
+ <a href="https://huggingface.co/spaces/openfree/Best-AI" target="_blank"><img src="https://img.shields.io/static/v1?label=OpenFree&message=BEST%20AI%20Services&color=%230000ff&labelColor=%23000080&logo=huggingface&logoColor=%23ffa500&style=for-the-badge" alt="badge"></a>
449
+ </p>
450
+ </header>
451
+ <div class="tabs" id="tabs"></div>
452
+ <div id="content"></div>
453
+
454
+ <script>
455
+ // Basic configuration
456
+ const cats = {{cats|tojson}};
457
+ const tabs = document.getElementById('tabs');
458
+ const content = document.getElementById('content');
459
+ let active = "";
460
+ let currentPage = 1;
461
+
462
+ // Simple utility functions
463
+ function makeRequest(url, method, data, callback) {
464
+ const xhr = new XMLHttpRequest();
465
+ xhr.open(method, url, true);
466
+ xhr.onreadystatechange = function() {
467
+ if (xhr.readyState === 4 && xhr.status === 200) {
468
+ callback(JSON.parse(xhr.responseText));
469
  }
470
+ };
471
+ if (method === 'POST') {
472
+ xhr.send(data);
473
+ } else {
474
+ xhr.send();
475
+ }
476
+ }
477
+
478
+ function updateTabs() {
479
+ Array.from(tabs.children).forEach(b => {
480
+ b.classList.toggle('active', b.dataset.c === active);
481
+ });
482
+ }
483
+
484
+ // Tab handlers
485
+ function loadCategory(cat, page) {
486
+ if(cat === active && currentPage === page) return;
487
+ active = cat;
488
+ currentPage = page || 1;
489
+ updateTabs();
490
+
491
+ content.innerHTML = '<p style="text-align:center;padding:40px">Loadingโ€ฆ</p>';
492
+
493
+ makeRequest('/api/category?name=' + encodeURIComponent(cat) + '&page=' + currentPage + '&per_page=4', 'GET', null, function(data) {
494
+ let html = '<div class="grid">';
495
+
496
+ if(data.items.length === 0) {
497
+ html += '<p style="grid-column:1/-1;text-align:center;padding:40px">No items in this category.</p>';
498
+ } else {
499
+ data.items.forEach(item => {
500
+ html += `
501
+ <div class="card">
502
+ <div class="card-label label-live">LIVE</div>
503
+ <div class="frame">
504
+ <iframe src="${item.iframe}" loading="lazy" sandbox="allow-forms allow-modals allow-popups allow-same-origin allow-scripts allow-downloads"></iframe>
505
+ </div>
506
+ <div class="foot">
507
+ <a href="${item.hf}" target="_blank">${item.title}</a>
508
+ </div>
509
+ </div>
510
+ `;
511
+ });
512
  }
513
+
514
+ html += '</div>';
515
+
516
+ // Add pagination
517
+ html += `
518
+ <div class="pagination">
519
+ <button ${currentPage <= 1 ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage-1})">ยซ Previous</button>
520
+ <span>Page ${currentPage} of ${data.total_pages}</span>
521
+ <button ${currentPage >= data.total_pages ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage+1})">Next ยป</button>
522
+ </div>
523
+ `;
524
+
525
+ content.innerHTML = html;
526
+ });
527
+ }
528
+
529
+ // Create tabs
530
+ // Special tabs first (Popular, BEST, NEW)
531
+ ['Popular', 'BEST', 'NEW'].forEach(specialCat => {
532
+ const b = document.createElement('button');
533
+ b.className = 'tab ' + specialCat.toLowerCase();
534
+ b.textContent = specialCat;
535
+ b.dataset.c = specialCat;
536
+ b.onclick = function() { loadCategory(specialCat, 1); };
537
+ tabs.appendChild(b);
538
+ });
539
+
540
+ // Regular category tabs
541
+ cats.forEach(c => {
542
+ if (!['Popular', 'BEST', 'NEW'].includes(c)) {
543
+ const b = document.createElement('button');
544
+ b.className = 'tab';
545
+ b.textContent = c;
546
+ b.dataset.c = c;
547
+ b.onclick = function() { loadCategory(c, 1); };
548
+ tabs.appendChild(b);
549
+ }
550
+ });
551
+
552
+ // Start with Popular tab
553
+ loadCategory('Popular', 1);
554
+ </script>
555
+ </body>
556
+ </html>''')
557
+
558
+ # Return the rendered template
559
+ return render_template('index.html', cats=list(CATEGORIES.keys()))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
 
561
+ if __name__ == '__main__':
562
+ app.run(host='0.0.0.0', port=7860)