lvtz}LDB6!@D3sH5-^%TH{7t7sn
z3jZkI0+Xt#qFbZC%*HnWA;EiiyEz@FEt}P8_e}w=sE4XL1en6b9p%Z>EQ!WAu5wJE
ztJ(tBu%&t`zmknTK@#epS68T5>hzO=E3oX)-ko0d@5fm}1iHNI?lY|Al
zn_$A9w(0F*Pze7;gw-IBP|Es1h|23Tr(vF_G$~#;9?vH
zjzzuaMUV{a=#BWySAZw$PC*J*`RbBVm9%L3S}m0gc+!_+*fgXYB}(R)7|n`jFZzsP
zc3eC0%X$ko)|*jocm=2G9p;Q7`M=c)WwLd~n_^HlvOEuL#)gKY)&m)J{mMYEzofUn
ztE7EzZ6Fvb?g{qy9ncMgI*ZDRf`KkNgDVZT!oUhE%0MEUWMSg+{SQ98KYo7U;%Qf|
zO`wOFNiQ(?XbM61&WMhgyt?!PF77;$FfZ?Gr~bYWj_k4&+~NKl!@R!FKiC=!wTA|R
zbe0>U(!V!;8uSd_+2zwOF!LjR6h&y?y~psZJoh|i-xZ55N;dD!>l)!fUSbfrxDr}F3o~)00%GEQ59X65v#XG=q
zsSpS6id;=`r9n?yDad%Rtxz&B*NErZMsc#k3H9o)9XVkBlxOj4NK-k|IMO*H9OGzF
z#w*Tp8ReMd;QuZ33dc1Leo-htBFam+x_!Q9$~Qm_nBX_-txU3q1WQzhKVMAJgMC>f
Z!V60RQ%fdPx~(vTBi)yv9Q}cUzX9J(WqSYs
diff --git a/Utils/AiUtils.py b/Utils/AiUtils.py
index 28c726a..cdbe7a3 100644
--- a/Utils/AiUtils.py
+++ b/Utils/AiUtils.py
@@ -686,15 +686,16 @@ class AiUtils(object):
def save_aclist_flat_append(cls, acList, filename="log/acList.json"):
"""
将 anchor 对象数组平铺追加到 JSON 文件(数组)中。
- 期望 acList 形如:
- [
- {"anchorId": "ldn327_", "country": ""},
- {"anchorId": "tianliang30", "country": ""}
- ]
+ 文件固定写到 项目根目录/log/acList.json
"""
+ # 找到当前文件所在目录,回退到项目根目录
+ root_dir = Path(__file__).resolve().parent.parent # 根据实际层级调整
+ log_dir = root_dir
+ log_dir.mkdir(parents=True, exist_ok=True) # 确保 log 目录存在
+
+ file_path = log_dir / filename
- file_path = Path(filename)
data = cls._read_json_list(file_path)
# 规范化输入,确保都是 {anchorId, country}
@@ -705,30 +706,12 @@ class AiUtils(object):
data.extend(to_add)
cls._write_json_list(file_path, data)
+ LogManager.method_info(f"写入的路径是:{file_path}", "写入数据")
LogManager.info(f"[acList] 已追加 {len(to_add)} 条,当前总数={len(data)} -> {file_path}")
- # -------- 弹出(取一个删一个) --------
- # @classmethod
- # def pop_aclist_first(cls, filename="log/acList.json"):
- # """
- # 从 JSON 数组中取出第一个 anchor 对象,并删除它;为空或文件不存在返回 None。
- # 返回形如:{"anchorId": "...", "country": "..."}
- # """
- # file_path = Path(filename)
- # data = cls._read_json_list(file_path)
- # if not data:
- # return None
- #
- # first = data.pop(0)
- # # 兜底保证结构
- # norm = cls._normalize_anchor_items(first)
- # first = norm[0] if norm else None
- #
- # cls._write_json_list(file_path, data)
- # return first
@classmethod
- def pop_aclist_first(cls, filename="Module/log/acList.json", mode="pop"):
+ def pop_aclist_first(cls, filename="log/acList.json", mode="pop"):
"""
从 JSON 数组/对象(anchorList)中取出第一个 anchor 对象。
- mode="pop" : 取出并删除
@@ -867,12 +850,16 @@ class AiUtils(object):
@classmethod
def delete_anchors_by_ids(cls, ids: list[str], filename="log/acList.json") -> int:
"""
- 根据 anchorId 列表从 JSON 文件中删除匹配的 anchor。
+ 根据 anchorId 列表从根目录/log/acList.json 中删除匹配的 anchor。
返回删除数量。
"""
- file_path = Path(filename)
+ # 确保路径固定在根目录下的 log 文件夹
+ root_dir = Path(__file__).resolve().parent.parent
+ file_path = root_dir / "log" / filename
+
if not file_path.exists():
return 0
+
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
@@ -881,15 +868,19 @@ class AiUtils(object):
except Exception as e:
LogManager.error(f"[delete_anchors_by_ids] 读取失败: {e}")
return 0
+
before = len(data)
# 保留不在 ids 里的对象
data = [d for d in data if isinstance(d, dict) and d.get("anchorId") not in ids]
deleted = before - len(data)
+
try:
+ file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
LogManager.error(f"[delete_anchors_by_ids] 写入失败: {e}")
+
return deleted
# -------- 查看第一个(取出但不删除) --------
@@ -903,7 +894,7 @@ class AiUtils(object):
return (base / p).resolve()
@classmethod
- def peek_aclist_first(cls, filename="Module/log/acList.json"):
+ def peek_aclist_first(cls, filename="log/acList.json"):
file_path = cls._resolve_path(filename)
if not file_path.exists():
print(f"[peek] 文件不存在: {file_path}")
diff --git a/Utils/ThreadManager.py b/Utils/ThreadManager.py
index 6740d08..649ce29 100644
--- a/Utils/ThreadManager.py
+++ b/Utils/ThreadManager.py
@@ -1,5 +1,6 @@
import threading
-from typing import Dict, Tuple
+from concurrent.futures import ThreadPoolExecutor, as_completed
+from typing import Dict, Tuple, List
from Utils.LogManager import LogManager
@@ -64,6 +65,37 @@ class ThreadManager:
LogManager.method_info(f"设备 {udid} 的任务停止成功", method="task")
return 200, f"当前任务停止成功 {udid}"
+ @classmethod
+ def batch_stop(cls, udids: List[str]) -> Tuple[int, List[str], str]:
+ """
+ 批量停止任务,使用多线程并发执行。
+ :param udids: 待停止的设备唯一标识列表
+ :return: (code, fail_list, msg)
+ code=200 全部成功,fail_list=[]
+ code=1001 部分/全部失败,fail_list 为失败描述字符串列表
+ """
+ if not udids:
+ return 200, [], "无设备需要停止"
+
+ fail_list: List[str] = []
+
+ with ThreadPoolExecutor(max_workers=min(32, len(udids))) as executor:
+ future_map = {executor.submit(cls.stop, udid): udid for udid in udids}
+
+ for future in as_completed(future_map):
+ udid = future_map[future]
+ try:
+ code, reason = future.result()
+ if code != 200:
+ fail_list.append(f"设备{udid}停止失败:{reason}")
+ except Exception as exc:
+ fail_list.append(f"设备{udid}停止异常:{exc}")
+
+ if fail_list:
+ return 1001, fail_list, "停止失败"
+ return 200, [], "全部设备停止成功"
+
+
@classmethod
def is_task_running(cls, udid: str) -> bool:
"""
diff --git a/Utils/__pycache__/AiUtils.cpython-312.pyc b/Utils/__pycache__/AiUtils.cpython-312.pyc
index a8e6334a0e235fbd2f9e58b841aa9f6b26f1b27e..bd1d4336480ae4a1d64dfdc65b054e8ca907c16b 100644
GIT binary patch
delta 2516
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
diff --git a/script/ScriptManager.py b/script/ScriptManager.py
index 26b177d..a14de8f 100644
--- a/script/ScriptManager.py
+++ b/script/ScriptManager.py
@@ -244,7 +244,7 @@ class ScriptManager():
session.double_tap(x, y)
print("--------------------------------------------")
- time.sleep(random.randint(180, 300))
+ time.sleep(random.randint(300, 600))
session.swipe_up()
# 正常退出(外部 event 触发)
@@ -320,11 +320,16 @@ class ScriptManager():
# 循环条件。1、 循环关闭 2、 数据处理完毕
while not event.is_set():
+ # 获取一个主播,
+ LogManager.method_info(f"开始获取数据", "关注打招呼", udid)
# 获取一个主播,
result = AiUtils.peek_aclist_first()
- state = result.get("state",0)
+ LogManager.method_info(f"数据是:{result}", "关注打招呼", udid)
+
+ state = result.get("state", 0)
if not state:
- LogManager.method_info(f"当前主播的状态是:{state} 不通行,取出数据移到列表尾部 继续下一个", "关注打招呼", udid)
+ LogManager.method_info(f"当前主播的状态是:{state} 不通行,取出数据移到列表尾部 继续下一个", "关注打招呼",
+ udid)
AiUtils.pop_aclist_first(mode="move")
continue
@@ -337,7 +342,7 @@ class ScriptManager():
time.sleep(30)
continue
- aid = anchor["anchorId"]
+ aid = anchor.get("anchorId", "")
anchorCountry = anchor.get("country", "")
LogManager.method_info(f"主播的数据,用户名:{aid},国家:{anchorCountry}", "关注打招呼", udid)
diff --git a/script/__pycache__/ScriptManager.cpython-312.pyc b/script/__pycache__/ScriptManager.cpython-312.pyc
index c6c2a23cc94f8fc38c7f807463606d329c676a90..b3eb96ea01b4e90b307ee4d34ed3f86df144110d 100644
GIT binary patch
delta 1473
zcmYk6drVVz6vyxBr4MKe0+zN&<>7dgH`o>^h2HYe!NPQi2verT@+fLyx2-64PLN>D
zm=q5P@v&@+8%~_V-N+W_)NRq3EHn5>G>yyrL&!{)%or0B{b#>lA<_HC=bn3h-{1HA
ze&;0ji*H5G&x<1eP%0xB@|0Wu>9Mf$`im{TH$=rByP)Rtj@!5)JeqR?;1lP>c~
zPB4yWykf|Uia8fMO$se)hEL)x!%FIetBgrYy5(4FTn#GRZ_J5`dqexuC&g
zOpC~V{MnemY8@JUXw*yNeKJyF1u9G}kbox(4fvT!4LY1P*|jTQo4Dj2>0)t)aU^1X
zQF2I=jP9bY@Fe1pd`dJ8*kekD6qFS=!zy|VNW}n&(`cNU`r6H}^2hn1opq!S%07zp
zDI6I)G8iVn^7XVWs4k7du99YKA@8$=S~kT=oJ;hu5;f);;@N86Ksal*Ll%Bes#Rva
zc6M1q4x23Tkd1pvZEV6I4vqRGt)N&8pehh-JY)uC^LFPvnJc&%)+)Z~jLaS^7J8=)AR%CnaI)p)wx
zCSB@F=61M
zOKfty#L2-LTH(-a`zzF873Nelf(-{Nt}Cy;F{cV_tb7lj6feWGVG;OCWhP5zKxUL1
z*USWD!xlpE@WWna58KT2g1!<9t5RXF&{UNHuowHQtHF)GSHH^e#*op^Up_ED^~pa+
zewZIQwlF@k@acK4f}|J2g~{*!yL=pfU2`LX2}-wjdtDxWuW)qjkeC>0>n4fO_jSWC
zDp=o56hk|1-S9%QkL>2xQ5+x$O87Rncc+K%M^{6YWSHhh@W+M@=)p2V2Igjz*#!mS}=4j^R`)6YA#$*f4n|>23
z*U6Yz%|!NS_OaaO^{8*z7jvsHr%l7mYizo93%gJX?G|CSWd#dUcxRgn>I9pe)52jK
z=*xhM!gSwisYIX`xqy%RH^UR;27X#LN)tyZkYWtw2mNpX#}2ka0AC!ek{qIGKiUTC
z)z?WFjN0Dq+TF&x?H*^hYpc`2-w@6Y?gNRJ>ULw+P<6~Ds$-olPf*;!cYAyIb{rTg
zkdIS^iDCk;3~kY!rZTzHy>+ME)#0^wwDDijMLEUSSahhe={OBfP>3l$CJ0Je$v7SE
zptRFY&fwwsP~p&jZZ{P@4B~Bf~K^V-!g;HciFJ(+9W-u6K
zLj}$T#7$)}=nRu6*SNUFXkwh1QsEckBWovaZBNQ9iC
z1Qtdx#hZu)%1~Askm4_$I#A=ik^;FVAUn%&;h1bL2#>fJxePQh!vHh&EEbx@9XM`I
zex!@Si{@g`;&l3iABRCp3mDL0%f!DN
zYBZF_KsN-YP
zlW?<2DK?xlyE7SItq6x)Jm$*6_40T&&+kBIcLJ&^O>F)ndw=DCydV%s`Yj|=iN~KR
ztJFo}`msPL@fKr+y;)uo&=T8>uiMRF!JGCRAnDz5@iV<9!5_;^q!U6cuqrjajBQny
z($f{3RUAwihB!Q6!|9@Indq?LImd3;K^n+Fl{1MXS#;{4obHiu<=EpiLWSt6q{K<5
z5+kXNqZrjtBYTPZr}1Ee
zM>a&v!6ZCfnGE&r^4D~Q!P@m`!~C9^JqrViy(BaN>YT
z^E1P=NZgVwhKX#G+{TX%bV(NsH=@#n+{VwOYL|l1#?I%?