Created
November 15, 2020 20:37
-
-
Save tombulled/712fd8e19ed0618c5f9f7d5f5f543782 to your computer and use it in GitHub Desktop.
Stream a file, in this case an mp4 video, supporting range-requests using starlette
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from starlette.applications import Starlette | |
from starlette.responses import StreamingResponse | |
from starlette.requests import Request | |
from starlette.routing import Route | |
from pathlib import Path | |
from typing import IO, Generator | |
""" | |
Stream a file, in this case an mp4 video, supporting range-requests using starlette | |
Reference: https://stackoverflow.com/questions/33208849/python-django-streaming-video-mp4-file-using-httpresponse | |
""" | |
VIDEO_PATH = '/tmp/video.mp4' | |
def ranged \ | |
( | |
file: IO[bytes], | |
start: int = 0, | |
end: int = None, | |
block_size: int = 8192, | |
) -> Generator[bytes, None, None]: | |
consumed = 0 | |
file.seek(start) | |
while True: | |
data_length = min(block_size, end - start - consumed) if end else block_size | |
if data_length <= 0: | |
break | |
data = file.read(data_length) | |
if not data: | |
break | |
consumed += data_length | |
yield data | |
if hasattr(file, 'close'): | |
file.close() | |
def homepage(request: Request) -> StreamingResponse: | |
path = Path(VIDEO_PATH) | |
file = path.open('rb') | |
file_size = path.stat().st_size | |
content_range = request.headers.get('range') | |
content_length = file_size | |
status_code = 200 | |
headers = {} | |
if content_range is not None: | |
content_range = content_range.strip().lower() | |
content_ranges = content_range.split('=')[-1] | |
range_start, range_end, *_ = map(str.strip, (content_ranges + '-').split('-')) | |
range_start = max(0, int(range_start)) if range_start else 0 | |
range_end = min(file_size - 1, int(range_end)) if range_end else file_size - 1 | |
content_length = (range_end - range_start) + 1 | |
file = ranged(file, start = range_start, end = range_end + 1) | |
status_code = 206 | |
headers['Content-Range'] = f'bytes {range_start}-{range_end}/{file_size}' | |
response = StreamingResponse \ | |
( | |
file, | |
media_type = 'video/mp4', | |
status_code = status_code, | |
) | |
response.headers.update \ | |
({ | |
'Accept-Ranges': 'bytes', | |
'Content-Length': str(content_length), | |
**headers, | |
}) | |
return response | |
app = Starlette \ | |
( | |
routes = \ | |
[ | |
Route('/', homepage), | |
], | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment