gopallikehack commited on
Commit
3d74855
·
verified ·
1 Parent(s): fc41fe0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +386 -53
app.py CHANGED
@@ -1,95 +1,428 @@
1
- from flask import Flask, render_template_string, send_from_directory
2
  import os
3
  import glob
 
4
 
5
  app = Flask(__name__)
6
 
7
  VIDEO_DIR = "/data"
8
-
9
- def get_videos():
10
- videos = []
11
- if os.path.exists(VIDEO_DIR):
12
- for ext in ['*.mp4', '*.mkv', '*.avi', '*.mov', '*.webm']:
13
- videos.extend(glob.glob(os.path.join(VIDEO_DIR, ext)))
14
- return sorted(videos)
15
-
16
  HTML_TEMPLATE = """
17
  <!DOCTYPE html>
18
- <html>
19
  <head>
20
- <title>🎬 Premium Capcut Course</title>
21
- <meta charset="utf-8">
22
- <meta name="viewport" content="width=device-width, initial-scale=1">
 
23
  <style>
24
  * { margin: 0; padding: 0; box-sizing: border-box; }
25
- body { font-family: system-ui, sans-serif; background: #0f0f1a; color: #fff; padding: 1rem; }
26
- .header { background: linear-gradient(135deg, #1a1a2e, #16213e); padding: 1.5rem; text-align: center; border-bottom: 3px solid #00b4ff; }
27
- .header h1 { color: #00b4ff; font-size: 2rem; }
28
- .container { max-width: 1200px; margin: 0 auto; padding: 1rem; display: flex; flex-wrap: wrap; gap: 1.5rem; }
29
- .player-wrapper { flex: 3; min-width: 300px; background: #000; border-radius: 1rem; overflow: hidden; }
30
- .player-wrapper video { width: 100%; height: auto; display: block; }
31
- .playlist { flex: 1; min-width: 200px; background: #1a1a2e; border-radius: 1rem; padding: 1rem; max-height: 500px; overflow-y: auto; }
32
- .playlist h3 { color: #00b4ff; margin-bottom: 1rem; }
33
- .playlist button { display: block; width: 100%; padding: 0.75rem; margin-bottom: 0.5rem; background: #2a2a4e; color: #fff; border: none; border-radius: 0.5rem; cursor: pointer; text-align: left; font-size: 0.9rem; }
34
- .playlist button:hover { background: #00b4ff; color: #000; }
35
- .playlist button.active { background: #00b4ff; color: #000; }
36
- .stats { text-align: center; padding: 1rem; color: #666; }
37
- @media (max-width: 768px) { .container { flex-direction: column; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </style>
39
  </head>
40
  <body>
41
- <div class="header">
42
- <h1>🎬 Premium Capcut Editing Course</h1>
43
- <p style="color: #888;">Master Video Editing Like a Pro</p>
44
- </div>
 
 
45
 
 
46
  <div class="container">
47
- <div class="player-wrapper">
48
- <video id="videoPlayer" controls autoplay style="width:100%; height:auto;">
49
- <source src="" type="video/mp4">
50
- Your browser does not support the video tag.
51
- </video>
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  </div>
53
- <div class="playlist">
54
- <h3>📚 Lectures</h3>
55
- {% for video in videos %}
56
- <button onclick="playVideo('{{ video }}')" class="{% if loop.first %}active{% endif %}">
57
- ▶️ {{ video|replace('/data/', '')|replace('.mp4', '') }}
58
- </button>
59
- {% endfor %}
 
 
 
 
 
 
 
 
 
 
 
 
60
  </div>
61
  </div>
62
 
63
- <div class="stats">📊 Total Videos: {{ videos|length }}</div>
 
 
 
64
 
65
  <script>
66
- function playVideo(path) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  const player = document.getElementById('videoPlayer');
68
- player.querySelector('source').src = path;
69
  player.load();
70
  player.play();
71
 
 
 
 
72
  // Highlight active
73
- document.querySelectorAll('.playlist button').forEach(b => b.classList.remove('active'));
74
- document.querySelectorAll('.playlist button').forEach(b => {
75
- if (b.textContent.includes(path.replace('/data/', ''))) {
76
- b.classList.add('active');
77
- }
78
  });
79
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  </script>
81
  </body>
82
  </html>
83
  """
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  @app.route('/')
86
  def index():
87
- videos = get_videos()
88
- return render_template_string(HTML_TEMPLATE, videos=videos)
89
 
90
  @app.route('/data/<path:filename>')
91
  def serve_video(filename):
92
- return send_from_directory('/data', filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  if __name__ == '__main__':
95
  app.run(host='0.0.0.0', port=7860, debug=False)
 
1
+ from flask import Flask, request, jsonify, render_template_string, send_from_directory
2
  import os
3
  import glob
4
+ import re
5
 
6
  app = Flask(__name__)
7
 
8
  VIDEO_DIR = "/data"
 
 
 
 
 
 
 
 
9
  HTML_TEMPLATE = """
10
  <!DOCTYPE html>
11
+ <html lang="en">
12
  <head>
13
+ <meta charset="UTF-8">
14
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
15
+ <title>🔥 PREMIUM CAPCUT COURSE 🔥</title>
16
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
17
  <style>
18
  * { margin: 0; padding: 0; box-sizing: border-box; }
19
+ body {
20
+ font-family: 'Segoe UI', system-ui, sans-serif;
21
+ background: #0a0a15;
22
+ color: #fff;
23
+ min-height: 100vh;
24
+ }
25
+ /* ===== HEADER ===== */
26
+ .header {
27
+ background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #0f0f1a 100%);
28
+ padding: 2rem 1rem;
29
+ text-align: center;
30
+ border-bottom: 3px solid #00f5ff;
31
+ position: relative;
32
+ overflow: hidden;
33
+ }
34
+ .header::before {
35
+ content: '';
36
+ position: absolute;
37
+ top: -50%;
38
+ left: -50%;
39
+ width: 200%;
40
+ height: 200%;
41
+ background: radial-gradient(circle at center, rgba(0, 245, 255, 0.05) 0%, transparent 70%);
42
+ animation: glow 10s infinite;
43
+ }
44
+ @keyframes glow {
45
+ 0% { transform: rotate(0deg); }
46
+ 100% { transform: rotate(360deg); }
47
+ }
48
+ .header h1 {
49
+ font-size: 2.8rem;
50
+ font-weight: 800;
51
+ background: linear-gradient(135deg, #00f5ff, #00b4ff, #ff6b6b);
52
+ -webkit-background-clip: text;
53
+ background-clip: text;
54
+ color: transparent;
55
+ position: relative;
56
+ z-index: 1;
57
+ }
58
+ .header p {
59
+ color: #888;
60
+ font-size: 1.1rem;
61
+ margin-top: 0.5rem;
62
+ position: relative;
63
+ z-index: 1;
64
+ }
65
+ .header .badge {
66
+ display: inline-block;
67
+ background: #00f5ff20;
68
+ border: 1px solid #00f5ff;
69
+ color: #00f5ff;
70
+ padding: 0.25rem 1.2rem;
71
+ border-radius: 2rem;
72
+ font-size: 0.8rem;
73
+ margin-top: 0.5rem;
74
+ position: relative;
75
+ z-index: 1;
76
+ }
77
+ /* ===== CONTAINER ===== */
78
+ .container {
79
+ max-width: 1400px;
80
+ margin: 0 auto;
81
+ padding: 2rem 1rem;
82
+ }
83
+ .grid {
84
+ display: grid;
85
+ grid-template-columns: 1fr 320px;
86
+ gap: 2rem;
87
+ }
88
+ /* ===== VIDEO PLAYER ===== */
89
+ .player-wrapper {
90
+ background: #000;
91
+ border-radius: 1.5rem;
92
+ overflow: hidden;
93
+ border: 1px solid #1a1a3e;
94
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
95
+ position: relative;
96
+ }
97
+ .player-wrapper video {
98
+ width: 100%;
99
+ height: auto;
100
+ display: block;
101
+ }
102
+ .video-info {
103
+ padding: 1rem 1.5rem;
104
+ background: #0f0f1a;
105
+ border-top: 1px solid #1a1a3e;
106
+ display: flex;
107
+ justify-content: space-between;
108
+ align-items: center;
109
+ }
110
+ .video-info .title {
111
+ font-weight: 600;
112
+ color: #00f5ff;
113
+ font-size: 1.1rem;
114
+ }
115
+ .video-info .counter {
116
+ color: #666;
117
+ font-size: 0.85rem;
118
+ }
119
+ /* ===== PLAYLIST ===== */
120
+ .playlist {
121
+ background: #0f0f1a;
122
+ border-radius: 1.5rem;
123
+ border: 1px solid #1a1a3e;
124
+ padding: 1.5rem;
125
+ max-height: 600px;
126
+ overflow-y: auto;
127
+ }
128
+ .playlist::-webkit-scrollbar { width: 4px; }
129
+ .playlist::-webkit-scrollbar-track { background: #0f0f1a; }
130
+ .playlist::-webkit-scrollbar-thumb { background: #00f5ff; border-radius: 2rem; }
131
+ .playlist h3 {
132
+ color: #00f5ff;
133
+ font-size: 1.2rem;
134
+ margin-bottom: 1rem;
135
+ display: flex;
136
+ align-items: center;
137
+ gap: 0.5rem;
138
+ }
139
+ .playlist h3 span { color: #666; font-size: 0.8rem; font-weight: 400; }
140
+ .playlist-item {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 0.8rem;
144
+ padding: 0.7rem 1rem;
145
+ margin-bottom: 0.5rem;
146
+ background: #1a1a2e;
147
+ border-radius: 0.75rem;
148
+ cursor: pointer;
149
+ transition: all 0.3s ease;
150
+ border: 1px solid transparent;
151
+ }
152
+ .playlist-item:hover {
153
+ background: #22224a;
154
+ border-color: #00f5ff40;
155
+ transform: translateX(4px);
156
+ }
157
+ .playlist-item.active {
158
+ background: #00f5ff20;
159
+ border-color: #00f5ff;
160
+ }
161
+ .playlist-item .num {
162
+ color: #666;
163
+ font-size: 0.8rem;
164
+ font-weight: 600;
165
+ min-width: 28px;
166
+ }
167
+ .playlist-item .name {
168
+ flex: 1;
169
+ font-size: 0.9rem;
170
+ color: #ccc;
171
+ }
172
+ .playlist-item .duration {
173
+ color: #555;
174
+ font-size: 0.75rem;
175
+ }
176
+ .playlist-item .play-icon {
177
+ color: #00f5ff;
178
+ font-size: 0.9rem;
179
+ }
180
+ /* ===== STATS BAR ===== */
181
+ .stats-bar {
182
+ display: flex;
183
+ justify-content: center;
184
+ gap: 3rem;
185
+ padding: 1.5rem;
186
+ background: #0f0f1a;
187
+ border-radius: 1.5rem;
188
+ border: 1px solid #1a1a3e;
189
+ margin-top: 2rem;
190
+ }
191
+ .stats-bar .stat {
192
+ text-align: center;
193
+ }
194
+ .stats-bar .stat .num {
195
+ font-size: 1.8rem;
196
+ font-weight: 700;
197
+ color: #00f5ff;
198
+ }
199
+ .stats-bar .stat .label {
200
+ color: #666;
201
+ font-size: 0.75rem;
202
+ text-transform: uppercase;
203
+ letter-spacing: 1px;
204
+ margin-top: 0.2rem;
205
+ }
206
+ /* ===== RESPONSIVE ===== */
207
+ @media (max-width: 992px) {
208
+ .grid { grid-template-columns: 1fr; }
209
+ .playlist { max-height: 300px; }
210
+ .header h1 { font-size: 2rem; }
211
+ }
212
+ @media (max-width: 600px) {
213
+ .stats-bar { flex-wrap: wrap; gap: 1rem; }
214
+ .header h1 { font-size: 1.5rem; }
215
+ .video-info { flex-direction: column; gap: 0.5rem; text-align: center; }
216
+ }
217
+ /* ===== FOOTER ===== */
218
+ .footer {
219
+ text-align: center;
220
+ padding: 2rem;
221
+ color: #444;
222
+ font-size: 0.85rem;
223
+ border-top: 1px solid #1a1a2e;
224
+ margin-top: 2rem;
225
+ }
226
+ .footer .dev {
227
+ color: #00f5ff;
228
+ }
229
  </style>
230
  </head>
231
  <body>
232
+ <!-- HEADER -->
233
+ <header class="header">
234
+ <h1>🎬 Premium Capcut Course</h1>
235
+ <p>Master Video Editing — All Lectures in One Place</p>
236
+ <span class="badge"><i class="fas fa-bolt"></i> PREMIUM 4K</span>
237
+ </header>
238
 
239
+ <!-- MAIN -->
240
  <div class="container">
241
+ <div class="grid">
242
+ <!-- Player -->
243
+ <div class="player-wrapper">
244
+ <video id="videoPlayer" controls>
245
+ <source src="" type="video/mp4">
246
+ Your browser does not support the video tag.
247
+ </video>
248
+ <div class="video-info">
249
+ <span class="title" id="currentTitle">🎬 Select a lecture</span>
250
+ <span class="counter" id="videoCounter">0 / 0</span>
251
+ </div>
252
+ </div>
253
+
254
+ <!-- Playlist -->
255
+ <div class="playlist">
256
+ <h3>📚 Lectures <span id="totalCount"></span></h3>
257
+ <div id="playlistContainer"></div>
258
+ </div>
259
  </div>
260
+
261
+ <!-- Stats -->
262
+ <div class="stats-bar">
263
+ <div class="stat">
264
+ <div class="num" id="statTotal">0</div>
265
+ <div class="label">Total Lectures</div>
266
+ </div>
267
+ <div class="stat">
268
+ <div class="num" id="statSize">0 GB</div>
269
+ <div class="label">Total Size</div>
270
+ </div>
271
+ <div class="stat">
272
+ <div class="num">⚡</div>
273
+ <div class="label">Premium Quality</div>
274
+ </div>
275
+ <div class="stat">
276
+ <div class="num" id="statDuration">0m</div>
277
+ <div class="label">Total Duration</div>
278
+ </div>
279
  </div>
280
  </div>
281
 
282
+ <!-- FOOTER -->
283
+ <footer class="footer">
284
+ Made with ❤️ by <span class="dev">Gopal Parmar</span> | Premium Capcut Course
285
+ </footer>
286
 
287
  <script>
288
+ // ===== DATA FROM API =====
289
+ let videos = [];
290
+
291
+ async function loadVideos() {
292
+ try {
293
+ const res = await fetch('/api/videos');
294
+ const data = await res.json();
295
+ videos = data.videos || [];
296
+ renderPlaylist();
297
+ updateStats();
298
+ if (videos.length > 0) {
299
+ playVideo(0);
300
+ }
301
+ } catch (e) {
302
+ console.error('Error loading videos:', e);
303
+ }
304
+ }
305
+
306
+ function renderPlaylist() {
307
+ const container = document.getElementById('playlistContainer');
308
+ container.innerHTML = '';
309
+ document.getElementById('totalCount').textContent = `(${videos.length})`;
310
+
311
+ videos.forEach((v, i) => {
312
+ const div = document.createElement('div');
313
+ div.className = 'playlist-item';
314
+ div.dataset.index = i;
315
+ div.innerHTML = `
316
+ <span class="num">${String(i+1).padStart(2, '0')}</span>
317
+ <span class="name">${v.name.replace(/\.[^.]+$/, '')}</span>
318
+ <span class="play-icon"><i class="fas fa-play-circle"></i></span>
319
+ `;
320
+ div.addEventListener('click', () => playVideo(i));
321
+ container.appendChild(div);
322
+ });
323
+ }
324
+
325
+ function playVideo(index) {
326
+ const video = videos[index];
327
+ if (!video) return;
328
+
329
  const player = document.getElementById('videoPlayer');
330
+ player.querySelector('source').src = video.url;
331
  player.load();
332
  player.play();
333
 
334
+ document.getElementById('currentTitle').textContent = `🎬 ${video.name.replace(/\.[^.]+$/, '')}`;
335
+ document.getElementById('videoCounter').textContent = `${index+1} / ${videos.length}`;
336
+
337
  // Highlight active
338
+ document.querySelectorAll('.playlist-item').forEach((el, i) => {
339
+ el.classList.toggle('active', i === index);
 
 
 
340
  });
341
  }
342
+
343
+ function updateStats() {
344
+ document.getElementById('statTotal').textContent = videos.length;
345
+ const totalSize = videos.reduce((acc, v) => acc + (v.size || 0), 0);
346
+ document.getElementById('statSize').textContent = (totalSize / (1024*1024*1024)).toFixed(1) + ' GB';
347
+ // Fake duration for demo
348
+ document.getElementById('statDuration').textContent = (videos.length * 12) + 'm';
349
+ }
350
+
351
+ // Keyboard shortcut: Next/Prev with Arrow keys
352
+ document.addEventListener('keydown', (e) => {
353
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
354
+ const current = document.querySelector('.playlist-item.active');
355
+ if (current) {
356
+ const next = current.nextElementSibling;
357
+ if (next) next.click();
358
+ }
359
+ } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
360
+ const current = document.querySelector('.playlist-item.active');
361
+ if (current) {
362
+ const prev = current.previousElementSibling;
363
+ if (prev) prev.click();
364
+ }
365
+ }
366
+ });
367
+
368
+ // Load on page load
369
+ document.addEventListener('DOMContentLoaded', loadVideos);
370
  </script>
371
  </body>
372
  </html>
373
  """
374
 
375
+ # ========== HELPERS ==========
376
+ def get_videos():
377
+ videos = []
378
+ if not os.path.exists(VIDEO_DIR):
379
+ return videos
380
+ for ext in ['*.mp4', '*.mkv', '*.avi', '*.mov', '*.webm']:
381
+ videos.extend(glob.glob(os.path.join(VIDEO_DIR, ext)))
382
+ videos = sorted(videos)
383
+ result = []
384
+ for i, v in enumerate(videos):
385
+ size = os.path.getsize(v) if os.path.exists(v) else 0
386
+ result.append({
387
+ "id": i + 1,
388
+ "name": os.path.basename(v),
389
+ "url": f"/data/{os.path.basename(v)}",
390
+ "size": size,
391
+ "path": v
392
+ })
393
+ return result
394
+
395
+ # ========== ROUTES ==========
396
  @app.route('/')
397
  def index():
398
+ return render_template_string(HTML_TEMPLATE)
 
399
 
400
  @app.route('/data/<path:filename>')
401
  def serve_video(filename):
402
+ return send_from_directory(VIDEO_DIR, filename, as_attachment=False)
403
+
404
+ @app.route('/api/videos')
405
+ def api_videos():
406
+ videos = get_videos()
407
+ return jsonify({
408
+ "total": len(videos),
409
+ "videos": videos,
410
+ "status": "success"
411
+ })
412
+
413
+ @app.route('/api/video/<int:video_id>')
414
+ def api_video(video_id):
415
+ videos = get_videos()
416
+ if 1 <= video_id <= len(videos):
417
+ return jsonify({
418
+ "video": videos[video_id - 1],
419
+ "status": "success"
420
+ })
421
+ return jsonify({"error": "Video not found"}), 404
422
+
423
+ @app.route('/health')
424
+ def health():
425
+ return jsonify({"status": "healthy", "videos": len(get_videos())})
426
 
427
  if __name__ == '__main__':
428
  app.run(host='0.0.0.0', port=7860, debug=False)