Как обслуживать mp3-файл со встроенным http-сервером на python - PullRequest
2 голосов
/ 14 апреля 2011

В настоящее время я пытаюсь обслуживать MP3-файлы с использованием Python. Проблема в том, что я могу воспроизвести MP3 только один раз. После этого средства управления мультимедиа перестают отвечать, и мне нужно полностью перезагрузить страницу, чтобы снова прослушать MP3. (проверено в Chrome)

Проблема : запуск приведенного ниже сценария и ввод http://127.0.0.1/test.mp3 в моем браузере вернет файлы MP3, которые можно будет воспроизвести только в случае обновления страницы

Примечания:

  • Сохранение страницы в формате HTML и загрузка ее напрямую с Chrome (без сервера Python) может привести к исчезновению проблемы.

  • Обслуживание файла с помощью Apache решит проблему, но это излишне: Я хочу сделать скрипт очень простым в использовании и не требовать установки Apache .

Вот код, который я использую:

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()

Ответы [ 2 ]

2 голосов
/ 18 апреля 2011

BaseServer является однопоточным, вы должны использовать ForkingMixIn или ThreadingMixIn для поддержки нескольких соединений.

Например, заменить строку:

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

на

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

server = ThreadedHTTPServer(('', 80), MyHandler)
2 голосов
/ 14 апреля 2011

РЕДАКТИРОВАТЬ: Я написал большую часть этого, прежде чем я понял, что Mapadd только планирует использовать это в лаборатории. WSGI, вероятно, не требуется для его варианта использования.

Если вы хотите запустить его как wsgi-приложение (которое я бы рекомендовал использовать вместо ванильного CGI для любой реальной масштабируемости), вы можете использовать скрипт, который я включил ниже.

Я позволил себе изменить ваш источник ... это работает с предположениями, изложенными выше ... кстати, вам следует потратить некоторое время на проверку того, что ваш HTML-код достаточно совместим ... это поможет вам улучшить кросс-браузер совместимость ... оригинал не имел тегов <head> или <body> ... мой (ниже) является строго прототипом html и может быть улучшен.

Чтобы запустить это, вы просто запускаете исполняемый файл python в своей оболочке и переходите на ipaddress машины на 8080. Если вы делали это для рабочего сайта, мы должны использовать lighttpd или apache для обслуживания файлов, но так как это просто для лабораторного использования, встроенный эталонный сервер wsgi должен быть в порядке. Замените строку WSGIServer внизу файла, если вы хотите запустить в apache или lighttpd.

Сохранить как 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)

Запустите из оболочки bash: twistd -n web --port 8080 --wsgi mp3.mp3_app из каталога, в который вы сохранили mp3.py (или просто поместите mp3.py где-нибудь в $PYTHONPATH).

Теперь зайдите на внешний ip (т. Е. http://some.ip.local:8080/), и он будет обслуживать mp3 напрямую.

Я попытался запустить ваше оригинальное приложение, как оно было опубликовано, и не смог получить его в качестве источника mp3, оно рявкнуло на меня с ошибкой в ​​Linux ...

...