appVideoDemos / app.py
0001AMA's picture
Upload app.py with huggingface_hub
176b41e verified
#!/usr/bin/env python3
"""
Video Player Web App - Read-only video streaming server.
Loads videos from the 'playlist' folder and serves them via a web interface.
"""
import os
import json
import mimetypes
from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import unquote, urlparse
import re
PLAYLIST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "playlist")
PORT = 8080
VIDEO_EXTENSIONS = {".mp4", ".webm", ".mov", ".m4v", ".avi", ".mkv", ".ogg", ".ogv"}
def get_video_files():
"""Get list of video files from playlist directory."""
videos = []
if not os.path.isdir(PLAYLIST_DIR):
return videos
for filename in sorted(os.listdir(PLAYLIST_DIR)):
ext = os.path.splitext(filename)[1].lower()
if ext in VIDEO_EXTENSIONS:
filepath = os.path.join(PLAYLIST_DIR, filename)
size = os.path.getsize(filepath)
videos.append({
"name": filename,
"title": os.path.splitext(filename)[0].replace("_", " ").replace("-", " ").title(),
"path": f"/playlist/{filename}",
"size": size,
"size_human": f"{size / (1024*1024):.1f} MB"
})
return videos
class VideoPlayerHandler(SimpleHTTPRequestHandler):
"""Custom handler for video player - read-only, no modifications allowed."""
def do_GET(self):
path = urlparse(self.path).path
if path == "/" or path == "/index.html":
self.serve_index()
elif path == "/api/playlist":
self.serve_playlist_api()
elif path.startswith("/playlist/"):
self.serve_video(path)
elif path == "/style.css":
self.serve_static("style.css", "text/css")
elif path == "/script.js":
self.serve_static("script.js", "application/javascript")
else:
self.send_error(404, "Not Found")
def do_POST(self):
self.send_error(405, "Method Not Allowed - Read Only")
def do_PUT(self):
self.send_error(405, "Method Not Allowed - Read Only")
def do_DELETE(self):
self.send_error(405, "Method Not Allowed - Read Only")
def serve_index(self):
"""Serve the main HTML page."""
html = self.get_index_html()
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", len(html.encode()))
self.end_headers()
self.wfile.write(html.encode())
def serve_playlist_api(self):
"""Serve playlist as JSON."""
videos = get_video_files()
data = json.dumps({"videos": videos})
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", len(data.encode()))
self.end_headers()
self.wfile.write(data.encode())
def serve_video(self, path):
"""Serve video file with range support for seeking."""
filename = unquote(path.replace("/playlist/", ""))
if "/" in filename or ".." in filename:
self.send_error(403, "Forbidden")
return
filepath = os.path.join(PLAYLIST_DIR, filename)
if not os.path.isfile(filepath):
self.send_error(404, "Video Not Found")
return
file_size = os.path.getsize(filepath)
content_type, _ = mimetypes.guess_type(filepath)
if not content_type:
content_type = "video/mp4"
range_header = self.headers.get("Range")
if range_header:
range_match = re.match(r"bytes=(\d+)-(\d*)", range_header)
if range_match:
start = int(range_match.group(1))
end = int(range_match.group(2)) if range_match.group(2) else file_size - 1
end = min(end, file_size - 1)
length = end - start + 1
self.send_response(206)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", length)
self.send_header("Content-Range", f"bytes {start}-{end}/{file_size}")
self.send_header("Accept-Ranges", "bytes")
self.end_headers()
with open(filepath, "rb") as f:
f.seek(start)
remaining = length
chunk_size = 64 * 1024
while remaining > 0:
chunk = f.read(min(chunk_size, remaining))
if not chunk:
break
self.wfile.write(chunk)
remaining -= len(chunk)
return
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", file_size)
self.send_header("Accept-Ranges", "bytes")
self.end_headers()
with open(filepath, "rb") as f:
chunk_size = 64 * 1024
while True:
chunk = f.read(chunk_size)
if not chunk:
break
self.wfile.write(chunk)
def serve_static(self, filename, content_type):
"""Serve static files (CSS, JS)."""
filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename)
if not os.path.isfile(filepath):
self.send_error(404, "Not Found")
return
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
self.send_response(200)
self.send_header("Content-Type", f"{content_type}; charset=utf-8")
self.send_header("Content-Length", len(content.encode()))
self.end_headers()
self.wfile.write(content.encode())
def get_index_html(self):
return '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video demos of apps</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<header>
<h1>๐ŸŽฌ Video demos of apps</h1>
</header>
<main>
<div class="player-section">
<div class="video-container">
<video id="videoPlayer" controls>
<source src="" type="video/mp4">
Your browser does not support the video tag.
</video>
<div class="video-overlay" id="videoOverlay">
<span>Select a video from the playlist</span>
</div>
</div>
<div class="controls-bar">
<div class="now-playing" id="nowPlaying">
<span class="label">Now Playing:</span>
<span class="title" id="currentTitle">-</span>
</div>
<div class="playback-controls">
<button id="prevBtn" title="Previous">โฎ</button>
<button id="playPauseBtn" title="Play/Pause">โ–ถ</button>
<button id="nextBtn" title="Next">โญ</button>
<button id="muteBtn" title="Mute">๐Ÿ”Š</button>
<input type="range" id="volumeSlider" min="0" max="1" step="0.1" value="1" title="Volume">
<button id="fullscreenBtn" title="Fullscreen">โ›ถ</button>
</div>
</div>
</div>
<aside class="playlist-section">
<h2>๐Ÿ“‹ Playlist</h2>
<div class="playlist" id="playlist">
<div class="loading">Loading playlist...</div>
</div>
</aside>
</main>
<footer>
<p>Read-only Video Player โ€ข Videos cannot be modified or deleted</p>
</footer>
</div>
<script src="/script.js"></script>
</body>
</html>'''
def log_message(self, format, *args):
print(f"[{self.log_date_time_string()}] {args[0]}")
def main():
os.chdir(os.path.dirname(os.path.abspath(__file__)))
videos = get_video_files()
print(f"\n{'='*50}")
print("๐ŸŽฌ Video Player Server")
print(f"{'='*50}")
print(f"๐Ÿ“ Playlist folder: {PLAYLIST_DIR}")
print(f"๐ŸŽฅ Videos found: {len(videos)}")
for v in videos:
print(f" โ€ข {v['name']} ({v['size_human']})")
print(f"\n๐ŸŒ Server starting at: http://localhost:{PORT}")
print(f"{'='*50}\n")
server = HTTPServer(("", PORT), VideoPlayerHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
print("\n\nServer stopped.")
server.shutdown()
if __name__ == "__main__":
main()