File size: 7,032 Bytes
ce9d458
e4f3113
2c9f7a9
feaf0e8
be45aa3
6bd2d6f
d08ffde
86a8e06
33779ec
be45aa3
d08ffde
2c9f7a9
6eb9546
2c9f7a9
6eb9546
f4c5627
be45aa3
e34f78d
88bbc50
 
e34f78d
 
88bbc50
 
 
 
 
 
 
 
2c9f7a9
e34f78d
6eb9546
 
2c9f7a9
88bbc50
 
6eb9546
88bbc50
 
 
 
e4f3113
86a8e06
e4f3113
ce9d458
 
 
 
6bd2d6f
be45aa3
6bd2d6f
 
be45aa3
0300e23
6bd2d6f
be45aa3
e34f78d
feaf0e8
0300e23
feaf0e8
be45aa3
 
 
0300e23
 
be45aa3
6bd2d6f
be45aa3
feaf0e8
be45aa3
6bd2d6f
 
 
 
 
be45aa3
 
 
 
 
feaf0e8
f4c5627
be45aa3
feaf0e8
 
be45aa3
 
 
6bd2d6f
be45aa3
 
 
 
 
 
0300e23
be45aa3
 
 
6bd2d6f
be45aa3
feaf0e8
 
e4f3113
be45aa3
e4f3113
be45aa3
f4c5627
86a8e06
e34f78d
e4f3113
f4c5627
e4f3113
86a8e06
 
 
 
f4c5627
86a8e06
 
f4c5627
86a8e06
 
 
 
 
0300e23
86a8e06
f4c5627
 
86a8e06
 
 
 
f4c5627
86a8e06
 
 
 
 
 
 
 
 
 
 
 
 
e34f78d
ce9d458
f4c5627
ce9d458
88bbc50
e34f78d
ce9d458
 
 
0300e23
 
 
ce9d458
 
 
 
be45aa3
 
 
 
 
 
 
 
 
ce9d458
e34f78d
0300e23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6957f64
 
 
0300e23
7a6085d
ce9d458
0300e23
ce9d458
6eb9546
6957f64
 
876a2ff
0300e23
feaf0e8
be45aa3
6bd2d6f
 
 
be45aa3
 
e6779ec
 
0300e23
 
f4c5627
0300e23
 
 
 
ce9d458
86a8e06
0300e23
 
 
 
 
 
 
 
 
ce9d458
 
 
feaf0e8
 
 
 
 
 
 
 
56ede89
d08ffde
0300e23
 
 
 
d08ffde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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"""
        <div class="card">
            <video src="{row['video']}" controls preload="metadata"></video>
            <div class="meta">
                <div class="date">{row['date']}</div>
                <div class="caption">{row['text']}</div>
            </div>
        </div>
        """)

    return f"<div class='grid'>{''.join(cards)}</div>", 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("<div id='ohamlab-footer'>© OhamLab Copyright</div>")


demo.launch()