图片问题--未完全修复
This commit is contained in:
parent
d1782c346e
commit
d6dced3c57
@ -49,7 +49,7 @@ class Sns:
|
|||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
lock.acquire(True)
|
lock.acquire(True)
|
||||||
sql = '''select UserName, Content from FeedsV20 where CreateTime>=?
|
sql = '''select UserName, Content, FeedId from FeedsV20 where CreateTime>=?
|
||||||
and CreateTime<=? order by CreateTime desc'''
|
and CreateTime<=? order by CreateTime desc'''
|
||||||
self.cursor.execute(sql, [start_time, end_time])
|
self.cursor.execute(sql, [start_time, end_time])
|
||||||
res = self.cursor.fetchall()
|
res = self.cursor.fetchall()
|
||||||
@ -58,6 +58,19 @@ class Sns:
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def get_comment_by_feed_id(self, feed_id):
|
||||||
|
if not self.open_flag:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
lock.acquire(True)
|
||||||
|
sql = '''select FromUserName, CommentType, Content from CommentV20 where FeedId=?
|
||||||
|
order by CreateTime desc'''
|
||||||
|
self.cursor.execute(sql, [feed_id])
|
||||||
|
res = self.cursor.fetchall()
|
||||||
|
finally:
|
||||||
|
lock.release()
|
||||||
|
return res
|
||||||
|
|
||||||
def get_cover_url(self) -> Optional[str]:
|
def get_cover_url(self) -> Optional[str]:
|
||||||
if not self.open_flag:
|
if not self.open_flag:
|
||||||
return None
|
return None
|
||||||
|
116
decrypter/image_decrypt.py
Normal file
116
decrypter/image_decrypt.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from datetime import date
|
||||||
|
from pathlib import Path
|
||||||
|
import filetype
|
||||||
|
|
||||||
|
import log
|
||||||
|
|
||||||
|
|
||||||
|
class ImageDecrypter:
|
||||||
|
|
||||||
|
def __init__(self, gui: 'Gui', file_path):
|
||||||
|
self.file_path = file_path
|
||||||
|
self.gui = gui
|
||||||
|
self.sns_cache_path = file_path + "/FileStorage/Sns/Cache"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_output_path(dir_name, md5, duration):
|
||||||
|
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||||||
|
# 这是到_internal文件夹
|
||||||
|
resource_dir = getattr(sys, '_MEIPASS')
|
||||||
|
# 获取_internal上一级文件夹再拼接
|
||||||
|
return os.path.join(os.path.dirname(resource_dir), 'output', dir_name, 'videos', f'{md5}_{duration}.mp4')
|
||||||
|
else:
|
||||||
|
return os.path.join(os.getcwd(), 'output', dir_name, 'videos', f'{md5}_{duration}.mp4')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def calculate_md5(file_path):
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
file_content = f.read()
|
||||||
|
return hashlib.md5(file_content).hexdigest()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_month_between_dates(start_date, end_date) -> list[str]:
|
||||||
|
result = []
|
||||||
|
current_date = start_date
|
||||||
|
while current_date <= end_date:
|
||||||
|
# 打印当前日期的年份和月份
|
||||||
|
result.append(current_date.strftime("%Y-%m"))
|
||||||
|
year = current_date.year + (current_date.month // 12)
|
||||||
|
month = current_date.month % 12 + 1
|
||||||
|
# 更新current_date到下个月的第一天
|
||||||
|
current_date = date(year, month, 1)
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode(magic, buf):
|
||||||
|
return bytearray([b ^ magic for b in list(buf)])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def guess_image_encoding_magic(buf):
|
||||||
|
header_code, check_code = 0xff, 0xd8
|
||||||
|
# 微信图片加密方法对字节逐一“异或”,即 源文件^magic(未知数)=加密后文件
|
||||||
|
# 已知jpg的头字节是0xff,将0xff与加密文件的头字节做异或运算求解magic码
|
||||||
|
magic = header_code ^ list(buf)[0] if buf else 0x00
|
||||||
|
# 尝试使用magic码解密,如果第二字节符合jpg特质,则图片解密成功
|
||||||
|
_, code = ImageDecrypter.decode(magic, buf[:2])
|
||||||
|
if check_code == code:
|
||||||
|
return magic
|
||||||
|
|
||||||
|
def decrypt_images(self, exporter, start_date, end_date, dir_name) -> None:
|
||||||
|
"""将图片文件从缓存中复制出来,重命名为{主图字节数}_{缩略图字节数}.jpg
|
||||||
|
duration单位为秒
|
||||||
|
"""
|
||||||
|
months = self.get_all_month_between_dates(start_date, end_date)
|
||||||
|
|
||||||
|
total_files = 0
|
||||||
|
processed_files = 0
|
||||||
|
for month in months:
|
||||||
|
source_dir = self.sns_cache_path + "/" + month
|
||||||
|
total_files = total_files + len(list(Path(source_dir).rglob('*')))
|
||||||
|
|
||||||
|
for month in months:
|
||||||
|
source_dir = self.sns_cache_path + "/" + month
|
||||||
|
for file in Path(source_dir).rglob('*'):
|
||||||
|
# 排除缩略图
|
||||||
|
if not exporter.stop_flag and file.is_file() and not file.name.endswith('_t'):
|
||||||
|
try:
|
||||||
|
with open(file, 'rb') as f:
|
||||||
|
buff = bytearray(f.read())
|
||||||
|
magic = self.guess_image_encoding_magic(buff)
|
||||||
|
if magic:
|
||||||
|
os.makedirs(f"output/{dir_name}/images/{month}/", exist_ok=True)
|
||||||
|
os.makedirs(f"output/{dir_name}/thumbs/{month}/", exist_ok=True)
|
||||||
|
main_file_size = file.stat().st_size
|
||||||
|
thumb_file_size = 0
|
||||||
|
# 找到对应缩略图
|
||||||
|
thumb_file = Path(f'{source_dir}/{file.name}_t')
|
||||||
|
if thumb_file.exists():
|
||||||
|
thumb_file_size = thumb_file.stat().st_size
|
||||||
|
# 读缩略图加密
|
||||||
|
with open(thumb_file, 'rb') as f:
|
||||||
|
thumb_buff = bytearray(f.read())
|
||||||
|
|
||||||
|
# 写缩略图
|
||||||
|
thumb_destination = (f"output/{dir_name}/thumbs/{month}/"
|
||||||
|
f"{main_file_size}_{thumb_file_size}.jpg")
|
||||||
|
with open(thumb_destination, 'wb') as f:
|
||||||
|
new_thumb_buff = self.decode(magic, thumb_buff)
|
||||||
|
f.write(new_thumb_buff)
|
||||||
|
|
||||||
|
destination = (f"output/{dir_name}/images/{month}/"
|
||||||
|
f"{main_file_size}_{thumb_file_size}.jpg")
|
||||||
|
with open(destination, 'wb') as f:
|
||||||
|
new_buf = self.decode(magic, buff)
|
||||||
|
f.write(new_buf)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
processed_files = processed_files + 1
|
||||||
|
# 15%的进度作为处理图片使用
|
||||||
|
progress = round(processed_files / total_files * 15)
|
||||||
|
self.gui.update_export_progressbar(progress)
|
@ -127,6 +127,6 @@ class VideoDecrypter:
|
|||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
processed_files = processed_files + 1
|
processed_files = processed_files + 1
|
||||||
# 前30%的进度作为 处理视频使用
|
# 15%的进度作为处理视频使用 + 15%(处理图像)
|
||||||
progress = round(processed_files / total_files * 30)
|
progress = round(processed_files / total_files * 15 + 15)
|
||||||
self.gui.update_export_progressbar(progress)
|
self.gui.update_export_progressbar(progress)
|
||||||
|
8
entity/comment.py
Normal file
8
entity/comment.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Comment:
|
||||||
|
from_user_name: str
|
||||||
|
comment_type: int
|
||||||
|
content: str
|
@ -22,12 +22,16 @@ class Url:
|
|||||||
type: str = field(metadata=config(field_name="@type"))
|
type: str = field(metadata=config(field_name="@type"))
|
||||||
text: str = field(metadata=config(field_name="#text"), default="")
|
text: str = field(metadata=config(field_name="#text"), default="")
|
||||||
md5: str = field(metadata=config(field_name="@md5"), default="")
|
md5: str = field(metadata=config(field_name="@md5"), default="")
|
||||||
|
token: str = field(metadata=config(field_name="@token"), default="")
|
||||||
|
enc_idx: str = field(metadata=config(field_name="@enc_idx"), default="")
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class Thumb:
|
class Thumb:
|
||||||
type: str = field(metadata=config(field_name="@type"))
|
type: str = field(metadata=config(field_name="@type"))
|
||||||
text: str = field(metadata=config(field_name="#text"))
|
text: str = field(metadata=config(field_name="#text"))
|
||||||
|
token: str = field(metadata=config(field_name="@token"), default="")
|
||||||
|
enc_idx: str = field(metadata=config(field_name="@enc_idx"), default="")
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
@ -91,6 +95,13 @@ class TimelineObject:
|
|||||||
beijing_timezone = timezone(timedelta(hours=8))
|
beijing_timezone = timezone(timedelta(hours=8))
|
||||||
time_formatted = dt.astimezone(beijing_timezone).strftime('%Y-%m-%d %H:%M:%S')
|
time_formatted = dt.astimezone(beijing_timezone).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
return time_formatted
|
return time_formatted
|
||||||
|
@property
|
||||||
|
def create_year_month(self)->str:
|
||||||
|
dt = datetime.fromtimestamp(self.createTime, timezone.utc)
|
||||||
|
# 转换为北京时间(UTC+8)
|
||||||
|
beijing_timezone = timezone(timedelta(hours=8))
|
||||||
|
time_formatted = dt.astimezone(beijing_timezone).strftime('%Y-%m')
|
||||||
|
return time_formatted
|
||||||
|
|
||||||
|
|
||||||
@dataclass_json
|
@dataclass_json
|
||||||
|
@ -7,6 +7,7 @@ from typing import Tuple
|
|||||||
|
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
|
from entity.comment import Comment
|
||||||
from entity.contact import Contact
|
from entity.contact import Contact
|
||||||
from exporter.avatar_exporter import AvatarExporter
|
from exporter.avatar_exporter import AvatarExporter
|
||||||
from exporter.emoji_exporter import EmojiExporter
|
from exporter.emoji_exporter import EmojiExporter
|
||||||
@ -67,7 +68,7 @@ def get_music_info(msg: MomentMsg) -> Tuple[str, str, str]:
|
|||||||
class HtmlExporter(threading.Thread):
|
class HtmlExporter(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, gui: 'Gui', dir_name: str, contacts_map: dict[str, Contact], begin_date: datetime.date,
|
def __init__(self, gui: 'Gui', dir_name: str, contacts_map: dict[str, Contact], begin_date: datetime.date,
|
||||||
end_date: datetime.date, download_pic: int, convert_video: int):
|
end_date: datetime.date, convert_video: int):
|
||||||
self.dir_name = dir_name
|
self.dir_name = dir_name
|
||||||
if Path(f"output/{self.dir_name}").exists():
|
if Path(f"output/{self.dir_name}").exists():
|
||||||
shutil.rmtree(f"output/{self.dir_name}")
|
shutil.rmtree(f"output/{self.dir_name}")
|
||||||
@ -83,7 +84,6 @@ class HtmlExporter(threading.Thread):
|
|||||||
self.contacts_map = contacts_map
|
self.contacts_map = contacts_map
|
||||||
self.begin_date = begin_date
|
self.begin_date = begin_date
|
||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
self.download_pic = download_pic
|
|
||||||
self.convert_video = convert_video
|
self.convert_video = convert_video
|
||||||
self.stop_flag = False
|
self.stop_flag = False
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -105,7 +105,7 @@ class HtmlExporter(threading.Thread):
|
|||||||
from app.DataBase import sns_db
|
from app.DataBase import sns_db
|
||||||
cover_url = sns_db.get_cover_url()
|
cover_url = sns_db.get_cover_url()
|
||||||
if cover_url:
|
if cover_url:
|
||||||
cover_path = self.image_exporter.save_image(cover_url, 'image')
|
cover_path = self.image_exporter.save_image((cover_url, "", ""), 'image')
|
||||||
self.html_head = self.html_head.replace("{cover_path}", cover_path)
|
self.html_head = self.html_head.replace("{cover_path}", cover_path)
|
||||||
|
|
||||||
self.file.write(self.html_head)
|
self.file.write(self.html_head)
|
||||||
@ -115,13 +115,20 @@ class HtmlExporter(threading.Thread):
|
|||||||
datetime.datetime(self.begin_date.year, self.begin_date.month, self.begin_date.day).timetuple())
|
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())
|
end_time = time.mktime(datetime.datetime(end_date.year, end_date.month, end_date.day).timetuple())
|
||||||
|
|
||||||
|
self.gui.image_decrypter.decrypt_images(self, self.begin_date, end_date, self.dir_name)
|
||||||
self.gui.video_decrypter.decrypt_videos(self, self.begin_date, end_date, self.dir_name, self.convert_video)
|
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)
|
message_datas = sns_db.get_messages_in_time(begin_time, end_time)
|
||||||
for index, message_data in enumerate(message_datas):
|
for index, message_data in enumerate(message_datas):
|
||||||
if not self.stop_flag:
|
if not self.stop_flag:
|
||||||
if message_data[0] in self.contacts_map:
|
if message_data[0] in self.contacts_map:
|
||||||
self.export_msg(message_data[1], self.contacts_map, self.download_pic)
|
comments_datas = sns_db.get_comment_by_feed_id(message_data[2])
|
||||||
|
comments: list[Comment] = []
|
||||||
|
for c in comments_datas:
|
||||||
|
contact = Comment(c[0], c[1], c[2])
|
||||||
|
comments.append(contact)
|
||||||
|
self.export_msg(message_data[1], comments, self.contacts_map)
|
||||||
# 更新进度条 前30%视频处理 后70%其他处理
|
# 更新进度条 前30%视频处理 后70%其他处理
|
||||||
progress = round(index / len(message_datas) * 70)
|
progress = round(index / len(message_datas) * 70)
|
||||||
self.gui.update_export_progressbar(30 + progress)
|
self.gui.update_export_progressbar(30 + progress)
|
||||||
@ -132,7 +139,7 @@ class HtmlExporter(threading.Thread):
|
|||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self.stop_flag = True
|
self.stop_flag = True
|
||||||
|
|
||||||
def export_msg(self, message: str, contacts_map: dict[str, Contact], download_pic: int) -> None:
|
def export_msg(self, message: str, comments: list[Comment], contacts_map: dict[str, Contact]) -> None:
|
||||||
|
|
||||||
LOG.info(message)
|
LOG.info(message)
|
||||||
# force_list: 强制要求转media为list
|
# force_list: 强制要求转media为list
|
||||||
@ -150,7 +157,7 @@ class HtmlExporter(threading.Thread):
|
|||||||
remark = contact.remark if contact.remark else contact.nickName
|
remark = contact.remark if contact.remark else contact.nickName
|
||||||
|
|
||||||
# 朋友圈图片
|
# 朋友圈图片
|
||||||
images = self.image_exporter.get_images(msg, download_pic)
|
images = self.image_exporter.get_images(msg)
|
||||||
|
|
||||||
# 朋友圈视频
|
# 朋友圈视频
|
||||||
videos = self.video_exporter.get_videos(msg)
|
videos = self.video_exporter.get_videos(msg)
|
||||||
@ -208,7 +215,8 @@ class HtmlExporter(threading.Thread):
|
|||||||
html += f' <div style="width:10rem; overflow:hidden">\n'
|
html += f' <div style="width:10rem; overflow:hidden">\n'
|
||||||
# 视频号图片
|
# 视频号图片
|
||||||
thumb_path = self.image_exporter.get_finder_images(msg)
|
thumb_path = self.image_exporter.get_finder_images(msg)
|
||||||
html += f' <img src="{thumb_path}" onclick="openWarningOverlay(event)" style="width:10rem;height:10rem;object-fit:cover;cursor:pointer;"/>\n'
|
html += f""" <img src=\"{thumb_path}\" onclick=\"openWarningOverlay(event)\"
|
||||||
|
style=\"width:10rem;height:10rem;object-fit:cover;cursor:pointer;\"/>\n"""
|
||||||
html += ' </div>\n'
|
html += ' </div>\n'
|
||||||
|
|
||||||
# 视频号说明
|
# 视频号说明
|
||||||
@ -221,7 +229,8 @@ class HtmlExporter(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
html += f' <div style="{get_img_div_css(len(images))}">\n'
|
html += f' <div style="{get_img_div_css(len(images))}">\n'
|
||||||
for thumb_path, image_path in images:
|
for thumb_path, image_path in images:
|
||||||
html += f' <img src="{thumb_path}" full_img="{image_path}" onclick="openFullSize(event)" style="{get_img_css(len(images))}"/>\n'
|
html += f""" <img src="{thumb_path}" full_img="{image_path}" onclick="openFullSize(event)"
|
||||||
|
style="{get_img_css(len(images))}"/>\n"""
|
||||||
html += ' </div>\n'
|
html += ' </div>\n'
|
||||||
|
|
||||||
html += ' <div>\n'
|
html += ' <div>\n'
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
|
from PIL import Image
|
||||||
from entity.moment_msg import MomentMsg, Media
|
from entity.moment_msg import MomentMsg, Media
|
||||||
import requests
|
import requests
|
||||||
import uuid
|
import uuid
|
||||||
@ -15,36 +16,52 @@ class ImageExporter:
|
|||||||
if not os.path.exists(f'output/{self.dir_name}/images/'):
|
if not os.path.exists(f'output/{self.dir_name}/images/'):
|
||||||
os.mkdir(f'output/{self.dir_name}/images/')
|
os.mkdir(f'output/{self.dir_name}/images/')
|
||||||
|
|
||||||
def save_image(self, url: str, img_type: str) -> str:
|
@staticmethod
|
||||||
""" 下载图片
|
def get_image(link: tuple) -> bytes:
|
||||||
|
""" 向微信服务器请求图片
|
||||||
"""
|
"""
|
||||||
if not (img_type == 'image' or img_type == 'thumb'):
|
url, idx, token = link
|
||||||
raise Exception("img_type 参数非法")
|
# 如果需要传递token
|
||||||
file_name = uuid.uuid4()
|
if idx and token:
|
||||||
|
url = f'{url}?idx={idx}&token={token}'
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
if response.ok:
|
if response.ok:
|
||||||
|
return response.content
|
||||||
|
|
||||||
|
def save_image(self, link: tuple, img_type: str) -> str:
|
||||||
|
""" 下载图片
|
||||||
|
"""
|
||||||
|
file_name = uuid.uuid4()
|
||||||
|
if not (img_type == 'image' or img_type == 'thumb'):
|
||||||
|
raise Exception("img_type 参数非法")
|
||||||
|
content = self.get_image(link)
|
||||||
|
if content:
|
||||||
with open(f'output/{self.dir_name}/{img_type}s/{file_name}.jpg', 'wb') as file:
|
with open(f'output/{self.dir_name}/{img_type}s/{file_name}.jpg', 'wb') as file:
|
||||||
file.write(response.content)
|
file.write(content)
|
||||||
return f'{img_type}s/{file_name}.jpg'
|
return f'{img_type}s/{file_name}.jpg'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_image_thumb_and_url(media_item) -> Tuple[str, str]:
|
def get_image_thumb_and_url(media_item, content_style:int) -> Tuple[Tuple, Tuple]:
|
||||||
""" 获取图片的缩略图与大图的链接
|
""" 获取图片的缩略图与大图的链接
|
||||||
"""
|
"""
|
||||||
thumb = None
|
thumb = None
|
||||||
url = None
|
url = None
|
||||||
# 普通图片
|
# 普通图片
|
||||||
if media_item.type == "2":
|
if media_item.type == "2":
|
||||||
thumb = media_item.thumb.text
|
thumb = (media_item.thumb.text, media_item.thumb.enc_idx, media_item.thumb.token)
|
||||||
url = media_item.url.text
|
url = (media_item.url.text, media_item.url.enc_idx, media_item.url.token)
|
||||||
# 微信音乐
|
# 微信音乐
|
||||||
if media_item.type == "5":
|
if media_item.type == "5":
|
||||||
thumb = media_item.thumb.text
|
thumb = (media_item.thumb.text, "", "")
|
||||||
url = media_item.thumb.text
|
url = (media_item.thumb.text, "", "")
|
||||||
|
# 超链接类型
|
||||||
|
if content_style == 3:
|
||||||
|
thumb = (media_item.thumb.text, "", "")
|
||||||
|
url = (media_item.thumb.text, "", "")
|
||||||
|
|
||||||
return thumb, url
|
return thumb, url
|
||||||
|
|
||||||
def get_images(self, msg: MomentMsg, download_pic: int) -> list[Tuple]:
|
def get_images(self, msg: MomentMsg) -> list[Tuple]:
|
||||||
""" 获取一条朋友圈的全部图像, 返回值是一个元组列表
|
""" 获取一条朋友圈的全部图像, 返回值是一个元组列表
|
||||||
[(缩略图路径,原图路径),(缩略图路径,原图路径)]
|
[(缩略图路径,原图路径),(缩略图路径,原图路径)]
|
||||||
"""
|
"""
|
||||||
@ -54,14 +71,43 @@ class ImageExporter:
|
|||||||
|
|
||||||
media = msg.timelineObject.ContentObject.mediaList.media
|
media = msg.timelineObject.ContentObject.mediaList.media
|
||||||
for media_item in media:
|
for media_item in media:
|
||||||
thumb, url = self.get_image_thumb_and_url(media_item)
|
thumb, url = self.get_image_thumb_and_url(media_item, msg.timelineObject.ContentObject.contentStyle)
|
||||||
if thumb and url:
|
if thumb and url:
|
||||||
if download_pic:
|
thumb_path = None
|
||||||
thumb_path = self.save_image(thumb, 'thumb')
|
image_path = None
|
||||||
image_path = self.save_image(url, 'image')
|
# 主图内容
|
||||||
|
image_content = self.get_image(url)
|
||||||
|
# 如果拿不到主图数据
|
||||||
|
if not image_content:
|
||||||
|
continue
|
||||||
|
# 如果在腾讯服务器获取到jpg图片
|
||||||
|
if image_content[:2] == b'\xff\xd8':
|
||||||
|
file_name = uuid.uuid4()
|
||||||
|
with open(f'output/{self.dir_name}/images/{file_name}.jpg', 'wb') as file:
|
||||||
|
file.write(image_content)
|
||||||
|
image_path = f'images/{file_name}.jpg'
|
||||||
|
# 缩略图内容
|
||||||
|
thumb_content = self.get_image(thumb)
|
||||||
|
file_name = uuid.uuid4()
|
||||||
|
with open(f'output/{self.dir_name}/thumbs/{file_name}.jpg', 'wb') as file:
|
||||||
|
file.write(thumb_content)
|
||||||
|
thumb_path = f'thumbs/{file_name}.jpg'
|
||||||
|
# 如果图片已加密,进入缓存图片中匹配
|
||||||
else:
|
else:
|
||||||
thumb_path = thumb
|
# 获取2024-06格式的时间
|
||||||
image_path = url
|
month = msg.timelineObject.create_year_month
|
||||||
|
image_content = self.get_image(url)
|
||||||
|
thumb_content = self.get_image(thumb)
|
||||||
|
# 从缓存里找文件
|
||||||
|
image_file = Path((f"output/{self.dir_name}/images/{month}/"
|
||||||
|
f"{len(image_content)}_{len(thumb_content)}.jpg"))
|
||||||
|
thumb_file = Path((f"output/{self.dir_name}/thumbs/{month}/"
|
||||||
|
f"{len(image_content)}_{len(thumb_content)}.jpg"))
|
||||||
|
if image_file.exists():
|
||||||
|
image_path = image_file.resolve()
|
||||||
|
if thumb_file.exists():
|
||||||
|
thumb_path = thumb_file.resolve()
|
||||||
|
|
||||||
if thumb_path and image_path:
|
if thumb_path and image_path:
|
||||||
results.append((thumb_path, image_path))
|
results.append((thumb_path, image_path))
|
||||||
|
|
||||||
@ -79,5 +125,5 @@ class ImageExporter:
|
|||||||
|
|
||||||
media = msg.timelineObject.ContentObject.finderFeed.mediaList.media
|
media = msg.timelineObject.ContentObject.finderFeed.mediaList.media
|
||||||
for media_item in media:
|
for media_item in media:
|
||||||
thumb_path = self.save_image(media_item.thumbUrl, 'thumb')
|
thumb_path = self.save_image((media_item.thumbUrl, "", ""), 'thumb')
|
||||||
return thumb_path
|
return thumb_path
|
||||||
|
13
gui/gui.py
13
gui/gui.py
@ -7,6 +7,7 @@ from pathlib import Path
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import tkcalendar
|
import tkcalendar
|
||||||
from decrypter.db_decrypt import DatabaseDecrypter
|
from decrypter.db_decrypt import DatabaseDecrypter
|
||||||
|
from decrypter.image_decrypt import ImageDecrypter
|
||||||
from decrypter.video_decrypt import VideoDecrypter
|
from decrypter.video_decrypt import VideoDecrypter
|
||||||
from gui.auto_scroll_guide import AutoScrollGuide
|
from gui.auto_scroll_guide import AutoScrollGuide
|
||||||
from gui.auto_scrolls_single_guide import AutoScrollSingleGuide
|
from gui.auto_scrolls_single_guide import AutoScrollSingleGuide
|
||||||
@ -35,8 +36,6 @@ class Gui:
|
|||||||
self.confirm_button_text = None
|
self.confirm_button_text = None
|
||||||
self.succeed_label_2 = None
|
self.succeed_label_2 = None
|
||||||
self.succeed_label = None
|
self.succeed_label = None
|
||||||
self.download_pic_var = Optional[tkinter.IntVar]
|
|
||||||
self.download_pic = None
|
|
||||||
self.auto_scroll_button_text = None
|
self.auto_scroll_button_text = None
|
||||||
self.warning_label = None
|
self.warning_label = None
|
||||||
self.root = None
|
self.root = None
|
||||||
@ -57,6 +56,7 @@ class Gui:
|
|||||||
self.decrypt_note_text = None
|
self.decrypt_note_text = None
|
||||||
self.account_info = None
|
self.account_info = None
|
||||||
self.video_decrypter = None
|
self.video_decrypter = None
|
||||||
|
self.image_decrypter = None
|
||||||
self.export_dir_name = None
|
self.export_dir_name = None
|
||||||
self.exporting = False
|
self.exporting = False
|
||||||
# 1: 自动滚动数据 2: 解密数据库 3: 导出
|
# 1: 自动滚动数据 2: 解密数据库 3: 导出
|
||||||
@ -167,6 +167,9 @@ class Gui:
|
|||||||
self.next_step_button.place_forget()
|
self.next_step_button.place_forget()
|
||||||
# 初始化视频导出器
|
# 初始化视频导出器
|
||||||
self.video_decrypter = VideoDecrypter(self, self.account_info.get("filePath"))
|
self.video_decrypter = VideoDecrypter(self, self.account_info.get("filePath"))
|
||||||
|
# 初始化图片导出器
|
||||||
|
self.image_decrypter = ImageDecrypter(self, self.account_info.get("filePath"))
|
||||||
|
|
||||||
|
|
||||||
self.page_stage = self.page_stage + 1
|
self.page_stage = self.page_stage + 1
|
||||||
|
|
||||||
@ -216,10 +219,6 @@ class Gui:
|
|||||||
self.end_calendar = tkcalendar.DateEntry(master=self.root, locale="zh_CN", maxdate=datetime.now())
|
self.end_calendar = tkcalendar.DateEntry(master=self.root, locale="zh_CN", maxdate=datetime.now())
|
||||||
self.end_calendar.place(relx=0.65, rely=0.3)
|
self.end_calendar.place(relx=0.65, rely=0.3)
|
||||||
|
|
||||||
self.download_pic_var = tkinter.IntVar(value=0)
|
|
||||||
self.download_pic = tkinter.ttk.Checkbutton(self.root, text='下载图片', variable=self.download_pic_var)
|
|
||||||
self.download_pic.place(relx=0.65, rely=0.4)
|
|
||||||
ToolTip(self.download_pic, "将图片下载到电脑上,网页\n可离线查看,导出速度变慢")
|
|
||||||
|
|
||||||
self.convert_video_var = tkinter.IntVar(value=0)
|
self.convert_video_var = tkinter.IntVar(value=0)
|
||||||
self.convert_video = tkinter.ttk.Checkbutton(self.root, text='视频转码', variable=self.convert_video_var)
|
self.convert_video = tkinter.ttk.Checkbutton(self.root, text='视频转码', variable=self.convert_video_var)
|
||||||
@ -278,7 +277,7 @@ class Gui:
|
|||||||
# 导出线程
|
# 导出线程
|
||||||
self.html_exporter_thread = HtmlExporter(self, self.export_dir_name, contact_map,
|
self.html_exporter_thread = HtmlExporter(self, self.export_dir_name, contact_map,
|
||||||
self.begin_calendar.get_date(), self.end_calendar.get_date(),
|
self.begin_calendar.get_date(), self.end_calendar.get_date(),
|
||||||
self.download_pic_var.get(), self.convert_video_var.get())
|
self.convert_video_var.get())
|
||||||
self.html_exporter_thread.start()
|
self.html_exporter_thread.start()
|
||||||
|
|
||||||
def update_decrypt_progressbar(self, progress):
|
def update_decrypt_progressbar(self, progress):
|
||||||
|
7
test.py
7
test.py
@ -1,4 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from decrypter.image_decrypt import ImageDecrypter
|
||||||
from decrypter.video_decrypt import VideoDecrypter
|
from decrypter.video_decrypt import VideoDecrypter
|
||||||
import threading
|
import threading
|
||||||
from time import sleep
|
from time import sleep
|
||||||
@ -12,8 +14,8 @@ def stage_3():
|
|||||||
gui_thread.start()
|
gui_thread.start()
|
||||||
gui.init_export_page()
|
gui.init_export_page()
|
||||||
|
|
||||||
gui.begin_calendar.set_date(datetime.date(2024, 3, 6))
|
gui.begin_calendar.set_date(datetime.date(2024, 5, 6))
|
||||||
gui.end_calendar.set_date(datetime.date(2024, 3, 6))
|
gui.end_calendar.set_date(datetime.date(2024, 5, 6))
|
||||||
|
|
||||||
# 后台读取微信信息
|
# 后台读取微信信息
|
||||||
# 请等待完全接入微信再进行UI操作
|
# 请等待完全接入微信再进行UI操作
|
||||||
@ -31,6 +33,7 @@ def stage_3():
|
|||||||
gui.waiting_label.config(text="微信已登录")
|
gui.waiting_label.config(text="微信已登录")
|
||||||
# 初始化视频导出器
|
# 初始化视频导出器
|
||||||
gui.video_decrypter = VideoDecrypter(gui, gui.account_info.get("filePath"))
|
gui.video_decrypter = VideoDecrypter(gui, gui.account_info.get("filePath"))
|
||||||
|
gui.image_decrypter = ImageDecrypter(gui, gui.account_info.get("filePath"))
|
||||||
gui.waiting_label.place_forget()
|
gui.waiting_label.place_forget()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user