20250918-新增主播库功能

This commit is contained in:
2025-09-18 20:15:07 +08:00
parent 2fe1576eaa
commit 11e72d0fae
6 changed files with 327 additions and 63 deletions

118
.idea/workspace.xml generated
View File

@@ -6,15 +6,11 @@
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成"> <list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build.bat" beforeDir="false" afterPath="$PROJECT_DIR$/build.bat" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Module/FlaskService.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/FlaskService.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/add.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/add.png" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Utils/AiUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/AiUtils.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/advertisement.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/advertisement.png" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Utils/ControlUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ControlUtils.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/back.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/back.png" afterDir="false" /> <change beforePath="$PROJECT_DIR$/script/ScriptManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/script/ScriptManager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/comment.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/comment.png" afterDir="false" /> <change beforePath="$PROJECT_DIR$/tidevice_entry.py" beforeDir="false" afterPath="$PROJECT_DIR$/tidevice_entry.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/fc18bc21951daf7be012a8a687b00a4de8b24c18/bgv.png" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/resources/icon.ico" beforeDir="false" afterPath="$PROJECT_DIR$/resources/icon.ico" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/like.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/like.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/resources/search.png" beforeDir="false" afterPath="$PROJECT_DIR$/resources/search.png" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -45,6 +41,9 @@
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/build.bat" root0="SKIP_INSPECTION" /> <setting file="file://$PROJECT_DIR$/build.bat" root0="SKIP_INSPECTION" />
</component> </component>
<component name="PerforceDirect.Settings">
<option name="CHARSET" value="无" />
</component>
<component name="ProjectColorInfo">{ <component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;, &quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 5 &quot;associatedIndex&quot;: 5
@@ -65,6 +64,8 @@
"Python.123.executor": "Run", "Python.123.executor": "Run",
"Python.Main.executor": "Run", "Python.Main.executor": "Run",
"Python.Test.executor": "Run", "Python.Test.executor": "Run",
"Python.test (1).executor": "Run",
"Python.test (2).executor": "Run",
"Python.test.executor": "Run", "Python.test.executor": "Run",
"Python.tidevice_entry.executor": "Run", "Python.tidevice_entry.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
@@ -74,7 +75,7 @@
"git-widget-placeholder": "main", "git-widget-placeholder": "main",
"javascript.nodejs.core.library.configured.version": "20.17.0", "javascript.nodejs.core.library.configured.version": "20.17.0",
"javascript.nodejs.core.library.typings.version": "20.17.58", "javascript.nodejs.core.library.typings.version": "20.17.58",
"last_opened_file_path": "C:/Users/zhangkai/Desktop/20250916ios/iOSAI/resources", "last_opened_file_path": "C:/Users/zhangkai/Desktop/20250916ios/iOSAI/Utils",
"node.js.detected.package.eslint": "true", "node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true", "node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.eslint": "(autodetect)",
@@ -86,6 +87,8 @@
}]]></component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\zhangkai\Desktop\20250916ios\iOSAI\Utils" />
<recent name="C:\Users\zhangkai\Desktop\20250916ios\iOSAI\script" />
<recent name="C:\Users\zhangkai\Desktop\20250916ios\iOSAI\resources" /> <recent name="C:\Users\zhangkai\Desktop\20250916ios\iOSAI\resources" />
</key> </key>
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
@@ -94,29 +97,6 @@
</key> </key>
</component> </component>
<component name="RunManager" selected="Python.Main"> <component name="RunManager" selected="Python.Main">
<configuration name="12" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="iOSAI" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/12.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="123" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="123" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="iOSAI" /> <module name="iOSAI" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
@@ -186,6 +166,52 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="test (1)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="iOSAI" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test (2)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="iOSAI" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/script" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/script/test.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="iOSAI" /> <module name="iOSAI" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
@@ -211,17 +237,19 @@
</configuration> </configuration>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Python.test (1)" />
<item itemvalue="Python.test (2)" />
<item itemvalue="Python.test" /> <item itemvalue="Python.test" />
<item itemvalue="Python.123" /> <item itemvalue="Python.123" />
<item itemvalue="Python.Test" /> <item itemvalue="Python.Test" />
<item itemvalue="Python.12" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-python-sdk-ce6832f46686-7b97d883f26b-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.25557.178" /> <option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-PY-241.18034.82" />
<option value="bundled-python-sdk-975db3bf15a3-2767605e8bc2-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.18034.82" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>
@@ -287,6 +315,10 @@
<workItem from="1757506636968" duration="5910000" /> <workItem from="1757506636968" duration="5910000" />
<workItem from="1757567423145" duration="16668000" /> <workItem from="1757567423145" duration="16668000" />
<workItem from="1757998910052" duration="3676000" /> <workItem from="1757998910052" duration="3676000" />
<workItem from="1758122148569" duration="213000" />
<workItem from="1758171936953" duration="7319000" />
<workItem from="1758180127232" duration="653000" />
<workItem from="1758182513694" duration="14452000" />
</task> </task>
<task id="LOCAL-00001" summary="ai 开始测试"> <task id="LOCAL-00001" summary="ai 开始测试">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -328,7 +360,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1757587781103</updated> <updated>1757587781103</updated>
</task> </task>
<option name="localTasksCounter" value="6" /> <task id="LOCAL-00006" summary="20250904-初步功能已完成">
<option name="closed" value="true" />
<created>1758121742405</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1758121742405</updated>
</task>
<option name="localTasksCounter" value="7" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -357,7 +397,7 @@
<component name="com.intellij.coverage.CoverageDataManagerImpl"> <component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/iOSAI$LogManager.coverage" NAME="LogManager 覆盖结果" MODIFIED="1756711414832" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Utils" /> <SUITE FILE_PATH="coverage/iOSAI$LogManager.coverage" NAME="LogManager 覆盖结果" MODIFIED="1756711414832" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Utils" />
<SUITE FILE_PATH="coverage/iOSAI$FlaskService.coverage" NAME="FlaskService 覆盖结果" MODIFIED="1756730187792" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Module" /> <SUITE FILE_PATH="coverage/iOSAI$FlaskService.coverage" NAME="FlaskService 覆盖结果" MODIFIED="1756730187792" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Module" />
<SUITE FILE_PATH="coverage/iOSAI$test.coverage" NAME="test 覆盖结果" MODIFIED="1756467664420" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$test.coverage" NAME="test 覆盖结果" MODIFIED="1758183945062" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Utils" />
<SUITE FILE_PATH="coverage/iOSAI$windows_run.coverage" NAME="windows_run Coverage Results" MODIFIED="1756473558532" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" /> <SUITE FILE_PATH="coverage/iOSAI$windows_run.coverage" NAME="windows_run Coverage Results" MODIFIED="1756473558532" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
<SUITE FILE_PATH="coverage/iOSAI$1352.coverage" NAME="1352 覆盖结果" MODIFIED="1757662777051" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$1352.coverage" NAME="1352 覆盖结果" MODIFIED="1757662777051" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$mac_wda_agent.coverage" NAME="mac_wda_agent Coverage Results" MODIFIED="1756473148639" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" /> <SUITE FILE_PATH="coverage/iOSAI$mac_wda_agent.coverage" NAME="mac_wda_agent Coverage Results" MODIFIED="1756473148639" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
@@ -365,9 +405,11 @@
<SUITE FILE_PATH="coverage/iOSAI$2111.coverage" NAME="2111 覆盖结果" MODIFIED="1757330714370" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$2111.coverage" NAME="2111 覆盖结果" MODIFIED="1757330714370" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$456.coverage" NAME="456 覆盖结果" MODIFIED="1757654671631" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$456.coverage" NAME="456 覆盖结果" MODIFIED="1757654671631" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$123__1_.coverage" NAME="123 (1) 覆盖结果" MODIFIED="1756897091135" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$123__1_.coverage" NAME="123 (1) 覆盖结果" MODIFIED="1756897091135" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$test__1_.coverage" NAME="test (1) 覆盖结果" MODIFIED="1758193027385" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$tidevice_entry.coverage" NAME="tidevice_entry 覆盖结果" MODIFIED="1757061969626" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$tidevice_entry.coverage" NAME="tidevice_entry 覆盖结果" MODIFIED="1757061969626" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$ScriptManager.coverage" NAME="ScriptManager 覆盖结果" MODIFIED="1756896057801" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" /> <SUITE FILE_PATH="coverage/iOSAI$ScriptManager.coverage" NAME="ScriptManager 覆盖结果" MODIFIED="1756896057801" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
<SUITE FILE_PATH="coverage/iOSAI$Main.coverage" NAME="Main 覆盖结果" MODIFIED="1758120400301" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" /> <SUITE FILE_PATH="coverage/iOSAI$Main.coverage" NAME="Main 覆盖结果" MODIFIED="1758196595807" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/iOSAI$123.coverage" NAME="123 覆盖结果" MODIFIED="1758115088356" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/iOSAI$123.coverage" NAME="123 覆盖结果" MODIFIED="1758115088356" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/iOSAI$test__2_.coverage" NAME="test (2) 覆盖结果" MODIFIED="1758192701951" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
</component> </component>
</project> </project>

View File

@@ -92,6 +92,7 @@ def start_socket_listener():
LogManager.error(f"[ERROR]Socket服务启动失败: {e}") LogManager.error(f"[ERROR]Socket服务启动失败: {e}")
print(f"[ERROR]Socket服务启动失败: {e}") print(f"[ERROR]Socket服务启动失败: {e}")
def _handle_conn(conn: socket.socket, addr): def _handle_conn(conn: socket.socket, addr):
try: try:
with conn: with conn:
@@ -99,16 +100,16 @@ def _handle_conn(conn: socket.socket, addr):
buffer = "" buffer = ""
while True: while True:
data = conn.recv(1024) data = conn.recv(1024)
if not data: # 对端关闭 if not data: # 对端关闭
break break
buffer += data.decode('utf-8', errors='ignore') buffer += data.decode('utf-8', errors='ignore')
# 2. 尝试切出完整 JSON简单按行也可按长度头、分隔符 # 2. 尝试切出完整 JSON简单按行也可按长度头、分隔符
while True: while True:
line, sep, buffer = buffer.partition('\n') line, sep, buffer = buffer.partition('\n')
if not sep: # 没找到完整行 if not sep: # 没找到完整行
break break
line = line.strip() line = line.strip()
if not line: # 空行跳过 if not line: # 空行跳过
continue continue
try: try:
obj = json.loads(line) obj = json.loads(line)
@@ -275,12 +276,14 @@ def stopScript():
@app.route('/passAnchorData', methods=['POST']) @app.route('/passAnchorData', methods=['POST'])
def passAnchorData(): def passAnchorData():
try: try:
LogManager.method_info("关注打招呼","关注打招呼") LogManager.method_info("关注打招呼", "关注打招呼")
data: Dict[str, Any] = request.get_json() data: Dict[str, Any] = request.get_json()
# 设备列表 # 设备列表
idList = data.get("deviceList", []) idList = data.get("deviceList", [])
# 主播列表 # 主播列表
acList = data.get("anchorList", []) acList = data.get("anchorList", [])
LogManager.info(f"[INFO] 获取数据: {idList} {acList}")
AiUtils.save_aclist_flat_append(acList) AiUtils.save_aclist_flat_append(acList)
# 是否需要回复 # 是否需要回复
@@ -401,6 +404,70 @@ def queryAnchorList():
return ResultData(data=data).toJson() return ResultData(data=data).toJson()
# 修改当前的主播列表数据
@app.route("/updateAnchorList", methods=['POST'])
def updateAnchorList():
"""
invitationType: 1 普票 2 金票
state: 1 通行(True) / 0 不通行(False)
"""
data = request.get_json(force=True, silent=True) or {}
invitationType = data.get("invitationType")
state = bool(data.get("state")) # 转成布尔
# 要更新成的值
new_status = 1 if state else 0
# 用工具类解析路径,避免 cwd 影响
file_path = AiUtils._resolve_path("Module/log/acList.json")
# 加载 JSON
try:
doc = json.loads(file_path.read_text(encoding="utf-8-sig"))
except Exception as e:
LogManager.error(f"[updateAnchorList] 读取失败: {e}")
return ResultData(code=500, massage=f"读取失败: {e}").toJson()
# 定位 anchorList
if isinstance(doc, list):
acList = doc
wrapper = None
elif isinstance(doc, dict) and isinstance(doc.get("anchorList"), list):
acList = doc["anchorList"]
wrapper = doc
else:
return ResultData(code=500, massage="文件格式不合法").toJson()
# 遍历并更新
updated = 0
for item in acList:
if isinstance(item, dict) and item.get("invitationType") == invitationType:
item["status"] = new_status
updated += 1
# 写回(保持原始结构)
try:
file_path.parent.mkdir(parents=True, exist_ok=True)
to_write = wrapper if wrapper is not None else acList
file_path.write_text(json.dumps(to_write, ensure_ascii=False, indent=2), encoding="utf-8")
except Exception as e:
LogManager.error(f"[updateAnchorList] 写入失败: {e}")
return ResultData(code=500, massage=f"写入失败: {e}").toJson()
if updated:
return ResultData(data=updated, massage=f"已更新 {updated} 条记录").toJson()
else:
return ResultData(data=0, massage="未找到符合条件的记录").toJson()
# 删除主播 # 删除主播
@app.route("/deleteAnchorWithIds", methods=['POST']) @app.route("/deleteAnchorWithIds", methods=['POST'])
def deleteAnchorWithIds(): def deleteAnchorWithIds():

View File

@@ -614,22 +614,55 @@ class AiUtils(object):
with open(file_path, "w", encoding="utf-8") as f: with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2) json.dump(data, f, ensure_ascii=False, indent=2)
# @staticmethod
# def _normalize_anchor_items(items):
# """
# 规范化输入为 [{anchorId, country}] 的列表:
# - 允许传入:单个对象、对象列表、字符串(当 anchorId 用)
# - 过滤不合规项
# """
# result = []
# if items is None:
# return result
#
# if isinstance(items, dict):
# # 单个对象
# aid = items.get("anchorId")
# if aid:
# result.append({"anchorId": str(aid), "country": items.get("country", "")})
# return result
#
# if isinstance(items, list):
# for it in items:
# if isinstance(it, dict):
# aid = it.get("anchorId")
# if aid:
# result.append({"anchorId": str(aid), "country": it.get("country", "")})
# elif isinstance(it, str):
# result.append({"anchorId": it, "country": ""})
# return result
#
# if isinstance(items, str):
# result.append({"anchorId": items, "country": ""})
# return result
@staticmethod @staticmethod
def _normalize_anchor_items(items): def _normalize_anchor_items(items):
""" """
规范化输入为 [{anchorId, country}] 的列表 规范化为 [{...}]
- 允许传入:单个对象、对象列表、字符串(当 anchorId 用) - 允许传入:单个对象、对象数组、字符串(当 anchorId 用)
- 过滤不合规项 - 保留原有字段,不限制只存 anchorId/country
- 字符串输入 → {"anchorId": xxx}
""" """
result = [] result = []
if items is None: if items is None:
return result return result
if isinstance(items, dict): if isinstance(items, dict):
# 单个对象
aid = items.get("anchorId") aid = items.get("anchorId")
if aid: if aid:
result.append({"anchorId": str(aid), "country": items.get("country", "")}) obj = dict(items) # 保留所有字段
result.append(obj)
return result return result
if isinstance(items, list): if isinstance(items, list):
@@ -637,15 +670,17 @@ class AiUtils(object):
if isinstance(it, dict): if isinstance(it, dict):
aid = it.get("anchorId") aid = it.get("anchorId")
if aid: if aid:
result.append({"anchorId": str(aid), "country": it.get("country", "")}) obj = dict(it)
result.append(obj)
elif isinstance(it, str): elif isinstance(it, str):
result.append({"anchorId": it, "country": ""}) result.append({"anchorId": it})
return result return result
if isinstance(items, str): if isinstance(items, str):
result.append({"anchorId": items, "country": ""}) result.append({"anchorId": items})
return result return result
# -------- 追加(对象数组平铺追加) -------- # -------- 追加(对象数组平铺追加) --------
@classmethod @classmethod
def save_aclist_flat_append(cls, acList, filename="log/acList.json"): def save_aclist_flat_append(cls, acList, filename="log/acList.json"):
@@ -657,6 +692,8 @@ class AiUtils(object):
{"anchorId": "tianliang30", "country": ""} {"anchorId": "tianliang30", "country": ""}
] ]
""" """
file_path = Path(filename) file_path = Path(filename)
data = cls._read_json_list(file_path) data = cls._read_json_list(file_path)
@@ -690,6 +727,97 @@ class AiUtils(object):
cls._write_json_list(file_path, data) cls._write_json_list(file_path, data)
return first return first
@classmethod
def bulk_update_anchors(cls, updates, filename="log/acList.json", case_insensitive=False):
"""
批量修改(文件根必须是数组,沿用 _read_json_list 的约定)
- updates:
dict: {"id1": {...}, "id2": {...}}
list[dict]: [{"anchorId":"id1", ...}, {"anchorId":"id2", ...}]
- case_insensitive: True 时用小写比较 anchorId
返回: {"updated": <int>, "missing": [ids...], "file": "<实际命中的路径>"}
"""
def norm_id(x: str) -> str:
s = str(x).strip()
return s.lower() if case_insensitive else s
# ✅ 关键:使用你已有的 _resolve_path避免受 cwd 影响
file_path = cls._resolve_path(filename)
data = cls._read_json_list(file_path)
if not data:
return {"updated": 0, "missing": cls._collect_all_ids(updates), "file": str(file_path)}
# 1) 归一化 updates -> map[normalized_id] = patch
upd_map = {}
raw_ids = [] # 保留原始传入 id用于返回 missing 时回显
if isinstance(updates, dict):
for aid, patch in updates.items():
if aid and isinstance(patch, dict):
key = norm_id(aid)
raw_ids.append(str(aid))
patch = {k: v for k, v in patch.items() if k != "anchorId"}
if patch:
upd_map[key] = {**upd_map.get(key, {}), **patch}
elif isinstance(updates, list):
for it in updates:
if isinstance(it, dict) and it.get("anchorId"):
rid = str(it["anchorId"])
key = norm_id(rid)
raw_ids.append(rid)
patch = {k: v for k, v in it.items() if k != "anchorId"}
if patch:
upd_map[key] = {**upd_map.get(key, {}), **patch}
if not upd_map:
return {"updated": 0, "missing": [], "file": str(file_path)}
# 2) 建索引map[normalized_id] -> item
index = {}
for item in data:
if isinstance(item, dict) and "anchorId" in item:
key = norm_id(item.get("anchorId", ""))
if key:
index[key] = item
# 3) 执行更新
updated, seen = 0, set()
for key, patch in upd_map.items():
target = index.get(key)
if target is not None:
target.update(patch)
updated += 1
seen.add(key)
# 4) 写回
if updated > 0:
cls._write_json_list(file_path, data)
# 5) 计算未命中(按传入原始 ID 回显)
missing = []
for rid in raw_ids:
if norm_id(rid) not in seen:
missing.append(rid)
return {"updated": updated, "missing": missing, "file": str(file_path)}
@staticmethod
def _collect_all_ids(updates):
ids = []
if isinstance(updates, dict):
ids = [str(k) for k in updates.keys()]
elif isinstance(updates, list):
for it in updates:
if isinstance(it, dict) and it.get("anchorId"):
ids.append(str(it["anchorId"]))
return ids
@classmethod @classmethod
def delete_anchors_by_ids(cls, ids: list[str], filename="log/acList.json") -> int: def delete_anchors_by_ids(cls, ids: list[str], filename="log/acList.json") -> int:
""" """
@@ -718,6 +846,39 @@ class AiUtils(object):
LogManager.error(f"[delete_anchors_by_ids] 写入失败: {e}") LogManager.error(f"[delete_anchors_by_ids] 写入失败: {e}")
return deleted return deleted
# -------- 查看第一个(取出但不删除) --------
@staticmethod
def _resolve_path(p) -> Path:
p = Path(p)
if p.is_absolute():
return p
# 以项目根目录 (iOSAI) 为基准 —— script 的上一级
base = Path(__file__).resolve().parents[1]
return (base / p).resolve()
@classmethod
def peek_aclist_first(cls, filename="Module/log/acList.json"):
file_path = cls._resolve_path(filename)
if not file_path.exists():
print(f"[peek] 文件不存在: {file_path}")
return None
try:
raw = file_path.read_text(encoding="utf-8-sig").strip()
if not raw:
return None
data = json.loads(raw)
arr = data if isinstance(data, list) else data.get("anchorList") if isinstance(data, dict) else None
if not arr:
return None
first = arr[0]
norm = cls._normalize_anchor_items(first)
return norm[0] if norm else None
except Exception as e:
print(f"[peek] 读取失败: {e}")
return None
@staticmethod @staticmethod
def run_tidevice_command(udid, action, bundle_id, timeout=30): def run_tidevice_command(udid, action, bundle_id, timeout=30):
""" """

View File

@@ -189,7 +189,7 @@ class ControlUtils(object):
def userClickProfile(cls, session, aid): def userClickProfile(cls, session, aid):
try: try:
user_btn = session.xpath("(//XCUIElementTypeButton[@name='用户' and @visible='true'])[1]") user_btn = session.xpath("(//XCUIElementTypeButton[@name='用户' and @visible='true'])[1]")
if user_btn: if user_btn.exists:
user_btn.click() user_btn.click()
time.sleep(3) time.sleep(3)
follow_btn = session.xpath( follow_btn = session.xpath(

View File

@@ -61,10 +61,6 @@ class ScriptManager():
session.appium_settings({"snapshotMaxDepth": 15}) session.appium_settings({"snapshotMaxDepth": 15})
# 判断当前页面上是否有推荐按钮 # 判断当前页面上是否有推荐按钮
# el = session(xpath='//XCUIElementTypeButton[@name="top_tabs_recomend"]')
# node = session.xpath(
# '//XCUIElementTypeButton[@name="top_tabs_recomend" or @name="推荐" or @label="推荐"]'
# )
el = session.xpath( el = session.xpath(
'//XCUIElementTypeButton[@name="top_tabs_recomend" or @name="推荐" or @label="推荐"]' '//XCUIElementTypeButton[@name="top_tabs_recomend" or @name="推荐" or @label="推荐"]'
@@ -278,10 +274,7 @@ class ScriptManager():
retries = 0 retries = 0
while not event.is_set(): while not event.is_set():
try: try:
# AiUtils.pop_aclist_first()
# anchor = AiUtils.pop_aclist_first()
# if not anchor:
# break
self.greetNewFollowers(udid, needReply, event) self.greetNewFollowers(udid, needReply, event)
except Exception as e: except Exception as e:
@@ -301,11 +294,13 @@ class ScriptManager():
LogManager.method_info(f"是否要自动回复消息:{needReply}", "关注打招呼", udid) LogManager.method_info(f"是否要自动回复消息:{needReply}", "关注打招呼", udid)
# 先关闭Tik Tok # 先关闭Tik Tok
ControlUtils.closeTikTok(session, udid) ControlUtils.closeTikTok(session, udid)
time.sleep(1) time.sleep(1)
# 重新打开Tik Tok # 重新打开Tik Tok
ControlUtils.openTikTok(session, udid) ControlUtils.openTikTok(session, udid)
time.sleep(3) time.sleep(3)
LogManager.method_info(f"重启tiktok", "关注打招呼", udid) LogManager.method_info(f"重启tiktok", "关注打招呼", udid)
# 设置查找深度 # 设置查找深度
@@ -369,7 +364,6 @@ class ScriptManager():
input.set_text(f"{aid or '暂无数据'}\n") input.set_text(f"{aid or '暂无数据'}\n")
# 定位 "关注" 按钮 通过关注按钮的位置点击主播首页 # 定位 "关注" 按钮 通过关注按钮的位置点击主播首页
session.appium_settings({"snapshotMaxDepth": 25}) session.appium_settings({"snapshotMaxDepth": 25})
try: try:
@@ -499,10 +493,9 @@ class ScriptManager():
chatInput = session.xpath("//TextView") chatInput = session.xpath("//TextView")
if chatInput.exists: if chatInput.exists:
chatInput.click() chatInput.click()
chatInput.set_text(f"{msg or '暂无数据'}\n")
time.sleep(2) time.sleep(2)
# 发送消息 # 发送消息
chatInput.set_text(f"{msg or '暂无数据'}\n")
# input.set_text(f"{aid or '暂无数据'}\n") # input.set_text(f"{aid or '暂无数据'}\n")
time.sleep(1) time.sleep(1)
@@ -603,6 +596,7 @@ class ScriptManager():
time.sleep(3) time.sleep(3)
continue # 重新进入 while 循环,调用 monitorMessages continue # 重新进入 while 循环,调用 monitorMessages
# 检查未读消息并回复 # 检查未读消息并回复
def monitorMessages(self, session, udid): def monitorMessages(self, session, udid):

View File

@@ -15,4 +15,4 @@ if __name__ == "__main__":
with open(os.path.expanduser("~/tidevice_crash.log"), "a", encoding="utf-8") as f: with open(os.path.expanduser("~/tidevice_crash.log"), "a", encoding="utf-8") as f:
traceback.print_exc(file=f) traceback.print_exc(file=f)
# 静默退出,**返回码 1**(父进程只认 returncode # 静默退出,**返回码 1**(父进程只认 returncode
sys.exit(1) sys.exit(1)