Spaces:
Sleeping
Sleeping
| from flask import Flask, request, jsonify, render_template_string, send_from_directory | |
| import os | |
| import glob | |
| import re | |
| app = Flask(__name__) | |
| VIDEO_DIR = "/data" | |
| OWNER_TG = "Virat_K0hli_18" | |
| PREMIUM_LINK = "https://drive.google.com/open?id=1lCzpPsjohRGR40kDDA-p2So-S2G1ZzZy" | |
| HTML_TEMPLATE = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>🔥 PREMIUM CAPCUT COURSE 🔥</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Segoe UI', system-ui, sans-serif; | |
| background: #0a0a15; | |
| color: #fff; | |
| min-height: 100vh; | |
| } | |
| ::-webkit-scrollbar { width: 4px; } | |
| ::-webkit-scrollbar-track { background: #0f0f1a; } | |
| ::-webkit-scrollbar-thumb { background: #00f5ff; border-radius: 2rem; } | |
| .header { | |
| background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #0f0f1a 100%); | |
| padding: 2rem 1rem; | |
| text-align: center; | |
| border-bottom: 3px solid #00f5ff; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: radial-gradient(circle at center, rgba(0, 245, 255, 0.05) 0%, transparent 70%); | |
| animation: glow 10s infinite; | |
| } | |
| @keyframes glow { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .header h1 { | |
| font-size: 2.8rem; | |
| font-weight: 800; | |
| background: linear-gradient(135deg, #00f5ff, #00b4ff, #ff6b6b); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header p { | |
| color: #888; | |
| font-size: 1.1rem; | |
| margin-top: 0.5rem; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header .badge { | |
| display: inline-block; | |
| background: #00f5ff20; | |
| border: 1px solid #00f5ff; | |
| color: #00f5ff; | |
| padding: 0.25rem 1.2rem; | |
| border-radius: 2rem; | |
| font-size: 0.8rem; | |
| margin-top: 0.5rem; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header .premium-link { | |
| display: inline-block; | |
| margin-top: 0.8rem; | |
| background: linear-gradient(135deg, #ff6b6b, #ee5a24); | |
| color: #fff; | |
| padding: 0.5rem 1.5rem; | |
| border-radius: 2rem; | |
| text-decoration: none; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| position: relative; | |
| z-index: 1; | |
| box-shadow: 0 0 20px rgba(255, 107, 107, 0.3); | |
| transition: 0.3s; | |
| } | |
| .header .premium-link:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 0 40px rgba(255, 107, 107, 0.5); | |
| } | |
| .container { max-width: 1400px; margin: 0 auto; padding: 2rem 1rem; } | |
| .grid { display: grid; grid-template-columns: 1fr 320px; gap: 2rem; } | |
| .player-wrapper { | |
| background: #000; | |
| border-radius: 1.5rem; | |
| overflow: hidden; | |
| border: 1px solid #1a1a3e; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8); | |
| } | |
| .player-wrapper video { width: 100%; height: auto; display: block; } | |
| .video-info { | |
| padding: 1rem 1.5rem; | |
| background: #0f0f1a; | |
| border-top: 1px solid #1a1a3e; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .video-info .title { font-weight: 600; color: #00f5ff; font-size: 1.1rem; } | |
| .video-info .counter { color: #666; font-size: 0.85rem; } | |
| .playlist { | |
| background: #0f0f1a; | |
| border-radius: 1.5rem; | |
| border: 1px solid #1a1a3e; | |
| padding: 1.5rem; | |
| max-height: 600px; | |
| overflow-y: auto; | |
| } | |
| .playlist h3 { | |
| color: #00f5ff; | |
| font-size: 1.2rem; | |
| margin-bottom: 1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .playlist h3 span { color: #666; font-size: 0.8rem; font-weight: 400; } | |
| .playlist-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.8rem; | |
| padding: 0.7rem 1rem; | |
| margin-bottom: 0.5rem; | |
| background: #1a1a2e; | |
| border-radius: 0.75rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| border: 1px solid transparent; | |
| } | |
| .playlist-item:hover { background: #22224a; border-color: #00f5ff40; transform: translateX(4px); } | |
| .playlist-item.active { background: #00f5ff20; border-color: #00f5ff; } | |
| .playlist-item .num { color: #666; font-size: 0.8rem; font-weight: 600; min-width: 28px; } | |
| .playlist-item .name { flex: 1; font-size: 0.9rem; color: #ccc; } | |
| .playlist-item .play-icon { color: #00f5ff; font-size: 0.9rem; } | |
| .playlist-item.na { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| border-color: #ff6b6b40; | |
| } | |
| .playlist-item.na .name { color: #666; } | |
| .playlist-item.na .play-icon { color: #ff6b6b; } | |
| .playlist-item .na-badge { | |
| background: #ff6b6b20; | |
| color: #ff6b6b; | |
| font-size: 0.65rem; | |
| padding: 0.1rem 0.6rem; | |
| border-radius: 1rem; | |
| border: 1px solid #ff6b6b40; | |
| } | |
| .contact-owner { | |
| text-align: center; | |
| padding: 1.5rem; | |
| margin-top: 2rem; | |
| background: #0f0f1a; | |
| border-radius: 1.5rem; | |
| border: 1px solid #1a1a3e; | |
| } | |
| .contact-owner a { | |
| display: inline-block; | |
| background: linear-gradient(135deg, #00f5ff, #00b4ff); | |
| color: #000; | |
| padding: 0.8rem 2.5rem; | |
| border-radius: 2rem; | |
| text-decoration: none; | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 0 30px rgba(0, 245, 255, 0.2); | |
| } | |
| .contact-owner a:hover { transform: scale(1.05); box-shadow: 0 0 50px rgba(0, 245, 255, 0.4); } | |
| .contact-owner p { color: #666; font-size: 0.85rem; margin-top: 0.5rem; } | |
| .stats-bar { | |
| display: flex; | |
| justify-content: center; | |
| gap: 3rem; | |
| padding: 1.5rem; | |
| background: #0f0f1a; | |
| border-radius: 1.5rem; | |
| border: 1px solid #1a1a3e; | |
| margin-top: 2rem; | |
| } | |
| .stats-bar .stat { text-align: center; } | |
| .stats-bar .stat .num { font-size: 1.8rem; font-weight: 700; color: #00f5ff; } | |
| .stats-bar .stat .label { color: #666; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; margin-top: 0.2rem; } | |
| .footer { | |
| text-align: center; | |
| padding: 2rem; | |
| color: #444; | |
| font-size: 0.85rem; | |
| border-top: 1px solid #1a1a2e; | |
| margin-top: 2rem; | |
| } | |
| .footer .dev { color: #00f5ff; } | |
| .footer .na-note { color: #ff6b6b; font-size: 0.8rem; margin-top: 0.5rem; } | |
| @media (max-width: 992px) { | |
| .grid { grid-template-columns: 1fr; } | |
| .playlist { max-height: 300px; } | |
| .header h1 { font-size: 2rem; } | |
| } | |
| @media (max-width: 600px) { | |
| .stats-bar { flex-wrap: wrap; gap: 1rem; } | |
| .header h1 { font-size: 1.5rem; } | |
| .video-info { flex-direction: column; gap: 0.5rem; text-align: center; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="header"> | |
| <h1>🎬 Premium Capcut Course</h1> | |
| <p>Master Video Editing — All Lectures in One Place</p> | |
| <span class="badge"><i class="fas fa-bolt"></i> PREMIUM 4K</span> | |
| <br> | |
| <a href="{{ premium_link }}" target="_blank" class="premium-link"> | |
| <i class="fas fa-crown"></i> Capcut Premium Resources | |
| </a> | |
| </header> | |
| <div class="container"> | |
| <div class="grid"> | |
| <div class="player-wrapper"> | |
| <video id="videoPlayer" controls> | |
| <source src="" type="video/mp4"> | |
| Your browser does not support the video tag. | |
| </video> | |
| <div class="video-info"> | |
| <span class="title" id="currentTitle">🎬 Select a lecture</span> | |
| <span class="counter" id="videoCounter">0 / 0</span> | |
| </div> | |
| </div> | |
| <div class="playlist"> | |
| <h3>📚 Lectures <span id="totalCount"></span></h3> | |
| <div id="playlistContainer"></div> | |
| </div> | |
| </div> | |
| <div class="stats-bar"> | |
| <div class="stat"><div class="num" id="statTotal">0</div><div class="label">Total Lectures</div></div> | |
| <div class="stat"><div class="num" id="statSize">0 GB</div><div class="label">Total Size</div></div> | |
| <div class="stat"><div class="num">⚡</div><div class="label">Premium Quality</div></div> | |
| <div class="stat"><div class="num" id="statDuration">0m</div><div class="label">Total Duration</div></div> | |
| </div> | |
| <div class="contact-owner"> | |
| <a href="https://t.me/{{ owner_tg }}" target="_blank"><i class="fab fa-telegram-plane"></i> Contact Owner</a> | |
| <p>Any issue with my project? Reach out directly on Telegram </p> | |
| </div> | |
| </div> | |
| <footer class="footer"> | |
| Made with ❤️ by <span class="dev">Gopal Parmar</span> | Premium Capcut Course | |
| <div class="na-note">⚠️ Lecture 19 is Not Available</div> | |
| </footer> | |
| <script> | |
| let videos = []; | |
| async function loadVideos() { | |
| try { | |
| const res = await fetch('/api/videos'); | |
| const data = await res.json(); | |
| videos = data.videos || []; | |
| renderPlaylist(); | |
| updateStats(); | |
| if (videos.length > 0) playVideo(0); | |
| } catch (e) { console.error('Error loading videos:', e); } | |
| } | |
| function renderPlaylist() { | |
| const container = document.getElementById('playlistContainer'); | |
| container.innerHTML = ''; | |
| document.getElementById('totalCount').textContent = `(${videos.length})`; | |
| videos.forEach((v, i) => { | |
| const div = document.createElement('div'); | |
| div.className = 'playlist-item'; | |
| div.dataset.index = i; | |
| const displayName = v.name.replace(/\.[^.]+$/, '').replace(/Capcut/i, 'Lecture '); | |
| const isNA = displayName.includes('19'); | |
| if (isNA) div.classList.add('na'); | |
| div.innerHTML = ` | |
| <span class="num">${String(i+1).padStart(2, '0')}</span> | |
| <span class="name">${displayName}</span> | |
| ${isNA ? '<span class="na-badge">Not Available</span>' : ''} | |
| <span class="play-icon"><i class="fas ${isNA ? 'fa-times-circle' : 'fa-play-circle'}"></i></span> | |
| `; | |
| if (!isNA) { | |
| div.addEventListener('click', () => playVideo(i)); | |
| } | |
| container.appendChild(div); | |
| }); | |
| } | |
| function playVideo(index) { | |
| const video = videos[index]; | |
| if (!video) return; | |
| const player = document.getElementById('videoPlayer'); | |
| player.querySelector('source').src = video.url; | |
| player.load(); | |
| player.play(); | |
| const displayName = video.name.replace(/\.[^.]+$/, '').replace(/Capcut/i, 'Lecture '); | |
| document.getElementById('currentTitle').textContent = `🎬 ${displayName}`; | |
| document.getElementById('videoCounter').textContent = `${index+1} / ${videos.length}`; | |
| document.querySelectorAll('.playlist-item').forEach((el, i) => { | |
| el.classList.toggle('active', i === index); | |
| }); | |
| } | |
| function updateStats() { | |
| document.getElementById('statTotal').textContent = videos.length; | |
| const totalSize = videos.reduce((acc, v) => acc + (v.size || 0), 0); | |
| document.getElementById('statSize').textContent = (totalSize / (1024*1024*1024)).toFixed(1) + ' GB'; | |
| document.getElementById('statDuration').textContent = (videos.length * 12) + 'm'; | |
| } | |
| document.addEventListener('keydown', (e) => { | |
| const current = document.querySelector('.playlist-item.active'); | |
| if (!current) return; | |
| if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { | |
| const next = current.nextElementSibling; | |
| if (next) next.click(); | |
| } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { | |
| const prev = current.previousElementSibling; | |
| if (prev) prev.click(); | |
| } | |
| }); | |
| document.addEventListener('DOMContentLoaded', loadVideos); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| # ========== HELPERS ========== | |
| def get_videos(): | |
| videos = [] | |
| if not os.path.exists(VIDEO_DIR): | |
| return videos | |
| for ext in ['*.mp4', '*.mkv', '*.avi', '*.mov', '*.webm']: | |
| videos.extend(glob.glob(os.path.join(VIDEO_DIR, ext))) | |
| videos = sorted(videos, key=lambda x: int(''.join(filter(str.isdigit, x.split('/')[-1])) or 0)) | |
| result = [] | |
| for v in videos: | |
| size = os.path.getsize(v) if os.path.exists(v) else 0 | |
| result.append({ | |
| "id": len(result) + 1, | |
| "name": os.path.basename(v), | |
| "url": f"/data/{os.path.basename(v)}", | |
| "size": size, | |
| "path": v | |
| }) | |
| return result | |
| # ========== ROUTES ========== | |
| def index(): | |
| return render_template_string(HTML_TEMPLATE, owner_tg=OWNER_TG, premium_link=PREMIUM_LINK) | |
| def serve_video(filename): | |
| return send_from_directory(VIDEO_DIR, filename, as_attachment=False) | |
| def api_videos(): | |
| videos = get_videos() | |
| return jsonify({ | |
| "total": len(videos), | |
| "videos": videos, | |
| "status": "success" | |
| }) | |
| def health(): | |
| return jsonify({"status": "healthy", "videos": len(get_videos())}) | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860, debug=False) |