0001AMA commited on
Commit
176b41e
·
verified ·
1 Parent(s): 53f5704

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +250 -0
app.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Video Player Web App - Read-only video streaming server.
4
+ Loads videos from the 'playlist' folder and serves them via a web interface.
5
+ """
6
+
7
+ import os
8
+ import json
9
+ import mimetypes
10
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
11
+ from urllib.parse import unquote, urlparse
12
+ import re
13
+
14
+ PLAYLIST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "playlist")
15
+ PORT = 8080
16
+
17
+ VIDEO_EXTENSIONS = {".mp4", ".webm", ".mov", ".m4v", ".avi", ".mkv", ".ogg", ".ogv"}
18
+
19
+
20
+ def get_video_files():
21
+ """Get list of video files from playlist directory."""
22
+ videos = []
23
+ if not os.path.isdir(PLAYLIST_DIR):
24
+ return videos
25
+
26
+ for filename in sorted(os.listdir(PLAYLIST_DIR)):
27
+ ext = os.path.splitext(filename)[1].lower()
28
+ if ext in VIDEO_EXTENSIONS:
29
+ filepath = os.path.join(PLAYLIST_DIR, filename)
30
+ size = os.path.getsize(filepath)
31
+ videos.append({
32
+ "name": filename,
33
+ "title": os.path.splitext(filename)[0].replace("_", " ").replace("-", " ").title(),
34
+ "path": f"/playlist/{filename}",
35
+ "size": size,
36
+ "size_human": f"{size / (1024*1024):.1f} MB"
37
+ })
38
+ return videos
39
+
40
+
41
+ class VideoPlayerHandler(SimpleHTTPRequestHandler):
42
+ """Custom handler for video player - read-only, no modifications allowed."""
43
+
44
+ def do_GET(self):
45
+ path = urlparse(self.path).path
46
+
47
+ if path == "/" or path == "/index.html":
48
+ self.serve_index()
49
+ elif path == "/api/playlist":
50
+ self.serve_playlist_api()
51
+ elif path.startswith("/playlist/"):
52
+ self.serve_video(path)
53
+ elif path == "/style.css":
54
+ self.serve_static("style.css", "text/css")
55
+ elif path == "/script.js":
56
+ self.serve_static("script.js", "application/javascript")
57
+ else:
58
+ self.send_error(404, "Not Found")
59
+
60
+ def do_POST(self):
61
+ self.send_error(405, "Method Not Allowed - Read Only")
62
+
63
+ def do_PUT(self):
64
+ self.send_error(405, "Method Not Allowed - Read Only")
65
+
66
+ def do_DELETE(self):
67
+ self.send_error(405, "Method Not Allowed - Read Only")
68
+
69
+ def serve_index(self):
70
+ """Serve the main HTML page."""
71
+ html = self.get_index_html()
72
+ self.send_response(200)
73
+ self.send_header("Content-Type", "text/html; charset=utf-8")
74
+ self.send_header("Content-Length", len(html.encode()))
75
+ self.end_headers()
76
+ self.wfile.write(html.encode())
77
+
78
+ def serve_playlist_api(self):
79
+ """Serve playlist as JSON."""
80
+ videos = get_video_files()
81
+ data = json.dumps({"videos": videos})
82
+ self.send_response(200)
83
+ self.send_header("Content-Type", "application/json")
84
+ self.send_header("Content-Length", len(data.encode()))
85
+ self.end_headers()
86
+ self.wfile.write(data.encode())
87
+
88
+ def serve_video(self, path):
89
+ """Serve video file with range support for seeking."""
90
+ filename = unquote(path.replace("/playlist/", ""))
91
+ if "/" in filename or ".." in filename:
92
+ self.send_error(403, "Forbidden")
93
+ return
94
+
95
+ filepath = os.path.join(PLAYLIST_DIR, filename)
96
+ if not os.path.isfile(filepath):
97
+ self.send_error(404, "Video Not Found")
98
+ return
99
+
100
+ file_size = os.path.getsize(filepath)
101
+ content_type, _ = mimetypes.guess_type(filepath)
102
+ if not content_type:
103
+ content_type = "video/mp4"
104
+
105
+ range_header = self.headers.get("Range")
106
+ if range_header:
107
+ range_match = re.match(r"bytes=(\d+)-(\d*)", range_header)
108
+ if range_match:
109
+ start = int(range_match.group(1))
110
+ end = int(range_match.group(2)) if range_match.group(2) else file_size - 1
111
+ end = min(end, file_size - 1)
112
+ length = end - start + 1
113
+
114
+ self.send_response(206)
115
+ self.send_header("Content-Type", content_type)
116
+ self.send_header("Content-Length", length)
117
+ self.send_header("Content-Range", f"bytes {start}-{end}/{file_size}")
118
+ self.send_header("Accept-Ranges", "bytes")
119
+ self.end_headers()
120
+
121
+ with open(filepath, "rb") as f:
122
+ f.seek(start)
123
+ remaining = length
124
+ chunk_size = 64 * 1024
125
+ while remaining > 0:
126
+ chunk = f.read(min(chunk_size, remaining))
127
+ if not chunk:
128
+ break
129
+ self.wfile.write(chunk)
130
+ remaining -= len(chunk)
131
+ return
132
+
133
+ self.send_response(200)
134
+ self.send_header("Content-Type", content_type)
135
+ self.send_header("Content-Length", file_size)
136
+ self.send_header("Accept-Ranges", "bytes")
137
+ self.end_headers()
138
+
139
+ with open(filepath, "rb") as f:
140
+ chunk_size = 64 * 1024
141
+ while True:
142
+ chunk = f.read(chunk_size)
143
+ if not chunk:
144
+ break
145
+ self.wfile.write(chunk)
146
+
147
+ def serve_static(self, filename, content_type):
148
+ """Serve static files (CSS, JS)."""
149
+ filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename)
150
+ if not os.path.isfile(filepath):
151
+ self.send_error(404, "Not Found")
152
+ return
153
+
154
+ with open(filepath, "r", encoding="utf-8") as f:
155
+ content = f.read()
156
+
157
+ self.send_response(200)
158
+ self.send_header("Content-Type", f"{content_type}; charset=utf-8")
159
+ self.send_header("Content-Length", len(content.encode()))
160
+ self.end_headers()
161
+ self.wfile.write(content.encode())
162
+
163
+ def get_index_html(self):
164
+ return '''<!DOCTYPE html>
165
+ <html lang="en">
166
+ <head>
167
+ <meta charset="UTF-8">
168
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
169
+ <title>Video demos of apps</title>
170
+ <link rel="stylesheet" href="/style.css">
171
+ </head>
172
+ <body>
173
+ <div class="container">
174
+ <header>
175
+ <h1>🎬 Video demos of apps</h1>
176
+ </header>
177
+
178
+ <main>
179
+ <div class="player-section">
180
+ <div class="video-container">
181
+ <video id="videoPlayer" controls>
182
+ <source src="" type="video/mp4">
183
+ Your browser does not support the video tag.
184
+ </video>
185
+ <div class="video-overlay" id="videoOverlay">
186
+ <span>Select a video from the playlist</span>
187
+ </div>
188
+ </div>
189
+
190
+ <div class="controls-bar">
191
+ <div class="now-playing" id="nowPlaying">
192
+ <span class="label">Now Playing:</span>
193
+ <span class="title" id="currentTitle">-</span>
194
+ </div>
195
+ <div class="playback-controls">
196
+ <button id="prevBtn" title="Previous">⏮</button>
197
+ <button id="playPauseBtn" title="Play/Pause">▶</button>
198
+ <button id="nextBtn" title="Next">⏭</button>
199
+ <button id="muteBtn" title="Mute">🔊</button>
200
+ <input type="range" id="volumeSlider" min="0" max="1" step="0.1" value="1" title="Volume">
201
+ <button id="fullscreenBtn" title="Fullscreen">⛶</button>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <aside class="playlist-section">
207
+ <h2>📋 Playlist</h2>
208
+ <div class="playlist" id="playlist">
209
+ <div class="loading">Loading playlist...</div>
210
+ </div>
211
+ </aside>
212
+ </main>
213
+
214
+ <footer>
215
+ <p>Read-only Video Player • Videos cannot be modified or deleted</p>
216
+ </footer>
217
+ </div>
218
+
219
+ <script src="/script.js"></script>
220
+ </body>
221
+ </html>'''
222
+
223
+ def log_message(self, format, *args):
224
+ print(f"[{self.log_date_time_string()}] {args[0]}")
225
+
226
+
227
+ def main():
228
+ os.chdir(os.path.dirname(os.path.abspath(__file__)))
229
+
230
+ videos = get_video_files()
231
+ print(f"\n{'='*50}")
232
+ print("🎬 Video Player Server")
233
+ print(f"{'='*50}")
234
+ print(f"📁 Playlist folder: {PLAYLIST_DIR}")
235
+ print(f"🎥 Videos found: {len(videos)}")
236
+ for v in videos:
237
+ print(f" • {v['name']} ({v['size_human']})")
238
+ print(f"\n🌐 Server starting at: http://localhost:{PORT}")
239
+ print(f"{'='*50}\n")
240
+
241
+ server = HTTPServer(("", PORT), VideoPlayerHandler)
242
+ try:
243
+ server.serve_forever()
244
+ except KeyboardInterrupt:
245
+ print("\n\nServer stopped.")
246
+ server.shutdown()
247
+
248
+
249
+ if __name__ == "__main__":
250
+ main()