From 3e00811e1c757910f132260655f3dddef76c5644 Mon Sep 17 00:00:00 2001 From: tech-shrimp Date: Tue, 16 Apr 2024 21:48:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=87=BA=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E7=B1=BB=E5=9E=8B=E7=9A=84=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++-- entity/moment_msg.py | 3 ++ exporter/html_exporter.py | 50 +++++++++++++++++++++++++++++++-- exporter/image_exporter.py | 28 ++++++++++++++---- resource/template/css/index.css | 9 ++++++ 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a1f4118..b410343 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,17 @@ ## 2. 已知问题 -* 视频下载不稳定(如有解决方案欢迎PR) +* 视频下载不稳定,视频可能不全(如有解决方案欢迎PR) +* 只能开小号导出自己朋友圈(如有解决方案欢迎PR) * HTML页面比较原始 -* 音乐等朋友圈格式不支持 * 自动浏览朋友圈的功能不稳定(如有解决方案欢迎PR) ## 3. 常见问题与解决方法 +问:怎么导出自己朋友圈 +- 近期的朋友圈可以浏览全部朋友,然后导出好友列表选自己 +- 需要导出长期的话,请开个小号然后使用搜一搜,详见[电脑微信浏览朋友圈](/doc/manual_guide.md) + 问:为什么导出的数据不全? - 答:软件只能导出在电脑微信**浏览过**的朋友圈记录,未浏览过的无法导出。 diff --git a/entity/moment_msg.py b/entity/moment_msg.py index 3022013..4d0960e 100644 --- a/entity/moment_msg.py +++ b/entity/moment_msg.py @@ -39,6 +39,8 @@ class Media: thumb: Optional[Thumb] = None thumbUrl: Optional[str] = None videoDuration: Optional[str] = None + title: Optional[str] = None + description: Optional[str] = None @dataclass_json @dataclass @@ -61,6 +63,7 @@ class ContentObject: contentStyle: int contentUrl: Optional[str] = "" title: Optional[str] = "" + description: Optional[str] = "" mediaList: Optional[MediaList] = None # 视频号消息 finderFeed: Optional[FinderFeed] = None diff --git a/exporter/html_exporter.py b/exporter/html_exporter.py index daed5d5..64b9c79 100644 --- a/exporter/html_exporter.py +++ b/exporter/html_exporter.py @@ -3,6 +3,7 @@ import json import shutil import threading import time +from typing import Tuple import xmltodict @@ -38,6 +39,31 @@ def get_img_css(size: int) -> str: return f'width:5rem;height:5rem;float:left;margin-bottom:0.2rem;margin-right:0.2rem;{img_style}' +def is_music_msg(msg: MomentMsg) -> bool: + """判断一个msg是否为音乐分享 + """ + if msg.timelineObject.ContentObject and msg.timelineObject.ContentObject.mediaList and msg.timelineObject.ContentObject.mediaList.media: + media = msg.timelineObject.ContentObject.mediaList.media[0] + if media.type == '5': + return True + return False + + +def get_music_info(msg: MomentMsg) -> Tuple[str, str, str]: + """获取音乐标题,演唱者,音乐源 + """ + title = "" + musician = "" + src = "" + if msg.timelineObject.ContentObject and msg.timelineObject.ContentObject.mediaList and msg.timelineObject.ContentObject.mediaList.media: + media = msg.timelineObject.ContentObject.mediaList.media[0] + title = media.title + musician = media.description + if media.url: + src = media.url.text + return title, musician, src + + class HtmlExporter(threading.Thread): def __init__(self, gui: 'Gui', dir_name: str, contacts_map: dict[str, Contact], begin_date: datetime.date, @@ -85,12 +111,12 @@ class HtmlExporter(threading.Thread): self.file.write(self.html_head) # 加一天 end_date = self.end_date + datetime.timedelta(days=1) - begin_time = time.mktime(datetime.datetime(self.begin_date.year, self.begin_date.month, self.begin_date.day).timetuple()) + begin_time = time.mktime( + datetime.datetime(self.begin_date.year, self.begin_date.month, self.begin_date.day).timetuple()) end_time = time.mktime(datetime.datetime(end_date.year, end_date.month, end_date.day).timetuple()) self.gui.video_decrypter.decrypt_videos(self, self.begin_date, end_date, self.dir_name, self.convert_video) - message_datas = sns_db.get_messages_in_time(begin_time, end_time) for index, message_data in enumerate(message_datas): if not self.stop_flag: @@ -157,6 +183,26 @@ class HtmlExporter(threading.Thread): html += f'
{msg.timelineObject.ContentObject.title}
\n' html += ' \n' html += ' \n' + # 音乐 + elif is_music_msg(msg): + + title, musician, src = get_music_info(msg) + html += f' \n' + html += ' \n' + html += ' \n' # 视频号 elif msg.timelineObject.ContentObject.finderFeed: html += f'
\n' diff --git a/exporter/image_exporter.py b/exporter/image_exporter.py index 580d1ae..73fcd36 100644 --- a/exporter/image_exporter.py +++ b/exporter/image_exporter.py @@ -27,6 +27,23 @@ class ImageExporter: file.write(response.content) return f'{img_type}s/{file_name}.jpg' + @staticmethod + def get_image_thumb_and_url(media_item) -> Tuple[str, str]: + """ 获取图片的缩略图与大图的链接 + """ + thumb = None + url = None + # 普通图片 + if media_item.type == "2": + thumb = media_item.thumb.text + url = media_item.url.text + # 微信音乐 + if media_item.type == "5": + thumb = media_item.thumb.text + url = media_item.thumb.text + + return thumb, url + def get_images(self, msg: MomentMsg, download_pic: int) -> list[Tuple]: """ 获取一条朋友圈的全部图像, 返回值是一个元组列表 [(缩略图路径,原图路径),(缩略图路径,原图路径)] @@ -37,13 +54,14 @@ class ImageExporter: media = msg.timelineObject.ContentObject.mediaList.media for media_item in media: - if media_item.type == "2": + thumb, url = self.get_image_thumb_and_url(media_item) + if thumb and url: if download_pic: - thumb_path = self.save_image(media_item.thumb.text, 'thumb') - image_path = self.save_image(media_item.url.text, 'image') + thumb_path = self.save_image(thumb, 'thumb') + image_path = self.save_image(url, 'image') else: - thumb_path = media_item.thumb.text - image_path = media_item.url.text + thumb_path = thumb + image_path = url if thumb_path and image_path: results.append((thumb_path, image_path)) diff --git a/resource/template/css/index.css b/resource/template/css/index.css index 390c40b..0104bde 100644 --- a/resource/template/css/index.css +++ b/resource/template/css/index.css @@ -60,6 +60,15 @@ padding-top:0.2rem; .out_link {background-color:#F7F7F7 ;font-size:0.7rem ;width:95% ;color:#41454D ;overflow: hidden; cursor:pointer;} .out_link img{width:2.5rem ;height:2.5rem ;margin:0.5rem 0.5rem ;float: left;} +.music_link {background-color:#F7F7F7 ;font-size:0.7rem ;width:95% ;color:#41454D ;overflow: hidden; cursor:pointer; display: flex; flex-direction:column} +.music_des {width:100%;display: flex;} +.music_title_musician {width:100%;margin-left: 1.5rem;display: flex;flex-direction:column} +.music_title {font-size:1.0rem;margin-top: 0.5rem;} +.music_musician {font-size:0.85rem;margin-top: 0.5rem;} +.music_link img{width:4rem ;height:4rem ;margin:0.2rem 0.2rem ;float: left;} +.music_audio {height: 1.5rem; width: 95%;} + + .text{float: left;margin-top: 1.1rem;width:80% ;} .pl{margin-top:0.5rem ;font-size:0.6rem ;color:#80858c ;clear: both;} .pl span{display:inline-block ;width:2rem ;}