"""Local server: serves clip-browser.html + handles POST /build-reel to run ffmpeg.""" import http.server, json, os, subprocess, urllib.parse BASE_DIR = os.path.dirname(os.path.dirname(__file__)) VID_DIR = os.path.join(BASE_DIR, "assets", "videos", "hero", "clips") REEL_OUT = os.path.join(BASE_DIR, "assets", "videos", "hero", "hero-reel.mp4") TOOLS_DIR = os.path.dirname(__file__) class Handler(http.server.BaseHTTPRequestHandler): def log_message(self, fmt, *args): pass def do_GET(self): path = self.path.split('?')[0] if path in ('/', '/tools/clip-browser.html'): self._serve_file(os.path.join(TOOLS_DIR, 'clip-browser.html'), 'text/html') elif path.startswith('/assets/videos/'): rel = path.lstrip('/') fpath = os.path.join(BASE_DIR, rel) if os.path.exists(fpath): self._serve_file(fpath, 'video/mp4') else: self._404() else: self._404() def do_POST(self): if self.path == '/tools/build-reel-api.py': length = int(self.headers.get('Content-Length', 0)) body = json.loads(self.rfile.read(length)) clips = body.get('clips', []) if not clips: self._json({'message': 'No clips provided.'}, 400) return concat_file = os.path.join(VID_DIR, 'concat-browser.txt') missing = [] with open(concat_file, 'w') as f: for name in clips: p = os.path.join(VID_DIR, f'{name}.mp4') if not os.path.exists(p): missing.append(name) else: f.write(f"file '{p}'\n") if missing: self._json({'message': f'Missing clips: {missing}'}, 400) return result = subprocess.run( ['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', concat_file, '-c:v', 'libx264', '-crf', '22', '-preset', 'fast', '-movflags', '+faststart', REEL_OUT], capture_output=True, text=True ) if result.returncode == 0: size = os.path.getsize(REEL_OUT) // 1024 self._json({'message': f'Reel built: hero-reel.mp4 ({size}KB). Now rebuild Docker: docker compose up -d --build'}) else: self._json({'message': f'ffmpeg error: {result.stderr[-300:]}'}, 500) else: self._404() def _serve_file(self, path, ctype): with open(path, 'rb') as f: data = f.read() self.send_response(200) self.send_header('Content-Type', ctype) self.send_header('Content-Length', len(data)) self.end_headers() self.wfile.write(data) def _json(self, obj, code=200): data = json.dumps(obj).encode() self.send_response(code) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', len(data)) self.end_headers() self.wfile.write(data) def _404(self): self.send_response(404) self.end_headers() if __name__ == '__main__': port = 8088 print(f'Clip browser running at http://localhost:{port}/') http.server.HTTPServer(('', port), Handler).serve_forever()