def gen_used_smiley_css(self): ret = HEAD for path in self.used_smileys: fname = os.path.join(STATIC_PATH, path) b64 = get_file_b64(fname) ret = ret + TEMPLATE.format(name=_css_class_name(path), b64=b64) return ret
def gen_used_smiley_css(self): ret = HEAD for sid in self.used_smiley_id: fname = os.path.join(STATIC_PATH, 'smileys', '{}.png'.format(sid)) b64 = get_file_b64(fname) ret = ret + TEMPLATE.format(name=sid, b64=b64) return ret
def get_jpg_b64(img_file): if not img_file: return None if not img_file.endswith('jpg') and \ imghdr.what(img_file) != 'jpeg': im = Image.open(open(img_file, 'rb')) buf = cStringIO.StringIO() im.save(buf, 'JPEG', quality=JPEG_QUALITY) return base64.b64encode(buf.getvalue()) return get_file_b64(img_file)
def get_jpg_b64(img_file): if not img_file: return None if not img_file.endswith('jpg') and \ imghdr.what(img_file) != 'jpeg': im = Image.open(open(img_file, 'rb')) buf = cStringIO.StringIO() im.convert('RGB').save(buf, 'JPEG', quality=JPEG_QUALITY) return base64.b64encode(buf.getvalue()) return get_file_b64(img_file)
def do_parse_wechat_audio_file(file_name): """ return a mp3 base64 string, and the duration""" if not file_name: return "", 0 mp3_file = os.path.join('/tmp', os.path.basename(file_name)[:-4] + '.mp3') with open(file_name) as f: header = f.read(10) if 'AMR' in header: # maybe this is faster than calling sox from command line? infile = pysox.CSoxStream(file_name) 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'] elif 'SILK' in header: raw_file = os.path.join('/tmp', os.path.basename(file_name)[:-4] + '.raw') proc = Popen('{0} {1} {2}'.format(SILK_DECODER, file_name, raw_file), shell=True, stdout=PIPE, stderr=PIPE) stdout = proc.communicate()[0] for line in stdout.split('\n'): if 'File length' in line: duration = float(line[13:-3].strip()) break else: raise RuntimeError("Error decoding silk audio file!") # I don't know how to do this with pysox proc = call('sox -r 24000 -e signed -b 16 -c 1 {} {}'.format( raw_file, mp3_file), shell=True) os.unlink(raw_file) else: raise NotImplementedError("Unsupported Audio Format! This is a bug!") try: mp3_string = get_file_b64(mp3_file) os.unlink(mp3_file) except: raise RuntimeError("Failed to decode audio file: {}".format(file_name)) return mp3_string, duration
def do_parse_wechat_audio_file(file_name): """ return a mp3 stored in base64 unicode string, and the duration""" if not file_name: return "", 0 mp3_file = os.path.join('/tmp', os.path.basename(file_name)[:-4] + '.mp3') with open(file_name, 'rb') as f: header = f.read(10) if b'AMR' in header: cmd = f"sox -e signed -c 1 {file_name} {mp3_file}" subproc_succ(cmd) cmd = f"soxi -D {mp3_file}" duration = float(subproc_succ(cmd)) # The below is python2 only. It should be equivalent to using sox from command line # import pysox # infile = pysox.CSoxStream(file_name) # 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'] elif b'SILK' in header: raw_file = os.path.join('/tmp', os.path.basename(file_name)[:-4] + '.raw') cmd = '{0} {1} {2}'.format(SILK_DECODER, file_name, raw_file) out = subproc_succ(cmd) for line in out.split(b'\n'): if b'File length' in line: duration = float(line[13:-3].strip()) break else: raise RuntimeError("Error decoding silk audio file!") # TODO don't know how to do this with python subproc_succ('sox -r 24000 -e signed -b 16 -c 1 {} {}'.format( raw_file, mp3_file)) os.unlink(raw_file) else: raise NotImplementedError("Audio file format cannot be recognized.") mp3_string = get_file_b64(mp3_file) os.unlink(mp3_file) return mp3_string, duration
def get_emoji(self, md5, pack_id): path = self.emoji_dir if pack_id: path = os.path.join(path, pack_id) candidates = glob.glob(os.path.join(path, '{}*'.format(md5))) candidates = [k for k in candidates if \ not k.endswith('_thumb') and not k.endswith('_cover')] if len(candidates) > 1: # annimation candidates = [k for k in candidates if not re.match('.*_[0-9]+$', k)] # only one file is the gif in need, others are frames or cover if len(candidates) == 0: # TODO stitch frames to gif logger.warning("Cannot find emoji: {}".format(md5)) return None, None if not candidates: return None, None fname = candidates[0] return get_file_b64(fname), imghdr.what(fname)
def get_emoji(self, md5, pack_id): path = self.emoji_dir if pack_id: path = os.path.join(path, pack_id) candidates = glob.glob(os.path.join(path, '{}*'.format(md5))) candidates = [k for k in candidates if \ not k.endswith('_thumb') and not k.endswith('_cover')] if len(candidates) > 1: # annimation candidates = [ k for k in candidates if not re.match('.*_[0-9]+$', k) ] # only one file is the gif in need, others are frames or cover if len(candidates) == 0: # TODO stitch frames to gif logger.warning("Cannot find emoji: {}".format(md5)) return None, None if not candidates: return None, None fname = candidates[0] return get_file_b64(fname), imghdr.what(fname)
def _get_res_emoji(self, md5, pack_id, allow_cover=False): """ pack_id: can be None allow_cover: Cover is non-animated. Can be used as a fallback. """ path = os.path.join(self.emoji_dir, pack_id or '') candidates = glob.glob(os.path.join(path, '{}*'.format(md5))) candidates = [k for k in candidates if not k.endswith('_thumb') \ and not re.match('.*_[0-9]+$', k)] def try_use(f): if not f: return None if not imghdr.what(f[0]): # cannot recognize file type return None return f[0] candidates = [k for k in candidates if (allow_cover or not k.endswith('_cover'))] for cand in candidates: if imghdr.what(cand): return get_file_b64(cand), imghdr.what(cand) return None, None
def _get_res_emoji(self, md5, pack_id): path = self.emoji_dir if pack_id: path = os.path.join(path, pack_id) candidates = glob.glob(os.path.join(path, '{}*'.format(md5))) candidates = [k for k in candidates if not k.endswith('_thumb') \ and not re.match('.*_[0-9]+$', k)] def try_use(f): if not f: return None if not imghdr.what(f[0]): # cannot recognize file type return None return f[0] f = try_use([k for k in candidates if not k.endswith('_cover')]) if f: return get_file_b64(f), imghdr.what(f) # don't try do use cover anymore. cover is not animated #f = try_use([k for k in candidates if k.endswith('_cover')]) #if f: #return get_file_b64(f), imghdr.what(f) return None, None
def render_msg(self, msg): """ render a message, return the html block""" # TODO for chatroom, add nickname on avatar sender = u'you ' + msg.talker if not msg.isSend else 'me' format_dict = {'sender_label': sender, 'time': msg.createTime} if (not msg.isSend and msg.is_chatroom()): format_dict[ 'nickname'] = '>\n <pre align=\'left\'>' + msg.talker_nickname + '</pre' else: format_dict['nickname'] = ' ' def fallback(): template = TEMPLATES[TYPE_MSG] content = msg.msg_str() format_dict['content'] = self.smiley.replace_smileycode(content) return template.format(**format_dict) template = TEMPLATES.get(msg.type) if msg.type == TYPE_SPEAK: audio_str, duration = self.res.get_voice_mp3(msg.imgPath) format_dict['voice_duration'] = duration format_dict['voice_str'] = audio_str return template.format(**format_dict) elif msg.type == TYPE_IMG: # imgPath was original THUMBNAIL_DIRPATH://th_xxxxxxxxx imgpath = msg.imgPath.split('_')[-1] if not imgpath: logger.warn( 'No imgpath in an image message. Perhaps a bug in wechat.') return fallback() bigimgpath = self.parser.imginfo.get(msg.msgSvrId) fnames = [k for k in [imgpath, bigimgpath] if k] img = self.res.get_img(fnames) if not img: logger.warn("No image thumbnail found for {}".format(imgpath)) return fallback() # TODO do not show fancybox when no bigimg found format_dict['img'] = (img, 'jpeg') return template.format(**format_dict) elif msg.type == TYPE_EMOJI or msg.type == TYPE_CUSTOM_EMOJI: if 'emoticonmd5' in msg.content: pq = PyQuery(msg.content) md5 = pq('emoticonmd5').text() else: md5 = msg.imgPath # TODO md5 could exist in both. # first is emoji md5, second is image2/ md5 # can use fallback here. if md5: emoji_img, format = self.res.get_emoji_by_md5(md5) format_dict['emoji_format'] = format format_dict['emoji_img'] = emoji_img else: import IPython as IP IP.embed() return template.format(**format_dict) elif msg.type == TYPE_LINK: content = msg.msg_str() # TODO show a short link with long href, if link too long if content.startswith(u'URL:'): url = content[4:] content = u'URL:<a target="_blank" href="{0}">{0}</a>'.format( url) format_dict['content'] = content return template.format(**format_dict) elif msg.type == TYPE_VIDEO_FILE: video = self.res.get_video(msg.imgPath) if video.endswith(".mp4"): video_str = get_file_b64(video) format_dict["video_str"] = video_str return template.format(**format_dict) elif video.endswith(".jpg"): # only has thumbnail image_str = get_file_b64(video) format_dict["img"] = (image_str, 'jpeg') return TEMPLATES[TYPE_IMG].format(**format_dict) return f"VIDEO FILE {msg.imgPath}" elif msg.type == TYPE_WX_VIDEO: # TODO: fetch video from resource return fallback() return fallback()
def _get_internal_emoji(self, fname): f = os.path.join(INTERNAL_EMOJI_DIR, fname) return get_file_b64(f), imghdr.what(f)
def parse_wechat_video_file(path): return get_file_b64(path), duration(path)
def get_internal_emoji(self, fname): f = os.path.join(INTERNAL_EMOJI_DIR, fname) return get_file_b64(f), imghdr.what(f)