第一步优化勾选功能

This commit is contained in:
27942
2026-02-07 21:23:42 +08:00
parent 9dfc919596
commit 1def24a130
514 changed files with 7820 additions and 11494 deletions

3821
1.html

File diff suppressed because one or more lines are too long

View File

@@ -1,81 +0,0 @@
"""
打包脚本 - 使用 PyInstaller 打包 Python 代码
只打包 Python 代码,不包含其他资源文件
"""
import os
import sys
import subprocess
def build():
"""使用 PyInstaller 打包应用"""
# 检查是否安装了 PyInstaller
try:
import PyInstaller
except ImportError:
print("正在安装 PyInstaller...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])
# PyInstaller 命令参数
cmd = [
"pyinstaller",
"--name=GUI应用", # 可执行文件名
"--onefile", # 打包成单个文件
"--windowed", # Windows 下不显示控制台窗口GUI应用
"--clean", # 清理临时文件
"--noconfirm", # 不询问覆盖
# 隐藏导入PyInstaller 可能无法自动检测的模块)
"--hidden-import=PyQt5",
"--hidden-import=PyQt5.QtCore",
"--hidden-import=PyQt5.QtGui",
"--hidden-import=PyQt5.QtWidgets",
"--hidden-import=qfluentwidgets",
"--hidden-import=pandas",
"--hidden-import=openpyxl",
"--hidden-import=loguru",
"--hidden-import=beautifulsoup4",
"--hidden-import=curl_cffi",
"--hidden-import=DrissionPage",
"--hidden-import=requests",
"--hidden-import=main", # 导入 main 模块
# 排除不需要的模块(减小体积)
"--exclude-module=matplotlib",
"--exclude-module=numpy.distutils",
"--exclude-module=scipy",
"--exclude-module=IPython",
"--exclude-module=jupyter",
# 主入口文件
"gui_app.py"
]
print("=" * 60)
print("开始打包...")
print("=" * 60)
print(f"执行命令: {' '.join(cmd)}")
print("=" * 60)
try:
# 执行打包命令
result = subprocess.run(cmd, check=True)
print("\n" + "=" * 60)
print("打包完成!")
print("=" * 60)
print(f"可执行文件位置: dist/GUI应用.exe")
print("=" * 60)
return True
except subprocess.CalledProcessError as e:
print(f"\n打包失败: {e}")
return False
except Exception as e:
print(f"\n打包过程中出现错误: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
success = build()
sys.exit(0 if success else 1)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -161,7 +161,6 @@ imports:
&#8226; <a href="#gui_worker">gui_worker</a> &#8226; <a href="#gui_worker">gui_worker</a>
&#8226; <a href="#heapq">heapq</a> &#8226; <a href="#heapq">heapq</a>
&#8226; <a href="#io">io</a> &#8226; <a href="#io">io</a>
&#8226; <a href="#json">json</a>
&#8226; <a href="#keyword">keyword</a> &#8226; <a href="#keyword">keyword</a>
&#8226; <a href="#linecache">linecache</a> &#8226; <a href="#linecache">linecache</a>
&#8226; <a href="#locale">locale</a> &#8226; <a href="#locale">locale</a>
@@ -194,7 +193,6 @@ imports:
&#8226; <a href="#sre_parse">sre_parse</a> &#8226; <a href="#sre_parse">sre_parse</a>
&#8226; <a href="#stat">stat</a> &#8226; <a href="#stat">stat</a>
&#8226; <a href="#sys">sys</a> &#8226; <a href="#sys">sys</a>
&#8226; <a href="#time">time</a>
&#8226; <a href="#traceback">traceback</a> &#8226; <a href="#traceback">traceback</a>
&#8226; <a href="#types">types</a> &#8226; <a href="#types">types</a>
&#8226; <a href="#typing">typing</a> &#8226; <a href="#typing">typing</a>
@@ -18335,7 +18333,6 @@ imported by:
&#8226; <a href="#curl_cffi.requests.utils">curl_cffi.requests.utils</a> &#8226; <a href="#curl_cffi.requests.utils">curl_cffi.requests.utils</a>
&#8226; <a href="#curl_cffi.requests.websockets">curl_cffi.requests.websockets</a> &#8226; <a href="#curl_cffi.requests.websockets">curl_cffi.requests.websockets</a>
&#8226; <a href="#dateutil.zoneinfo">dateutil.zoneinfo</a> &#8226; <a href="#dateutil.zoneinfo">dateutil.zoneinfo</a>
&#8226; <a href="#gui_app.py">gui_app.py</a>
&#8226; <a href="#importlib.metadata">importlib.metadata</a> &#8226; <a href="#importlib.metadata">importlib.metadata</a>
&#8226; <a href="#json.decoder">json.decoder</a> &#8226; <a href="#json.decoder">json.decoder</a>
&#8226; <a href="#json.encoder">json.encoder</a> &#8226; <a href="#json.encoder">json.encoder</a>
@@ -56594,7 +56591,6 @@ imported by:
&#8226; <a href="#filelock._api">filelock._api</a> &#8226; <a href="#filelock._api">filelock._api</a>
&#8226; <a href="#filelock.asyncio">filelock.asyncio</a> &#8226; <a href="#filelock.asyncio">filelock.asyncio</a>
&#8226; <a href="#gc">gc</a> &#8226; <a href="#gc">gc</a>
&#8226; <a href="#gui_app.py">gui_app.py</a>
&#8226; <a href="#gui_worker">gui_worker</a> &#8226; <a href="#gui_worker">gui_worker</a>
&#8226; <a href="#gzip">gzip</a> &#8226; <a href="#gzip">gzip</a>
&#8226; <a href="#http.cookiejar">http.cookiejar</a> &#8226; <a href="#http.cookiejar">http.cookiejar</a>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -76,15 +76,20 @@ class WorkerThread(QThread):
self.log_message.emit(f"使用预查找的文件列表,共 {len(self.prepared_files)} 个文件") self.log_message.emit(f"使用预查找的文件列表,共 {len(self.prepared_files)} 个文件")
self.progress.emit(30) self.progress.emit(30)
first_file = self.prepared_files[0] # 将预查找的文件按类型分类,直接传递给 action(),跳过文件夹扫描
if first_file['path'].is_file(): prepared_video_files = [f['path'] for f in self.prepared_files
folder_path = str(first_file['path'].parent.parent) if f['path'].is_file() and any(
else: f['path'].suffix.lower() == ext for ext in VIDEO_EXTENSIONS)]
folder_path = str(first_file['path'].parent.parent) prepared_image_folders = [f['path'] for f in self.prepared_files
if f['path'].is_dir()]
self.log_message.emit(f"使用文件夹路径: {folder_path}") self.log_message.emit(f"预查找文件: {len(prepared_video_files)} 个视频, {len(prepared_image_folders)} 个图片文件夹")
try: try:
result = pdd.action(folder_path=folder_path, collect_all_videos=False) result = pdd.action(
prepared_video_files=prepared_video_files,
prepared_image_folders=prepared_image_folders,
collect_all_videos=False
)
ok = bool(result.get("ok")) if isinstance(result, dict) else True ok = bool(result.get("ok")) if isinstance(result, dict) else True
if not ok: if not ok:
self.log_message.emit(f"失败原因: {result.get('reason') if isinstance(result, dict) else ''}") self.log_message.emit(f"失败原因: {result.get('reason') if isinstance(result, dict) else ''}")
@@ -222,9 +227,10 @@ class WorkerThread(QThread):
for idx, image_folder_info in enumerate(image_folders): for idx, image_folder_info in enumerate(image_folders):
if not self.is_running: if not self.is_running:
break break
image_folder = str(image_folder_info['path']) image_folder_path = image_folder_info['path']
self.log_message.emit(f"处理第 {idx + 1}/{len(image_folders)} 个图片文件夹: {image_folder}") self.log_message.emit(f"处理第 {idx + 1}/{len(image_folders)} 个图片文件夹: {image_folder_path}")
pdd.action(folder_path=image_folder) # 直接传递图片文件夹路径,跳过文件夹扫描
pdd.action(prepared_image_folders=[image_folder_path])
self.log_message.emit(f"图片文件夹 {idx + 1} 上传完成") self.log_message.emit(f"图片文件夹 {idx + 1} 上传完成")
self.log_message.emit(f"所有图片文件夹上传完成,共处理 {len(image_folders)} 个文件夹") self.log_message.emit(f"所有图片文件夹上传完成,共处理 {len(image_folders)} 个文件夹")
@@ -264,7 +270,8 @@ class WorkerThread(QThread):
if not self.is_running: if not self.is_running:
break break
self.log_message.emit(f"处理第 {idx + 1}/{len(image_folders)} 个图片文件夹: {image_folder}") self.log_message.emit(f"处理第 {idx + 1}/{len(image_folders)} 个图片文件夹: {image_folder}")
pdd.action(folder_path=image_folder) # 直接传递图片文件夹路径,跳过文件夹扫描
pdd.action(prepared_image_folders=[Path(image_folder)])
self.log_message.emit(f"图片文件夹 {idx + 1} 上传完成") self.log_message.emit(f"图片文件夹 {idx + 1} 上传完成")
self.log_message.emit(f"所有图片文件夹上传完成,共处理 {len(image_folders)} 个文件夹") self.log_message.emit(f"所有图片文件夹上传完成,共处理 {len(image_folders)} 个文件夹")

212
main.py
View File

@@ -364,12 +364,15 @@ class Pdd:
print(f"下载失败: {e}") print(f"下载失败: {e}")
return None return None
def action(self, folder_path=None, collect_all_videos=False): def action(self, folder_path=None, collect_all_videos=False, prepared_video_files=None,
prepared_image_folders=None):
logger.info("=" * 50) logger.info("=" * 50)
logger.info("开始执行 action 方法(单个/批量上传模式)") logger.info("开始执行 action 方法(单个/批量上传模式)")
logger.info(f"多多ID: {self.user_id}, 序号: {self.index}") logger.info(f"多多ID: {self.user_id}, 序号: {self.index}")
logger.info(f"文件夹路径: {folder_path}") logger.info(f"文件夹路径: {folder_path}")
logger.info(f"批量上传模式: {collect_all_videos}") logger.info(f"批量上传模式: {collect_all_videos}")
logger.info(
f"使用预查找文件: 视频={len(prepared_video_files) if prepared_video_files else 0}, 图片文件夹={len(prepared_image_folders) if prepared_image_folders else 0}")
logger.info("=" * 50) logger.info("=" * 50)
logger.info("步骤1: 创建浏览器页面...") logger.info("步骤1: 创建浏览器页面...")
@@ -388,14 +391,11 @@ class Pdd:
logger.info(" ✓ 已打开登录页面") logger.info(" ✓ 已打开登录页面")
for i in range(100): for i in range(100):
if self.page.ele("x://*[text()='登录']", timeout=5): if self.page.ele("x://*[text()='主播/作者管理']", timeout=5):
logger.warning(" 请登录》》》")
elif self.page.ele("x://*[text()='主播/作者管理']", timeout=5):
logger.info(" ✓ 检测到已登录") logger.info(" ✓ 检测到已登录")
break break
else:
if i in [5,10,15,20,35,30]: time.sleep(2 * 60)
self.page.get(url="https://mcn.pinduoduo.com/register")
else: else:
logger.error(" ✗ 未登录!!!") logger.error(" ✗ 未登录!!!")
return {"ok": False, "reason": "未登录"} return {"ok": False, "reason": "未登录"}
@@ -441,15 +441,28 @@ class Pdd:
# 从文件夹读取文件 # 从文件夹读取文件
video_file_paths = [] # 视频文件列表 video_file_paths = [] # 视频文件列表
image_folder_paths = [] # 图片文件夹列表(用于存储图片文件夹路径) image_folder_paths = [] # 图片文件夹列表(用于存储图片文件夹路径)
if folder_path and os.path.exists(folder_path):
# 支持的视频格式
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.webm']
# 支持的图片格式
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
if prepared_video_files is not None or prepared_image_folders is not None:
# 使用 GUI "更新数据"按钮预查找的文件,跳过文件夹扫描
video_file_paths = list(prepared_video_files) if prepared_video_files else []
image_folder_paths = list(prepared_image_folders) if prepared_image_folders else []
logger.info("=" * 50)
logger.info("使用预查找的文件列表,跳过文件夹扫描")
logger.info(f" 视频文件数量: {len(video_file_paths)}")
logger.info(f" 图片文件夹数量: {len(image_folder_paths)}")
if video_file_paths:
logger.info("视频文件列表:")
for idx, video_path in enumerate(video_file_paths, 1):
logger.info(f" {idx}. {video_path}")
elif folder_path and os.path.exists(folder_path):
logger.info(f"开始读取文件夹: {folder_path}") logger.info(f"开始读取文件夹: {folder_path}")
logger.info(f"查找序号为 '{self.index}' 的文件") logger.info(f"查找序号为 '{self.index}' 的文件")
# 支持的视频格式
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.webm']
# 支持的图片格式
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
# 第一步:先收集所有匹配的视频文件和图片文件夹 # 第一步:先收集所有匹配的视频文件和图片文件夹
logger.info("=" * 50) logger.info("=" * 50)
if collect_all_videos: if collect_all_videos:
@@ -549,102 +562,92 @@ class Pdd:
for idx, video_path in enumerate(video_file_paths, 1): for idx, video_path in enumerate(video_file_paths, 1):
logger.info(f" {idx}. {video_path}") logger.info(f" {idx}. {video_path}")
# 第二步:如果有视频文件,批量上传所有视频 # 第二步:如果有视频文件,批量上传所有视频(此段代码在 if/elif 外面,对预查找和扫描两种方式都生效)
if video_file_paths: if video_file_paths:
logger.info("=" * 50) logger.info("=" * 50)
logger.info(f"第二步:开始批量上传 {len(video_file_paths)} 个视频文件...") logger.info(f"第二步:开始批量上传 {len(video_file_paths)} 个视频文件...")
# 检查是否有"支持批量上传"按钮,如果有则使用批量上传 # 检查是否有"支持批量上传"按钮,如果有则使用批量上传
logger.info("查找批量上传按钮...") logger.info("查找批量上传按钮...")
batch_upload_btn = creator_tab.ele("x://*[text()='支持批量上传']", timeout=3) batch_upload_btn = creator_tab.ele("x://*[text()='支持批量上传']", timeout=3)
if batch_upload_btn: if batch_upload_btn:
logger.info("✓ 找到'支持批量上传'按钮") logger.info("✓ 找到'支持批量上传'按钮")
if len(video_file_paths) > 1: if len(video_file_paths) > 1:
logger.info(f"使用批量上传模式,准备上传 {len(video_file_paths)} 个视频") logger.info(f"使用批量上传模式,准备上传 {len(video_file_paths)} 个视频")
logger.info(f"上传文件列表: {[str(p) for p in video_file_paths]}") logger.info(f"上传文件列表: {[str(p) for p in video_file_paths]}")
batch_upload_btn.click.to_upload(video_file_paths) batch_upload_btn.click.to_upload(video_file_paths)
logger.info(f"✓ 已触发批量上传,上传 {len(video_file_paths)} 个视频") logger.info(f"✓ 已触发批量上传,上传 {len(video_file_paths)} 个视频")
else:
logger.info(f"只有1个视频使用批量上传按钮上传")
batch_upload_btn.click.to_upload(video_file_paths)
logger.info(f"✓ 已触发上传")
else: else:
logger.warning("✗ 未找到'支持批量上传'按钮,尝试使用'添加视频'按钮") logger.info(f"只有1个视频使用批量上传按钮上传")
# 使用单个上传(兼容旧版本或只有一个视频的情况) batch_upload_btn.click.to_upload(video_file_paths)
upload_btn = creator_tab.ele("x://*[text()='添加视频']", timeout=3) logger.info(f"✓ 已触发上传")
if upload_btn:
logger.info("✓ 找到'添加视频'按钮")
logger.info(f"使用添加视频按钮,准备上传 {len(video_file_paths)} 个视频")
logger.info(f"上传文件列表: {[str(p) for p in video_file_paths]}")
upload_btn.click.to_upload(video_file_paths)
logger.info(f"✓ 已触发上传,上传 {len(video_file_paths)} 个视频")
else:
logger.error("✗ 未找到任何上传按钮!")
logger.info("等待上传完成...")
while True:
# if creator_tab.ele("x://div[contains(text(), '视频上传成功')]", timeout=3):
if creator_tab.ele('x://*[contains(., "视频上传成功")]', timeout=3):
print(1)
break
time.sleep(5)
# 输入视频描述(只输入第一个视频的描述,因为批量上传后可能需要单独处理每个视频)
if video_file_paths:
first_video_name = video_file_paths[0].name
file_names = first_video_name.split("-")
if len(file_names) > 0:
desc_text = file_names[-1].split(".")[0] + self.ht
logger.info(f"准备输入视频描述: {desc_text[:50]}...")
desc_input = creator_tab.ele(f'x://*[@id="magicdomid1"]', timeout=3)
if desc_input:
desc_input.input(vals=desc_text, clear=True)
logger.info(f"✓ 已输入视频描述")
else:
logger.warning("✗ 未找到描述输入框")
else: else:
logger.warning("未找到任何视频文件,跳过视频上传") logger.warning("未找到'支持批量上传'按钮,尝试使用'添加视频'按钮")
# 使用单个上传(兼容旧版本或只有一个视频的情况)
upload_btn = creator_tab.ele("x://*[text()='添加视频']", timeout=3)
if upload_btn:
logger.info("✓ 找到'添加视频'按钮")
logger.info(f"使用添加视频按钮,准备上传 {len(video_file_paths)} 个视频")
logger.info(f"上传文件列表: {[str(p) for p in video_file_paths]}")
upload_btn.click.to_upload(video_file_paths)
logger.info(f"✓ 已触发上传,上传 {len(video_file_paths)} 个视频")
else:
logger.error("✗ 未找到任何上传按钮!")
# 第三步:如果有图片文件夹,逐个上传图片 logger.info("等待上传完成...")
if image_folder_paths: while True:
logger.info(f"找到 {len(image_folder_paths)} 个图片文件夹,开始逐个上传...") # if creator_tab.ele("x://div[contains(text(), '视频上传成功')]", timeout=3):
for image_folder_path in image_folder_paths: if creator_tab.ele('x://*[contains(., "视频上传成功")]', timeout=3):
image_files = [] print(1)
# 收集图片文件夹中的所有图片文件 break
if image_folder_path.is_dir(): time.sleep(5)
for img_file in os.listdir(image_folder_path):
img_path = Path(os.path.join(image_folder_path, img_file))
if img_path.is_file() and any(img_path.suffix.lower() == ext for ext in image_extensions):
image_files.append(str(img_path))
if image_files: # 输入视频描述(只输入第一个视频的描述,因为批量上传后可能需要单独处理每个视频)
creator_tab.ele('x://*[text()="添加图片"]').click.to_upload(image_files) if video_file_paths:
time.sleep(3) first_video_name = video_file_paths[0].name
file_names = first_video_name.split("-")
if len(file_names) > 0:
desc_text = file_names[-1].split(".")[0] + self.ht
logger.info(f"准备输入视频描述: {desc_text[:50]}...")
desc_input = creator_tab.ele(f'x://*[@id="magicdomid1"]', timeout=3)
if desc_input:
desc_input.input(vals=desc_text, clear=True)
logger.info(f"✓ 已输入视频描述")
else:
logger.warning("✗ 未找到描述输入框")
else:
logger.warning("未找到任何视频文件,跳过视频上传")
# 提取文件夹名称用于标题和描述 # 第三步:如果有图片文件夹,逐个上传图片
folder_name = image_folder_path.name if image_folder_paths:
file_names = folder_name.split("-") logger.info(f"找到 {len(image_folder_paths)} 个图片文件夹,开始逐个上传...")
if len(file_names) > 1: for image_folder_path in image_folder_paths:
creator_tab.ele('x://*[@placeholder="添加标题"]').input(vals=file_names[1], clear=True) image_files = []
# 收集图片文件夹中的所有图片文件
if image_folder_path.is_dir():
for img_file in os.listdir(image_folder_path):
img_path = Path(os.path.join(image_folder_path, img_file))
if img_path.is_file() and any(img_path.suffix.lower() == ext for ext in image_extensions):
image_files.append(str(img_path))
xpath_path = creator_tab.ele('x://*[text()="添加视频描述"]').xpath if image_files:
# 方法2使用正则表达式替换最后一个div[1] creator_tab.ele('x://*[text()="添加图片"]').click.to_upload(image_files)
new_path = re.sub(r'div\[1\]$', 'div[2]', xpath_path) time.sleep(3)
new_path += "/div/div[3]/div/div/div"
if len(file_names) > 2:
creator_tab.ele(f'x:{new_path}').input(vals=file_names[2] + " " + self.ht, clear=True)
logger.info(f"已上传图片文件夹: {folder_name}")
# if ".mp4" in file_path: # 提取文件夹名称用于标题和描述
# # 上传视频 folder_name = image_folder_path.name
# creator_tab.ele("x://*[text()='添加视频']").click.to_upload(file_path) file_names = folder_name.split("-")
# else: if len(file_names) > 1:
# # 上传图片 creator_tab.ele('x://*[@placeholder="添加标题"]').input(vals=file_names[1], clear=True)
# image_files = [f for f in files if os.path.splitext(f)[1].lower() in image_extensions]
# path_datas = image_files xpath_path = creator_tab.ele('x://*[text()="添加视频描述"]').xpath
# if path_datas: # 方法2使用正则表达式替换最后一个div[1]
# creator_tab.ele('x://*[text()="添加图片"]').click.to_upload(path_datas) new_path = re.sub(r'div\[1\]$', 'div[2]', xpath_path)
new_path += "/div/div[3]/div/div/div"
if len(file_names) > 2:
creator_tab.ele(f'x:{new_path}').input(vals=file_names[2] + " " + self.ht, clear=True)
logger.info(f"已上传图片文件夹: {folder_name}")
time.sleep(1) time.sleep(1)
@@ -945,14 +948,11 @@ class Pdd:
logger.info(" ✓ 已打开登录页面") logger.info(" ✓ 已打开登录页面")
for i in range(100): for i in range(100):
if self.page.ele("x://*[text()='登录']", timeout=5): if self.page.ele("x://*[text()='主播/作者管理']", timeout=5):
logger.warning(" 请登录》》》")
elif self.page.ele("x://*[text()='主播/作者管理']", timeout=5):
logger.info(" ✓ 检测到已登录") logger.info(" ✓ 检测到已登录")
break break
else:
if i in [5, 10, 15, 20, 35, 30]: time.sleep(5 * 60)
self.page.get(url="https://mcn.pinduoduo.com/register")
else: else:
logger.error(" ✗ 未登录!!!") logger.error(" ✗ 未登录!!!")
return {"ok": False, "results": [], "reason": "未登录"} return {"ok": False, "results": [], "reason": "未登录"}

View File

@@ -1 +0,0 @@
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJMSUNFTlNFIiwicm9vdF9oYXNoIjoiUGIwc2tBVUxaUzFqWldTQnctV0hIRkltRlhVcExiZDlUcVkwR2ZHSHBWcyJ9LHsicGF0aCI6ImNybC1zZXQiLCJyb290X2hhc2giOiIyaUpyeWQ0NlRvUGxPYWxPb3l1RmExT2hrVzBIMHlVQnlvV3NvdDlTOWpNIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6Ik8tcnZVcExmSk9sb1FDeDlaWVpScjdjUHlkWVhEVEY0dms0bGNlMi1zSUUifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJoZm5rcGltbGhoZ2llYWRkZ2ZlbWpob2ZtZmJsbW5pYiIsIml0ZW1fdmVyc2lvbiI6IjEwMzI2IiwicHJvdG9jb2xfdmVyc2lvbiI6MX0","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"S-w8zKp-2whgHXEXNExGRf9tHgnQLRg0JOhoIL9SdD3WQwR8wlXXaoMqI9Ex10hxs5CiNr3SneL7uuxTmi_R4wrxV4FlXTqaPpY1bwzXRXoVeuOgsqvONYlmkgLbIboQp-BRfS9BRHjj9fTgCArM8CXbqf2MYcoltBRtkOhu_WBEuykkE-5KfruOspjbBrbgkQFnSZ6MhjHxw2WRchSP2BLGp_Lhix-Na7ORyIZR5sRfABjubAp28DNF0nZ4-j4Ic-Cxi4V2An6SeAuum_JI1-rKg1VO1wqvk2sq493-w9ByO72zR4i53ztq383TWkUOtYzKU3GFPu5NdAuDBTRErw"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"MXk8uIFz1lA6bNw1JRoi9IT9Afxzk1sEb4jvFNG8vxDStZ0pHTg5U1Wp4VulDg1xxFZcCbjRAifH3LDs8SEnJhhQ_LO7rfNil36g8_7RWS_45l3V8gfHo7A45lRzRfH8ofJOFu6z6vyPAslZ7NYTYvCDHEQODfky3m8_AxSnkYPWD9ZooraVABzvf39BChyl58e0ehpRalk0APEVlxGzKmuePjTLpSGU_pbwauXCtsNKob1gnV4NWuPNubngR_kNGzxhN7aZmJxySH10Qpne9xCGrrU9j8V2RtiR84MWvrd4Uwow0qw57Rgvu_aZQicejqvbbrymGyp1skpdTNlo9w"}]}}]

View File

@@ -1,5 +0,0 @@
{
"manifest_version": 2,
"name": "crl-set-816998340439263387.data",
"version": "10326"
}

View File

@@ -0,0 +1 @@
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJMSUNFTlNFIiwicm9vdF9oYXNoIjoiUGIwc2tBVUxaUzFqWldTQnctV0hIRkltRlhVcExiZDlUcVkwR2ZHSHBWcyJ9LHsicGF0aCI6ImNybC1zZXQiLCJyb290X2hhc2giOiJObVhQUWc1WTZvZ09OUjJSTHNzdTQtcEJNRXBLcGlKSk5QTjRMbFJqU1E0In0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6InpPcGlYNXJrNFp1NFlLSUUyUkVwS1dXZWFJUDNtS0hsak5ObkdycDJuUmMifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJoZm5rcGltbGhoZ2llYWRkZ2ZlbWpob2ZtZmJsbW5pYiIsIml0ZW1fdmVyc2lvbiI6IjEwMzMxIiwicHJvdG9jb2xfdmVyc2lvbiI6MX0","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"Z4e0tOPYmXmWA1r-dqISL5iVnQN7tTTW_yUoUfh0mmuxrvSC7uBWcsPh7Tc7AFyj43bgIM4FQAdmQW72N-Vuj9fLdC1O-HFYhjrP1WR3WnaFxl7KEqEg76IAHlk7BIVuW0TpZr-v5S5yCfVBAoVXcMKsZvVJLbDejlbdTfdNLbyjewkVsmpksyTS1vbIPkQx0g-XbpmnHKu_i5ed5SVXCjWjAC1SMSUL1asei_7VYucxXE8Jez59fW6n7T1P7iTlw8R2-HMVSvmItFQPu3XtjO3gp5pnKHmmsrEq-FIhnOsb5RwqZxm7udhlLsItrBVBwYlTG7G-NAIl82xdPMxrFA"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"PGAIGHVxTb6u8O3khEmgWcGRi4yljbZSSRK3k2BSy51jKwNN27bCeuBwvkt99utaoiJs3hTtxnNu2SD-uXZU7i7Dyb_ddXL02TdCjtkLZfWcXKtzfEQyyUaVhkewok24bytwPTuBBJ3Rv1oEbsGJA3VvzFfct71iZHn6aNUnRYY92o0ydSzJnYqiOvWEEJIWQWj-Co8I4apo0QRKnnz-j7_EVQszXH8-dBtZwp2mmf80BHCOueyiAlFrSjGB-91SVo3u4LkHnCOxUcSifjKKdvamu1ipXfR3JyiVp9Gval-AIuLBGbnTObM3irV6cHFNrEFcI8TbbknXBN3OCSpTpg"}]}}]

View File

@@ -0,0 +1,5 @@
{
"manifest_version": 2,
"name": "crl-set-12388884131062641242.data",
"version": "10331"
}

View File

@@ -1 +0,0 @@
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJQcmVsb2FkIERhdGEiLCJyb290X2hhc2giOiI5MFptcFJQTGh3c2xxbXBka2hMclUzODgzSHVTYXJkUFBkYVZfSWF2ZUtZIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6Imh5Z0tCY2hTMXhKTXRvV2tIWURCaDZmYVBGaU1haFFlMkxFb3o1cWxTREkifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJnZ2trZWhnYm5manBlZ2dmcGxlZWFrcGlkYmtpYmJtbiIsIml0ZW1fdmVyc2lvbiI6IjIwMjYuMi4yLjEyMSIsInByb3RvY29sX3ZlcnNpb24iOjF9","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"IG_xMeVVGDbd9FINYr7EcsO1rObec7fteiBx7PwXXzKnxGyRYlwkRCHb-baFAcLekoUCL780XLPjMbkQVnb7PqsTyrXfX7-MCSjLM1kcNIiCpm9GoXg_m0SH2TgmsX8RR3xLNgpvwhu3ulyzIXnzvD_xEed_6vgqSbjMhqPv9VsV3clh_NzFqPIz1J_uJkcicPKxQ7xJAZBKhF6UZLdY2Pqewr2q4Moiz2RhraeDCfhTy0iaio994QXKd1Eu4yIQINQw-zyBut4rFlCNdZT8xjcpukpvfxgSt-J07R6sFuvMZESu3-NGjIKoEzZckLisOCtGYnF75Lp-tDkxCDy6PwOhfAneodxYpO-swCQjaNz1oKfjvXpJTi0r9FoNYfKHKEsZEBOLejpTj3pQsp-ZMNnd77W0daCr3avNOCjEqRXjnc7IfoaJgLK9xXhj8D8WihQnpLTE3Wih3KHzgqaU0LJ0GPGb0rXvAvtvKExBzuiClTpcwWdJwaut1aL7-YbkdPGQ2qYm0IMAMb7QiSpR48zjKyXt_97fjSvXMt75I1fpkxbBLmU2wVO6LiPTXMPSaDM_Uz0KPd8ToCg1xJd5n-n-BHHHFLeNlV0wYoR-N2wgCUpF0YMEcBplKeyeEVw3A_wnQRd6TzyWz0vZE1SLpusHaVvn404mrrU8EIoeLao"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"CR_JAvPr3BrvEdRIKZ2FxmoxYoBWnZWhgSl5Ush_kT5S3DZu_e-OOVHhpIZI4vM7cYfaChgG72LNI2E_-g7HCf1nEaM5_smP0TxWKXH15X1zvW0SqSPYaXR3An_2TDh9dg2PQoyxk3CxWFFG2OiZfdA3sCBW6ezL_PfGrVK5YT00uKs0o4cXSqRbtNRDsYf18NyEw-z2VAS9qAbjMTZ4NqZTz8A9r1OvSrZPzONB4kwmfDc7wzzBne0cJbmUAeU1VETFaqyfH4HV3WnIM-9Rux0OAX2zqLuff_JUQKJ0He399JYsfsRIlGZl3_u3Hfj9oMNfOplRTrmvBl7cMFtdlw"}]}}]

View File

@@ -0,0 +1 @@
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJQcmVsb2FkIERhdGEiLCJyb290X2hhc2giOiJoekY1UkNteUtTbXM5UFpCRWxRMGZyaUtyQVJEcVdwLWppbG02Z2hCRjBzIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6IkhhZjZ6WmU5Y3lOSG10aEM4eDI3ZUg4aG5pN0dHUC1HMTFVYTlVMVV1aXcifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJnZ2trZWhnYm5manBlZ2dmcGxlZWFrcGlkYmtpYmJtbiIsIml0ZW1fdmVyc2lvbiI6IjIwMjYuMi41LjEyMSIsInByb3RvY29sX3ZlcnNpb24iOjF9","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"eVJJF-PSQ3jAPfMh2Dbrzkft-cpwj6HWMYUXJTFAReyynTotCOghPOzytVefekiDF2kvyE5Xv0jS0Nnfxib4lOh-ANN1w1rPX5YkHJEQJAbw9Gs7r1gPMfYNEpcCCZVgSZrhixcQTd0I-Hz2BXNlOKKfHu8erxD9vYXpOty13J8amhWkMeFc_OXx6mujUh8dTbNA8A2IXIf9GHdm_P72nqYgDYi6ctF2uzoBjgAYgJhoNet0K0eAbYop7YXFOSEsdJCyRqeo1bC8uQACp-aIiYItvxBWgHEdY3tBe-M2eiQbNEcaP00c20BBIEP9-FE5kDhXSQTmnxD3SvD5mmtqHsmPOlnwNMyKKoRLWVFrlemVuuvPJ-woW044SqEAJZw8qGrnABanerLHfNwrN2DKPr2lJdcUz2GTIhEsQggYP-QgZSLvfQBzdvfx0QmJPV8CXAfAiGcri6lxMda2nouzjkR0Bcergoij87co3V3xvH132EgFLC4u3htrvgAq-byuJICK3a9w-enUNUQOmFpeX7hwsmNzh7gnZKehZpi9TulCPmNfiwhIfazH4Gaa-iqDkfQx4zb0P_wNxO3tazaDjDpfuYdbwg35ixZ_Xw6w5ehNNwu95LPHs1kQixys8URcNFm6q72XyYjeCi_ukOrAUROSNR2BezGjL-SNHmILtHQ"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"M-2Vet11iRJ-SJPwHqhbjf-pCmQEWop3VBUAnVDIpPtxbSY7Nbhr0ZMWjO0uSyqYYpxNkzsYHG6mhckLzmK3ZEOwv51BLYTvHfEZYPVGno_5u42XVLYdSQJ6yX3DFNUbePDfjyilv5IOvybFtkyP6KNjT6q29vUFRUzdQ-UtkxNc42VmBD3L7gvbscGJTVIlRTfrrhytffYRoUGsFvSQSiWt-PZODTqnHsQTG0QUjTWNkQhDMcxqev1ZY8ZhkAqpfAaHEyeqefZNdlmdNy_AsNyJ9tEFxCMv9HttaqbNbx1PZwandPKUiSKu1W-iwObnLLlVU-nF_f8aXVKt-sxR7Q"}]}}]

View File

@@ -2,5 +2,5 @@
"manifest_version": 2, "manifest_version": 2,
"name": "Crowd Deny", "name": "Crowd Deny",
"preload_data_format": 1, "preload_data_format": 1,
"version": "2026.2.2.121" "version": "2026.2.5.121"
} }

View File

@@ -20,7 +20,14 @@
"padded_top_topics_start_index": 0, "padded_top_topics_start_index": 0,
"taxonomy_version": 0, "taxonomy_version": 0,
"top_topics_and_observing_domains": [ ] "top_topics_and_observing_domains": [ ]
}, {
"calculation_time": "13414941052302885",
"config_version": 0,
"model_version": "0",
"padded_top_topics_start_index": 0,
"taxonomy_version": 0,
"top_topics_and_observing_domains": [ ]
} ], } ],
"hex_encoded_hmac_key": "434BF7DBD7DA573B45E0A11AD9045A61B6221D14AE2F9A341E2FEF659AF071F6", "hex_encoded_hmac_key": "434BF7DBD7DA573B45E0A11AD9045A61B6221D14AE2F9A341E2FEF659AF071F6",
"next_scheduled_calculation_time": "13414862793247092" "next_scheduled_calculation_time": "13415545852303557"
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Some files were not shown because too many files have changed in this diff Show More