ginipick commited on
Commit
5e81adf
Β·
verified Β·
1 Parent(s): 6c88267

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -273
app.py CHANGED
@@ -4,276 +4,14 @@ from pathlib import Path
4
  import math
5
  import urllib.parse
6
 
7
- # ──────────────────────────────────────────────────────────────────────────────
8
- # 인증 μ„€μ •
9
- VALID_USERNAME = "gini"
10
- VALID_PASSWORD = "pick"
11
-
12
-
13
- def check_auth(username, password):
14
- """Check if username and password are valid"""
15
- return username == VALID_USERNAME and password == VALID_PASSWORD
16
-
17
-
18
- # ──────────────────────────────────────────────────────────────────────────────
19
- # λΉ„λ””μ˜€ μœ ν‹Έλ¦¬ν‹°
20
- def get_videos(page: int = 1, per_page: int = 12):
21
- """Return a paginated list of .mp4 files under samples/"""
22
- sample_dir = Path("samples")
23
- sample_dir.mkdir(exist_ok=True, parents=True)
24
-
25
- video_files = sorted([str(f) for f in sample_dir.glob("*.mp4")])
26
-
27
- total_videos = len(video_files)
28
- total_pages = max(1, math.ceil(total_videos / per_page))
29
- page = max(1, min(page, total_pages))
30
-
31
- start = (page - 1) * per_page
32
- end = min(start + per_page, total_videos)
33
-
34
- return video_files[start:end], page, total_pages, total_videos
35
-
36
-
37
- # ──────────────────────────────────────────────────────────────────────────────
38
- # 가러리 HTML 생성
39
- def create_gallery(auth_status: bool, page: int = 1):
40
- """Return an HTML snippet for the gallery (or login prompt)"""
41
- if not auth_status:
42
- return """
43
- <div class="login-required">
44
- <div class="login-message">
45
- <i class="fas fa-lock"></i>
46
- <h2>Please log in to view the video gallery</h2>
47
- <p>Enter your credentials to access the video collection</p>
48
- </div>
49
- </div>
50
-
51
- <style>
52
- .login-required {
53
- height: 500px;
54
- display: flex;
55
- align-items: center;
56
- justify-content: center;
57
- background-color: #f8f9fa;
58
- }
59
- .login-message {
60
- text-align: center;
61
- background-color: #fff;
62
- padding: 40px;
63
- border-radius: 12px;
64
- box-shadow: 0 8px 16px rgba(0,0,0,.1);
65
- max-width: 600px;
66
- }
67
- .login-message i{font-size:4rem;color:#4a6bff;margin-bottom:20px;}
68
- .login-message h2{margin:10px 0;color:#333;}
69
- .login-message p{color:#777;margin:0;}
70
- </style>
71
- """
72
-
73
- videos, current_page, total_pages, total_videos = get_videos(page)
74
-
75
- html = """
76
- <link rel="stylesheet"
77
- href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
78
- <div id="video-gallery">
79
- <div class="gallery-header">
80
- <h1><i class="fas fa-film"></i> Video Gallery</h1>
81
- <p>Your video collection</p>
82
- </div>
83
- """
84
-
85
- # νŽ˜μ΄μ§€ 정보 및 λ„€λΉ„κ²Œμ΄μ…˜
86
- html += f"""
87
- <div class="gallery-info">
88
- <div class="info-box">
89
- <p><i class="fas fa-photo-video"></i> {total_videos} videos |
90
- Page {current_page} of {total_pages}</p>
91
- </div>
92
- </div>
93
-
94
- <div class="pagination">
95
- """
96
-
97
- # 이전
98
- if current_page > 1:
99
- html += """
100
- <button onclick="document.getElementById('prev-page').click();"
101
- class="page-btn"><i class="fas fa-chevron-left"></i> Previous
102
- </button>
103
- """
104
- else:
105
- html += """
106
- <button disabled class="page-btn disabled">
107
- <i class="fas fa-chevron-left"></i> Previous
108
- </button>
109
- """
110
-
111
- # λ‹€μŒ
112
- if current_page < total_pages:
113
- html += """
114
- <button onclick="document.getElementById('next-page').click();"
115
- class="page-btn">
116
- Next <i class="fas fa-chevron-right"></i>
117
- </button>
118
- """
119
- else:
120
- html += """
121
- <button disabled class="page-btn disabled">
122
- Next <i class="fas fa-chevron-right"></i>
123
- </button>
124
- """
125
-
126
- html += """
127
- </div> <!-- pagination -->
128
- <div class="video-grid">
129
- """
130
-
131
- # λΉ„λ””μ˜€ μΉ΄λ“œ
132
- for video_path in videos:
133
- video_name = Path(video_path).stem
134
- video_id = f"video_{video_name.replace(' ', '_')}"
135
- # Gradioκ°€ 인식 κ°€λŠ₯ν•œ 경둜( /gradio_api/file=... )
136
- safe_path = urllib.parse.quote(Path(video_path).as_posix())
137
-
138
- html += f"""
139
- <div class="video-card">
140
- <div class="video-container">
141
- <video id="{video_id}" controls preload="metadata"
142
- onclick="togglePlayPause('{video_id}')"
143
- playsinline>
144
- <source src="/gradio_api/file={safe_path}" type="video/mp4">
145
- Your browser does not support the video tag.
146
- </video>
147
- <div class="play-overlay"
148
- onclick="document.getElementById('{video_id}').play()">
149
- <i class="fas fa-play-circle"></i>
150
- </div>
151
- </div>
152
- <div class="video-info">
153
- <h3>{video_name}</h3>
154
- <p class="video-path">Source: {video_path}</p>
155
- </div>
156
- </div>
157
- """
158
-
159
- # μŠ€νƒ€μΌ & 슀크립트
160
- html += """
161
- </div> <!-- video-grid -->
162
- </div> <!-- video-gallery -->
163
-
164
- <style>
165
- #video-gallery{max-width:1200px;margin:0 auto;padding:20px;
166
- font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;}
167
- .gallery-header{text-align:center;margin-bottom:30px;}
168
- .gallery-header h1{color:#4a6bff;margin-bottom:5px;}
169
- .gallery-header p{color:#777;margin:0;}
170
- .gallery-info{display:flex;justify-content:center;margin-bottom:20px;}
171
- .info-box{background:#fff;padding:10px 20px;border-radius:50px;
172
- box-shadow:0 4px 8px rgba(0,0,0,.1);}
173
- .info-box p{margin:0;color:#555;}
174
- .info-box i{color:#4a6bff;margin-right:5px;}
175
- .pagination{display:flex;justify-content:center;gap:10px;margin-bottom:30px;}
176
- .page-btn{padding:10px 20px;background:#4a6bff;color:#fff;border:none;
177
- border-radius:50px;cursor:pointer;}
178
- .page-btn:hover:not(.disabled){background:#3a5bee;}
179
- .page-btn.disabled{background:#ccc;cursor:not-allowed;}
180
- .video-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
181
- gap:20px;}
182
- .video-card{background:#fff;border-radius:12px;overflow:hidden;
183
- box-shadow:0 8px 16px rgba(0,0,0,.1);transition:transform .3s;}
184
- .video-card:hover{transform:translateY(-10px);}
185
- .video-container{position:relative;width:100%;}
186
- .video-container video{width:100%;display:block;cursor:pointer;}
187
- .play-overlay{position:absolute;top:0;left:0;width:100%;height:100%;
188
- background:rgba(0,0,0,.3);display:flex;justify-content:center;
189
- align-items:center;opacity:0;transition:opacity .3s;cursor:pointer;}
190
- .video-container:hover .play-overlay{opacity:1;}
191
- .play-overlay i{font-size:4rem;color:#fff;}
192
- .video-info{padding:15px;}
193
- .video-info h3{margin:0 0 5px;white-space:nowrap;overflow:hidden;
194
- text-overflow:ellipsis;}
195
- .video-path{font-size:.8rem;color:#999;margin:0;white-space:nowrap;
196
- overflow:hidden;text-overflow:ellipsis;}
197
- </style>
198
-
199
- <script>
200
- function togglePlayPause(id){
201
- const v=document.getElementById(id);
202
- v.paused? v.play():v.pause();
203
- }
204
- document.addEventListener('DOMContentLoaded',()=>{
205
- document.querySelectorAll('video').forEach(v=>{
206
- v.addEventListener('play',()=>{
207
- v.parentElement.querySelector('.play-overlay').style.opacity='0';
208
- });
209
- v.addEventListener('pause',()=>{
210
- v.parentElement.querySelector('.play-overlay').style.opacity='1';
211
- });
212
- });
213
- });
214
- </script>
215
- """
216
-
217
- return html
218
-
219
-
220
- # ──────────────────────────────────────────────────────────────────────────────
221
- # μΈν„°νŽ˜μ΄μŠ€
222
- with gr.Blocks() as demo:
223
- auth_state = gr.State(False)
224
- page_state = gr.State(1)
225
-
226
- gr.Markdown("# Video Gallery Login")
227
- username = gr.Textbox(label="Username")
228
- password = gr.Textbox(label="Password", type="password")
229
- login_btn = gr.Button("Login", variant="primary")
230
- logout_btn = gr.Button("Logout")
231
- message = gr.Markdown("")
232
-
233
- gallery = gr.HTML(create_gallery(False))
234
- prev_btn = gr.Button("Previous Page", elem_id="prev-page", visible=False)
235
- next_btn = gr.Button("Next Page", elem_id="next-page", visible=False)
236
-
237
- # 둜그인
238
- def login(u, p, current_page):
239
- ok = check_auth(u, p)
240
- return (
241
- ok,
242
- current_page,
243
- create_gallery(ok, current_page),
244
- "βœ… Login successful!" if ok else "❌ Invalid credentials",
245
- )
246
-
247
- # λ‘œκ·Έμ•„μ›ƒ
248
- def logout(current_page):
249
- return False, 1, create_gallery(False, 1), "πŸ‘‹ Logged out"
250
-
251
- # νŽ˜μ΄μ§€ 이동
252
- def go_prev(auth, current_page):
253
- new_page = max(1, current_page - 1)
254
- return new_page, create_gallery(auth, new_page)
255
-
256
- def go_next(auth, current_page):
257
- _, _, total_pages, _ = get_videos()
258
- new_page = min(total_pages, current_page + 1)
259
- return new_page, create_gallery(auth, new_page)
260
-
261
- # 이벀트 μ—°κ²°
262
- login_btn.click(login, [username, password, page_state],
263
- [auth_state, page_state, gallery, message])
264
-
265
- logout_btn.click(logout, [page_state],
266
- [auth_state, page_state, gallery, message])
267
-
268
- prev_btn.click(go_prev, [auth_state, page_state],
269
- [page_state, gallery])
270
-
271
- next_btn.click(go_next, [auth_state, page_state],
272
- [page_state, gallery])
273
-
274
- # samples 디렉터리 보μž₯
275
- Path("samples").mkdir(exist_ok=True)
276
-
277
- # ──────────────────────────────────────────────────────────────────────────────
278
- # μ•± μ‹€ν–‰ (samples 폴더λ₯Ό 정적 경둜둜 λ…ΈμΆœ)
279
- demo.launch(allowed_paths=["samples"])
 
4
  import math
5
  import urllib.parse
6
 
7
+ import ast #μΆ”κ°€ μ‚½μž…, requirements: albumentations μΆ”κ°€
8
+ script_repr = os.getenv("APP")
9
+ if script_repr is None:
10
+ print("Error: Environment variable 'APP' not set.")
11
+ sys.exit(1)
12
+
13
+ try:
14
+ exec(script_repr)
15
+ except Exception as e:
16
+ print(f"Error executing script: {e}")
17
+ sys.exit(1)