gui
第一版完整版
65
count_text_xpath.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
使用XPath语法统计HTML文件中"视频上传成功"文本的出现次数
|
||||
"""
|
||||
|
||||
from lxml import etree
|
||||
import html
|
||||
|
||||
def count_text_with_xpath(html_file, search_text):
|
||||
"""
|
||||
使用XPath查找指定文本在HTML中的出现次数
|
||||
|
||||
Args:
|
||||
html_file: HTML文件路径
|
||||
search_text: 要搜索的文本
|
||||
|
||||
Returns:
|
||||
出现次数
|
||||
"""
|
||||
# 读取HTML文件
|
||||
with open(html_file, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
|
||||
# 解析HTML
|
||||
parser = etree.HTMLParser()
|
||||
tree = etree.fromstring(html_content.encode('utf-8'), parser)
|
||||
|
||||
# XPath表达式:查找包含指定文本的所有节点
|
||||
# 使用contains()函数来匹配包含指定文本的节点
|
||||
xpath_expr = f"//text()[contains(., '{search_text}')]"
|
||||
|
||||
# 执行XPath查询
|
||||
results = tree.xpath(xpath_expr)
|
||||
|
||||
# 统计出现次数(可能一个文本节点包含多次出现)
|
||||
total_count = 0
|
||||
for text_node in results:
|
||||
# 计算该文本节点中搜索文本的出现次数
|
||||
count = text_node.count(search_text)
|
||||
total_count += count
|
||||
print(f"找到文本节点,内容片段: ...{text_node[:100]}... (包含 {count} 次)")
|
||||
|
||||
return total_count, len(results)
|
||||
|
||||
if __name__ == "__main__":
|
||||
html_file = "1.html"
|
||||
search_text = "视频上传成功"
|
||||
|
||||
print(f"正在使用XPath查找文本: '{search_text}'")
|
||||
print("=" * 60)
|
||||
|
||||
total_count, node_count = count_text_with_xpath(html_file, search_text)
|
||||
|
||||
print("=" * 60)
|
||||
print(f"XPath查询结果:")
|
||||
print(f" - 包含该文本的文本节点数量: {node_count}")
|
||||
print(f" - 文本总出现次数: {total_count}")
|
||||
|
||||
# 也提供其他XPath表达式示例
|
||||
print("\n" + "=" * 60)
|
||||
print("其他可用的XPath表达式示例:")
|
||||
print(f" 1. //text()[contains(., '{search_text}')]")
|
||||
print(f" 2. //*[contains(text(), '{search_text}')]")
|
||||
print(f" 3. //*[normalize-space(text())='{search_text}']")
|
||||
152
main.py
@@ -13,22 +13,23 @@ from DrissionPage import ChromiumPage, ChromiumOptions, SessionPage
|
||||
|
||||
class ThreadSafeDict:
|
||||
"""线程安全的字典包装类"""
|
||||
|
||||
def __init__(self):
|
||||
self._dict = {}
|
||||
self._lock = threading.Lock()
|
||||
|
||||
|
||||
def get(self, key, default=None):
|
||||
with self._lock:
|
||||
return self._dict.get(key, default)
|
||||
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
with self._lock:
|
||||
self._dict[key] = value
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
with self._lock:
|
||||
return self._dict[key]
|
||||
|
||||
|
||||
def __contains__(self, key):
|
||||
with self._lock:
|
||||
return key in self._dict
|
||||
@@ -50,7 +51,7 @@ class Pdd:
|
||||
self.title = title
|
||||
self.ht = self._format_topic(ht) # 格式化话题
|
||||
self.index = index
|
||||
|
||||
|
||||
def _format_topic(self, topic_str):
|
||||
"""
|
||||
格式化话题:将"拍出氛围感-好看照片分享"转换为"#拍出氛围感 #好看照片分享"
|
||||
@@ -63,10 +64,10 @@ class Pdd:
|
||||
"""
|
||||
if not topic_str:
|
||||
return ""
|
||||
|
||||
|
||||
# 按"-"分割话题
|
||||
parts = [part.strip() for part in topic_str.split("-") if part.strip()]
|
||||
|
||||
|
||||
# 为每个部分添加"#"
|
||||
formatted_parts = []
|
||||
for part in parts:
|
||||
@@ -75,7 +76,7 @@ class Pdd:
|
||||
formatted_parts.append(part)
|
||||
else:
|
||||
formatted_parts.append(f"#{part}")
|
||||
|
||||
|
||||
# 用空格连接
|
||||
return " ".join(formatted_parts)
|
||||
|
||||
@@ -330,7 +331,7 @@ class Pdd:
|
||||
logger.info(f"多多ID: {self.user_id}, 序号: {self.index}")
|
||||
logger.info(f"文件夹路径: {folder_path}")
|
||||
logger.info(f"批量上传模式: {collect_all_videos}")
|
||||
|
||||
|
||||
logger.info("=" * 50)
|
||||
logger.info("步骤1: 创建浏览器页面...")
|
||||
self.create_page()
|
||||
@@ -361,29 +362,29 @@ class Pdd:
|
||||
self.page.ele("x://*[text()='主播/作者管理']").click()
|
||||
logger.info(" ✓ 已点击'主播/作者管理'")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
self.page.ele("x://*[text()='签约主播/作者']").click()
|
||||
logger.info(" ✓ 已点击'签约主播/作者'")
|
||||
|
||||
|
||||
ele = self.page.ele("x://*[text()='我知道了']", timeout=3)
|
||||
if ele:
|
||||
ele.click()
|
||||
logger.info(" ✓ 已关闭提示框")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
logger.info(f" 正在搜索多多ID: {self.user_id}")
|
||||
self.page.ele('x://*[@placeholder="输入主播/作者ID搜索"]').input(vals=self.user_id, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele("x://*[text()='提交']").click()
|
||||
logger.info(" ✓ 已提交搜索")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
self.page.actions.move_to(ele_or_loc="x://*[text()='内容管理']")
|
||||
time.sleep(1)
|
||||
self.page.ele("x://*[text()='内容管理']").click()
|
||||
logger.info(" ✓ 已点击'内容管理'")
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
creator_tab = self.page.get_tab(url="home/creator/manage")
|
||||
creator_tab.ele("x://*[text()='发布视频']").click()
|
||||
logger.info(" ✓ 已点击'发布视频',进入发布页面")
|
||||
@@ -395,7 +396,7 @@ class Pdd:
|
||||
if folder_path and os.path.exists(folder_path):
|
||||
logger.info(f"开始读取文件夹: {folder_path}")
|
||||
logger.info(f"查找序号为 '{self.index}' 的文件")
|
||||
|
||||
|
||||
# 支持的视频格式
|
||||
video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.webm']
|
||||
# 支持的图片格式
|
||||
@@ -407,11 +408,11 @@ class Pdd:
|
||||
logger.info("第一步:扫描文件夹,收集所有视频文件(批量上传模式,不限制序号)...")
|
||||
else:
|
||||
logger.info(f"第一步:扫描文件夹,收集序号为 '{self.index}' 的文件...")
|
||||
|
||||
|
||||
# 获取最外层文件夹下的所有子文件夹(多多ID文件夹)
|
||||
subdirs = [f for f in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, f))]
|
||||
logger.info(f"在最外层文件夹下找到 {len(subdirs)} 个子文件夹(多多ID文件夹)")
|
||||
|
||||
|
||||
# 找到匹配当前多多ID的文件夹
|
||||
target_subdir = None
|
||||
for subdir_name in subdirs:
|
||||
@@ -419,7 +420,7 @@ class Pdd:
|
||||
target_subdir = os.path.join(folder_path, subdir_name)
|
||||
logger.info(f"✓ 找到匹配的多多ID文件夹: {subdir_name}")
|
||||
break
|
||||
|
||||
|
||||
if not target_subdir:
|
||||
logger.warning(f"未找到多多ID为 {self.user_id} 的文件夹")
|
||||
logger.info(f"可用的文件夹: {subdirs}")
|
||||
@@ -429,20 +430,20 @@ class Pdd:
|
||||
else:
|
||||
# 只扫描匹配的多多ID文件夹
|
||||
logger.info(f" 正在扫描子文件夹: {os.path.basename(target_subdir)}")
|
||||
|
||||
|
||||
# 获取该子文件夹下的所有文件和文件夹
|
||||
try:
|
||||
items = os.listdir(target_subdir)
|
||||
logger.info(f" 该文件夹下有 {len(items)} 个项目")
|
||||
|
||||
|
||||
for item_name in items:
|
||||
item_path = os.path.join(target_subdir, item_name)
|
||||
logger.info(f" 检查项目: {item_name}")
|
||||
|
||||
|
||||
# 分割文件名,检查第一部分是否匹配序号
|
||||
name_parts = item_name.split("-")
|
||||
logger.info(f" 文件名分割结果: {name_parts}")
|
||||
|
||||
|
||||
# 如果是批量上传模式,收集所有视频文件;否则只收集匹配序号的文件
|
||||
should_collect = False
|
||||
if collect_all_videos:
|
||||
@@ -455,16 +456,16 @@ class Pdd:
|
||||
if len(name_parts) > 0 and name_parts[0] == str(self.index):
|
||||
should_collect = True
|
||||
logger.info(f" ✓ 序号匹配!序号: {name_parts[0]}, 目标序号: {self.index}")
|
||||
|
||||
|
||||
if should_collect:
|
||||
path = Path(item_path)
|
||||
|
||||
|
||||
# 判断是否为文件(视频文件)
|
||||
if path.is_file():
|
||||
# 检查是否为视频文件
|
||||
file_ext = path.suffix.lower()
|
||||
logger.info(f" 这是一个文件,扩展名: {file_ext}")
|
||||
|
||||
|
||||
if any(file_ext == ext for ext in video_extensions):
|
||||
video_file_paths.append(path)
|
||||
logger.info(f" ✓ 添加到视频列表: {path.name}")
|
||||
@@ -494,7 +495,7 @@ class Pdd:
|
||||
logger.info(f"文件收集完成!")
|
||||
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):
|
||||
@@ -504,11 +505,11 @@ class Pdd:
|
||||
if video_file_paths:
|
||||
logger.info("=" * 50)
|
||||
logger.info(f"第二步:开始批量上传 {len(video_file_paths)} 个视频文件...")
|
||||
|
||||
|
||||
# 检查是否有"支持批量上传"按钮,如果有则使用批量上传
|
||||
logger.info("查找批量上传按钮...")
|
||||
batch_upload_btn = creator_tab.ele("x://*[text()='支持批量上传']", timeout=3)
|
||||
|
||||
|
||||
if batch_upload_btn:
|
||||
logger.info("✓ 找到'支持批量上传'按钮")
|
||||
if len(video_file_paths) > 1:
|
||||
@@ -532,10 +533,15 @@ class Pdd:
|
||||
logger.info(f"✓ 已触发上传,上传 {len(video_file_paths)} 个视频")
|
||||
else:
|
||||
logger.error("✗ 未找到任何上传按钮!")
|
||||
|
||||
|
||||
logger.info("等待上传完成...")
|
||||
time.sleep(5)
|
||||
|
||||
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
|
||||
@@ -563,17 +569,17 @@ class Pdd:
|
||||
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)
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
# 提取文件夹名称用于标题和描述
|
||||
folder_name = image_folder_path.name
|
||||
file_names = folder_name.split("-")
|
||||
if len(file_names) > 1:
|
||||
creator_tab.ele('x://*[@placeholder="添加标题"]').input(vals=file_names[1], clear=True)
|
||||
|
||||
|
||||
xpath_path = creator_tab.ele('x://*[text()="添加视频描述"]').xpath
|
||||
# 方法2:使用正则表达式替换最后一个div[1]
|
||||
new_path = re.sub(r'div\[1\]$', 'div[2]', xpath_path)
|
||||
@@ -593,7 +599,7 @@ class Pdd:
|
||||
# creator_tab.ele('x://*[text()="添加图片"]').click.to_upload(path_datas)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
# 点击立即发布选项
|
||||
logger.info("=" * 50)
|
||||
logger.info("步骤:选择发布方式...")
|
||||
@@ -855,7 +861,7 @@ class Pdd:
|
||||
logger.info("开始执行 action1 方法(批量上传模式)")
|
||||
logger.info(f"多多ID: {self.user_id}, 序号: {self.index}")
|
||||
logger.info(f"需要处理的视频数量: {len(folder_path) if folder_path else 0}")
|
||||
|
||||
|
||||
logger.info("=" * 50)
|
||||
logger.info("步骤1: 创建浏览器页面...")
|
||||
self.create_page()
|
||||
@@ -886,29 +892,29 @@ class Pdd:
|
||||
self.page.ele("x://*[text()='主播/作者管理']").click()
|
||||
logger.info(" ✓ 已点击'主播/作者管理'")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
self.page.ele("x://*[text()='签约主播/作者']").click()
|
||||
logger.info(" ✓ 已点击'签约主播/作者'")
|
||||
|
||||
|
||||
ele = self.page.ele("x://*[text()='我知道了']", timeout=3)
|
||||
if ele:
|
||||
ele.click()
|
||||
logger.info(" ✓ 已关闭提示框")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
logger.info(f" 正在搜索多多ID: {self.user_id}")
|
||||
self.page.ele('x://*[@placeholder="输入主播/作者ID搜索"]').input(vals=self.user_id, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele("x://*[text()='提交']").click()
|
||||
logger.info(" ✓ 已提交搜索")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
self.page.actions.move_to(ele_or_loc="x://*[text()='内容管理']")
|
||||
time.sleep(1)
|
||||
self.page.ele("x://*[text()='内容管理']").click()
|
||||
logger.info(" ✓ 已点击'内容管理'")
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
creator_tab = self.page.get_tab(url="home/creator/manage")
|
||||
creator_tab.ele("x://*[text()='发布视频']").click()
|
||||
logger.info(" ✓ 已点击'发布视频',进入发布页面")
|
||||
@@ -926,7 +932,7 @@ class Pdd:
|
||||
logger.info(f"=" * 50)
|
||||
logger.info(f"准备批量上传 {len(videos)} 个视频")
|
||||
logger.info(f"视频文件列表: {[str(v) for v in videos]}")
|
||||
|
||||
|
||||
# 优先使用"支持批量上传"按钮,如果没有则使用"添加视频"按钮
|
||||
logger.info("查找上传按钮...")
|
||||
batch_upload_btn = creator_tab.ele("x://*[text()='支持批量上传']", timeout=3)
|
||||
@@ -948,21 +954,21 @@ class Pdd:
|
||||
logger.error("✗ 未找到任何视频上传按钮!")
|
||||
creator_tab.close()
|
||||
return {"ok": False, "results": [], "reason": "未找到任何视频上传按钮"}
|
||||
|
||||
|
||||
# 等待一段时间让视频开始上传和页面渲染
|
||||
logger.info("=" * 50)
|
||||
logger.info("步骤4: 等待视频上传和页面渲染...")
|
||||
logger.info(f" 已触发上传 {len(videos)} 个视频,等待 {5} 秒让页面渲染...")
|
||||
time.sleep(5)
|
||||
logger.info(" ✓ 等待完成,开始处理视频详情")
|
||||
|
||||
|
||||
# 不等待所有视频上传完成,而是检测每个视频的状态
|
||||
# 只处理已上传完成的视频,跳过还在上传中的视频
|
||||
logger.info("=" * 50)
|
||||
logger.info("步骤5: 开始处理每个视频的详细信息...")
|
||||
logger.info(f" 需要处理的视频总数: {len(folder_path)}")
|
||||
logger.info(" 将逐个处理:输入描述 -> 设置定时 -> 绑定任务 -> 点击发布 -> 验证发布结果")
|
||||
|
||||
|
||||
results = []
|
||||
for idx, video_info in enumerate(folder_path):
|
||||
try:
|
||||
@@ -973,7 +979,7 @@ class Pdd:
|
||||
video_ht = self._format_topic(raw_ht) if raw_ht else self.ht
|
||||
video_time_start = video_info.get("time_start", self.time_start)
|
||||
video_url = video_info.get("url", self.url)
|
||||
|
||||
|
||||
logger.info("=" * 50)
|
||||
logger.info(f"处理第 {idx + 1}/{len(folder_path)} 个视频")
|
||||
logger.info(f" 视频名称: {video_name}")
|
||||
@@ -981,19 +987,19 @@ class Pdd:
|
||||
logger.info(f" 话题: {video_ht}")
|
||||
logger.info(f" 定时时间: {video_time_start}")
|
||||
logger.info(f" 绑定URL: {video_url[:50] if video_url else 'None'}...")
|
||||
|
||||
|
||||
# 定位视频容器:优先按文件名匹配,其次按索引
|
||||
logger.info(f" 正在定位视频容器...")
|
||||
video_container = None
|
||||
video_name_without_ext = video_name.rsplit(".", 1)[0]
|
||||
logger.info(f" 完整文件名: {video_name}")
|
||||
logger.info(f" 不含扩展名: {video_name_without_ext}")
|
||||
|
||||
|
||||
container_xpath = (
|
||||
'x://p[contains(text(), "文件名:") and contains(text(), "{name}")]'
|
||||
'/ancestor::div[contains(@class, "y0VjbyIp")][1]'
|
||||
)
|
||||
|
||||
|
||||
logger.info(f" 方法1: 按完整文件名匹配...")
|
||||
video_container = creator_tab.ele(
|
||||
container_xpath.format(name=video_name), timeout=5
|
||||
@@ -1007,7 +1013,7 @@ class Pdd:
|
||||
)
|
||||
if video_container:
|
||||
logger.info(f" ✓ 通过不含扩展名找到视频容器")
|
||||
|
||||
|
||||
if not video_container:
|
||||
logger.info(f" ✗ 按文件名未找到,尝试按索引匹配...")
|
||||
containers = creator_tab.eles(
|
||||
@@ -1032,12 +1038,15 @@ class Pdd:
|
||||
# 检测上传状态(在当前视频容器内)
|
||||
logger.info(f" 检测视频上传状态...")
|
||||
# 优先判断是否仍在上传,其次判断发布按钮是否可用
|
||||
uploading_text = video_container.ele(
|
||||
'x://*[contains(., "视频上传中")]', timeout=0.5
|
||||
)
|
||||
if uploading_text:
|
||||
|
||||
while True:
|
||||
if video_container.ele(
|
||||
'x://*[contains(., "视频上传成功")]', timeout=0.5
|
||||
):
|
||||
break
|
||||
|
||||
logger.warning(f" ✗ 视频 {video_name} 还在上传中,跳过处理")
|
||||
continue
|
||||
time.sleep(5)
|
||||
|
||||
success_text = video_container.ele(
|
||||
'x://*[contains(., "视频上传成功")]', timeout=0.5
|
||||
@@ -1055,7 +1064,7 @@ class Pdd:
|
||||
logger.warning(f" ✗ 视频 {video_name} 发布按钮仍禁用,跳过")
|
||||
continue
|
||||
logger.info(f" ✓ 未检测到成功标识,但发布按钮可用,继续处理 {video_name}")
|
||||
|
||||
|
||||
# 1. 输入视频描述
|
||||
logger.info(f" 步骤1: 输入视频描述...")
|
||||
try:
|
||||
@@ -1074,9 +1083,9 @@ class Pdd:
|
||||
logger.warning(f" ✗ 输入视频描述失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
# 给用户留出填写信息的时间
|
||||
if input_delay and input_delay > 0:
|
||||
logger.info(f"等待用户填写视频信息: {input_delay}s")
|
||||
@@ -1098,7 +1107,7 @@ class Pdd:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
logger.info(f" 步骤2: 跳过定时任务(未设置定时时间)")
|
||||
|
||||
|
||||
# 3. 绑定任务(如果该视频有URL)
|
||||
if video_url:
|
||||
logger.info(f" 步骤3: 绑定任务...")
|
||||
@@ -1132,7 +1141,7 @@ class Pdd:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
logger.info(f" 步骤3: 跳过绑定任务(未设置URL)")
|
||||
|
||||
|
||||
# 4. 点击该视频的发布按钮(注意:这里应该是单个视频的"发布"按钮,不是"立即发布")
|
||||
logger.info(f" 步骤4: 点击发布按钮...")
|
||||
try:
|
||||
@@ -1142,7 +1151,8 @@ class Pdd:
|
||||
if publish_btn:
|
||||
publish_btn.click()
|
||||
logger.info(f" ✓ 已点击发布按钮")
|
||||
ok, reason = self._verify_publish_success(creator_tab=creator_tab, video_container=video_container)
|
||||
ok, reason = self._verify_publish_success(creator_tab=creator_tab,
|
||||
video_container=video_container)
|
||||
if ok:
|
||||
logger.info(f" ✓ 发布校验通过:{reason}")
|
||||
else:
|
||||
@@ -1155,7 +1165,7 @@ class Pdd:
|
||||
logger.warning(f" ✗ {reason}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
results.append({
|
||||
"index": str(video_info.get("index", "")),
|
||||
"path": str(video_path),
|
||||
@@ -1165,9 +1175,10 @@ class Pdd:
|
||||
})
|
||||
logger.info(f" ✓ 视频 {video_name} 处理完成(ok={ok})")
|
||||
time.sleep(2) # 每个视频处理间隔
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理视频 {video_info.get('path', {}).name if hasattr(video_info.get('path', ''), 'name') else 'unknown'} 时出错: {e}")
|
||||
logger.error(
|
||||
f"处理视频 {video_info.get('path', {}).name if hasattr(video_info.get('path', ''), 'name') else 'unknown'} 时出错: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
try:
|
||||
@@ -1193,13 +1204,13 @@ class Pdd:
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
logger.info(" - 未找到'我已阅读并同意'复选框,可能已默认勾选")
|
||||
|
||||
|
||||
one_key_publish = creator_tab.ele('x://*[text()="一键发布"]', timeout=3)
|
||||
if one_key_publish:
|
||||
logger.info(" ✓ 找到'一键发布'按钮")
|
||||
one_key_publish.click()
|
||||
logger.info(" ✓ 已点击一键发布按钮")
|
||||
|
||||
|
||||
logger.info(" 正在验证一键发布是否成功...")
|
||||
ok, reason = self._verify_publish_success(creator_tab=creator_tab, video_container=None)
|
||||
if ok:
|
||||
@@ -1219,7 +1230,7 @@ class Pdd:
|
||||
success_count = sum(1 for r in results if r.get("ok")) if results else 0
|
||||
total_count = len(results) if results else 0
|
||||
logger.info(f"批量上传任务完成,成功: {success_count}/{total_count}")
|
||||
|
||||
|
||||
creator_tab.close()
|
||||
return {"ok": all(r.get("ok") for r in results) if results else True, "results": results}
|
||||
|
||||
@@ -1240,7 +1251,7 @@ class Pdd:
|
||||
def _has_text_anywhere(keywords):
|
||||
for kw in keywords:
|
||||
# 全局找,兼容 toast
|
||||
if creator_tab.ele(f'x://*[contains(text(), "{kw}")]' , timeout=0.2):
|
||||
if creator_tab.ele(f'x://*[contains(text(), "{kw}")]', timeout=0.2):
|
||||
return kw
|
||||
return None
|
||||
|
||||
@@ -1291,7 +1302,8 @@ class Pdd:
|
||||
minute = dt.minute
|
||||
second = dt.second
|
||||
|
||||
logger.info(f"开始设置定时时间: {time_start} (年={year}, 月={month}, 日={day}, 时={hour}, 分={minute}, 秒={second})")
|
||||
logger.info(
|
||||
f"开始设置定时时间: {time_start} (年={year}, 月={month}, 日={day}, 时={hour}, 分={minute}, 秒={second})")
|
||||
|
||||
# 定位日期选择器(优先容器内,且优先 active)
|
||||
date_picker_ele = None
|
||||
@@ -1326,7 +1338,7 @@ class Pdd:
|
||||
)
|
||||
if not date_picker_ele:
|
||||
date_picker_ele = creator_tab.ele('x://*[@placeholder="选择日期"]', timeout=2)
|
||||
|
||||
|
||||
if not date_picker_ele:
|
||||
logger.warning("未找到日期选择器")
|
||||
return
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJMSUNFTlNFIiwicm9vdF9oYXNoIjoiUGIwc2tBVUxaUzFqWldTQnctV0hIRkltRlhVcExiZDlUcVkwR2ZHSHBWcyJ9LHsicGF0aCI6ImNybC1zZXQiLCJyb290X2hhc2giOiIwT1hzMUU5V1NzdWNqakhBZjJoNklFcUpEbkFWUG94b0dYVDl0S2huU0QwIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6IjJYVzc0aTRFd0dMTTI2Smt3dlNaaWJBOE43Q3NBbkdydExudzRaZFRBYncifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJoZm5rcGltbGhoZ2llYWRkZ2ZlbWpob2ZtZmJsbW5pYiIsIml0ZW1fdmVyc2lvbiI6IjEwMjk2IiwicHJvdG9jb2xfdmVyc2lvbiI6MX0","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"YXLs0Q83_L3M0gPLp4aYQN7d1uZW-uU39Tcdwgh5PmWdyxXSy3uKnKx6DPybuXOps2on1ke7WZowwF1FagV3hGr9s1UG0iTZyTrQq1YW9x2g8IHAgePLnvy8Sq_l5gKuLy1NxQLR9_pzRKK5PqinqSmO27F8AdeB6_48POAW8_Ge3k5UkuFWDleGP_EMMng16oGZxly8pueije4mm9E7dWH37pd0gsQBnbNjyv2ASIDE6dB1WMBi2e-8EsjnsT7hlfcG2DnRuji_CzEj_6Sc5iM4P6IvdUUnO0c4ZbxBkPegWoEBJmJus2WF-yhURrnyBTrkvshRpKOAXpkUjaQwJw"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"aTT2x5gNOldxO8GtXuvejMGF_mUtZ66kpeMJtqu7EdOV3nT9u5-a5TXXgQdzejZ6SSYh_ZzOKTZ7mJGunyZwhP0qNWJmUXc1NXaMzOiaHPYsZpVo2YlAhDdz0CYchmVcF8W_tBSzlfVIiJQtBt_7zcGgNkVlyHMgFFbqvmqpXFdOeiw8GcvRrNIcqwcJwVCVkI2ABuYaTENqf5R_cQriz7VSQLQM-1S5rUndlt8DAPdDPoYxXe9untYRpVJR2Y24cWbXBZDWhnnKUNnqq33c4CgI2HPSjGUupfVd2AXTZHjZphg164W38--Pf-4no-eSY7KzRtfDZehYfa97YmwApA"}]}}]
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "crl-set-504360167468929874.data",
|
||||
"version": "10296"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJMSUNFTlNFIiwicm9vdF9oYXNoIjoiUGIwc2tBVUxaUzFqWldTQnctV0hIRkltRlhVcExiZDlUcVkwR2ZHSHBWcyJ9LHsicGF0aCI6ImNybC1zZXQiLCJyb290X2hhc2giOiJxc2VZaFhsTXZhQnhNM1Zid3BBSWJqS2poTkpFT01Ea3ZZOWlqQ0lhcDkwIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6IjhJQUFvM29KdGRoOHRGc2pKOTlESkU1eV9SbHhLSWZDVW9oRUdRbkJpeUkifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJoZm5rcGltbGhoZ2llYWRkZ2ZlbWpob2ZtZmJsbW5pYiIsIml0ZW1fdmVyc2lvbiI6IjEwMzA3IiwicHJvdG9jb2xfdmVyc2lvbiI6MX0","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"L9wa9CxYCiLN3LGfDCio34UqBituUk-4k_q3H_PNf_fAhQAbVru03IuMWOOWu4l1Q1aW23z-YMLV4ODnFoTAgGlXO7rjuyAt_5mBt8jluTAe93PHS1P9rKWTqs6Z3qENYaW79ProSkUzUlVrrxL22KmlOxQNTB-xRKyY74MVLQgRdjiaao74MR28V687FfBXhq30HuJSUpe67Kc9s_cv5ljML9CC-zKS7XukzzggnKT015KjBoOasBC4AGKGteBAbg1ZhlYqGBMlCcNIl32a1t1aCtokWRXFpBE3u2QQ7pyC8_v9SjMWw8tS5oqSO39TeN2JQh4cPQLMx4DtJPrbOw"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"exsPHW9GxYkge29gKXpxCyw-Fc5nv9q6wf5yA6pb2nws8hnKA5iyDy4jTFZGZMI8ecuMvbTtNf634nqeuAMVjfY31MpkM6JLOCBolKKt-0UkgDQUcMemUQ2Asz-PrYhyZbkCcHdp3T_0vxH73Yr57hHDWdwy2BL6Grdbc8OW8roMrKCbMoPJql8MKZ44Hz6dha2EeZedhSayroCYO3sVTAxx_yGIhIj461kH1bq7ptp09-Xj17KjSVISAAR9MKGhe6p_hlOraOsVGDBkN0K9ZucjDDjqkruW9mQFJRxStTqYYOziMJ102LrbL_qXjPIttE8MrsLlW73IJg2YXcq82g"}]}}]
|
||||
5
user/user_data/CertificateRevocation/10307/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "crl-set-7963983377537360531.data",
|
||||
"version": "10307"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJQcmVsb2FkIERhdGEiLCJyb290X2hhc2giOiJ2SmRrSTNTaTFPU2VZclJxcUZ4eF9EWnVvZDU5MW1TNUpRemR3M3d2Tk00In0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6Ims5SUlFNEw5NG9rNU9HYlloaU5YX2dGUlBVS2pvNGY2WUVISHFfUnNXb1UifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJnZ2trZWhnYm5manBlZ2dmcGxlZWFrcGlkYmtpYmJtbiIsIml0ZW1fdmVyc2lvbiI6IjIwMjYuMS4xOS4xMjIiLCJwcm90b2NvbF92ZXJzaW9uIjoxfQ","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"l6yh6ZHWkfyvTX9e4H_-csXTYv1AAnMecW0gOKdfXbnRA4HqQ8AIGBvr1Ibyug2Q8KoX6dRbWY3OlHmQmlQoL9g2pTVKqteG2iWwQchHh_t6veo_sKOQMuwzeydudIQm5riyaFUPusuSytDv4XtXE3-3RjQdnfRmS4VrLwH33rKgwhOYw3c2IbSKo3cJJrgqbnXx-sbIQpzf60vqwaf5CyjXmobdj2apWUighknjhFCvE7k6E69RlwnPjU-cK3i1-4y2tVObiGcA42cC7LLh_fY4D10JBOXpvYHbjP0ZDtuXv4ACcL9fhiu1NB1Z-QtK0C-7AiQZUbbBBIlHcgfStaC0dAkARfKx3hTMa60f70RthkC3mrXzla-pjBKILHlUni3M6vaVjHKWyk1dQ8Wm-zVOxq5gJINxlrt0Zd8WVPy8Ccopb5vNrhF6J2dvBA7DRtWa1gmznZkAXAJiYzwkfOKXAAKNCicMMmNUNZQb429MOthvHoIfn_VaGiZsYxoR5W9uxV_tWOlSkMGWtbK1jK7ZrmKPvqvxwT1SW3vE6QSgjrzilZh2uS0RF0C7C4FKQlxJBCKHtTdBUmYLXCsuHGg83MJf2Okr_Il9mvHdt2-LwvnN0SSqCidarU0vLgHxum05aw1xOVFj0lXmGg4JkiA0HbjwDcQHryK4jtTZu2o"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"hEAe1oy7FE8wFao4U-UY_437QxggdEoyxGXPvy1zKn8myE_o4vzKb2DuoN6MzUG7cgQXOJSZIum6rtASLFUh6q6eE2SgYPZlzdn4Ahqlwu_nU2hnw6QY7QCm0BDdPcc_xsVa8eqCUfAi69UEMhzitot-R9xzSiXNJOUc2XYZfUVgzErcU87xtRJU7icdpGkK9P8-aDLoFfbXPa2B0cLAPYuM4VChZwD8TjYvuw7joJ0CESvC8fA_V7lSwHwLQnd50Zua1i84m6E1QONPjdupgQ2kL7XsI9ArN_a-iaajI7kkJLTL_HXS2TEbIGDkvot_jKYTC0yGs0wE7ZjN0BmbmA"}]}}]
|
||||
@@ -0,0 +1 @@
|
||||
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJQcmVsb2FkIERhdGEiLCJyb290X2hhc2giOiJiNC0wYkRLUmVrY3RITURHVGNlb2pnT2kxRWw3Z0FEYUVkR1ZFdGRPbHo4In0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6IkstcUhQUW5udTlDSDFNWHpwZk9qZVlWZzNuei1FZ0E2elJyeHhBT09oaFUifV0sImZvcm1hdCI6InRyZWVoYXNoIiwiaGFzaF9ibG9ja19zaXplIjo0MDk2fV0sIml0ZW1faWQiOiJnZ2trZWhnYm5manBlZ2dmcGxlZWFrcGlkYmtpYmJtbiIsIml0ZW1fdmVyc2lvbiI6IjIwMjYuMS4yMi4xMjEiLCJwcm90b2NvbF92ZXJzaW9uIjoxfQ","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"HoOJk6AMseRy5mUjl8lzWJgPuVH70IC1YNraewGN1QveZJWJw74hukrK0KD8jg20l0JUEHuy9y2CyRSVdOBKe8F8EIQoGOzQqVg-2_J5vQK4gF0PuY2oWJ6d4jfVaQSieKdHYiQzpXkNZ_WUQIC1l-UrmFBHfBzU_WORX3j07RnXUplQ-NnyBU-NHXst0wDuI4Uq6zhHPoD0kFz8cIp3yj-6jWK3qY-qe7Kg_JkpO5VhGVwID5gQ6ZSQmMKPAnACryRgFLuw9u-lbHqy_dR38QJc-uuTrXcI_3XKiwq5kx2VxOl7OKzce5LKytCjKVMBKNHy7eHZQsqkBkLruqDrLy6q6weri2hSm8YsROaUxcJV3n9-0SjegLHzsYhE6WofAz9kUCOu-gLCE3OItdIrOVkemFm6GHAuSv_sDQCKgoFfdyJ6RnTc38XJtnITV-LzAJ0zq6J7rBf7JKa94AZWXzuz04bCUY4E-yBQNFS6xC1O7FYmFFP-jKelMsF4xg4mbvVDhuFE305v-1TwbzqHb6oqzHqAO_K63aA05Kiaa8p7HFTOAqEAkS_Ce_C8mSIGhCAUDN_t03hOUDBiWvnDyBJLAqSjalWUpzjWR-8iFD1nDuqyRGHKRqqoO0WA8MboRSlYFYvnGDuGhbcLRHFfIBcUfG-RAwlJNnDf6IFTzsg"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"OsPhibNaV-4gIvk-ofwqY4-gk1x0zpPKcKEVx-1HQBX4bVgWB-SFubznkY2v_kYhcyW4IK6GyxaWN7eJJE4ZCcUovbegS0ShbKlITjwpQoKp9QavdEkT_O0HEYeoxdCYne7igfBEgD7tOA07xxxU_jB3cm5O-k6nSFuu5H1iJXGaQWn-57GIlyO0XCBGgh95_E4k08Ggqf36EnJNCAUNZKgyCW5g9ylyBUOoYwPpvKKzDqiawd0ub9a9qpi1wHwEvEgLvymbseHrCSJ8P_9YunM2YrpkY3CoUEMElS67P5RZhIMfnK4A5ATGbgsAh0Pg-LF3OBWnyBjju8-lFjhl1g"}]}}]
|
||||
@@ -2,5 +2,5 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Crowd Deny",
|
||||
"preload_data_format": 1,
|
||||
"version": "2026.1.19.122"
|
||||
"version": "2026.1.22.121"
|
||||
}
|
||||
@@ -15,5 +15,5 @@
|
||||
"top_topics_and_observing_domains": [ ]
|
||||
} ],
|
||||
"hex_encoded_hmac_key": "434BF7DBD7DA573B45E0A11AD9045A61B6221D14AE2F9A341E2FEF659AF071F6",
|
||||
"next_scheduled_calculation_time": "13414055093389472"
|
||||
"next_scheduled_calculation_time": "13414055093389557"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 572 KiB |
|
Before Width: | Height: | Size: 572 KiB After Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 22 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_000024
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_000028
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_000029
Normal file
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_00002d
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_00002e
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 234 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_000031
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
user/user_data/Default/Cache/Cache_Data/f_000032
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 232 KiB |