Spaces:
Running
Running
from flask import Flask, render_template, request, jsonify | |
import os, re, json | |
app = Flask(__name__) | |
# ββββββββββββββββββββββββββ 1. CONFIGURATION ββββββββββββββββββββββββββ | |
# Domains that commonly block iframes | |
BLOCKED_DOMAINS = [ | |
"naver.com", "daum.net", "google.com", | |
"facebook.com", "instagram.com", "kakao.com", | |
"ycombinator.com" | |
] | |
# ββββββββββββββββββββββββββ 2. CURATED CATEGORIES ββββββββββββββββββββββββββ | |
CATEGORIES = { | |
"Popular": [ | |
"https://huggingface.co/spaces/fantaxy/ofai-flx-logo", | |
"https://huggingface.co/spaces/aiqtech/FLUX-Ghibli-Studio-LoRA", | |
"https://huggingface.co/spaces/seawolf2357/REALVISXL-V5", | |
"https://huggingface.co/spaces/fantos/flx8lora", | |
"https://huggingface.co/spaces/ginipick/Realtime-FLUX", | |
"https://huggingface.co/spaces/fantaxy/flx-pulid", | |
"https://huggingface.co/spaces/ginipick/FLUX-Prompt-Generator", | |
"https://huggingface.co/spaces/aiqtech/kofaceid", | |
"https://huggingface.co/spaces/aiqtech/flxgif", | |
"https://huggingface.co/spaces/fantos/flxfashmodel", | |
"https://huggingface.co/spaces/fantos/flxcontrol", | |
"https://huggingface.co/spaces/fantos/textcutobject", | |
"https://huggingface.co/spaces/seawolf2357/flxloraexp", | |
"https://huggingface.co/spaces/fantaxy/flxloraexp", | |
"https://huggingface.co/spaces/aiqtech/imaginpaint", | |
"https://huggingface.co/spaces/ginipick/FLUXllama", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored", | |
"https://huggingface.co/spaces/fantaxy/flx-upscale", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video", | |
"https://huggingface.co/spaces/fantos/VoiceClone", | |
"https://huggingface.co/spaces/fantaxy/Rolls-Royce", | |
"https://huggingface.co/spaces/aiqtech/FLUX-military", | |
"https://huggingface.co/spaces/fantaxy/FLUX-Animations", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo", | |
"https://huggingface.co/spaces/ginipick/Time-Stream", | |
"https://huggingface.co/spaces/seawolf2357/sd-prompt-gen", | |
"https://huggingface.co/spaces/openfree/MagicFace-V3", | |
"https://huggingface.co/spaces/Heartsync/adult", | |
"https://huggingface.co/spaces/Heartsync/wan2-1-fast-security", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL", | |
"https://huggingface.co/spaces/seawolf2357/img2vid", | |
"https://huggingface.co/spaces/openfree/image-to-vector", | |
"https://huggingface.co/spaces/openfree/DreamO-video", | |
"https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape", | |
"https://huggingface.co/spaces/fantaxy/Sound-AI-SFX", | |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS", | |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB", | |
"https://huggingface.co/spaces/Heartsync/NSFW-image", | |
"https://huggingface.co/spaces/Heartsync/NSFW-detection", | |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime", | |
"https://huggingface.co/spaces/ginigen/VEO3-Free", | |
"https://huggingface.co/spaces/ginigen/FLUX-Text-Tree-Image", | |
"https://huggingface.co/spaces/ginigen/text3d-r1", | |
"https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX", | |
], | |
"BEST": [ | |
"https://huggingface.co/spaces/openfree/Cycle-Navigator", | |
"https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA", | |
"https://huggingface.co/spaces/ginigen/Seedance-Free", | |
"https://huggingface.co/spaces/VIDraft/SOMA-AGI", | |
"https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard", | |
"https://huggingface.co/spaces/VIDraft/DNA-CASINO", | |
"https://huggingface.co/spaces/aiqtech/SOMA-Oriental", | |
"https://huggingface.co/spaces/fantaxy/YTB-TEST", | |
"https://huggingface.co/spaces/aiqtech/Contributors-Leaderboard", | |
"https://huggingface.co/spaces/ginigen/text3d-r1", | |
"https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX", | |
"https://huggingface.co/spaces/openfree/Korean-Leaderboard", | |
"https://huggingface.co/spaces/fantos/flxcontrol", | |
"https://huggingface.co/spaces/aiqtech/FLUX-Ghibli-Studio-LoRA", | |
"https://huggingface.co/spaces/openfree/AI-Podcast", | |
"https://huggingface.co/spaces/ginigen/Workflow-Canvas", | |
"https://huggingface.co/spaces/ginigen/3D-LLAMA", | |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS", | |
"https://huggingface.co/spaces/VIDraft/ACE-Singer", | |
"https://huggingface.co/spaces/ginipick/AI-BOOK", | |
"https://huggingface.co/spaces/immunobiotech/drug-discovery", | |
"https://huggingface.co/spaces/VIDraft/Robo-Beam", | |
"https://huggingface.co/spaces/fantaxy/fantasy-novel", | |
"https://huggingface.co/spaces/immunobiotech/Gemini-MICHELIN", | |
"https://huggingface.co/spaces/openfree/Chart-GPT", | |
"https://huggingface.co/spaces/ginipick/NH-Korea", | |
"https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast", | |
"https://huggingface.co/spaces/ginipick/Private-AI", | |
"https://huggingface.co/spaces/ginigen/Flux-VIDEO", | |
"https://huggingface.co/spaces/openfree/open-GAMMA", | |
"https://huggingface.co/spaces/ginipick/PharmAI-Korea", | |
"https://huggingface.co/spaces/ginipick/Pharmacy", | |
"https://huggingface.co/spaces/ginipick/PDF-EXAM", | |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN", | |
"https://huggingface.co/spaces/openfree/DreamO-video", | |
"https://huggingface.co/spaces/ginipick/10m-marketing", | |
"https://huggingface.co/spaces/VIDraft/voice-trans", | |
"https://huggingface.co/spaces/VIDraft/NH-Prediction", | |
"https://huggingface.co/spaces/fantos/flx8lora", | |
"https://huggingface.co/spaces/ginigen/MagicFace-V3", | |
"https://huggingface.co/spaces/openfree/Live-Podcast", | |
"https://huggingface.co/spaces/seawolf2357/ocrlatex", | |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime", | |
"https://huggingface.co/spaces/ginigen/VEO3-Free", | |
"https://huggingface.co/spaces/openfree/MagicFace-V3", | |
"https://huggingface.co/spaces/aiqtech/FLUX-military", | |
"https://huggingface.co/spaces/fantaxy/flxloraexp", | |
"https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX", | |
"https://huggingface.co/spaces/ginigen/FLUXllama-Multilingual", | |
"https://huggingface.co/spaces/Heartsync/wan2-1-fast-security", | |
"https://huggingface.co/spaces/fantaxy/Rolls-Royce", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL", | |
"https://huggingface.co/spaces/ginipick/Realtime-FLUX", | |
"https://huggingface.co/spaces/aiqtech/imaginpaint", | |
"https://huggingface.co/spaces/aiqtech/flxgif", | |
"https://huggingface.co/spaces/fantos/flxfashmodel", | |
"https://huggingface.co/spaces/aiqtech/kofaceid", | |
"https://huggingface.co/spaces/ginipick/FLUX-Prompt-Generator", | |
"https://huggingface.co/spaces/seawolf2357/REALVISXL-V5", | |
"https://huggingface.co/spaces/fantaxy/FLUX-Animations", | |
"https://huggingface.co/spaces/fantaxy/flx-pulid", | |
"https://huggingface.co/spaces/fantaxy/ofai-flx-logo", | |
"https://huggingface.co/spaces/openfree/image-to-vector", | |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB", | |
"https://huggingface.co/spaces/seawolf2357/sd-prompt-gen", | |
"https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape", | |
"https://huggingface.co/spaces/ginipick/FLUXllama", | |
"https://huggingface.co/spaces/Heartsync/NSFW-image", | |
"https://huggingface.co/spaces/seawolf2357/img2vid", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2", | |
"https://huggingface.co/spaces/Heartsync/NSFW-detection", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video", | |
"https://huggingface.co/spaces/Heartsync/adult", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored", | |
"https://huggingface.co/spaces/fantos/VoiceClone", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo", | |
"https://huggingface.co/spaces/fantaxy/flx-upscale", | |
"https://huggingface.co/spaces/seawolf2357/flxloraexp", | |
"https://huggingface.co/spaces/ginipick/Time-Stream", | |
"https://huggingface.co/spaces/fantos/textcutobject", | |
], | |
"NEW": [ | |
"https://huggingface.co/spaces/VIDraft/ACE-Singer", | |
"https://huggingface.co/spaces/ginipick/AI-BOOK", | |
"https://huggingface.co/spaces/openfree/Best-AI", | |
"https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard", | |
"https://huggingface.co/spaces/VIDraft/DNA-CASINO", | |
"https://huggingface.co/spaces/ginigen/Seedance-Free", | |
"https://huggingface.co/spaces/aiqtech/SOMA-Oriental", | |
"https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA", | |
"https://huggingface.co/spaces/VIDraft/SOMA-AGI", | |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime", | |
"https://huggingface.co/spaces/openfree/Open-GAMMA", | |
"https://huggingface.co/spaces/ginigen/VEO3-Free", | |
"https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX", | |
"https://huggingface.co/spaces/VIDraft/voice-trans", | |
"https://huggingface.co/spaces/VIDraft/Robo-Beam", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL", | |
"https://huggingface.co/spaces/fantaxy/fantasy-novel", | |
"https://huggingface.co/spaces/openfree/Chart-GPT", | |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW", | |
"https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2", | |
"https://huggingface.co/spaces/Heartsync/WAN-VIDEO-AUDIO", | |
"https://huggingface.co/spaces/Heartsync/wan2-1-fast-security", | |
"https://huggingface.co/spaces/ginigen/Flux-VIDEO", | |
"https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE", | |
"https://huggingface.co/spaces/VIDraft/Mistral-RAG-BitSix", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video", | |
"https://huggingface.co/spaces/fantaxy/YTB-TEST", | |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB", | |
"https://huggingface.co/spaces/Heartsync/adult", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored", | |
"https://huggingface.co/spaces/openfree/Live-Podcast", | |
"https://huggingface.co/spaces/openfree/AI-Podcast", | |
"https://huggingface.co/spaces/ginipick/NH-Korea", | |
"https://huggingface.co/spaces/VIDraft/NH-Prediction", | |
"https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast", | |
"https://huggingface.co/spaces/ginipick/PDF-EXAM", | |
"https://huggingface.co/spaces/openfree/Game-Gallery", | |
"https://huggingface.co/spaces/openfree/Vibe-Game", | |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN", | |
"https://huggingface.co/spaces/openfree/Cycle-Navigator", | |
"https://huggingface.co/spaces/openfree/DreamO-video", | |
"https://huggingface.co/spaces/Heartsync/NSFW-detection", | |
], | |
"Productivity": [ | |
"https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard", | |
"https://huggingface.co/spaces/VIDraft/DNA-CASINO", | |
"https://huggingface.co/spaces/openfree/Open-GAMMA", | |
"https://huggingface.co/spaces/VIDraft/Robo-Beam", | |
"https://huggingface.co/spaces/VIDraft/voice-trans", | |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB", | |
"https://huggingface.co/spaces/openfree/Chart-GPT", | |
"https://huggingface.co/spaces/ginipick/AI-BOOK", | |
"https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast", | |
"https://huggingface.co/spaces/ginipick/PDF-EXAM", | |
"https://huggingface.co/spaces/ginigen/perflexity-clone", | |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN", | |
"https://huggingface.co/spaces/ginipick/10m-marketing", | |
"https://huggingface.co/spaces/openfree/Live-Podcast", | |
"https://huggingface.co/spaces/openfree/AI-Podcast", | |
"https://huggingface.co/spaces/ginipick/QR-Canvas-plus", | |
"https://huggingface.co/spaces/openfree/Badge", | |
"https://huggingface.co/spaces/VIDraft/mouse-webgen", | |
"https://huggingface.co/spaces/openfree/Vibe-Game", | |
"https://huggingface.co/spaces/VIDraft/NH-Prediction", | |
"https://huggingface.co/spaces/ginipick/NH-Korea", | |
"https://huggingface.co/spaces/openfree/Naming", | |
"https://huggingface.co/spaces/ginipick/Change-Hair", | |
], | |
"Multimodal": [ | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo", | |
"https://huggingface.co/spaces/fantaxy/YTB-TEST", | |
"https://huggingface.co/spaces/ginigen/Seedance-Free", | |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime", | |
"https://huggingface.co/spaces/ginigen/VEO3-Free", | |
"https://huggingface.co/spaces/ginigen/VEO3-Directors", | |
"https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX", | |
"https://huggingface.co/spaces/Heartsync/adult", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video2", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-video", | |
"https://huggingface.co/spaces/Heartsync/WAN-VIDEO-AUDIO", | |
"https://huggingface.co/spaces/Heartsync/wan2-1-fast-security", | |
"https://huggingface.co/spaces/ginigen/Flux-VIDEO", | |
"https://huggingface.co/spaces/ginigen/3D-LLAMA-V1", | |
"https://huggingface.co/spaces/ginigen/Flux-VIDEO", | |
"https://huggingface.co/spaces/openfree/Multilingual-TTS", | |
"https://huggingface.co/spaces/VIDraft/ACE-Singer", | |
"https://huggingface.co/spaces/openfree/DreamO-video", | |
"https://huggingface.co/spaces/fantaxy/Sound-AI-SFX", | |
"https://huggingface.co/spaces/ginigen/SFX-Sound-magic", | |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS", | |
"https://huggingface.co/spaces/aiqcamp/ENGLISH-Speaking-Scoring", | |
"https://huggingface.co/spaces/fantaxy/Remove-Video-Background", | |
], | |
"Professional": [ | |
"https://huggingface.co/spaces/Heartsync/NSFW-novels", | |
"https://huggingface.co/spaces/aiqtech/SOMA-Oriental", | |
"https://huggingface.co/spaces/VIDraft/SOMA-AGI", | |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW", | |
"https://huggingface.co/spaces/fantaxy/fantasy-novel", | |
"https://huggingface.co/spaces/VIDraft/money-radar", | |
"https://huggingface.co/spaces/immunobiotech/drug-discovery", | |
"https://huggingface.co/spaces/immunobiotech/Gemini-MICHELIN", | |
"https://huggingface.co/spaces/openfree/Cycle-Navigator", | |
"https://huggingface.co/spaces/VIDraft/Fashion-Fit", | |
"https://huggingface.co/spaces/openfree/Stock-Trading-Analysis", | |
"https://huggingface.co/spaces/ginipick/AgentX-Papers", | |
"https://huggingface.co/spaces/Heartsync/Papers-Leaderboard", | |
"https://huggingface.co/spaces/VIDraft/PapersImpact", | |
"https://huggingface.co/spaces/ginigen/multimodal-chat-mbti-korea", | |
], | |
"Image": [ | |
"https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL", | |
"https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2", | |
"https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE", | |
"https://huggingface.co/spaces/VIDraft/BAGEL-Websearch", | |
"https://huggingface.co/spaces/ginigen/Every-Text", | |
"https://huggingface.co/spaces/ginigen/text3d-r1", | |
"https://huggingface.co/spaces/ginipick/FLUXllama", | |
"https://huggingface.co/spaces/ginigen/Workflow-Canvas", | |
"https://huggingface.co/spaces/ginigen/canvas-studio", | |
"https://huggingface.co/spaces/VIDraft/ReSize-Image-Outpainting", | |
"https://huggingface.co/spaces/Heartsync/FLUX-Vision", | |
"https://huggingface.co/spaces/fantos/textcutobject", | |
"https://huggingface.co/spaces/aiqtech/imaginpaint", | |
"https://huggingface.co/spaces/openfree/ColorRevive", | |
"https://huggingface.co/spaces/openfree/ultpixgen", | |
"https://huggingface.co/spaces/VIDraft/Polaroid-Style", | |
"https://huggingface.co/spaces/ginigen/VisualCloze", | |
"https://huggingface.co/spaces/fantaxy/ofai-flx-logo", | |
"https://huggingface.co/spaces/ginigen/interior-design", | |
"https://huggingface.co/spaces/ginigen/MagicFace-V3", | |
"https://huggingface.co/spaces/fantaxy/flx-pulid", | |
"https://huggingface.co/spaces/seawolf2357/Ghibli-Multilingual-Text-rendering", | |
"https://huggingface.co/spaces/VIDraft/Open-Meme-Studio", | |
"https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX", | |
"https://huggingface.co/spaces/aiqtech/flxgif", | |
"https://huggingface.co/spaces/openfree/VectorFlow", | |
"https://huggingface.co/spaces/ginigen/3D-LLAMA", | |
"https://huggingface.co/spaces/ginigen/Multi-LoRAgen", | |
], | |
"LLM / VLM": [ | |
"https://huggingface.co/spaces/fantaxy/fantasy-novel", | |
"https://huggingface.co/spaces/ginigen/deepseek-r1-0528-API", | |
"https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API", | |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528", | |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528-qwen3-8b", | |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528", | |
"https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API", | |
"https://huggingface.co/spaces/VIDraft/Mistral-RAG-BitSix", | |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-4B", | |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-12B", | |
"https://huggingface.co/spaces/ginigen/Mistral-Perflexity", | |
"https://huggingface.co/spaces/aiqcamp/gemini-2.5-flash-preview", | |
"https://huggingface.co/spaces/openfree/qwen3-30b-a3b-research", | |
"https://huggingface.co/spaces/openfree/qwen3-235b-a22b-research", | |
"https://huggingface.co/spaces/openfree/Llama-4-Maverick-17B-Research", | |
], | |
} | |
# ββββββββββββββββββββββββββ 3. URL HELPERS ββββββββββββββββββββββββββ | |
def direct_url(hf_url): | |
m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url) | |
if not m: | |
return hf_url | |
owner, name = m.groups() | |
owner = owner.lower() | |
name = name.replace('.', '-').replace('_', '-').lower() | |
return f"https://{owner}-{name}.hf.space" | |
def screenshot_url(url): | |
return f"https://image.thum.io/get/fullpage/{url}" | |
def process_url_for_preview(url): | |
"""Returns (preview_url, mode)""" | |
# Handle blocked domains first | |
if any(d for d in BLOCKED_DOMAINS if d in url): | |
return screenshot_url(url), "snapshot" | |
# Special case handling for problematic URLs | |
if "vibe-coding-tetris" in url or "World-of-Tank-GAME" in url or "Minesweeper-Game" in url: | |
return screenshot_url(url), "snapshot" | |
# General HF space handling | |
try: | |
if "huggingface.co/spaces" in url: | |
parts = url.rstrip("/").split("/") | |
if len(parts) >= 5: | |
owner = parts[-2] | |
name = parts[-1] | |
embed_url = f"https://huggingface.co/spaces/{owner}/{name}/embed" | |
return embed_url, "iframe" | |
except Exception: | |
return screenshot_url(url), "snapshot" | |
# Default handling | |
return url, "iframe" | |
# ββββββββββββββββββββββββββ 4. API ROUTES ββββββββββββββββββββββββββ | |
def api_category(): | |
cat = request.args.get('name', '') | |
urls = CATEGORIES.get(cat, []) | |
# Add pagination for categories | |
page = int(request.args.get('page', 1)) | |
per_page = int(request.args.get('per_page', 4)) | |
total_pages = max(1, (len(urls) + per_page - 1) // per_page) | |
start = (page - 1) * per_page | |
end = min(start + per_page, len(urls)) | |
urls_page = urls[start:end] | |
items = [ | |
{ | |
"title": url.split('/')[-1], | |
"owner": url.split('/')[-2] if '/spaces/' in url else '', | |
"iframe": direct_url(url), | |
"shot": screenshot_url(url), | |
"hf": url | |
} for url in urls_page | |
] | |
return jsonify({ | |
"items": items, | |
"page": page, | |
"total_pages": total_pages | |
}) | |
# ββββββββββββββββββββββββββ 5. MAIN ROUTES ββββββββββββββββββββββββββ | |
def home(): | |
os.makedirs('templates', exist_ok=True) | |
with open('templates/index.html', 'w', encoding='utf-8') as fp: | |
fp.write(r'''<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Web Gallery</title> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;600&display=swap'); | |
body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;} | |
.tabs{display:flex;flex-wrap:wrap;gap:8px;padding:16px;} | |
.tab{padding:6px 14px;border:none;border-radius:18px;background:#e2e8f0;font-weight:600;cursor:pointer;} | |
.tab.active{background:#a78bfa;color:#1a202c;} | |
.tab.popular{background:#ff6b6b;color:white;} | |
.tab.popular.active{background:#fa5252;color:white;} | |
.tab.best{background:#4ecdc4;color:white;} | |
.tab.best.active{background:#38d9a9;color:white;} | |
.tab.new{background:#ffe066;color:#1a202c;} | |
.tab.new.active{background:#ffd43b;color:#1a202c;} | |
/* Updated grid to show 2x2 layout */ | |
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:0 16px 60px;max-width:1200px;margin:0 auto;} | |
@media(max-width:800px){.grid{grid-template-columns:1fr;}} | |
/* Increased card height for larger display */ | |
.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;} | |
.frame{flex:1;position:relative;overflow:hidden;} | |
.frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0;} | |
.frame img{width:100%;height:100%;object-fit:cover;} | |
.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);} | |
.label-live{background:linear-gradient(135deg, #00c6ff, #0072ff);color:white;} | |
.label-static{background:linear-gradient(135deg, #ff9a9e, #fad0c4);color:#333;} | |
.foot{height:44px;background:#fafafa;display:flex;align-items:center;justify-content:center;border-top:1px solid #eee;} | |
.foot a{font-size:.82rem;font-weight:700;color:#4a6dd8;text-decoration:none;} | |
.pagination{display:flex;justify-content:center;margin:20px 0;gap:10px;} | |
.pagination button{padding:5px 15px;border:none;border-radius:20px;background:#e2e8f0;cursor:pointer;} | |
.pagination button:disabled{opacity:0.5;cursor:not-allowed;} | |
</style> | |
</head> | |
<body> | |
<header style="text-align: center; padding: 20px; background: linear-gradient(135deg, #f6f8fb, #e2e8f0); border-bottom: 1px solid #ddd;"> | |
<h1 style="margin-bottom: 10px;">πOPEN & Free: BEST AI Playground</h1> | |
<p> | |
<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> | |
<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> | |
<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> | |
</p> | |
</header> | |
<div class="tabs" id="tabs"></div> | |
<div id="content"></div> | |
<script> | |
// Basic configuration | |
const cats = {{cats|tojson}}; | |
const tabs = document.getElementById('tabs'); | |
const content = document.getElementById('content'); | |
let active = ""; | |
let currentPage = 1; | |
// Simple utility functions | |
function makeRequest(url, method, data, callback) { | |
const xhr = new XMLHttpRequest(); | |
xhr.open(method, url, true); | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState === 4 && xhr.status === 200) { | |
callback(JSON.parse(xhr.responseText)); | |
} | |
}; | |
if (method === 'POST') { | |
xhr.send(data); | |
} else { | |
xhr.send(); | |
} | |
} | |
function updateTabs() { | |
Array.from(tabs.children).forEach(b => { | |
b.classList.toggle('active', b.dataset.c === active); | |
}); | |
} | |
// Tab handlers | |
function loadCategory(cat, page) { | |
if(cat === active && currentPage === page) return; | |
active = cat; | |
currentPage = page || 1; | |
updateTabs(); | |
content.innerHTML = '<p style="text-align:center;padding:40px">Loadingβ¦</p>'; | |
makeRequest('/api/category?name=' + encodeURIComponent(cat) + '&page=' + currentPage + '&per_page=4', 'GET', null, function(data) { | |
let html = '<div class="grid">'; | |
if(data.items.length === 0) { | |
html += '<p style="grid-column:1/-1;text-align:center;padding:40px">No items in this category.</p>'; | |
} else { | |
data.items.forEach(item => { | |
html += ` | |
<div class="card"> | |
<div class="card-label label-live">LIVE</div> | |
<div class="frame"> | |
<iframe src="${item.iframe}" loading="lazy" sandbox="allow-forms allow-modals allow-popups allow-same-origin allow-scripts allow-downloads"></iframe> | |
</div> | |
<div class="foot"> | |
<a href="${item.hf}" target="_blank">${item.title}</a> | |
</div> | |
</div> | |
`; | |
}); | |
} | |
html += '</div>'; | |
// Add pagination | |
html += ` | |
<div class="pagination"> | |
<button ${currentPage <= 1 ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage-1})">Β« Previous</button> | |
<span>Page ${currentPage} of ${data.total_pages}</span> | |
<button ${currentPage >= data.total_pages ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage+1})">Next Β»</button> | |
</div> | |
`; | |
content.innerHTML = html; | |
}); | |
} | |
// Create tabs | |
// Special tabs first (Popular, BEST, NEW) | |
['Popular', 'BEST', 'NEW'].forEach(specialCat => { | |
const b = document.createElement('button'); | |
b.className = 'tab ' + specialCat.toLowerCase(); | |
b.textContent = specialCat; | |
b.dataset.c = specialCat; | |
b.onclick = function() { loadCategory(specialCat, 1); }; | |
tabs.appendChild(b); | |
}); | |
// Regular category tabs | |
cats.forEach(c => { | |
if (!['Popular', 'BEST', 'NEW'].includes(c)) { | |
const b = document.createElement('button'); | |
b.className = 'tab'; | |
b.textContent = c; | |
b.dataset.c = c; | |
b.onclick = function() { loadCategory(c, 1); }; | |
tabs.appendChild(b); | |
} | |
}); | |
// Start with Popular tab | |
loadCategory('Popular', 1); | |
</script> | |
</body> | |
</html>''') | |
# Return the rendered template | |
return render_template('index.html', cats=list(CATEGORIES.keys())) | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=7860) |