librelist archives

« back to archive

response to a range request

response to a range request

From:
Christoph Weyer
Date:
2011-10-05 @ 11:27
Hello everyone,
I'm currently developing an online musicplayer for private use ( in fact 
it's an simple webinterface, which allows friends of mine to play and 
download self produced music files from my private server in a user 
friendly way ). The application is build as flask app using jplayer and 
some simple javascript controls on the client-side.
Currently I'm extending the app by adding a sqlalchemy database for the 
files, but there's still one major problem left: Jplayer doesn't display 
the duration time correctly ( in fact, the duration time increases until 
the file is fully transmitted, which is very annoying ). After a little 
bit of investigation I found out, that there is a byte range request, 
which seems to be unsupported. I can see the range request being 
transmitted in the chrome debugger, but there is no answer from my server.
Now I'm not shure how to answer the request. I think I have to filter 
for the range request in the view function ( perhaps by look for the 
request.headers["Range"] ) and then raise a special response, but as I 
found nothing about this in the doc I have no idea how to alter the 
response object.
I found out that Werkzeug seems to support manipulation of the range 
header: 

http://werkzeug.pocoo.org/docs/datastructures/?highlight=range#werkzeug.datastructures.Range

... but how do I deal with it?

Any help appreciated
Christoph

Re: [flask] response to a range request

From:
Christoph Weyer
Date:
2011-10-23 @ 14:53
As I don't want to leave the question unanswered in the mailinglist, 
I'll post my own solution. Perhaps it will help someone, who has a 
similiar problem.
In fact I found a php function in the jplayer doc 
(https://groups.google.com/forum/#!msg/jplayer/nSM2UmnSKKA/bC-l3k0pCPMJ) 
to solve my problem, so I rewrote it in python.


from flask import Response, request
from werkzeug.datastructures import Headers
from time import time
from re import findall

def send_file( musicFile , cachetimout, stream=True):
	headers = Headers()
	headers.add('Content-Disposition', 
'attachment',filename=musicFile.filename)
	headers.add('Content-Transfer-Encoding','binary')

	status = 200
	size = getsize(musicFile.path)
	begin=0;
	end=size-1;

	if request.headers.has_key("Range") and rangerequest:
		status = 206
		headers.add('Accept-Ranges','bytes')
		ranges=findall(r"\d+", request.headers["Range"])
		begin = int( ranges[0] )
		if len(ranges)>1:
			end=int( ranges[1] )
		headers.add('Content-Range','bytes %s-%s/%s' % 
(str(begin),str(end),str(end-begin)) )
	
	headers.add('Content-Length',str((end-begin)+1))
	
	#Add mimetype	
	mimetypes = {u"mp3":"audio/mpeg",u"ogg":"audio/ogg"}
	if stream==True:
		mimetype = mimetypes[musicFile.filetype]
	else:
		mimetype = "application/octet-stream"
	
	response = Response( file(musicFile.path), status=status, 
mimetype=mimetype, headers=headers, direct_passthrough=True)
	
	#enable browser file caching with etags
	response.cache_control.public = True
	response.cache_control.max_age = int(cachetimout)
	response.last_modified = int(musicFile.changetime)
	response.expires=int( time() + int(cachetimout) )
	response.set_etag('%s%s' % ( musicFile.id,musicFile.changetime ))
	response.make_conditional(request)
	
	return response

Re: [flask] response to a range request

From:
Adam Patterson
Date:
2011-10-24 @ 01:40
Can I ask why you are using send_file instead of just letting your
front end server deliver it? You can keep all the file information in
the database (url, title, etc) but don't have flask deliver the file
itself. Let me know if I'm missing something.



On Sun, Oct 23, 2011 at 9:53 PM, Christoph Weyer <crazzyman2526@gmx.de> wrote:
> As I don't want to leave the question unanswered in the mailinglist,
> I'll post my own solution. Perhaps it will help someone, who has a
> similiar problem.
> In fact I found a php function in the jplayer doc
> (https://groups.google.com/forum/#!msg/jplayer/nSM2UmnSKKA/bC-l3k0pCPMJ)
> to solve my problem, so I rewrote it in python.
>
>
> from flask import Response, request
> from werkzeug.datastructures import Headers
> from time import time
> from re import findall
>
> def send_file( musicFile , cachetimout, stream=True):
>        headers = Headers()
>        headers.add('Content-Disposition',
> 'attachment',filename=musicFile.filename)
>        headers.add('Content-Transfer-Encoding','binary')
>
>        status = 200
>        size = getsize(musicFile.path)
>        begin=0;
>        end=size-1;
>
>        if request.headers.has_key("Range") and rangerequest:
>                status = 206
>                headers.add('Accept-Ranges','bytes')
>                ranges=findall(r"\d+", request.headers["Range"])
>                begin = int( ranges[0] )
>                if len(ranges)>1:
>                        end=int( ranges[1] )
>                headers.add('Content-Range','bytes %s-%s/%s' %
> (str(begin),str(end),str(end-begin)) )
>
>        headers.add('Content-Length',str((end-begin)+1))
>
>        #Add mimetype
>        mimetypes = {u"mp3":"audio/mpeg",u"ogg":"audio/ogg"}
>        if stream==True:
>                mimetype = mimetypes[musicFile.filetype]
>        else:
>                mimetype = "application/octet-stream"
>
>        response = Response( file(musicFile.path), status=status,
> mimetype=mimetype, headers=headers, direct_passthrough=True)
>
>        #enable browser file caching with etags
>        response.cache_control.public = True
>        response.cache_control.max_age = int(cachetimout)
>        response.last_modified = int(musicFile.changetime)
>        response.expires=int( time() + int(cachetimout) )
>        response.set_etag('%s%s' % ( musicFile.id,musicFile.changetime ))
>        response.make_conditional(request)
>
>        return response
>

Re: [flask] response to a range request

From:
Christoph Weyer
Date:
2011-10-25 @ 19:00
I think you aren't missing something... I thought abour serving the 
files directly by the server, but before using this feature I wanted the 
app working by itself... and I didn't want to give up early as I thought 
that this has to work... so it was more a learning exercise if you want 
so ;)

On 10/24/2011 03:40 AM, Adam Patterson wrote:
> Can I ask why you are using send_file instead of just letting your
> front end server deliver it? You can keep all the file information in
> the database (url, title, etc) but don't have flask deliver the file
> itself. Let me know if I'm missing something.