fix: 删除任务/分组持久化(重启不再复活)+ feat: APK 安装支持本机 USB 设备
This commit is contained in:
+25
-16
@@ -274,27 +274,36 @@ class ApkManager:
|
||||
def _install_one(self, serial, apk_path, package_name=""):
|
||||
"""直连设备安装 APK。
|
||||
|
||||
serial 即设备的 adb 网络地址(IP:5555),直接 adb connect 即可。
|
||||
serial 为 adb 网络地址(IP:5555)时直接 adb connect;
|
||||
本机 USB 有线设备(serial 无冒号,如 ZY322XXXX)已在 adb 中,无需 connect。
|
||||
不经过 STF occupy/release,安装完断开连接,app 永久保留。
|
||||
"""
|
||||
from .adb_helper import _adb
|
||||
self._set_item(serial, "installing", f"正在连接 {serial}...")
|
||||
try:
|
||||
# adb connect(带重试,设备网络可能需要几次握手)
|
||||
connected = False
|
||||
last_out = ""
|
||||
for attempt in range(1, 4): # 最多 3 次
|
||||
out = _adb("connect", serial)
|
||||
last_out = out
|
||||
if "connected" in out.lower() and "failed" not in out.lower():
|
||||
connected = True
|
||||
break
|
||||
_log.info(f"[{serial}] adb connect 第{attempt}次: {out}")
|
||||
time.sleep(1 + attempt)
|
||||
if not connected:
|
||||
_log.warning(f"[{serial}] adb connect 失败: 输出={last_out}")
|
||||
self._set_item(serial, "failed", f"adb connect 失败: {last_out[:150]}")
|
||||
return
|
||||
if ":" in serial:
|
||||
# 网络设备:adb connect(带重试,设备网络可能需要几次握手)
|
||||
connected = False
|
||||
last_out = ""
|
||||
for attempt in range(1, 4): # 最多 3 次
|
||||
out = _adb("connect", serial)
|
||||
last_out = out
|
||||
if "connected" in out.lower() and "failed" not in out.lower():
|
||||
connected = True
|
||||
break
|
||||
_log.info(f"[{serial}] adb connect 第{attempt}次: {out}")
|
||||
time.sleep(1 + attempt)
|
||||
if not connected:
|
||||
_log.warning(f"[{serial}] adb connect 失败: 输出={last_out}")
|
||||
self._set_item(serial, "failed", f"adb connect 失败: {last_out[:150]}")
|
||||
return
|
||||
else:
|
||||
# 本机 USB 设备:确认已在 adb devices 列表中
|
||||
dev_out = _adb("devices")
|
||||
if serial not in (dev_out or ""):
|
||||
self._set_item(serial, "failed",
|
||||
"设备未在本地 adb 列表中,请检查 USB 连接/驱动")
|
||||
return
|
||||
|
||||
# 安装
|
||||
self._set_item(serial, "installing", "正在安装...")
|
||||
|
||||
@@ -458,6 +458,12 @@ class TaskManager:
|
||||
if name in self.groups:
|
||||
del self.groups[name]
|
||||
self._save_groups()
|
||||
# 同上:显式删数据库行,避免删除的分组重启后复活
|
||||
with self._db():
|
||||
row = GroupRow.query.filter_by(name=name).first()
|
||||
if row:
|
||||
db.session.delete(row)
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -492,6 +498,13 @@ class TaskManager:
|
||||
if job_id in self.jobs:
|
||||
del self.jobs[job_id]
|
||||
self._save_jobs()
|
||||
# _save_jobs 只做 upsert 不会删行:必须显式删数据库行,
|
||||
# 否则删除的任务重启后会从数据库重新加载回来
|
||||
with self._db():
|
||||
row = JobRow.query.get(job_id)
|
||||
if row:
|
||||
db.session.delete(row)
|
||||
db.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -2365,11 +2365,10 @@ async function deleteApk(id){
|
||||
async function openInstallModal(apkId){
|
||||
const apk=_apksCache.find(a=>a.id===apkId);
|
||||
const apkName=apk?(apk.display_name||'APK'):'APK';
|
||||
const sr=await apiGet('/api/status');if(!sr||!sr.ok)return;
|
||||
const devices=sr.devices||[];
|
||||
const online=devices.filter(d=>d.present);
|
||||
const r=await apiGet('/api/apks/install/devices');
|
||||
const online=(r&&r.ok)?(r.devices||[]):[];
|
||||
if(!online.length){
|
||||
showToast('没有在线设备','error');return;
|
||||
showToast('没有可用设备(STF 池或本机 adb 均无)','error');return;
|
||||
}
|
||||
const body=
|
||||
'<div style="margin-bottom:12px">'+
|
||||
@@ -2383,11 +2382,11 @@ async function openInstallModal(apkId){
|
||||
'</div>'+
|
||||
'<div id="install-dev-list" class="device-list">'+
|
||||
online.map(d=>{
|
||||
const running=d.worker_status==='running'||d.worker_status==='connecting';
|
||||
const st='<span class="label label-'+(running?'success':'default')+'" style="margin-left:6px">'+(running?'运行中':'空闲')+'</span>';
|
||||
const isUsb=d.source==='usb';
|
||||
const st='<span class="label label-'+(isUsb?'primary':'default')+'" style="margin-left:6px">'+(isUsb?'本地USB':(d.source==='stf'?'STF':'adb'))+'</span>';
|
||||
// serial 唯一,显示 IP + 型号便于区分同型号设备
|
||||
const label=(d.serial||'').replace(/:5555$/,'')+' · '+(d.device_name||d.model||'设备');
|
||||
return '<label><input type="checkbox" value="'+esc(d.serial)+'"'+(running?'':' checked')+'>'+esc(label)+st+'</label>';
|
||||
const label=(d.serial||'').replace(/:5555$/,'')+' · '+(d.model||(isUsb?'USB 有线设备':'设备'));
|
||||
return '<label><input type="checkbox" value="'+esc(d.serial)+'" checked>'+esc(label)+st+'</label>';
|
||||
}).join('')+
|
||||
'</div>';
|
||||
|
||||
|
||||
+30
-2
@@ -363,7 +363,7 @@ def api_groups_update(name):
|
||||
|
||||
|
||||
@app.route("/api/groups/<name>", methods=["DELETE"])
|
||||
@login_required
|
||||
@perm_required(PERM_TASKS)
|
||||
def api_groups_delete(name):
|
||||
if mgr.delete_group(name):
|
||||
return jsonify({"ok": True, "msg": "分组已删除"})
|
||||
@@ -728,8 +728,36 @@ def api_apks_delete(apk_id):
|
||||
return jsonify({"ok": False, "error": msg}), 400
|
||||
|
||||
|
||||
@app.route("/api/apks/install/devices")
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_install_devices():
|
||||
"""可安装设备列表(应用管理):STF 在线设备池 + 本机 adb 设备(含 USB 有线连接)。
|
||||
|
||||
source: stf=STF 池 / usb=本机有线 adb / adb=本机网络 adb。
|
||||
"""
|
||||
devices, seen = [], set()
|
||||
try:
|
||||
for d in stf.list_all_devices():
|
||||
serial = d.get("serial", "")
|
||||
if serial and d.get("present") and d.get("ready"):
|
||||
devices.append({"serial": serial,
|
||||
"model": d.get("model") or d.get("manufacturer") or "",
|
||||
"source": "stf"})
|
||||
seen.add(serial)
|
||||
except Exception:
|
||||
pass
|
||||
out = _adb("devices")
|
||||
for line in (out or "").splitlines()[1:]:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2 and parts[0] and parts[0] not in seen:
|
||||
devices.append({"serial": parts[0], "model": "",
|
||||
"source": "usb" if ":" not in parts[0] else "adb"})
|
||||
seen.add(parts[0])
|
||||
return jsonify({"ok": True, "devices": devices})
|
||||
|
||||
|
||||
@app.route("/api/apks/install", methods=["POST"])
|
||||
@login_required
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_install():
|
||||
"""批量安装 APK 到指定设备。参数: {apk_id, serials:[]}"""
|
||||
data = request.json or {}
|
||||
|
||||
Reference in New Issue
Block a user