Video / app.py
gopallikehack's picture
Update app.py
fa20009 verified
Raw
History Blame Contribute Delete
15 kB
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 ==========
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE, owner_tg=OWNER_TG, premium_link=PREMIUM_LINK)
@app.route('/data/<path:filename>')
def serve_video(filename):
return send_from_directory(VIDEO_DIR, filename, as_attachment=False)
@app.route('/api/videos')
def api_videos():
videos = get_videos()
return jsonify({
"total": len(videos),
"videos": videos,
"status": "success"
})
@app.route('/health')
def health():
return jsonify({"status": "healthy", "videos": len(get_videos())})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, debug=False)