Commit fa735a33 authored by Per Cederqvist's avatar Per Cederqvist

Create mp3 files inside the mp3-cache directory.

Rewrote path handling in using a PathHandler helper class.

Create the mp3 files atomically.
parent 975d5fe0
import os
import stat
import time
import io
import urllib.parse
import sys
......@@ -67,41 +68,25 @@ def process_request(environ, start_response):
if path == "" and environ.get("SCRIPT_NAME", "") != "":
raise MovedPermanently(environ["SCRIPT_NAME"] + "/")
parts = os_path_split_asunder(path)
if len(parts) > 0 and parts[0] == "/":
parts = parts[1:]
ph = PathHandler(path)
status, real_file = ph.real_stat_and_file()
realfile = os.path.join(mp3wavcfg.BASE, *parts)
try:
status = os.stat(realfile)
except (os.error, FileNotFoundError):
dirparts, basename, ops = locate_mp3(parts)
if ops is not None and len(ops) > 0 and ops[0] in MP3_OPS:
rel_base = os.path.join(*(dirparts + [basename]))
basefile = os.path.join(mp3wavcfg.BASE, rel_base)
try:
wavstat = os.stat(basefile + ".wav")
except os.error:
pass
else:
return MP3_OPS[ops[0]](environ, start_response,
rel_base, ops[1:])
if ph.extension() == ".mp3" and ph.real_stat(".wav") is not None:
op_func = MP3_OPS.get(ph.operation())
if op_func is not None:
return op_func(environ, start_response, ph)
if status is None:
raise NotFound()
if stat.S_ISDIR(status.st_mode):
return handle_directory(environ, start_response,
os.path.join(*parts))
elif stat.S_ISDIR(status.st_mode):
return handle_directory(environ, start_response, ph, real_file)
elif stat.S_ISREG(status.st_mode):
return handle_file(environ, start_response,
os.path.join(*parts))
raise NotFound()
return handle_file(environ, start_response, ph, real_file)
else:
raise NotFound()
def convert(environ, start_response, rel_base, ops):
if len(ops) > 0:
def convert(environ, start_response, ph):
if len(ph.op_args()) > 0:
raise NotFound()
if environ["REQUEST_METHOD"] == "GET":
......@@ -115,8 +100,28 @@ def convert(environ, start_response, rel_base, ops):
if environ["REQUEST_METHOD"] != "POST":
raise MethodNotAllowed('GET, POST')
c = multiprocessing.connection.Client(mp3wavcfg.socketpath, 'AF_UNIX')
c.send(("encode", rel_base))
try:
c = multiprocessing.connection.Client(mp3wavcfg.socketpath, 'AF_UNIX')
except FileNotFoundError:
try:
sys.stderr.write("Removing %s\n" % mp3wavcfg.socketpath)
os.remove(mp3wavcfg.socketpath)
sys.stderr.write("Removed %s\n" % mp3wavcfg.socketpath)
except FileNotFoundError:
sys.stderr.write("Failed to remove %s\n" % mp3wavcfg.socketpath)
pass
sys.stderr.write("Spawning wav2mp3d...\n")
pid = os.spawnl(os.P_NOWAIT,
os.path.join(os.path.dirname(__file__),
"wav2mp3d.py"),
"wav2mp3d.py")
sys.stderr.write("Spawned wav2mp3d (pid %d). Waiting one sec.\n" % pid)
time.sleep(1.0)
sys.stderr.write("Connecting to new wav2mp3d.\n")
c = multiprocessing.connection.Client(mp3wavcfg.socketpath, 'AF_UNIX')
sys.stderr.write("Connected to new wav2mp3d.\n")
c.send(("encode", ph.rel_base()))
while True:
msg = c.recv()
if msg[0] != "progress":
......@@ -128,14 +133,75 @@ MP3_OPS = {
'convert': convert,
}
def locate_mp3(parts):
pos = len(parts) - 1
while pos >= 0:
fn, ext = os.path.splitext(parts[pos])
if ext == ".mp3":
return parts[:pos], fn, parts[pos+1:]
pos -= 1
return None, None, None
class PathHandler(object):
def __init__(self, path):
self.__parts = os_path_split_asunder(path)
if len(self.__parts) > 0 and self.__parts[0] == "/":
self.__parts = self.__parts[1:]
(self.__dirparts, self.__basename,
self.__extension, self.__ops) = self.__split_path(self.__parts)
def __split_path(self, parts):
pos = len(parts) - 1
while pos >= 0:
fn, ext = os.path.splitext(parts[pos])
if ext in [".mp3", ".wav"]:
return parts[:pos], fn, ext, parts[pos+1:]
pos -= 1
return parts[:-1], parts[-1], "", None
def path_parts(self, ext_override=None):
fn = self.__basename + self.extension(ext_override)
return self.__dirparts + [fn]
def real_stat_and_file(self, ext_override=None):
path_parts = self.path_parts(ext_override)
real_file = os.path.join(mp3wavcfg.BASE, *path_parts)
try:
return os.stat(real_file), real_file
except FileNotFoundError:
pass
if self.extension(ext_override) == ".mp3":
mp3file = os.path.join(mp3wavcfg.BASE, "mp3-cache", *path_parts)
try:
return os.stat(mp3file), mp3file
except FileNotFoundError:
pass
return None, None
def real_stat(self, ext_override=None):
return self.real_stat_and_file(ext_override)[0]
def real_file(self, ext_override=None):
return self.real_stat_and_file(ext_override)[1]
def extension(self, ext_override=None):
if ext_override is None:
return self.__extension
else:
return ext_override
def operation(self):
if self.__ops is None:
return None
if len(self.__ops) <1:
return None
return self.__ops[0]
def op_args(self):
assert self.__ops is not None
assert len(self.__ops) > 0
return self.__ops[1:]
def rel_base(self):
return os.path.join(*(self.__dirparts + [self.__basename]))
def descendant(self, filename):
return PathHandler(os.path.join(self.rel_base() + self.__extension,
filename))
def sizeof_fmt(num):
for x in ['bytes','KB','MB','GB']:
......@@ -144,13 +210,18 @@ def sizeof_fmt(num):
num /= 1024.0
return "%.1f %s" % (num, 'TB')
def handle_directory(environ, start_response, rel_path):
def handle_directory(environ, start_response, ph, real_dir):
wavs = {}
mp3s = {}
dirs = set()
for fn in os.listdir(os.path.join(mp3wavcfg.BASE, rel_path)):
realfile = os.path.join(mp3wavcfg.BASE, rel_path, fn)
for fn in os.listdir(real_dir):
# Don't expose the cache directory directly.
if fn == "mp3-cache":
continue
realfile = os.path.join(real_dir, fn)
try:
status = os.stat(realfile)
except os.error:
......@@ -170,14 +241,14 @@ def handle_directory(environ, start_response, rel_path):
res.write("<head>")
res.write("</head>")
res.write("<body>")
parts = rel_path.split("/")
parts = ph.path_parts()
if len(parts) > 1:
res.write("\n<a href=\"%s\">(root)</a>" % ("../" * (len(parts) - 1)))
for n, part in enumerate(parts[:-1]):
res.write("\n / <a href=\"%s\">%s</a>" % (
"../" * (len(parts) - n - 2), part))
res.write("<ul>")
if rel_path != "":
if len(parts) > 1:
res.write("<li><a href=\"..\">(up one level)</a></li>\n")
for d in sorted(dirs):
res.write("<li>")
......@@ -189,8 +260,13 @@ def handle_directory(environ, start_response, rel_path):
quote(w), w))
res.write(" (" + sizeof_fmt(wavs[w].st_size) + ")")
if w in mp3s:
mp3stat = mp3s[w]
else:
mp3stat = ph.descendant(w + ".mp3").real_stat()
if mp3stat is not None:
res.write(" <a href=\"%s.mp3\">.mp3</a>" % quote(w))
res.write(" (" + sizeof_fmt(mp3s[w].st_size) + ")")
res.write(" (" + sizeof_fmt(mp3stat.st_size) + ")")
else:
res.write("<a class=\"convert\" href=\"%s.mp3/convert\">\n" % (
quote(w)))
......@@ -208,15 +284,15 @@ def handle_directory(environ, start_response, rel_path):
("Content-Length", str(len(page)))])
return [page]
def handle_file(environ, start_response, rel_path):
root, ext = os.path.splitext(rel_path)
def handle_file(environ, start_response, ph, real_file):
content_type = {".wav": "audio/vnd.wave",
".mp3": "audio/mpeg"}.get(ext)
".mp3": "audio/mpeg"}.get(ph.extension())
if content_type is None:
start_response("403 Forbidden", [])
return []
file = open(os.path.join(mp3wavcfg.BASE, rel_path), "r")
file = open(real_file, "r")
start_response("200 OK", [("Content-Type", content_type)])
if 'wsgi.file_wrapper' in environ:
return environ['wsgi.file_wrapper'](file, 1024 * 1024)
......
......@@ -3,6 +3,7 @@
import re
import os
import sys
import errno
import subprocess
import multiprocessing
import multiprocessing.connection
......@@ -92,8 +93,17 @@ def start_encoding():
def encode(fn):
c = multiprocessing.connection.Client(mp3wavcfg.socketpath, 'AF_UNIX')
result_file = os.path.join(mp3wavcfg.BASE, "mp3-cache", fn + ".mp3")
result_dir = os.path.dirname(result_file)
try:
os.makedirs(result_dir)
except os.error as e:
if e.errno != errno.EEXIST:
raise
cmd = ["lame", "--preset", "standard", "--nohist",
os.path.join(mp3wavcfg.BASE, fn + ".wav")]
os.path.join(mp3wavcfg.BASE, fn + ".wav"),
result_file + ".tmp"]
lame = subprocess.Popen(args=cmd, bufsize=0, stdin=subprocess.DEVNULL,
stderr=subprocess.PIPE)
percent_match = re.compile(".*\\(([0-9]+)%\\)")
......@@ -106,6 +116,13 @@ def encode(fn):
if m is not None:
c.send(("progress", m.group(1)))
x = lame.wait()
if x == 0:
os.rename(result_file + ".tmp", result_file)
else:
try:
os.remove(result_file + ".tmp")
except OSError:
pass
sys.exit(x)
if __name__ == '__main__':
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment