From f35e4248f6e220694a27f384e37e13d1daeda385 Mon Sep 17 00:00:00 2001 From: ppwwyyxx Date: Thu, 25 Dec 2014 23:20:35 +0800 Subject: [PATCH] add pysox, 2x faster --- lib/render.py | 11 ++++------- lib/res.py | 27 ++++++++++++++++----------- lib/utils.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/lib/render.py b/lib/render.py index 5a4728d..8b858d9 100644 --- a/lib/render.py +++ b/lib/render.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: UTF-8 -*- # File: render.py -# Date: Thu Dec 25 10:18:20 2014 +0800 +# Date: Thu Dec 25 22:05:41 2014 +0800 # Author: Yuxin Wu import os @@ -20,7 +20,7 @@ except: css_compress = lambda x: x from .msg import * -from .utils import ensure_unicode, ProgressReporter, pmap +from .utils import ensure_unicode, ProgressReporter, pmap, timing from .smiley import SmileyProvider from .msgslice import MessageSlicerByTime, MessageSlicerBySize @@ -97,7 +97,6 @@ class HTMLRender(object): return fallback() bigimgpath = self.parser.imginfo.get(msg.msgSvrId) fnames = [k for k in [imgpath, bigimgpath] if k] - assert len(fnames) > 0, msg.msg_str() bigimg, smallimg = self.res.get_img(fnames) if not smallimg: logger.warn("No image thumbnail found for {}".format(imgpath)) @@ -130,8 +129,6 @@ class HTMLRender(object): def _render_partial_msgs(self, msgs): """ return single html""" - talker_name = msgs[0].talker - avatars = self.get_avatar_pair(talker_name) slicer = MessageSlicerByTime() slices = slicer.slice(msgs) @@ -151,12 +148,12 @@ class HTMLRender(object): extra_js=self.js_string, talker=msgs[0].talker_name, messages=u''.join(blocks), - avatars=avatars) - + avatars=self.avatars) def render_msgs(self, msgs): """ render msgs of one friend, return a list of html""" talker_name = msgs[0].talker + self.avatars = self.get_avatar_pair(talker_name) logger.info(u"Rendering {} messages of {}({})".format( len(msgs), self.parser.contacts[talker_name], talker_name)) diff --git a/lib/res.py b/lib/res.py index 283e2d1..62366c7 100644 --- a/lib/res.py +++ b/lib/res.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: UTF-8 -*- # File: res.py -# Date: Thu Dec 25 10:04:29 2014 +0800 +# Date: Thu Dec 25 23:19:32 2014 +0800 # Author: Yuxin Wu import glob @@ -14,9 +14,10 @@ import logging import imghdr logger = logging.getLogger(__name__) -import eyed3 +import pysox -from lib.avatar import AvatarReader +from .avatar import AvatarReader +from .utils import timing LIB_PATH = os.path.dirname(os.path.abspath(__file__)) INTERNAL_EMOJI_DIR = os.path.join(LIB_PATH, 'static', 'internal_emoji') @@ -53,21 +54,25 @@ class Resource(object): "Error interpreting the protocol, this is a bug!" self.speak_data[key] = full_path + @timing(total=True) def get_voice_mp3(self, imgpath): """ return base64 string, and voice duration""" amr_fpath = self.speak_data[imgpath] assert amr_fpath.endswith('.amr') mp3_file = os.path.join('/tmp', os.path.basename(amr_fpath)[:-4] + '.mp3') - # TODO is there a library to use? - ret = os.system('sox {} {}'.format(amr_fpath, mp3_file)) - if ret != 0: - logger.warn("Sox Failed!") - return "" - mp3_string = open(mp3_file, 'rb').read() - duration = eyed3.load(mp3_file).info.time_secs + + infile = pysox.CSoxStream(amr_fpath) + outfile = pysox.CSoxStream(mp3_file, 'w', infile.get_signal()) + chain = pysox.CEffectsChain(infile, outfile) + chain.flow_effects() + outfile.close() + + signal = infile.get_signal().get_signalinfo() + duration = signal['length'] * 1.0 / signal['rate'] + mp3_string = Resource.get_file_b64(mp3_file) os.unlink(mp3_file) - return base64.b64encode(mp3_string), duration + return mp3_string, duration @staticmethod def get_file_b64(fname): diff --git a/lib/utils.py b/lib/utils.py index 6fecd18..b06c5b3 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -1,11 +1,16 @@ #!/usr/bin/env python2 # -*- coding: UTF-8 -*- # File: utils.py -# Date: Thu Dec 25 10:11:21 2014 +0800 +# Date: Thu Dec 25 16:27:39 2014 +0800 # Author: Yuxin Wu import sys import time +import logging +logger = logging.getLogger(__name__) + +import time, functools +from collections import defaultdict def ensure_bin_str(s): if type(s) == str: @@ -111,3 +116,43 @@ def pimap(map_func, iterator, nr_proc=None, nr_precompute=None): def pmap(map_func, iterator, nr_proc=None, nr_precompute=None): '''parallel map''' return list(pimap(map_func, iterator, nr_proc, nr_precompute)) + + +class TotalTimer(object): + def __init__(self): + self.times = defaultdict(float) + + def add(self, name, t): + self.times[name] += t + + def reset(self): + self.times = defaultdict(float) + + def __del__(self): + for k, v in self.times.iteritems(): + logger.info("{} took {} seconds in total.".format(k, v)) + +_total_timer = TotalTimer() +class timing(object): + def __init__(self, total=False): + self.total = total + + def __call__(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + start_time = time.time() + ret = func(*args, **kwargs) + duration = time.time() - start_time + + if hasattr(func, '__name__'): + func_name = func.__name__ + else: + func_name = 'function in module {}'.format(func.__module__) + if self.total: + _total_timer.add(func_name, duration) + else: + logger.info('Duration for `{}\': {}'.format( + func_name, duration)) + return ret + return wrapper +