diff --git a/app/DataBase/__init__.py b/app/DataBase/__init__.py index d5c33c0..28f97df 100644 --- a/app/DataBase/__init__.py +++ b/app/DataBase/__init__.py @@ -9,6 +9,7 @@ """ from .hard_link import HardLink from .micro_msg import MicroMsg +from .media_msg import MediaMsg # from . import data # from . import output from .misc import Misc @@ -18,4 +19,5 @@ misc_db = Misc() msg_db = Msg() micro_msg_db = MicroMsg() hard_link_db = HardLink() -__all__ = ["data", 'output', 'misc_db', 'micro_msg_db', 'msg_db', 'hard_link_db','MsgType'] +media_msg_db = MediaMsg() +__all__ = ["data", 'output', 'misc_db', 'micro_msg_db', 'msg_db', 'hard_link_db','MsgType', "media_msg_db"] diff --git a/app/DataBase/media_msg.py b/app/DataBase/media_msg.py index d1a57ad..9b77605 100644 --- a/app/DataBase/media_msg.py +++ b/app/DataBase/media_msg.py @@ -1,9 +1,12 @@ import os.path +from os import system import sqlite3 import threading +import xml.etree.ElementTree as ET +from pilk import decode lock = threading.Lock() -db_path = "./app/Database/Msg/MediaMSG3.db" +db_path = "./app/Database/Msg/MediaMSG.db" def singleton(cls): @@ -16,7 +19,6 @@ def singleton(cls): return inner - @singleton class MediaMsg: def __init__(self): @@ -36,7 +38,6 @@ class MediaMsg: lock.release() def get_media_buffer(self, reserved0): - pass sql = ''' select Buf from Media @@ -45,14 +46,40 @@ class MediaMsg: try: lock.acquire(True) self.cursor.execute(sql, [reserved0]) - return self.cursor.fetchone() + return self.cursor.fetchone()[0] finally: lock.release() + def get_audio(self, reserved0, output_path): + buf = self.get_media_buffer(reserved0) + silk_path = f"{output_path}\\{reserved0}.silk" + pcm_path = f"{output_path}\\{reserved0}.pcm" + mp3_path = f"{output_path}\\{reserved0}.mp3" + slik_path = silk_path.replace("/", "\\") + pcm_path = pcm_path.replace("/", "\\") + mp3_path = mp3_path.replace("/", "\\") + print(mp3_path) + if os.path.exists(mp3_path): + return mp3_path + open(silk_path, "wb").write(buf) + decode(silk_path, pcm_path, 44100) + system(f'ffmpeg.exe -loglevel quiet -y -f s16le -i "{pcm_path}" -ar 44100 -ac 1 "{mp3_path}"') + system(f'del "{silk_path}"') + system(f'del "{pcm_path}"') + return mp3_path + + def get_audio_text(self, content): + try: + root = ET.fromstring(content) + transtext = root.find(".//voicetrans").get("transtext") + return transtext + except ET.ParseError: + return "" + if __name__ == '__main__': - db_path = './Msg/MediaMSG3.db' + db_path = './Msg/MediaMSG.db' media_msg_db = MediaMsg() - reserved = '823076859361714342' - buf = media_msg_db.get_media_buffer(reserved) - print(buf) + reserved = '2865682741418252473' + path = media_msg_db.get_audio(reserved, r"D:\gou\message\WeChatMsg") + print(path) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 75f0376..795dbbd 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -8,6 +8,7 @@ from PyQt5.QtWidgets import QFileDialog from . import msg_db, micro_msg_db from .package_msg import PackageMsg from ..DataBase import hard_link_db +from ..DataBase import media_msg_db from ..person_pc import MePC from ..util import path import shutil @@ -220,6 +221,30 @@ class ChildThread(QThread): f'''{str_time} {name}\n[图片]\n\n''' ) + def audio(self, doc, message): + origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" + str_content = message[7] + str_time = message[8] + is_send = message[4] + avatar = 'myhead.png' if is_send else 'tahead.png' + timestamp = message[5] + msgSvrId = message[9] + if self.output_type == Output.HTML: + try: + audio_path = media_msg_db.get_audio(msgSvrId, output_path=origin_docx_path + "/voice") + audio_path = audio_path.replace('\\', '/') + voice_to_text = media_msg_db.get_audio_text(str_content) + except: + return + if self.is_5_min(timestamp): + doc.write( + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + ) + doc.write( + f'''{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}'}},''' + ) + + def emoji(self, doc, message): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" str_content = message[7] @@ -381,6 +406,8 @@ class ChildThread(QThread): self.text(f, message) elif type_ == 3 and self.message_types.get(type_): self.image(f, message) + elif type_ == 34 and self.message_types.get(type_): + self.audio(f, message) elif type_ == 43 and self.message_types.get(type_): self.video(f, message) elif type_ == 47 and self.message_types.get(type_): @@ -694,6 +721,12 @@ body{ margin-left: 18px; max-width: 350px; } +.chat-audio{ + max-width: 300px; +} +audio{ + right: 25px; +} .input-area{ border-top:0.5px solid #e0e0e0; height: 150px; @@ -908,8 +941,8 @@ html_end = ''' else if (message.type == 49) { if (message.sub_type == 57){ if (message.is_send == 1) { - messageElement.className = "item item-right"; - messageElement.innerHTML = `
${message.text}
` + messageElement.className = "item item-right"; + messageElement.innerHTML = `
${message.text}
` } else if (message.is_send == 0) { messageElement.className = "item item-left"; @@ -917,6 +950,16 @@ html_end = ''' } } } + else if (message.type == 34) { + if (message.is_send == 1) { + messageElement.className = "item item-right"; + messageElement.innerHTML = `
${message.voice_to_text == "" ? "" : `
${message.voice_to_text}
`}
` + } + else if (message.is_send == 0) { + messageElement.className = "item item-left"; + messageElement.innerHTML = `
${message.voice_to_text == "" ? "" : `
${message.voice_to_text}
`}
` + } + } chatContainer.appendChild(messageElement); } document.querySelector("#chat-container").scrollTop = 0; diff --git a/app/ui_pc/contact/export_dialog.py b/app/ui_pc/contact/export_dialog.py index 70349b3..cf71ec0 100644 --- a/app/ui_pc/contact/export_dialog.py +++ b/app/ui_pc/contact/export_dialog.py @@ -6,6 +6,7 @@ from app.DataBase.output_pc import Output types = { '文本': 1, '图片': 3, + '语音': 34, '视频': 43, '表情包': 47, '拍一拍等系统消息': 10000 @@ -26,7 +27,7 @@ class ExportDialog(QDialog): self.contact = contact if file_type == 'html': self.export_type = Output.HTML - self.export_choices = {"文本": True, "图片": True, "视频": True, "表情包": True, + self.export_choices = {"文本": True, "图片": True, "语音": True, "视频": True, "表情包": True, '拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择 elif file_type == 'csv': self.export_type = Output.CSV diff --git a/app/util/emoji.py b/app/util/emoji.py index 141cf20..d2a4d7f 100644 --- a/app/util/emoji.py +++ b/app/util/emoji.py @@ -41,6 +41,7 @@ def get_image_format(header): @log def parser_xml(xml_string): + assert type(xml_string) == str # Parse the XML string root = ET.fromstring(xml_string) emoji = root.find('./emoji') diff --git a/requirements_pc.txt b/requirements_pc.txt index e8b56b0..18c0be7 100644 --- a/requirements_pc.txt +++ b/requirements_pc.txt @@ -15,4 +15,5 @@ jieba==0.42.1 google==3.0.0 protobuf==4.25.1 soupsieve==2.5 -lz4==4.3.2 \ No newline at end of file +lz4==4.3.2 +pilk \ No newline at end of file