import os import math import time import random import tempfile import gradio as gr import pandas as pd from datasets import load_dataset from huggingface_hub import snapshot_download # ====================================================== # QUOTA CONFIG # ====================================================== LIMIT = 3 WINDOW = 86400 usage = {} def quota_guard(fn): def wrapper(*args, request: gr.Request = None, **kwargs): session = request.session_hash if request else "global" now = time.time() count, start = usage.get(session, (0, now)) if now - start > WINDOW: count = 0 start = now if count >= LIMIT: raise gr.Error( "🚫 Demo limit reached.\n\n" "You can unlock only 3 times per day.\n" "Please try again after 24 hours." ) usage[session] = (count + 1, start) return fn(*args, request=request, **kwargs) return wrapper # ====================================================== # CONFIG # ====================================================== APP_PASSWORD = os.getenv("APP_PASSWORD") if not APP_PASSWORD: raise RuntimeError("APP_PASSWORD environment variable not set") # PRO CONTENT PRO_DATASET = "rahul7star/Wan-video" # FREE PREVIEW (MODEL REPO) FREE_VIDEO_REPO = "rahul7star/ohamlab" FREE_VIDEO_FOLDER = "showcase/video" MAX_FREE_VIDEOS = 5 # ====================================================== # LOAD FREE VIDEOS (MODEL REPO) # ====================================================== def load_free_videos(): local_dir = snapshot_download( repo_id=FREE_VIDEO_REPO, repo_type="model", allow_patterns=["showcase/video/*"], local_dir=tempfile.mkdtemp(), local_dir_use_symlinks=False, ) video_dir = os.path.join(local_dir, FREE_VIDEO_FOLDER) if not os.path.isdir(video_dir): print(f"[WARN] Free video folder not found: {video_dir}") return [] videos = [ os.path.join(video_dir, f) for f in os.listdir(video_dir) if f.lower().endswith((".mp4", ".webm", ".mov")) ] print(f"[INFO] Loaded free videos: {videos}") return videos FREE_VIDEOS = load_free_videos() def pick_free_videos(max_count=MAX_FREE_VIDEOS): if not FREE_VIDEOS: return [] if len(FREE_VIDEOS) >= max_count: return random.sample(FREE_VIDEOS, max_count) # Repeat if fewer than max_count result = [] while len(result) < max_count: result.extend(FREE_VIDEOS) return result[:max_count] # ====================================================== # LOAD PRO DATASET # ====================================================== dataset = load_dataset(PRO_DATASET, split="test") df = dataset.to_pandas()[["video", "text", "date"]].dropna().reset_index(drop=True) # ====================================================== # PAGINATION # ====================================================== def render_grid(page, page_size): page = int(page) page_size = int(page_size) total_pages = max(1, math.ceil(len(df) / page_size)) page = max(0, min(page, total_pages - 1)) batch = df.iloc[page * page_size:(page + 1) * page_size] cards = [] for _, row in batch.iterrows(): cards.append(f"""
{row['date']}
{row['text']}
""") return f"
{''.join(cards)}
", page def next_page(page, page_size): return render_grid(page + 1, page_size) def prev_page(page, page_size): return render_grid(page - 1, page_size) def reset_page(page_size): return render_grid(0, page_size) # ====================================================== # AUTH # ====================================================== @quota_guard def check_password(user_password, request: gr.Request): if user_password == APP_PASSWORD: html, page = render_grid(0, 5) return ( gr.update(visible=False), # login gr.update(visible=False), # free preview gr.update(visible=True), # pro content html, page, "✅ Access granted" ) return ( gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), "", 0, "❌ Incorrect password" ) # ====================================================== # CSS # ====================================================== css = """ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 18px; } .card { background: #0b1220; border-radius: 14px; padding: 12px; } .card video { width: 100%; border-radius: 12px; } footer, .gradio-footer { display: none !important; } #ohamlab-footer { position: fixed; bottom: 0; left: 0; width: 100%; text-align: center; padding: 8px; font-size: 12px; background: #f8f9fb; color: #555; border-top: 1px solid #e5e7eb; } """ # ====================================================== # UI # ====================================================== with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo: gr.Markdown("# 🔐 OhamLab Video Showcase- For PRO Users 18+ only unlock") # -------- LOGIN -------- with gr.Column(visible=True) as login_box: password_input = gr.Textbox(type="password", label="Password") login_btn = gr.Button("Unlock") login_status = gr.Markdown() # -------- FREE PREVIEW -------- with gr.Column(visible=True) as free_preview: gr.Markdown("### 🎬 Free Preview") free_videos = pick_free_videos() with gr.Row(): for v in free_videos: gr.Video(v) # -------- PRO CONTENT -------- with gr.Column(visible=False) as app_content: with gr.Row(): page_size = gr.Dropdown([1, 5, 10], value=5) page_state = gr.State(0) gallery = gr.HTML() with gr.Row(): prev_btn = gr.Button("⬅ Previous") next_btn = gr.Button("Next ➡") prev_btn.click(prev_page, [page_state, page_size], [gallery, page_state]) next_btn.click(next_page, [page_state, page_size], [gallery, page_state]) page_size.change(reset_page, [page_size], [gallery, page_state]) # -------- EVENTS -------- login_btn.click( check_password, inputs=password_input, outputs=[ login_box, free_preview, app_content, gallery, page_state, login_status, ], ) # -------- FOOTER -------- gr.HTML("") demo.launch()