From 4815b319670e40b0be328aa2e6a48ee41432ac14 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Fri, 15 Dec 2023 14:48:54 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E7=BB=99=E9=9F=B3=E9=A2=91=E7=9A=84?= =?UTF-8?q?=E5=85=83=E6=95=B0=E6=8D=AE=E5=8A=A0=E4=B8=8A=E5=88=9B=E4=BD=9C?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF=EF=BC=8C=E4=BE=BF=E4=BA=8E=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=BD=92=E7=B1=BB=E6=9F=A5=E6=89=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/output_pc.py | 15 +++++++++++++++ requirements.txt | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 92e4938..7e9fb3d 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -4,6 +4,7 @@ import os from re import findall from PyQt5.QtCore import pyqtSignal, QThread from PyQt5.QtWidgets import QFileDialog +from eyed3 import load from . import msg_db, micro_msg_db from .package_msg import PackageMsg @@ -136,6 +137,18 @@ class Output(QThread): self.requestInterruption() +def modify_audio_metadata(audiofile, new_artist): # 修改音频元数据中的“创作者”标签 + audiofile = load(audiofile) + + # 检查文件是否有标签 + if audiofile.tag is None: + audiofile.initTag() + + # 修改艺术家名称 + audiofile.tag.artist = new_artist + audiofile.tag.save() + + class ChildThread(QThread): """ 子线程,用于导出部分聊天记录 @@ -235,12 +248,14 @@ class ChildThread(QThread): str_time = message[8] is_send = message[4] avatar = 'myhead.png' if is_send else 'tahead.png' + creatorName = MePC().name if is_send else self.contact.remark 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('/', '\\') + modify_audio_metadata(audio_path, creatorName) os.utime(audio_path, (timestamp, timestamp)) audio_path = audio_path.replace('\\', '/') voice_to_text = media_msg_db.get_audio_text(str_content) diff --git a/requirements.txt b/requirements.txt index 713b142..dc07800 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,5 @@ google==3.0.0 protobuf==4.25.1 soupsieve==2.5 lz4==4.3.2 -pilk==0.2.4 \ No newline at end of file +pilk==0.2.4 +eyed3==0.9.7 \ No newline at end of file From b1f42aa0bb30ac06dddf67bd665e45bff6a13042 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Sat, 16 Dec 2023 12:29:51 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E8=A1=A8=E6=83=85=E5=8C=85=E7=9A=84=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/output_pc.py | 8 ++- app/util/emoji.py | 108 +++++++++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 7e9fb3d..ae4193e 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -284,7 +284,11 @@ class ChildThread(QThread): timestamp = message[5] if self.output_type == Output.HTML: emoji_path = get_emoji(str_content, thumb=True, output_path=origin_docx_path + '/emoji') - emoji_path = './emoji/' + os.path.basename(emoji_path) + if emoji_path == "": + shutil.copy(f"{os.path.abspath('.')}/app/resources/icons/404.png", origin_docx_path + '/emoji/404.png') + emoji_path = "./emoji/404.png" + else: + emoji_path = './emoji/' + os.path.basename(emoji_path) if self.is_5_min(timestamp): doc.write( f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' @@ -463,7 +467,7 @@ class ChildThread(QThread): self.emoji(f, message) elif type_ == 10000 and self.message_types.get(type_): self.system_msg(f, message) - elif type_ == 49 and sub_type == 57: + elif type_ == 49 and sub_type == 57 and self.message_types.get(1): self.refermsg(f,message) f.write(html_end) f.close() diff --git a/app/util/emoji.py b/app/util/emoji.py index d2a4d7f..f6b707a 100644 --- a/app/util/emoji.py +++ b/app/util/emoji.py @@ -11,7 +11,8 @@ emoji.py import os import traceback import xml.etree.ElementTree as ET - +import sqlite3 +import threading import requests from app.log import log, logger @@ -43,7 +44,10 @@ def get_image_format(header): def parser_xml(xml_string): assert type(xml_string) == str # Parse the XML string - root = ET.fromstring(xml_string) + try: + root = ET.fromstring(xml_string) + except: + root = ET.fromstring(xml_string.replace("&", "&")) emoji = root.find('./emoji') # Accessing attributes of the 'emoji' element fromusername = emoji.get('fromusername') @@ -61,10 +65,67 @@ def parser_xml(xml_string): 'height': height, 'cdnurl': cdnurl, 'thumburl': thumburl if thumburl else cdnurl, - 'md5': md5 if md5 else androidmd5, + 'md5': (md5 if md5 else androidmd5).lower(), } -@log +def singleton(cls): + _instance = {} + + def inner(): + if cls not in _instance: + _instance[cls] = cls() + return _instance[cls] + + return inner + +lock = threading.Lock() +db_path = "./app/Database/Msg/Emotion.db" + +@singleton +class Emotion: + def __init__(self): + self.DB = None + self.cursor: sqlite3.Cursor = None + self.open_flag = False + self.init_database() + + def init_database(self): + if not self.open_flag: + if os.path.exists(db_path): + self.DB = sqlite3.connect(db_path, check_same_thread=False) + # '''创建游标''' + self.cursor = self.DB.cursor() + self.open_flag = True + if lock.locked(): + lock.release() + + def get_emoji_url(self, md5: str): + sql = ''' + select CDNUrl + from CustomEmotion + where md5 = ? + ''' + try: + lock.acquire(True) + self.cursor.execute(sql, [md5]) + return self.cursor.fetchone()[0] + except: + md5 = md5.upper() + sql = """ + select Data + from EmotionItem + where md5 = ? + """ + self.cursor.execute(sql, [md5]) + try: + return self.cursor.fetchone()[0] + except: + return "" + finally: + lock.release() + +emoji_db = Emotion() + def download(url, output_dir, name, thumb=False): if not url: return ':/icons/icons/404.png' @@ -92,23 +153,44 @@ def get_emoji(xml_string, thumb=True, output_path=root_path) -> str: prefix = 'th_' if thumb else '' file_path = os.path.join(output_path, prefix + md5 + f) if os.path.exists(file_path): - print('表情包已存在') + # print('表情包已存在', file_path) return file_path url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl'] - print("下载表情包ing:", url) - emoji_path = download(url, output_path, md5, thumb) - return emoji_path + if not url or url == "": + url = emoji_db.get_emoji_url(md5) + if type(url) == str and url != "": + print("下载表情包ing:", url) + emoji_path = download(url, output_path, md5, thumb) + return emoji_path + elif type(url) == bytes: + image_format = get_image_format(url[:8]) + if image_format: + if thumb: + output_path = os.path.join(output_path, 'th_' + md5 + '.' + image_format) + else: + output_path = os.path.join(output_path, md5 + '.' + image_format) + else: + output_path = os.path.join(output_path, md5) + with open(output_path, 'wb') as f: + f.write(url) + print("表情包数据库加载", output_path) + return output_path + else: + print("!!!未知表情包数据,信息:", xml_string, emoji_info, url) + return "" except: logger.error(traceback.format_exc()) return "" if __name__ == '__main__': - xml_string = ' ' - res1 = parser_xml(xml_string) - print(res1, res1['md5']) + db_path = r"..\DataBase\Msg\Emotion.db" + xml_string = '' + print(get_emoji(xml_string, thumb=False, output_path=".")) + # ET.fromstring() + # print(res1, res1['md5']) # download(res1['cdnurl'], "./data/emoji/", res1['md5']) # download(res1['thumburl'], "./data/emoji/", res1['md5'], True) - print(get_emoji(xml_string, True)) - print(get_emoji(xml_string, False)) + # print(get_emoji(xml_string, True)) + # print(get_emoji(xml_string, False)) # http://vweixinf.tc.qq.com/110/20403/stodownload?m=3a4d439aba02dce4834b2c54e9f15597&filekey=3043020101042f302d02016e0402534804203361346434333961626130326463653438333462326335346539663135353937020213f0040d00000004627466730000000131&hy=SH&storeid=323032313037323030373236313130303039653236646365316535316534383236386234306230303030303036653033303034666233&ef=3&bizid=1022 From 777756ae5a6a3aedd91669b6db9d3497b761b986 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Sat, 16 Dec 2023 12:58:29 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E5=AF=B9=E8=AF=AD?= =?UTF-8?q?=E9=9F=B3=E8=BD=AC=E7=9A=84=E6=96=87=E5=AD=97escape=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/output_pc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index ae4193e..508191c 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -258,7 +258,7 @@ class ChildThread(QThread): modify_audio_metadata(audio_path, creatorName) os.utime(audio_path, (timestamp, timestamp)) audio_path = audio_path.replace('\\', '/') - voice_to_text = media_msg_db.get_audio_text(str_content) + voice_to_text = escape_js_and_html(media_msg_db.get_audio_text(str_content)) except: return if self.is_5_min(timestamp): From 2d0cbff14acae2fc23100d8d428e769347e9139b Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Sun, 17 Dec 2023 10:56:27 +0800 Subject: [PATCH 04/15] =?UTF-8?q?Revert=20"=E5=9F=BA=E6=9C=AC=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E5=8A=A0=E5=AF=86=E8=A1=A8=E6=83=85=E5=8C=85=E7=9A=84?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b1f42aa0bb30ac06dddf67bd665e45bff6a13042. --- app/DataBase/output_pc.py | 8 +-- app/util/emoji.py | 108 +++++--------------------------------- 2 files changed, 15 insertions(+), 101 deletions(-) diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 11600f7..492c5fb 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -284,11 +284,7 @@ class ChildThread(QThread): timestamp = message[5] if self.output_type == Output.HTML: emoji_path = get_emoji(str_content, thumb=True, output_path=origin_docx_path + '/emoji') - if emoji_path == "": - shutil.copy(f"{os.path.abspath('.')}/app/resources/icons/404.png", origin_docx_path + '/emoji/404.png') - emoji_path = "./emoji/404.png" - else: - emoji_path = './emoji/' + os.path.basename(emoji_path) + emoji_path = './emoji/' + os.path.basename(emoji_path) if self.is_5_min(timestamp): doc.write( f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' @@ -473,7 +469,7 @@ class ChildThread(QThread): self.emoji(f, message) elif type_ == 10000 and self.message_types.get(type_): self.system_msg(f, message) - elif type_ == 49 and sub_type == 57 and self.message_types.get(1): + elif type_ == 49 and sub_type == 57: self.refermsg(f,message) f.write(html_end) f.close() diff --git a/app/util/emoji.py b/app/util/emoji.py index f6b707a..d2a4d7f 100644 --- a/app/util/emoji.py +++ b/app/util/emoji.py @@ -11,8 +11,7 @@ emoji.py import os import traceback import xml.etree.ElementTree as ET -import sqlite3 -import threading + import requests from app.log import log, logger @@ -44,10 +43,7 @@ def get_image_format(header): def parser_xml(xml_string): assert type(xml_string) == str # Parse the XML string - try: - root = ET.fromstring(xml_string) - except: - root = ET.fromstring(xml_string.replace("&", "&")) + root = ET.fromstring(xml_string) emoji = root.find('./emoji') # Accessing attributes of the 'emoji' element fromusername = emoji.get('fromusername') @@ -65,67 +61,10 @@ def parser_xml(xml_string): 'height': height, 'cdnurl': cdnurl, 'thumburl': thumburl if thumburl else cdnurl, - 'md5': (md5 if md5 else androidmd5).lower(), + 'md5': md5 if md5 else androidmd5, } -def singleton(cls): - _instance = {} - - def inner(): - if cls not in _instance: - _instance[cls] = cls() - return _instance[cls] - - return inner - -lock = threading.Lock() -db_path = "./app/Database/Msg/Emotion.db" - -@singleton -class Emotion: - def __init__(self): - self.DB = None - self.cursor: sqlite3.Cursor = None - self.open_flag = False - self.init_database() - - def init_database(self): - if not self.open_flag: - if os.path.exists(db_path): - self.DB = sqlite3.connect(db_path, check_same_thread=False) - # '''创建游标''' - self.cursor = self.DB.cursor() - self.open_flag = True - if lock.locked(): - lock.release() - - def get_emoji_url(self, md5: str): - sql = ''' - select CDNUrl - from CustomEmotion - where md5 = ? - ''' - try: - lock.acquire(True) - self.cursor.execute(sql, [md5]) - return self.cursor.fetchone()[0] - except: - md5 = md5.upper() - sql = """ - select Data - from EmotionItem - where md5 = ? - """ - self.cursor.execute(sql, [md5]) - try: - return self.cursor.fetchone()[0] - except: - return "" - finally: - lock.release() - -emoji_db = Emotion() - +@log def download(url, output_dir, name, thumb=False): if not url: return ':/icons/icons/404.png' @@ -153,44 +92,23 @@ def get_emoji(xml_string, thumb=True, output_path=root_path) -> str: prefix = 'th_' if thumb else '' file_path = os.path.join(output_path, prefix + md5 + f) if os.path.exists(file_path): - # print('表情包已存在', file_path) + print('表情包已存在') return file_path url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl'] - if not url or url == "": - url = emoji_db.get_emoji_url(md5) - if type(url) == str and url != "": - print("下载表情包ing:", url) - emoji_path = download(url, output_path, md5, thumb) - return emoji_path - elif type(url) == bytes: - image_format = get_image_format(url[:8]) - if image_format: - if thumb: - output_path = os.path.join(output_path, 'th_' + md5 + '.' + image_format) - else: - output_path = os.path.join(output_path, md5 + '.' + image_format) - else: - output_path = os.path.join(output_path, md5) - with open(output_path, 'wb') as f: - f.write(url) - print("表情包数据库加载", output_path) - return output_path - else: - print("!!!未知表情包数据,信息:", xml_string, emoji_info, url) - return "" + print("下载表情包ing:", url) + emoji_path = download(url, output_path, md5, thumb) + return emoji_path except: logger.error(traceback.format_exc()) return "" if __name__ == '__main__': - db_path = r"..\DataBase\Msg\Emotion.db" - xml_string = '' - print(get_emoji(xml_string, thumb=False, output_path=".")) - # ET.fromstring() - # print(res1, res1['md5']) + xml_string = ' ' + res1 = parser_xml(xml_string) + print(res1, res1['md5']) # download(res1['cdnurl'], "./data/emoji/", res1['md5']) # download(res1['thumburl'], "./data/emoji/", res1['md5'], True) - # print(get_emoji(xml_string, True)) - # print(get_emoji(xml_string, False)) + print(get_emoji(xml_string, True)) + print(get_emoji(xml_string, False)) # http://vweixinf.tc.qq.com/110/20403/stodownload?m=3a4d439aba02dce4834b2c54e9f15597&filekey=3043020101042f302d02016e0402534804203361346434333961626130326463653438333462326335346539663135353937020213f0040d00000004627466730000000131&hy=SH&storeid=323032313037323030373236313130303039653236646365316535316534383236386234306230303030303036653033303034666233&ef=3&bizid=1022 From ef7bea7f9c22a916fbe7aa4eb9cfc6483b8952ef Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Sun, 17 Dec 2023 18:33:06 +0800 Subject: [PATCH 05/15] =?UTF-8?q?html=E7=BE=A4=E8=81=8A=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=B4=E5=83=8F=E5=92=8C=E6=98=B5=E7=A7=B0?= =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80=E4=BA=9B=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/msg.py | 15 ++ app/DataBase/output_pc.py | 349 ++++++++++++++++++++++++++---------- app/DataBase/package_msg.py | 64 ++++++- app/util/emoji.py | 91 +++++++++- main.py | 1 + 5 files changed, 424 insertions(+), 96 deletions(-) diff --git a/app/DataBase/msg.py b/app/DataBase/msg.py index 037c5eb..b0ff151 100644 --- a/app/DataBase/msg.py +++ b/app/DataBase/msg.py @@ -57,6 +57,21 @@ class Msg: lock.release() def get_messages(self, username_): + ''' + return list + a[0]: localId, + a[1]: talkerId, (和strtalker对应的,不是群聊信息发送人) + a[2]: type, + a[3]: subType, + a[4]: is_sender, + a[5]: timestamp, + a[6]: status, (没啥用) + a[7]: str_content, + a[8]: str_time, (格式化的时间) + a[9]: msgSvrId, + a[10]: BytesExtra, + a[11]: CompressContent, + ''' if not self.open_flag: return None sql = ''' diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 60569c7..329d5ab 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -27,6 +27,7 @@ def makedirs(path): os.makedirs(os.path.join(path, 'video'), exist_ok=True) os.makedirs(os.path.join(path, 'voice'), exist_ok=True) os.makedirs(os.path.join(path, 'file'), exist_ok=True) + os.makedirs(os.path.join(path, 'avatar'), exist_ok=True) def escape_js_and_html(input_str): @@ -181,20 +182,32 @@ class ChildThread(QThread): str_content = message[7] str_time = message[8] is_send = message[4] - avatar = 'myhead.png' if is_send else 'tahead.png' timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) if self.output_type == Output.HTML: str_content = escape_js_and_html(str_content) if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) emojiText = findall(r"(\[.+?\])", str_content) for emoji_text in emojiText: if emoji_text in emoji: str_content = str_content.replace(emoji_text, emoji[emoji_text]) doc.write( - f'''{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -208,9 +221,21 @@ class ChildThread(QThread): str_content = message[7] str_time = message[8] is_send = message[4] - avatar = 'myhead.png' if is_send else 'tahead.png' - timestamp = message[5] BytesExtra = message[10] + timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) if self.output_type == Output.HTML: str_content = escape_js_and_html(str_content) image_path = hard_link_db.get_image(str_content, BytesExtra, thumb=False) @@ -231,10 +256,10 @@ class ChildThread(QThread): # print(f"tohtml:---{image_path}") if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) doc.write( - f'''{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -247,15 +272,26 @@ class ChildThread(QThread): str_content = message[7] str_time = message[8] is_send = message[4] - avatar = 'myhead.png' if is_send else 'tahead.png' - creatorName = MePC().name if is_send else self.contact.remark - timestamp = message[5] msgSvrId = message[9] + timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) 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('/', '\\') - modify_audio_metadata(audio_path, creatorName) + modify_audio_metadata(audio_path, displayname) os.utime(audio_path, (timestamp, timestamp)) audio_path = audio_path.replace('\\', '/') voice_to_text = escape_js_and_html(media_msg_db.get_audio_text(str_content)) @@ -263,10 +299,10 @@ class ChildThread(QThread): return if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) doc.write( - f'''{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}'}},''' + f'''{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) if self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -279,17 +315,33 @@ class ChildThread(QThread): str_content = message[7] str_time = message[8] is_send = message[4] - avatar = 'myhead.png' if is_send else 'tahead.png' timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) if self.output_type == Output.HTML: emoji_path = get_emoji(str_content, thumb=True, output_path=origin_docx_path + '/emoji') - emoji_path = './emoji/' + os.path.basename(emoji_path) + if emoji_path == "": + shutil.copy(f"{os.path.abspath('.')}/app/resources/icons/404.png", origin_docx_path + '/emoji/404.png') + emoji_path = "./emoji/404.png" + else: + emoji_path = './emoji/' + os.path.basename(emoji_path) if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) doc.write( - f'''{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -317,6 +369,20 @@ class ChildThread(QThread): avatar = 'myhead.png' if is_send else 'tahead.png' content = parser_reply(message[11]) refer_msg = content.get('refer') + timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) if self.output_type == Output.HTML: contentText = content.get('title') emojiText = findall(r"(\[.+?\])", contentText) @@ -330,11 +396,11 @@ class ChildThread(QThread): if emoji_text in emoji: referText = referText.replace(emoji_text, emoji[emoji_text]) doc.write( - f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}'}},''' + f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) else: doc.write( - f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}'}},''' + f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -351,10 +417,12 @@ class ChildThread(QThread): str_content = message[7] is_send = message[4] str_time = message[8] + timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 str_content = escape_js_and_html(str_content.lstrip('').rstrip('')) if self.output_type == Output.HTML: doc.write( - f'''{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:''}},''' + f'''{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:''}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -369,8 +437,20 @@ class ChildThread(QThread): str_time = message[8] is_send = message[4] BytesExtra = message[10] - avatar = 'myhead.png' if is_send else 'tahead.png' timestamp = message[5] + is_chatroom = 1 if self.contact.is_chatroom else 0 + if is_chatroom: + avatar = f"./avatar/{message[12].wxid}.png" + else: + avatar = "./avatar/" + ('myhead.png' if is_send else 'tahead.png') + if is_chatroom: + if is_send: + displayname = MePC().name + else: + displayname = message[12].remark + else: + displayname = MePC().name if is_send else self.contact.remark + displayname = escape_js_and_html(displayname) if self.output_type == Output.HTML: video_path = hard_link_db.get_video(str_content, BytesExtra, thumb=False) image_path = hard_link_db.get_video(str_content, BytesExtra, thumb=True) @@ -385,14 +465,14 @@ class ChildThread(QThread): # print(f"tohtml:---{image_path}") if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) doc.write( - f'''{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) except: doc.write( - f'''{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) return if video_path is None and image_path is None: @@ -407,10 +487,10 @@ class ChildThread(QThread): video_path = video_path.replace('\\', '/') if self.is_5_min(timestamp): doc.write( - f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:''}},''' + f'''{{ type:0, text: '{str_time}',is_send:0,avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) doc.write( - f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}'}},''' + f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{displayname}'}},''' ) elif self.output_type == Output.TXT: name = '你' if is_send else self.contact.remark @@ -443,12 +523,27 @@ class ChildThread(QThread): def to_html_(self): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" makedirs(origin_docx_path) - messages = msg_db.get_messages(self.contact.wxid) + if self.contact.is_chatroom: + packagemsg = PackageMsg() + messages = packagemsg.get_package_message_by_wxid(self.contact.wxid) + else: + messages = msg_db.get_messages(self.contact.wxid) filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html" f = open(filename, 'w', encoding='utf-8') - f.write(html_head) - MePC().avatar.save(os.path.join(origin_docx_path, 'myhead.png')) - self.contact.avatar.save(os.path.join(origin_docx_path, 'tahead.png')) + f.write(html_head.replace("Chat Records", f"{self.contact.remark}")) + MePC().avatar.save(os.path.join(f"{origin_docx_path}/avatar/{MePC().wxid}.png")) + if self.contact.is_chatroom: + for message in messages: + if message[4]: # is_send + continue + try: + chatroom_avatar_path = f"{origin_docx_path}/avatar/{message[12].wxid}.png" + if not os.path.exists(chatroom_avatar_path): + message[12].avatar.save(chatroom_avatar_path) + except: + pass + else: + self.contact.avatar.save(os.path.join(f"{origin_docx_path}/avatar/{self.contact.wxid}.png")) self.rangeSignal.emit(len(messages)) total_steps = len(messages) for index, message in enumerate(messages): @@ -467,7 +562,7 @@ class ChildThread(QThread): self.emoji(f, message) elif type_ == 10000 and self.message_types.get(type_): self.system_msg(f, message) - elif type_ == 49 and sub_type == 57: + elif type_ == 49 and sub_type == 57 and self.message_types.get(type_): self.refermsg(f, message) f.write(html_end) f.close() @@ -562,6 +657,7 @@ emoji = { '[阴险]': '', '[亲亲]': '', '[可怜]': '', + '[Whimper]': '', '[笑脸]': '', '[生病]': '', '[脸红]': '', @@ -691,7 +787,8 @@ body{ word-wrap:break-word; word-break:normal; } -.chat-refer{ +.chat-refer { + margin-top: 5px; max-width: 400px; padding: 6px; border-radius: 5px; @@ -701,11 +798,11 @@ body{ word-wrap:break-word; word-break:normal; } -.chat-refer-right{ - margin-right:55px; +.chat-refer-right { + margin-right: 55px; } .chat-refer-left{ - margin-left:55px; + margin-left: 15px; } .item-left .bubble{ margin-left: 15px; @@ -737,8 +834,8 @@ body{ border-bottom: 10px solid transparent; right: -20px; } -.item{ - white-space: pre-line; +.item { + white-space: pre-wrap; margin-top: 15px; display: flex; width: 100%; @@ -765,6 +862,20 @@ body{ user-select:none; } +.content-wrapper { + display: flex; + flex-direction: column; + align-items: baseline; +} + +.displayname { + margin-left: 13px; + margin-left: 13px; + font-size: 13px; + margin-bottom: 5px; + color: darkgray; +} + .chat-image img{ margin-right: 18px; margin-left: 18px; @@ -960,84 +1071,142 @@ html_end = ''' const startIndex = (page - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; console.log(page); + + // 生成各类标签的函数 + function messageBubble(message, side) { + const messageBubbleTag = document.createElement('div'); + messageBubbleTag.className = `bubble bubble-${side}`; + messageBubbleTag.innerHTML = message.text; + return messageBubbleTag; + } + function displayNameBox(message) { + const displayName = document.createElement('div'); + displayName.className = "displayname"; + displayName.innerHTML = message.displayname; + return displayName; + } + function avatarBox(message) { + const avatarTag = document.createElement('div'); + avatarTag.className = "avatar"; + avatarTag.innerHTML = `` + return avatarTag; + } + function messageImgBox(message) { + const messageImgTag = document.createElement('div'); + messageImgTag.className = `chat-image`; + messageImgTag.innerHTML = ``; + return messageImgTag; + } + function messageVideoBox(message) { + const messageVideoTag = document.createElement('div'); + messageVideoTag.className = `chat-video`; + messageVideoTag.innerHTML = `