增加flask启动端口检测,增加H2协议的支持。

This commit is contained in:
2025-11-17 17:13:22 +08:00
parent 13c7930f88
commit 317fc2586a
8 changed files with 144 additions and 18 deletions

View File

@@ -317,7 +317,7 @@ def deviceList():
@app.route('/passToken', methods=['POST'])
def passToken():
data = request.get_json()
print(data)
print(json.dumps(data))
return ResultData(data="").toJson()
# 获取设备应用列表

View File

@@ -1,10 +1,20 @@
import asyncio
# ===== Main.py 顶部放置(所有 import 之前)=====
import os
import sys
from pathlib import Path
from asgiref.wsgi import WsgiToAsgi
from Utils.AiUtils import AiUtils
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:
base_path = Path(sys.argv[0]).resolve()
@@ -14,13 +24,6 @@ if "IOSAI_PYTHON" not in os.environ:
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 文件所在目录
BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent
LOG_DIR = BASE / "log"
@@ -33,8 +36,32 @@ def _run_flask_role():
print("Flask Pid:", os.getpid())
port = int(os.getenv("FLASK_COMM_PORT", "34566")) # 固定端口的兜底仍是 34567
app = get_app()
flaskPort = port + 1
AiUtils.flask_port_free(flaskPort)
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:
_run_flask_role()
@@ -51,7 +78,7 @@ if __name__ == "__main__":
# 清空日志
LogManager.clearLogs()
# main(sys.argv)
main(sys.argv)
# 添加iOS开发包到电脑上
deployer = DevDiskImageDeployer(verbose=True)

View File

@@ -2,7 +2,11 @@ import html
import json
import os
import re
import signal
import socket
import subprocess
import sys
import time
import xml.etree.ElementTree as ET
from pathlib import Path
import cv2
@@ -62,6 +66,56 @@ class AiUtils(object):
# print(e)
# 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
def findImageInScreen(cls, target, udid):
try:

View File

@@ -26,7 +26,7 @@ def _force_utf8_everywhere():
except Exception:
pass
# _force_utf8_everywhere()
_force_utf8_everywhere()
class LogManager:
"""

View File

@@ -9,7 +9,7 @@ python -m nuitka Module\Main.py ^
--windows-console-mode=disable ^
--output-filename=IOSAI ^
--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=SupportFiles=SupportFiles ^
--include-data-files="resources/iproxy/*=resources/iproxy/" ^

21
resources/cert.pem Normal file
View 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
View 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-----

View File

@@ -669,11 +669,7 @@ class ScriptManager():
event.wait(timeout=2)
session.appium_settings({"snapshotMaxDepth": 12})
LogManager.method_info(f"检查当前是否为视频页面", "关注打招呼", udid)
is_back_enabled = ControlUtils.isClickBackEnabled(session)
# 最多尝试 3 次(第一次 + 再试两次)
for attempt in range(3):
is_back_enabled = ControlUtils.isClickBackEnabled(session)