HomeDashboard/.venv/lib/python3.12/site-packages/nicegui/app/range_response.py

59 lines
2.1 KiB
Python
Raw Permalink Normal View History

2026-01-03 14:54:18 +01:00
import hashlib
import mimetypes
from collections.abc import Generator
from datetime import datetime, timezone
from pathlib import Path
from fastapi import Request
from fastapi.responses import Response, StreamingResponse
mimetypes.init()
def get_range_response(file: Path, request: Request, chunk_size: int) -> Response:
"""Get a Response for the given file, supporting range-requests, E-Tag and Last-Modified."""
file_size = file.stat().st_size
last_modified_time = datetime.fromtimestamp(file.stat().st_mtime, timezone.utc)
start = 0
end = file_size - 1
status_code = 200
e_tag = hashlib.md5((str(last_modified_time) + str(file_size)).encode()).hexdigest()
if_match_header = request.headers.get('If-None-Match')
if if_match_header and if_match_header == e_tag:
return Response(status_code=304) # Not Modified
headers = {
'E-Tag': e_tag,
'Last-Modified': last_modified_time.strftime(r'%a, %d %b %Y %H:%M:%S GMT'),
}
range_header = request.headers.get('range')
media_type = mimetypes.guess_type(str(file))[0] or 'application/octet-stream'
if range_header is not None:
byte1, byte2 = range_header.split('=')[1].split('-')
start = int(byte1)
if byte2:
end = int(byte2)
status_code = 206 # Partial Content
content_length = end - start + 1
headers.update({
'Content-Length': str(content_length),
'Content-Range': f'bytes {start}-{end}/{file_size}',
'Accept-Ranges': 'bytes',
})
def content_reader(file: Path, start: int, end: int) -> Generator[bytes, None, None]:
with open(file, 'rb') as data:
data.seek(start)
remaining_bytes = end - start + 1
while remaining_bytes > 0:
chunk = data.read(min(chunk_size, remaining_bytes))
if not chunk:
break
yield chunk
remaining_bytes -= len(chunk)
return StreamingResponse(
content_reader(file, start, end),
media_type=media_type,
headers=headers,
status_code=status_code,
)