#!/usr/bin/env python3 from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer import os import argparse import sys def parse_args(): p = argparse.ArgumentParser(description="Semplice server HTTP per HLS con CORS") p.add_argument("root", nargs="?", default="hls_ts_test", help="Directory da servire (default: hls_ts_test)") p.add_argument("-p", "--port", type=int, default=9999, help="Porta su cui ascoltare (default: 9999)") return p.parse_args() class HlsHandler(SimpleHTTPRequestHandler): extensions_map = { **SimpleHTTPRequestHandler.extensions_map, ".m3u8": "application/vnd.apple.mpegurl", ".ts": "video/mp2t", } # 🔥 Patch: disabilita 304 e gestisce Range correttamente def send_head(self): path = self.translate_path(self.path) if os.path.isdir(path): return self.list_directory(path) ctype = self.guess_type(path) try: f = open(path, "rb") except OSError: self.send_error(404, "File not found") return None fs = os.fstat(f.fileno()) size = fs.st_size # Gestione Range (necessaria per Chromecast) range_header = self.headers.get("Range") if range_header: try: start = int(range_header.replace("bytes=", "").split("-")[0]) except: start = 0 self.send_response(206) self.send_header("Content-type", ctype) self.send_header("Content-Range", f"bytes {start}-{size-1}/{size}") self.send_header("Content-Length", str(size - start)) self.end_headers() f.seek(start) return f # Risposta normale 200 OK (mai 304) self.send_response(200) self.send_header("Content-type", ctype) self.send_header("Content-Length", str(size)) self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) self.end_headers() return f def end_headers(self): # CORS e cache control self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Expose-Headers", "Content-Length, Content-Type, Accept-Ranges") self.send_header("Cache-Control", "no-cache") super().end_headers() def do_OPTIONS(self): self.send_response(204) self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Range") self.end_headers() def main(): args = parse_args() root = os.path.abspath(args.root) port = args.port if not os.path.isdir(root): print(f"Directory non trovata: {root}", file=sys.stderr) sys.exit(1) os.chdir(root) server = ThreadingHTTPServer(("0.0.0.0", port), HlsHandler) print(f"Serving {root} on http://0.0.0.0:{port}", flush=True) try: server.serve_forever() except KeyboardInterrupt: print("\nServer interrotto dall'utente") finally: server.server_close() if __name__ == "__main__": main()