inspirations

This commit is contained in:
xds
2026-02-24 16:42:46 +03:00
parent bc9230a49b
commit ecc8d69039
16 changed files with 458 additions and 17 deletions

View File

@@ -42,8 +42,9 @@ async def get_asset(
if not asset:
raise HTTPException(status_code=404, detail="Asset not found")
headers = {
"Cache-Control": "public, max-age=31536000, immutable"
base_headers = {
"Cache-Control": "public, max-age=31536000, immutable",
"Accept-Ranges": "bytes"
}
# Thumbnail: маленький, можно грузить в RAM
@@ -51,17 +52,70 @@ async def get_asset(
if asset.minio_thumbnail_object_name and s3_adapter:
thumb_bytes = await s3_adapter.get_file(asset.minio_thumbnail_object_name)
if thumb_bytes:
return Response(content=thumb_bytes, media_type="image/jpeg", headers=headers)
return Response(content=thumb_bytes, media_type="image/jpeg", headers=base_headers)
# Fallback: thumbnail in DB
if asset.thumbnail:
return Response(content=asset.thumbnail, media_type="image/jpeg", headers=headers)
return Response(content=asset.thumbnail, media_type="image/jpeg", headers=base_headers)
# No thumbnail available — fall through to main content
# Main content: стримим из S3 без загрузки в RAM
if asset.minio_object_name and s3_adapter:
content_type = "image/png"
# if asset.content_type == AssetContentType.VIDEO:
# content_type = "video/mp4"
if asset.content_type == AssetContentType.VIDEO:
content_type = "video/mp4" # Or detect from extension if stored
elif asset.content_type == AssetContentType.IMAGE:
content_type = "image/png" # Default for images
# Better content type detection based on extension if possible, but for now this is okay
if asset.minio_object_name.endswith(".mp4"):
content_type = "video/mp4"
elif asset.minio_object_name.endswith(".mov"):
content_type = "video/quicktime"
elif asset.minio_object_name.endswith(".jpg") or asset.minio_object_name.endswith(".jpeg"):
content_type = "image/jpeg"
# Handle Range requests for video streaming
range_header = request.headers.get("range")
file_size = await s3_adapter.get_file_size(asset.minio_object_name)
if range_header and file_size:
try:
# Parse Range header: bytes=start-end
byte_range = range_header.replace("bytes=", "")
start_str, end_str = byte_range.split("-")
start = int(start_str)
end = int(end_str) if end_str else file_size - 1
# Validate range
if start >= file_size:
# 416 Range Not Satisfiable
return Response(status_code=416, headers={"Content-Range": f"bytes */{file_size}"})
chunk_size = end - start + 1
headers = base_headers.copy()
headers.update({
"Content-Range": f"bytes {start}-{end}/{file_size}",
"Content-Length": str(chunk_size),
})
# Pass the exact range string to S3
s3_range = f"bytes={start}-{end}"
return StreamingResponse(
s3_adapter.stream_file(asset.minio_object_name, range_header=s3_range),
status_code=206,
headers=headers,
media_type=content_type
)
except ValueError:
pass # Fallback to full content if range parsing fails
# Full content response
headers = base_headers.copy()
if file_size:
headers["Content-Length"] = str(file_size)
return StreamingResponse(
s3_adapter.stream_file(asset.minio_object_name),
media_type=content_type,
@@ -70,7 +124,7 @@ async def get_asset(
# Fallback: data stored in DB (legacy)
if asset.data:
return Response(content=asset.data, media_type="image/png", headers=headers)
return Response(content=asset.data, media_type="image/png", headers=base_headers)
raise HTTPException(status_code=404, detail="Asset data not found")