99 lines
3.2 KiB
Python
99 lines
3.2 KiB
Python
#!/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()
|