开发者

How to serve an mp3 file with built-in python http server

开发者 https://www.devze.com 2023-02-26 04:35 出处:网络
I am currently trying to serve MP3 Files using Python. The problem is that I can only play the MP3 once. Afterwards media controls stop responding and I need to reload entirely the page to be able to

I am currently trying to serve MP3 Files using Python. The problem is that I can only play the MP3 once. Afterwards media controls stop responding and I need to reload entirely the page to be able to listen again to the MP3. (tested in Chrome)

Problem: running the script below, and entering http://127.0.0.1/test.mp3 on my browser will return an MP3 files which can be replayed only if I refresh the page

Notes:

    开发者_开发问答
  • Saving the page as HTML and loading it directly with Chrome (without Python server) would make the problem disappear.

  • Serving the file with Apache would solve the problem, but this is overkilled: I want to make the script very easy to use and not require installing Apache.

Here is the code I use:

import string
import os
import urllib
import socket

# Setup web server import string,cgi,time
import string,cgi,time
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import hashlib

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            # serve mp3 files
            if self.path.endswith(".mp3"):
                print curdir + sep + self.path
                f = open(curdir + sep + self.path, 'rb')
                st = os.fstat( f.fileno() )
                length = st.st_size
                data = f.read()
                md5 = hashlib.md5()
                md5.update(data)
                md5_key = self.headers.getheader('If-None-Match')
                if md5_key:
                  if md5_key[1:-1] == md5.hexdigest():
                    self.send_response(304)
                    self.send_header('ETag', '"{0}"'.format(md5.hexdigest()))
                    self.send_header('Keep-Alive', 'timeout=5, max=100')
                    self.end_headers()
                    return

                self.send_response(200)
                self.send_header('Content-type',    'audio/mpeg')
                self.send_header('Content-Length', length )
                self.send_header('ETag', '"{0}"'.format(md5.hexdigest()))
                self.send_header('Accept-Ranges', 'bytes')
                self.send_header('Last-Modified', time.strftime("%a %d %b %Y %H:%M:%S GMT",time.localtime(os.path.getmtime('test.mp3'))))
                self.end_headers()
                self.wfile.write(data)
                f.close()
            return
        except IOError:
           self.send_error(404,'File Not Found: %s' % self.path)

from SocketServer import ThreadingMixIn
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

if __name__ == "__main__":
    try:
       server = ThreadedHTTPServer(('', 80), MyHandler)
       print 'started httpserver...'
       server.serve_forever()
    except KeyboardInterrupt:
       print '^C received, shutting down server'
       server.socket.close()


BaseServer is single-threaded, you should use either ForkingMixIn or ThreadingMixIn to support multiple connections.

For example replace line:

server = HTTPServer(('', 80), MyHandler)

with

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

server = ThreadedHTTPServer(('', 80), MyHandler)


EDIT: I wrote much of this before I realized Mapadd only planned to use this in a lab. WSGI probably is not required for his use case.

If you are willing to run this as a wsgi app (which I would recommend over vanilla CGI for any real scalability), you can use the script I have included below.

I took the liberty of modifying your source... this works with the assumptions above.. btw, you should spend some time checking that your html is reasonably compliant... this will help ensure that you get better cross-browser compatibility... the original didn't have <head> or <body> tags... mine (below) is strictly prototype html, and could be improved.

To run this, you just run the python executable in your shell and surf to the ipaddress of the machine on 8080. If you were doing this for a production website, we should be using lighttpd or apache for serving files, but since this is simply for lab use, the embedded wsgi reference server should be fine. Substitute the WSGIServer line at the bottom of the file if you want to run in apache or lighttpd.

Save as mp3.py

from webob import Request
import re
import os
import sys

####
#### Run with:
#### twistd -n web --port 8080 --wsgi mp3.mp3_app

_MP3DIV = """<div id="musicHere"></div>"""

_MP3EMBED = """<embed src="mp3/" loop="true" autoplay="false" width="145" height="60"></embed>"""

_HTML = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head></head><body> Hello %s %s</body></html> ''' % (_MP3DIV, _MP3EMBED)

def mp3_html(environ, start_response):
    """This function will be mounted on "/" and refer the browser to the mp3 serving URL."""

    start_response('200 OK', [('Content-Type', 'text/html')])
    return [_HTML]

def mp3_serve(environ, start_response):
    """Serve the MP3, one chunk at a time with a generator"""
    file_path = "/file/path/to/test.mp3"
    mimetype = "application/x-mplayer2"
    size = os.path.getsize(file_path)
    headers = [
        ("Content-type", mimetype),
        ("Content-length", str(size)),
    ]
    start_response("200 OK", headers)
    return send_file(file_path, size)

def send_file(file_path, size):
    BLOCK_SIZE = 4096
    fh = open(file_path, 'r')
    while True:
        block = fh.read(BLOCK_SIZE)
        if not block:
            fh.close()
            break
        yield block

def _not_found(environ,start_response):
    """Called if no URL matches."""
    start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
    return ['Not Found']

def mp3_app(environ,start_response):
    """
    The main WSGI application. Dispatch the current request to
    the functions andd store the regular expression
    captures in the WSGI environment as  `mp3app.url_args` so that
    the functions from above can access the url placeholders.

    If nothing matches call the `not_found` function.
    """
    # map urls to functions
    urls = [
        (r'^$', mp3_html),
        (r'mp3/?$', mp3_serve),
    ]
    path = environ.get('PATH_INFO', '').lstrip('/')
    for regex, callback in urls:
        match = re.search(regex, path)
        if match is not None:
            # assign http environment variables...
            environ['mp3app.url_args'] = match.groups()
            return callback(environ, start_response)
    return _not_found(environ, start_response)

Run from the bash shell with: twistd -n web --port 8080 --wsgi mp3.mp3_app from the directory where you saved mp3.py (or just put mp3.py somewhere in $PYTHONPATH).

Now surf to the external ip (i.e. http://some.ip.local:8080/) and it will serve the mp3 directly.

I tried running your original app as it was posted, and could not get it to source the mp3, it barked at me with an error in linux...

0

精彩评论

暂无评论...
验证码 换一张
取 消