增加flask启动端口检测,增加H2协议的支持。
This commit is contained in:
@@ -317,7 +317,7 @@ def deviceList():
|
|||||||
@app.route('/passToken', methods=['POST'])
|
@app.route('/passToken', methods=['POST'])
|
||||||
def passToken():
|
def passToken():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
print(data)
|
print(json.dumps(data))
|
||||||
return ResultData(data="").toJson()
|
return ResultData(data="").toJson()
|
||||||
|
|
||||||
# 获取设备应用列表
|
# 获取设备应用列表
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
|
import asyncio
|
||||||
# ===== Main.py 顶部放置(所有 import 之前)=====
|
# ===== Main.py 顶部放置(所有 import 之前)=====
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from asgiref.wsgi import WsgiToAsgi
|
||||||
|
|
||||||
|
from Utils.AiUtils import AiUtils
|
||||||
from Utils.LogManager import LogManager
|
from Utils.LogManager import LogManager
|
||||||
import logging
|
from hypercorn.asyncio import serve
|
||||||
|
from hypercorn.config import Config
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from Module.DeviceInfo import DeviceInfo
|
||||||
|
from Module.FlaskSubprocessManager import FlaskSubprocessManager
|
||||||
|
from Utils.DevDiskImageDeployer import DevDiskImageDeployer
|
||||||
|
|
||||||
if "IOSAI_PYTHON" not in os.environ:
|
if "IOSAI_PYTHON" not in os.environ:
|
||||||
base_path = Path(sys.argv[0]).resolve()
|
base_path = Path(sys.argv[0]).resolve()
|
||||||
@@ -14,13 +24,6 @@ if "IOSAI_PYTHON" not in os.environ:
|
|||||||
os.environ["IOSAI_PYTHON"] = str(sidecar)
|
os.environ["IOSAI_PYTHON"] = str(sidecar)
|
||||||
# ==============================================
|
# ==============================================
|
||||||
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from Module.DeviceInfo import DeviceInfo
|
|
||||||
from Module.FlaskSubprocessManager import FlaskSubprocessManager
|
|
||||||
from Utils.DevDiskImageDeployer import DevDiskImageDeployer
|
|
||||||
|
|
||||||
# 确定 exe 或 py 文件所在目录
|
# 确定 exe 或 py 文件所在目录
|
||||||
BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent
|
BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent
|
||||||
LOG_DIR = BASE / "log"
|
LOG_DIR = BASE / "log"
|
||||||
@@ -33,8 +36,32 @@ def _run_flask_role():
|
|||||||
print("Flask Pid:", os.getpid())
|
print("Flask Pid:", os.getpid())
|
||||||
port = int(os.getenv("FLASK_COMM_PORT", "34566")) # 固定端口的兜底仍是 34567
|
port = int(os.getenv("FLASK_COMM_PORT", "34566")) # 固定端口的兜底仍是 34567
|
||||||
app = get_app()
|
app = get_app()
|
||||||
|
flaskPort = port + 1
|
||||||
|
AiUtils.flask_port_free(flaskPort)
|
||||||
bootstrap_server_side_effects()
|
bootstrap_server_side_effects()
|
||||||
app.run(host="0.0.0.0", port=port + 1, debug=False, use_reloader=False, threaded=True)
|
|
||||||
|
# 把 WSGI Flask app 包成 ASGI app
|
||||||
|
asgi_app = WsgiToAsgi(app)
|
||||||
|
|
||||||
|
# Hypercorn 配置
|
||||||
|
# 自动定位 resources 目录
|
||||||
|
base_dir = os.path.dirname(os.path.abspath(__file__)) # 当前 py 的目录(Module/)
|
||||||
|
project_root = os.path.dirname(base_dir) # 回到项目根目录(iOSAi/)
|
||||||
|
resource_dir = os.path.join(project_root, "resources") # 拼到 resources
|
||||||
|
|
||||||
|
config = Config()
|
||||||
|
config.bind = [f"0.0.0.0:{flaskPort}"]
|
||||||
|
config.alpn_protocols = ["h2"] # 开 HTTP/2
|
||||||
|
config.certfile = os.path.join(resource_dir, "cert.pem")
|
||||||
|
config.keyfile = os.path.join(resource_dir, "key.pem")
|
||||||
|
|
||||||
|
print(f"Starting Hypercorn on port {flaskPort} (HTTP/2 enabled)")
|
||||||
|
|
||||||
|
# 开启 HTTP/2
|
||||||
|
config.alpn_protocols = ["h2"]
|
||||||
|
|
||||||
|
print(f"Starting Hypercorn on https://localhost:{flaskPort} (HTTP/2 enabled)")
|
||||||
|
asyncio.run(serve(asgi_app, config))
|
||||||
|
|
||||||
if "--role=flask" in sys.argv:
|
if "--role=flask" in sys.argv:
|
||||||
_run_flask_role()
|
_run_flask_role()
|
||||||
@@ -51,7 +78,7 @@ if __name__ == "__main__":
|
|||||||
# 清空日志
|
# 清空日志
|
||||||
LogManager.clearLogs()
|
LogManager.clearLogs()
|
||||||
|
|
||||||
# main(sys.argv)
|
main(sys.argv)
|
||||||
|
|
||||||
# 添加iOS开发包到电脑上
|
# 添加iOS开发包到电脑上
|
||||||
deployer = DevDiskImageDeployer(verbose=True)
|
deployer = DevDiskImageDeployer(verbose=True)
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import html
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import cv2
|
import cv2
|
||||||
@@ -62,6 +66,56 @@ class AiUtils(object):
|
|||||||
# print(e)
|
# print(e)
|
||||||
# return -1, -1
|
# return -1, -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def flask_port_free(cls,port):
|
||||||
|
"""无需 psutil 的版本,通过系统命令查 PID"""
|
||||||
|
|
||||||
|
def can_bind(p):
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
s.bind(("0.0.0.0", p))
|
||||||
|
s.close()
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
s.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if can_bind(port):
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"[ensure_port_free] Port {port} is occupied. Searching PID...")
|
||||||
|
|
||||||
|
pids = set()
|
||||||
|
|
||||||
|
if sys.platform.startswith("darwin") or sys.platform.startswith("linux"):
|
||||||
|
cmd = f"lsof -t -i:{port}"
|
||||||
|
proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
for line in proc.stdout.splitlines():
|
||||||
|
if line.strip().isdigit():
|
||||||
|
pids.add(int(line.strip()))
|
||||||
|
|
||||||
|
elif sys.platform.startswith("win"):
|
||||||
|
cmd = f"netstat -ano | findstr :{port}"
|
||||||
|
proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
for line in proc.stdout.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 5 and parts[-1].isdigit():
|
||||||
|
pids.add(int(parts[-1]))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Unsupported platform for ensure_port_free")
|
||||||
|
|
||||||
|
for pid in pids:
|
||||||
|
try:
|
||||||
|
print(f"[ensure_port_free] Killing PID {pid}...")
|
||||||
|
os.kill(pid, signal.SIGKILL)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ensure_port_free] Failed to kill PID {pid}: {e}")
|
||||||
|
|
||||||
|
time.sleep(0.3)
|
||||||
|
if not can_bind(port):
|
||||||
|
raise RuntimeError(f"[ensure_port_free] Port {port} still occupied after kill.")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def findImageInScreen(cls, target, udid):
|
def findImageInScreen(cls, target, udid):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def _force_utf8_everywhere():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# _force_utf8_everywhere()
|
_force_utf8_everywhere()
|
||||||
|
|
||||||
class LogManager:
|
class LogManager:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ python -m nuitka Module\Main.py ^
|
|||||||
--windows-console-mode=disable ^
|
--windows-console-mode=disable ^
|
||||||
--output-filename=IOSAI ^
|
--output-filename=IOSAI ^
|
||||||
--include-package=Module,Utils,Entity,script ^
|
--include-package=Module,Utils,Entity,script ^
|
||||||
--include-module=flask,wda,psutil,portalocker,flask_cors,cv2,lxml.etree,requests,urllib3,certifi,idna,setuptools ^
|
--include-module=flask,wda,psutil,portalocker,flask_cors,cv2,lxml.etree,requests,urllib3,certifi,idna,setuptools,asgiref,hypercorn,h2,hpack,wsproto,priority,anyio,sniffio ^
|
||||||
--include-data-dir=resources=resources ^
|
--include-data-dir=resources=resources ^
|
||||||
--include-data-dir=SupportFiles=SupportFiles ^
|
--include-data-dir=SupportFiles=SupportFiles ^
|
||||||
--include-data-files="resources/iproxy/*=resources/iproxy/" ^
|
--include-data-files="resources/iproxy/*=resources/iproxy/" ^
|
||||||
|
|||||||
21
resources/cert.pem
Normal file
21
resources/cert.pem
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDizCCAnOgAwIBAgIUXpRsBS0IBEvAw22Ii/wtu/gdooUwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBUxvY2FsMQ4wDAYDVQQHDAVMb2Nh
|
||||||
|
bDEMMAoGA1UECgwDRGV2MQwwCgYDVQQLDANEZXYxEjAQBgNVBAMMCWxvY2FsaG9z
|
||||||
|
dDAeFw0yNTExMTcwODI2MzdaFw0yNjExMTcwODI2MzdaMF0xCzAJBgNVBAYTAkNO
|
||||||
|
MQ4wDAYDVQQIDAVMb2NhbDEOMAwGA1UEBwwFTG9jYWwxDDAKBgNVBAoMA0RldjEM
|
||||||
|
MAoGA1UECwwDRGV2MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4IBDwAwggEKAoIBAQCixGQJ0gdzcQoqdOv4lLvfH5Kz4C/t4/WCZfheF3Z7
|
||||||
|
cYiog0Ql8URyn6bF8ux97X4TtpJ621jM/lfxc2hKrqXpbsyO2EKgT3OEpuv/lyqC
|
||||||
|
YQqbHUUIAiXI9OF/iAANY5rBIeEDEUhcH4Ngt6EJ1FZdU+tfgV8V9zNUSeSJ+VtF
|
||||||
|
C+0LsTyWy7eqnXkXnPZvitVeZU85Zy5lWdC1mp9cOoyElmYuGxIQkW6ZtEzMjVp4
|
||||||
|
Eim3RsZgk6ZYRAdMGfdaa6YrmDDqhEZVEEL55dstOqfIWKUppazC9HSs7FnGsaUn
|
||||||
|
xkdqOArACdhU9y3f+yyeLC93Xllx9kgFfvLueysUqSgdAgMBAAGjQzBBMCAGA1Ud
|
||||||
|
EQQZMBeCCWxvY2FsaG9zdIcEfwAAAYcEwKgB2jAdBgNVHQ4EFgQURY1BDcpSTUNO
|
||||||
|
D0olM9H84Gu/QQ0wDQYJKoZIhvcNAQELBQADggEBAFrCAqIlpzncH6owBImN8Ub5
|
||||||
|
8ZwtTm+C3nQZF5FkCdsXHfqtPTEk4bX7IFHaj7saqroCYXfgopzvk2QX16wlPwk2
|
||||||
|
SKA/pF6I2bNNozlcVN9QAf9ue6xa8g8AxwPT46gbKTKFyG5lg1umYXhCGKVIJ/1l
|
||||||
|
B4Bh8KmPfzNWxiKOzflGNx5j1BHPZ9S7jt9wtiEwENceGZXVE8ANMiNR44+suuM7
|
||||||
|
6/syQUetPN+VWW+/14OrDeYQDLZTbUVigY75KIuLF41PxNWG745Qlcu/5nmvBnHv
|
||||||
|
5NNm2Zs8GYKLqLIlUQNU8x0R03FBLCYjDKRJsNpsZPqjI5cDSPj5vHZrzD3Jh0s=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
resources/key.pem
Normal file
28
resources/key.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCixGQJ0gdzcQoq
|
||||||
|
dOv4lLvfH5Kz4C/t4/WCZfheF3Z7cYiog0Ql8URyn6bF8ux97X4TtpJ621jM/lfx
|
||||||
|
c2hKrqXpbsyO2EKgT3OEpuv/lyqCYQqbHUUIAiXI9OF/iAANY5rBIeEDEUhcH4Ng
|
||||||
|
t6EJ1FZdU+tfgV8V9zNUSeSJ+VtFC+0LsTyWy7eqnXkXnPZvitVeZU85Zy5lWdC1
|
||||||
|
mp9cOoyElmYuGxIQkW6ZtEzMjVp4Eim3RsZgk6ZYRAdMGfdaa6YrmDDqhEZVEEL5
|
||||||
|
5dstOqfIWKUppazC9HSs7FnGsaUnxkdqOArACdhU9y3f+yyeLC93Xllx9kgFfvLu
|
||||||
|
eysUqSgdAgMBAAECggEAGY29qvEHbG9Vyj6bAWbQbAI39PeAbte4JqW9rX//gPfd
|
||||||
|
HZ+mJlLPjTNVaoRt7oNHpO6n5pPjSCOySNz2hasPrytPACoho6t1lmDicjkYWmnD
|
||||||
|
0YBx4wT7S6ZudKg0YeW+WQ3plqKy+ouUA64woStt968CJ/dWp0stCtGjCKpWUuuB
|
||||||
|
TXvAUM91qrQlpIuqQI0QYRcKaoKPV6IckWPGeLvKi8EhVOFCNdya8jTP/ayxT06r
|
||||||
|
/cNucO+Tqe0PUe6jet6Ecx7Av4h8QXg9FMoF40RKxD6Q+sK/bM1c51KvUZVt9v4F
|
||||||
|
epAHjQyrzPjUozjtKzQvibexBvSwz/XaFCDgr7OwPQKBgQDOJtILcxjJDZfk8LUG
|
||||||
|
GNF8XVtgDu2FFVw79MSuQgERC+w2b+P2feffwdI4vu2+8jkWCHnttXNgdrS3r41C
|
||||||
|
gak3Xb2Jzx8M06eNC9QCtjicrWNTmJtcp5YZm1vS+fAPQG4DPLK+J4EmLNNyQ9nC
|
||||||
|
0vMfYRwKr3TX5FU6MyJbAsVnkwKBgQDKH/zUhMPdN6+0upEVoUJrI3BOhVdN8eEp
|
||||||
|
eqNWYU2Hhmg5mHsnoxz8Rjxw5ZKx108BT1JNea/rrNgOhe8TTpiAIVl5ZMN0OcO2
|
||||||
|
INlfteCtXY5nzU8HilfFBPwroR8Msx662GpM8TRA9RfTGJ7nKmiXhC1jnmkPecwX
|
||||||
|
+f+LaLCfjwKBgQC11H3dxXYuF8RLFZjFuOxFIl7vOht8D9wbsggsn2ErdPWzCjvq
|
||||||
|
9SCpNt7CWH2At0tsyKsq5KnQgsNhZQFWkOD9SbxdKgf8G0+k07L7dVg3saNzX55h
|
||||||
|
OhvlmCeEzhlUioK+bjJGELgUQON73Kbc9Y2lttSyBBIuPmKCBAogdjBB6wKBgGnM
|
||||||
|
VIro85zXiSEQhuDLh/iMlDyFjy09bp5HkzejtvE5aVS8e7pDpuhl2z087YwpJzGI
|
||||||
|
U4w6Jds2neD8Oifg+/IVgsAH/kbX9ZlfmGiAyxnz3pZ24OcRgt+dvGEZ9Sawm2Ux
|
||||||
|
4nJjzvYxVEcqnAJkMFse1KNQR63SEwJ52Ukfg1QBAoGBAKByisJiMTi/gmSlPFf6
|
||||||
|
3ZfxCd5ReMS/Ak4JIBN4S+GoAZp6yMZy1jYFHqV2k3slhmcswy+V9DGxtldHlDu9
|
||||||
|
+90zV1btfaB3nq5ydkmj32SU9MRqpzP4axHYTi8cvmOBlOEpL5e7KWTg072rGHAC
|
||||||
|
+ih8VPn9LTKk2GCtCCp4r2jI
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -669,11 +669,7 @@ class ScriptManager():
|
|||||||
event.wait(timeout=2)
|
event.wait(timeout=2)
|
||||||
|
|
||||||
session.appium_settings({"snapshotMaxDepth": 12})
|
session.appium_settings({"snapshotMaxDepth": 12})
|
||||||
|
|
||||||
LogManager.method_info(f"检查当前是否为视频页面", "关注打招呼", udid)
|
LogManager.method_info(f"检查当前是否为视频页面", "关注打招呼", udid)
|
||||||
|
|
||||||
is_back_enabled = ControlUtils.isClickBackEnabled(session)
|
|
||||||
|
|
||||||
# 最多尝试 3 次(第一次 + 再试两次)
|
# 最多尝试 3 次(第一次 + 再试两次)
|
||||||
for attempt in range(3):
|
for attempt in range(3):
|
||||||
is_back_enabled = ControlUtils.isClickBackEnabled(session)
|
is_back_enabled = ControlUtils.isClickBackEnabled(session)
|
||||||
|
|||||||
Reference in New Issue
Block a user