Skip to content

Commit d13a3cf

Browse files
author
kevin.zhang
committed
refactor: Streaming MP4 files in the browser using video html element instead of waiting for the entire file to download before playing
1 parent 0550e43 commit d13a3cf

File tree

1 file changed

+35
-5
lines changed

1 file changed

+35
-5
lines changed

app/controllers/v1/video.py

+35-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import shutil
44

55
from fastapi import Request, Depends, Path, BackgroundTasks, UploadFile
6-
from fastapi.responses import FileResponse
6+
from fastapi.responses import FileResponse, StreamingResponse
77
from fastapi.params import File
88
from loguru import logger
99

@@ -137,7 +137,37 @@ def upload_bgm_file(request: Request, file: UploadFile = File(...)):
137137
async def stream_video(request: Request, file_path: str):
138138
tasks_dir = utils.task_dir()
139139
video_path = os.path.join(tasks_dir, file_path)
140-
if os.path.isfile(video_path):
141-
return FileResponse(video_path, media_type="video/mp4", filename=file_path)
142-
else:
143-
return {"message": "File not found."}
140+
range_header = request.headers.get('Range')
141+
video_size = os.path.getsize(video_path)
142+
start, end = 0, video_size - 1
143+
144+
length = video_size
145+
if range_header:
146+
range_ = range_header.split('bytes=')[1]
147+
start, end = [int(part) if part else None for part in range_.split('-')]
148+
if start is None:
149+
start = video_size - end
150+
end = video_size - 1
151+
if end is None:
152+
end = video_size - 1
153+
length = end - start + 1
154+
155+
def file_iterator(file_path, offset=0, bytes_to_read=None):
156+
with open(file_path, 'rb') as f:
157+
f.seek(offset, os.SEEK_SET)
158+
remaining = bytes_to_read or video_size
159+
while remaining > 0:
160+
bytes_to_read = min(4096, remaining)
161+
data = f.read(bytes_to_read)
162+
if not data:
163+
break
164+
remaining -= len(data)
165+
yield data
166+
167+
response = StreamingResponse(file_iterator(video_path, start, length), media_type='video/mp4')
168+
response.headers['Content-Range'] = f'bytes {start}-{end}/{video_size}'
169+
response.headers['Accept-Ranges'] = 'bytes'
170+
response.headers['Content-Length'] = str(length)
171+
response.status_code = 206 # Partial Content
172+
173+
return response

0 commit comments

Comments
 (0)