Skip to content

Instantly share code, notes, and snippets.

@tombulled
Created November 15, 2020 20:37
Show Gist options
  • Save tombulled/712fd8e19ed0618c5f9f7d5f5f543782 to your computer and use it in GitHub Desktop.
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
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