19 Episoden (4 Staffeln), 159 verifizierte Zitate mit Audio-Timestamps, 7 Themenkomplexe, interaktive Mindmap-Webapp. Nutzt podcast-mindmap als Tool (../podcast-mindmap). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""HTTP server with Range request support for audio streaming."""
|
|
|
|
import os
|
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
|
|
MIME_OVERRIDES = {'.m4a': 'audio/mp4', '.srt': 'text/plain'}
|
|
|
|
class RangeHandler(SimpleHTTPRequestHandler):
|
|
|
|
def do_GET(self):
|
|
path = self.translate_path(self.path)
|
|
|
|
if not os.path.isfile(path):
|
|
return super().do_GET()
|
|
|
|
range_header = self.headers.get('Range')
|
|
file_size = os.path.getsize(path)
|
|
ext = os.path.splitext(path)[1].lower()
|
|
ctype = MIME_OVERRIDES.get(ext) or self.guess_type(path)
|
|
|
|
if not range_header:
|
|
# Normal response but with Accept-Ranges
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', ctype)
|
|
self.send_header('Content-Length', str(file_size))
|
|
self.send_header('Accept-Ranges', 'bytes')
|
|
self.send_header('Access-Control-Allow-Origin', '*')
|
|
self.end_headers()
|
|
with open(path, 'rb') as f:
|
|
self.copyfile(f, self.wfile)
|
|
return
|
|
|
|
# Parse range
|
|
try:
|
|
range_spec = range_header.replace('bytes=', '')
|
|
parts = range_spec.split('-')
|
|
start = int(parts[0]) if parts[0] else 0
|
|
end = int(parts[1]) if parts[1] else file_size - 1
|
|
end = min(end, file_size - 1)
|
|
length = end - start + 1
|
|
except (ValueError, IndexError):
|
|
self.send_error(416)
|
|
return
|
|
|
|
self.send_response(206)
|
|
self.send_header('Content-Type', ctype)
|
|
self.send_header('Content-Range', f'bytes {start}-{end}/{file_size}')
|
|
self.send_header('Content-Length', str(length))
|
|
self.send_header('Accept-Ranges', 'bytes')
|
|
self.send_header('Access-Control-Allow-Origin', '*')
|
|
self.end_headers()
|
|
|
|
with open(path, 'rb') as f:
|
|
f.seek(start)
|
|
remaining = length
|
|
buf_size = 65536
|
|
while remaining > 0:
|
|
chunk = f.read(min(buf_size, remaining))
|
|
if not chunk:
|
|
break
|
|
self.wfile.write(chunk)
|
|
remaining -= len(chunk)
|
|
|
|
def end_headers(self):
|
|
# Don't double-add Accept-Ranges
|
|
super().end_headers()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
port = 9123
|
|
print(f"http://0.0.0.0:{port}")
|
|
HTTPServer(('0.0.0.0', port), RangeHandler).serve_forever()
|