<?xml version="1.0" encoding="UTF-8"?> 
<rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
        xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
        >
<channel>
  <title>asgaard</title>
  <description>Flask</description>
  <link>https://blog.asgaard.co.uk/t/flask</link>
  <lastBuildDate>Sun, 19 Apr 26 10:00:48 +0000</lastBuildDate>
  <language>en</language>
  <count>1</count>
  <offset>0</offset>
      <item>
    <title>HTTP 206 (Partial Content) For Flask/Python</title>
    <link>https://blog.asgaard.co.uk/2012/08/03/http-206-partial-content-for-flask-python</link>
    <pubDate>Fri, 03 Aug 12 11:32:53 +0000</pubDate>
    <guid>https://blog.asgaard.co.uk/2012/08/03/http-206-partial-content-for-flask-python</guid>
    <description><![CDATA[
<p>
Serving media files with HTTP 200 seems to make browsers act a bit strangely. Chrome refuses to fire onended events, and neither Chrome nor Firefox seems happy seeking through them. I am Not entirely sure for the reasons for this; I guess that they try to play the media before receiving all of it and can&#039;t guarantee they&#039;ll have downloaded the right chunk for seeking, while having no way of requesting it specifically. Who knows.
<p>
Here&#039;s a simple replacement to Flask&#039;s send_file that supports 206 partial content via byte ranges (and a function that adds the appropriate headers to all responses). It doesn&#039;t fully wrap send_file, if you care about all the other arguments and mirroring the exact behaviour, it needs a bit more work, but should suffice for 99% of uses.
<p>
[update 31/8: sorry there was a bug in this which caused strange things with certain Chrome versions, it&#039;s now been fixed]
<p>
<pre>import mimetypes
import os
import re

from flask import request, send_file, Response

@app.af</pre>[...]]]></description>
    <content:encoded><![CDATA[
<p>
Serving media files with HTTP 200 seems to make browsers act a bit strangely. Chrome refuses to fire onended events, and neither Chrome nor Firefox seems happy seeking through them. I am Not entirely sure for the reasons for this; I guess that they try to play the media before receiving all of it and can&#039;t guarantee they&#039;ll have downloaded the right chunk for seeking, while having no way of requesting it specifically. Who knows.
<p>
Here&#039;s a simple replacement to Flask&#039;s send_file that supports 206 partial content via byte ranges (and a function that adds the appropriate headers to all responses). It doesn&#039;t fully wrap send_file, if you care about all the other arguments and mirroring the exact behaviour, it needs a bit more work, but should suffice for 99% of uses.
<p>
[update 31/8: sorry there was a bug in this which caused strange things with certain Chrome versions, it&#039;s now been fixed]
<p>
<pre>import mimetypes
import os
import re

from flask import request, send_file, Response

@app.after_request
def after_request(response):
    response.headers.add('Accept-Ranges', 'bytes')
    return response


def send_file_partial(path):
    &quot;&quot;&quot; 
        Simple wrapper around send_file which handles HTTP 206 Partial Content
        (byte ranges)
        TODO: handle all send_file args, mirror send_file's error handling
        (if it has any)
    &quot;&quot;&quot;
    range_header = request.headers.get('Range', None)
    if not range_header: return send_file(path)
    
    size = os.path.getsize(path)    
    byte1, byte2 = 0, None
    
    m = re.search('(\d+)-(\d*)', range_header)
    g = m.groups()
    
    if g[0]: byte1 = int(g[0])
    if g[1]: byte2 = int(g[1])

    length = size - byte1
    if byte2 is not None:
        length = byte2 - byte1
    
    data = None
    with open(path, 'rb') as f:
        f.seek(byte1)
        data = f.read(length)

    rv = Response(data, 
        206,
        mimetype=mimetypes.guess_type(path)[0], 
        direct_passthrough=True)
    rv.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(byte1, byte1 + length - 1, size))

    return rv</pre>
<br>
    ]]></content:encoded>
  </item>
  </channel>
</rss>