diff --git a/app/DataBase/exporter_html.py b/app/DataBase/exporter_html.py index 5b85959..f69414c 100644 --- a/app/DataBase/exporter_html.py +++ b/app/DataBase/exporter_html.py @@ -12,13 +12,24 @@ from app.DataBase.package_msg import PackageMsg from app.log import logger from app.person import Me from app.util import path -from app.util.compress_content import parser_reply, share_card, music_share +from app.util.compress_content import parser_reply, share_card, music_share, file from app.util.emoji import get_emoji_url -from app.util.file import get_file from app.util.image import get_image_path, get_image from app.util.music import get_music_path +icon_files = { + './icon/word.png': ['doc', 'docx'], + './icon/excel.png': ['xls', 'xlsx'], + './icon/csv.png': ['csv'], + './icon/txt.png': ['txt'], + './icon/zip.png': ['zip', '7z','rar'], + './icon/ppt.png': ['ppt', 'pptx'], + './icon/pdf.png': ['pdf'], +} + + + class HtmlExporter(ExporterBase): def text(self, doc, message): type_ = message[2] @@ -95,21 +106,33 @@ class HtmlExporter(ExporterBase): def file(self, doc, message): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" bytesExtra = message[10] + compress_content = message[11] str_time = message[8] is_send = message[4] timestamp = message[5] is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - link = get_file(bytesExtra, thumb=True, output_path=origin_docx_path + '/file') - file_name = '' - file_path = './icon/file.png' - if link != "": - file_name = os.path.basename(link) - link = './file/' + file_name - doc.write( - f'''{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',link: '{link}',sub_type:6,file_name: '{file_name}'}},''' - ) + file_info = file(bytesExtra, compress_content, output_path=origin_docx_path + '/file') + if file_info.get('is_error') == False: + icon_path = None + for icon, extensions in icon_files.items(): + if file_info.get('file_ext') in extensions: + icon_path = icon + break + # 如果没有与文件后缀匹配的图标,则使用默认图标 + if icon_path is None: + default_icon = './icon/file.png' + icon_path = default_icon + file_path = file_info.get('file_path') + if file_path != "": + file_path = './file/' + file_info.get('file_name') + doc.write( + f'''{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp} + ,is_chatroom:{is_chatroom},displayname:'{display_name}',icon_path: '{icon_path}' + ,sub_type:6,file_name: '{file_info.get('file_name')}',file_size: '{file_info.get('file_len')}' + ,app_name: '{file_info.get('app_name')}'}},''' + ) def refermsg(self, doc, message): """ @@ -200,16 +223,16 @@ class HtmlExporter(ExporterBase): timestamp = message[5] content = music_share(message[11]) music_path = '' - if content.get('audio_url') != '': - music_path = get_music_path(content.get('audio_url'), content.get('title'), - output_path=origin_docx_path + '/music') - if music_path != '': - music_path = f'./music/{os.path.basename(music_path)}' - music_path = music_path.replace('\\', '/') - is_chatroom = 1 if self.contact.is_chatroom else 0 - avatar = self.get_avatar_path(is_send, message) - display_name = self.get_display_name(is_send, message) if content.get('is_error') == False: + if content.get('audio_url') != '': + music_path = get_music_path(content.get('audio_url'), content.get('title'), + output_path=origin_docx_path + '/music') + if music_path != '': + music_path = f'./music/{os.path.basename(music_path)}' + music_path = music_path.replace('\\', '/') + is_chatroom = 1 if self.contact.is_chatroom else 0 + avatar = self.get_avatar_path(is_send, message) + display_name = self.get_display_name(is_send, message) doc.write( f'''{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}', timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',sub_type:3,title:'{content.get('title')}', diff --git a/app/DataBase/output.py b/app/DataBase/output.py index 88d01aa..e8dd1e8 100644 --- a/app/DataBase/output.py +++ b/app/DataBase/output.py @@ -5,6 +5,7 @@ import shutil import sys import time import traceback +import filecmp from re import findall import docx @@ -51,21 +52,24 @@ def makedirs(path): os.makedirs(os.path.join(path, 'avatar'), exist_ok=True) os.makedirs(os.path.join(path, 'music'), exist_ok=True) os.makedirs(os.path.join(path, 'icon'), exist_ok=True) - file = './app/resources/data/file.png' - if not os.path.exists(file): - resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - file = os.path.join(resource_dir, 'app', 'resources', 'data', 'file.png') - shutil.copy(file, path + '/icon/file.png') - play_file = './app/resources/data/play.png' - if not os.path.exists(play_file): - resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - play_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'play.png') - shutil.copy(play_file, path + '/icon/play.png') - pause_file = './app/resources/data/pause.png' - if not os.path.exists(pause_file): - resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - pause_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'pause.png') - shutil.copy(pause_file, path + '/icon/pause.png') + resource_dir = os.path.join('app', 'resources', 'data', 'icons') + target_folder = os.path.join(path, 'icon') + # 拷贝一些必备的图标 + for root, dirs, files in os.walk(resource_dir): + relative_path = os.path.relpath(root, resource_dir) + target_path = os.path.join(target_folder, relative_path) + + # 遍历文件夹中的文件 + for file in files: + source_file_path = os.path.join(root, file) + target_file_path = os.path.join(target_path, file) + if not os.path.exists(target_file_path): + shutil.copy(source_file_path, target_file_path) + else: + # 比较文件内容 + if not filecmp.cmp(source_file_path, target_file_path, shallow=False): + # 文件内容不一致,进行覆盖拷贝 + shutil.copy(source_file_path, target_file_path) def escape_js_and_html(input_str): diff --git a/app/resources/data/icons/csv.png b/app/resources/data/icons/csv.png new file mode 100644 index 0000000..28b84e2 Binary files /dev/null and b/app/resources/data/icons/csv.png differ diff --git a/app/resources/data/icons/excel.png b/app/resources/data/icons/excel.png new file mode 100644 index 0000000..121ad33 Binary files /dev/null and b/app/resources/data/icons/excel.png differ diff --git a/app/resources/data/icons/file.png b/app/resources/data/icons/file.png new file mode 100644 index 0000000..565cf36 Binary files /dev/null and b/app/resources/data/icons/file.png differ diff --git a/app/resources/data/icons/pause.png b/app/resources/data/icons/pause.png new file mode 100644 index 0000000..5cdcf61 Binary files /dev/null and b/app/resources/data/icons/pause.png differ diff --git a/app/resources/data/icons/pdf.png b/app/resources/data/icons/pdf.png new file mode 100644 index 0000000..2e94ec4 Binary files /dev/null and b/app/resources/data/icons/pdf.png differ diff --git a/app/resources/data/icons/play.png b/app/resources/data/icons/play.png new file mode 100644 index 0000000..596fcdc Binary files /dev/null and b/app/resources/data/icons/play.png differ diff --git a/app/resources/data/icons/ppt.png b/app/resources/data/icons/ppt.png new file mode 100644 index 0000000..9a452c5 Binary files /dev/null and b/app/resources/data/icons/ppt.png differ diff --git a/app/resources/data/icons/txt.png b/app/resources/data/icons/txt.png new file mode 100644 index 0000000..f2fc060 Binary files /dev/null and b/app/resources/data/icons/txt.png differ diff --git a/app/resources/data/icons/word.png b/app/resources/data/icons/word.png new file mode 100644 index 0000000..5a0bd48 Binary files /dev/null and b/app/resources/data/icons/word.png differ diff --git a/app/resources/data/icons/zip.png b/app/resources/data/icons/zip.png new file mode 100644 index 0000000..a5ceb5d Binary files /dev/null and b/app/resources/data/icons/zip.png differ diff --git a/app/resources/data/pause.png b/app/resources/data/pause.png deleted file mode 100644 index cdf0cf4..0000000 Binary files a/app/resources/data/pause.png and /dev/null differ diff --git a/app/resources/data/play.png b/app/resources/data/play.png deleted file mode 100644 index 09f6fe2..0000000 Binary files a/app/resources/data/play.png and /dev/null differ diff --git a/app/resources/data/template.html b/app/resources/data/template.html index 65cf521..939bb7a 100644 --- a/app/resources/data/template.html +++ b/app/resources/data/template.html @@ -187,30 +187,7 @@ audio{ margin-right: 9px; } -.chat-file { - width: 300px; - background-color: #fff; - margin-right: 20px; -} -.chat-file a ,.chat-file div{ - display: flex; - color: #000; - outline: none; - text-decoration: none; - margin: 0 20px 20px 20px; -} -.chat-file div{ - margin: 20px; -} -.chat-file a span ,.chat-file div span{ - /* flex-grow: 1; */ - width: 200px; -} -.chat-file a img,.chat-file div img{ - width: 100px; -} - -.chat-music-audio { +.chat-music-audio, .chat-file{ width: 300px; margin-right: 20px; display: flex; @@ -222,8 +199,11 @@ audio{ height: 100px; margin-left: 10px; } +.chat-file img{ + width: 50px; +} -.chat-music-audio .player-box { +.chat-music-audio .player-box,.chat-file .file-box { width: 300px; display: flex; align-items: center; @@ -231,43 +211,43 @@ audio{ height: 80px; } -.chat-music-audio .player-original { +.chat-music-audio .player-original ,.chat-file .app-info{ border-top: 1px solid #ede3e3; } -.chat-music-audio .player-original p { +.chat-music-audio .player-original p ,.chat-file .app-info p{ margin-top: 5px; color: #888; } -.chat-music-audio .player-controls { +.chat-music-audio .player-controls, .chat-file .file-img{ display: flex; align-items: center;; } -.chat-music-audio .flex1 { +.chat-music-audio .flex1,.chat-file .flex1 { flex: 1; justify-content: start; } -.chat-music-audio .flex2 { +.chat-music-audio .flex2 ,.chat-file .flex2{ flex: 2; justify-content: end; } -.chat-music-audio .player-info { +.chat-music-audio .player-info ,.chat-file .file-info{ width: 200px; height: 80px; white-space: normal; flex-basis: 200px; } -.chat-music-audio .song-title { +.chat-music-audio .song-title,.chat-file .file-name { font-weight: bold; overflow-wrap: break-word; } -.chat-music-audio .artist { +.chat-music-audio .artist ,.chat-file .file-size{ margin-top: 10px; color: #888; } @@ -749,12 +729,26 @@ input { function messageFileBox(message) { const messageFileTag = document.createElement('div'); messageFileTag.className = `chat-file`; - if (message.link !== ''){ - messageFileTag.innerHTML = ` - ${message.file_name}` - }else{ - messageFileTag.innerHTML = `
文件已丢失
`; + messageFileTag.onclick = function (event) { + if (message.text !== '') { + window.open(message.text, '_blank'); + }else{ + alert("文件可能丢失、过期或不存放在该主机上") + } } + if (message.file_name.length >= 26) { + message.file_name = message.file_name.slice(0, 25) + '...' + } + messageFileTag.innerHTML = `
+
+
${message.file_name}
+
${message.file_size}
+
+
+ +
+
+

${message.app_name}

` return messageFileTag; } diff --git a/app/util/compress_content.py b/app/util/compress_content.py index b9b2bbb..f8397e4 100644 --- a/app/util/compress_content.py +++ b/app/util/compress_content.py @@ -4,10 +4,13 @@ import xml.etree.ElementTree as ET import lz4.block import requests +import re from urllib.parse import urlparse from bs4 import BeautifulSoup from app.DataBase.hard_link import parseBytes +from ..util.file import get_file + def decompress_CompressContent(data): @@ -212,3 +215,58 @@ def get_audio_url(url): except Exception as e: print(f"Get Audio Url Error: {e}") return path + + +def file(bytes_extra, compress_content, output_path): + xml_content = decompress_CompressContent(compress_content) + if not xml_content: + return { + 'type': 6, + 'title': "发生错误", + "is_error": True + } + try: + root = ET.XML(xml_content) + appmsg = root.find('appmsg') + msg_type = int(appmsg.find('type').text) + file_name = appmsg.find('title').text + pattern = r'[\\/:*?"<>|\r\n]+' + file_name = re.sub(pattern, "_", file_name) + appattach = appmsg.find('appattach') + file_len = int(appattach.find('totallen').text) + app_name = '' + file_len = format_bytes(file_len) + file_ext = appattach.find('fileext').text + if root.find("appinfo") is not None: + app_info = root.find('appinfo') + app_name = app_info.find('appname').text + if app_name is None: + app_name = '' + file_path = get_file(bytes_extra, file_name, output_path) + return { + 'type': msg_type, + 'file_name': file_name, + 'file_len': file_len, + 'file_ext': file_ext, + 'file_path': file_path, + 'app_name': app_name, + "is_error": False + } + except Exception as e: + print(f"File Get Info Error: {e}") + return { + 'type': 6, + 'title': "发生错误", + "is_error": True + } + + +def format_bytes(size): + units = ["B", "KB", "MB", "GB"] + + def convert_bytes(size, unit_index): + if size < 1024 or unit_index >= len(units) - 1: + return size, unit_index + return convert_bytes(size / 1024, unit_index + 1) + final_size, final_unit_index = convert_bytes(size, 0) + return f"{final_size:.2f} {units[final_unit_index]}" diff --git a/app/util/file.py b/app/util/file.py index e2d8c56..8c8562f 100644 --- a/app/util/file.py +++ b/app/util/file.py @@ -20,25 +20,21 @@ class File: self.open_flag = False -def get_file(bytes_extra, thumb=False, output_path=root_path) -> str: +def get_file(bytes_extra, file_name, output_path=root_path) -> str: try: msg_bytes = MessageBytesExtra() msg_bytes.ParseFromString(bytes_extra) - file_original_path = '' file_path = '' - file_name = '' real_path = '' if len(msg_bytes.message2) > 0: - file_field = msg_bytes.message2[-1].field2 - if file_field.find('sec_msg_node') == -1: - file_original_path = file_field - file_name = os.path.basename(file_original_path) - if file_name != '' and file_name != Me().wxid: + for filed in msg_bytes.message2: + if filed.field1 == 4: + file_original_path = filed.field2 file_path = os.path.join(output_path, file_name) if os.path.exists(file_path): - print('文件' + file_path + '已存在') + # print('文件' + file_path + '已存在') return file_path - if os.path.isabs(file_original_path): + if os.path.isabs(file_original_path): # 绝对路径可能迁移过文件目录,也可能存在其他位置 if os.path.exists(file_original_path): real_path = file_original_path else: # 如果没找到再判断一次是否是迁移了目录