mirror of
https://github.com/wess09/AzurLaneAutoScript.git
synced 2026-05-14 01:18:15 +08:00
add:截图查看
This commit is contained in:
parent
4882c78545
commit
10c4034897
@ -171,6 +171,260 @@
|
||||
obs.observe(document.body, { childList: true, subtree: true });
|
||||
})();
|
||||
|
||||
// ============================================================
|
||||
// 实时截图预览(H264/H265 over fragmented MP4 WebSocket)
|
||||
// ============================================================
|
||||
(function () {
|
||||
var state = {
|
||||
socket: null,
|
||||
mediaSource: null,
|
||||
sourceBuffer: null,
|
||||
queue: [],
|
||||
objectUrl: '',
|
||||
instance: 'alas',
|
||||
codec: localStorage.getItem('alas_live_preview_codec') || 'h264',
|
||||
open: false,
|
||||
transportId: 0
|
||||
};
|
||||
|
||||
function sanitizeText(text) {
|
||||
return String(text || '').replace(/[<>&]/g, function (ch) {
|
||||
return ({ '<': '<', '>': '>', '&': '&' })[ch];
|
||||
});
|
||||
}
|
||||
|
||||
function ensurePanel() {
|
||||
var panel = document.getElementById('alas-live-preview');
|
||||
if (panel) return panel;
|
||||
|
||||
panel = document.createElement('div');
|
||||
panel.id = 'alas-live-preview';
|
||||
panel.innerHTML = [
|
||||
'<div class="alas-live-preview-head">',
|
||||
'<span class="alas-live-preview-title">实时截图</span>',
|
||||
'<select class="alas-live-preview-codec" title="编码">',
|
||||
'<option value="h264">H264</option>',
|
||||
'<option value="h265">H265</option>',
|
||||
'</select>',
|
||||
'<button class="alas-live-preview-close" type="button" title="关闭">×</button>',
|
||||
'</div>',
|
||||
'<video class="alas-live-preview-video" muted autoplay playsinline></video>',
|
||||
'<div class="alas-live-preview-status">连接中</div>'
|
||||
].join('');
|
||||
|
||||
var style = document.createElement('style');
|
||||
style.textContent = [
|
||||
'#alas-live-preview{position:fixed;right:18px;bottom:18px;width:min(560px,calc(100vw - 36px));background:#101418;border:1px solid rgba(255,255,255,.14);border-radius:8px;box-shadow:0 12px 36px rgba(0,0,0,.35);z-index:99990;overflow:hidden;display:none;}',
|
||||
'.alas-live-preview-head{height:38px;display:flex;align-items:center;gap:8px;padding:0 8px 0 12px;background:#1b222b;color:#f2f5f8;font-size:14px;}',
|
||||
'.alas-live-preview-title{font-weight:600;margin-right:auto;}',
|
||||
'.alas-live-preview-codec{height:26px;border-radius:4px;border:1px solid rgba(255,255,255,.2);background:#111820;color:#f2f5f8;padding:0 6px;}',
|
||||
'.alas-live-preview-close{width:28px;height:28px;border:0;background:transparent;color:#f2f5f8;font-size:24px;line-height:24px;cursor:pointer;}',
|
||||
'.alas-live-preview-video{display:block;width:100%;aspect-ratio:16/9;background:#000;object-fit:contain;}',
|
||||
'.alas-live-preview-status{position:absolute;left:12px;bottom:10px;max-width:calc(100% - 24px);padding:4px 8px;border-radius:4px;background:rgba(0,0,0,.58);color:#fff;font-size:12px;line-height:1.35;pointer-events:none;}'
|
||||
].join('');
|
||||
document.head.appendChild(style);
|
||||
document.body.appendChild(panel);
|
||||
|
||||
panel.querySelector('.alas-live-preview-close').onclick = function () {
|
||||
window.alasStopLivePreview();
|
||||
};
|
||||
panel.querySelector('.alas-live-preview-codec').onchange = function (e) {
|
||||
state.codec = e.target.value;
|
||||
localStorage.setItem('alas_live_preview_codec', state.codec);
|
||||
if (state.open) start(state.instance, state.codec);
|
||||
};
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
function setStatus(text) {
|
||||
var panel = ensurePanel();
|
||||
var status = panel.querySelector('.alas-live-preview-status');
|
||||
status.innerHTML = sanitizeText(text);
|
||||
status.style.display = text ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function cleanupTransport() {
|
||||
state.transportId += 1;
|
||||
if (state.socket) {
|
||||
state.socket.onclose = null;
|
||||
state.socket.onerror = null;
|
||||
state.socket.onmessage = null;
|
||||
try { state.socket.close(); } catch (e) { }
|
||||
state.socket = null;
|
||||
}
|
||||
if (state.sourceBuffer) {
|
||||
state.sourceBuffer.onupdateend = null;
|
||||
state.sourceBuffer = null;
|
||||
}
|
||||
if (state.mediaSource) {
|
||||
try {
|
||||
if (state.mediaSource.readyState === 'open') state.mediaSource.endOfStream();
|
||||
} catch (e) { }
|
||||
state.mediaSource = null;
|
||||
}
|
||||
if (state.objectUrl) {
|
||||
URL.revokeObjectURL(state.objectUrl);
|
||||
state.objectUrl = '';
|
||||
}
|
||||
state.queue = [];
|
||||
}
|
||||
|
||||
function appendNext(transportId) {
|
||||
if (transportId !== state.transportId) return;
|
||||
var sb = state.sourceBuffer;
|
||||
if (!sb || sb.updating || !state.queue.length) return;
|
||||
try {
|
||||
sb.appendBuffer(state.queue.shift());
|
||||
} catch (e) {
|
||||
if (transportId !== state.transportId) return;
|
||||
setStatus(e.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
function attachMedia(socket, codec, mime, transportId) {
|
||||
var panel = ensurePanel();
|
||||
var video = panel.querySelector('.alas-live-preview-video');
|
||||
if (!state.open || transportId !== state.transportId) {
|
||||
try { socket.close(); } catch (e) { }
|
||||
return;
|
||||
}
|
||||
|
||||
state.socket = socket;
|
||||
state.mediaSource = new MediaSource();
|
||||
state.objectUrl = URL.createObjectURL(state.mediaSource);
|
||||
video.src = state.objectUrl;
|
||||
|
||||
state.mediaSource.addEventListener('sourceopen', function () {
|
||||
if (!state.open || transportId !== state.transportId || state.socket !== socket) {
|
||||
return;
|
||||
}
|
||||
if (!MediaSource.isTypeSupported(mime)) {
|
||||
setStatus(codec.toUpperCase() + ' 当前浏览器不支持');
|
||||
cleanupTransport();
|
||||
return;
|
||||
}
|
||||
state.sourceBuffer = state.mediaSource.addSourceBuffer(mime);
|
||||
state.sourceBuffer.mode = 'segments';
|
||||
state.sourceBuffer.onupdateend = function () {
|
||||
appendNext(transportId);
|
||||
};
|
||||
state.socket.onmessage = function (event) {
|
||||
if (!state.open || transportId !== state.transportId || state.socket !== socket) return;
|
||||
if (typeof event.data === 'string') {
|
||||
try {
|
||||
var msg = JSON.parse(event.data);
|
||||
if (msg.type === 'error') setStatus(msg.message);
|
||||
} catch (e) { }
|
||||
return;
|
||||
}
|
||||
state.queue.push(event.data);
|
||||
setStatus('');
|
||||
appendNext(transportId);
|
||||
};
|
||||
state.socket.onerror = function () {
|
||||
if (transportId === state.transportId) setStatus('实时截图连接错误');
|
||||
};
|
||||
state.socket.onclose = function () {
|
||||
if (state.open && transportId === state.transportId) setStatus('实时截图已断开');
|
||||
};
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
function getSocketCandidates() {
|
||||
var scheme = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
var query = '?instance=' + encodeURIComponent(state.instance) +
|
||||
'&codec=' + encodeURIComponent(state.codec) + '&fps=5&width=640';
|
||||
var candidates = [scheme + location.host + '/ws/live_screenshot' + query];
|
||||
var pathParts = location.pathname.split('/').filter(Boolean);
|
||||
var firstPart = pathParts.length ? pathParts[0] : '';
|
||||
|
||||
// Alas 远程访问入口通常是 /{sock_name}/...,其中 sock_name 为 8+ 位小写字母数字。
|
||||
if (/^[a-z0-9]{8,}$/.test(firstPart)) {
|
||||
candidates.unshift(scheme + location.host + '/' + firstPart + '/ws/live_screenshot' + query);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function start(instance, codec) {
|
||||
var panel = ensurePanel();
|
||||
cleanupTransport();
|
||||
state.open = true;
|
||||
state.instance = instance || 'alas';
|
||||
state.codec = codec || state.codec || 'h264';
|
||||
panel.style.display = 'block';
|
||||
panel.querySelector('.alas-live-preview-codec').value = state.codec;
|
||||
setStatus('连接中');
|
||||
var transportId = state.transportId;
|
||||
var candidates = getSocketCandidates();
|
||||
var attempt = 0;
|
||||
|
||||
function connectNext() {
|
||||
if (!state.open || transportId !== state.transportId) return;
|
||||
if (attempt >= candidates.length) {
|
||||
setStatus('实时截图连接失败');
|
||||
return;
|
||||
}
|
||||
|
||||
var socket = new WebSocket(candidates[attempt++]);
|
||||
var ready = false;
|
||||
var advanced = false;
|
||||
function advance() {
|
||||
if (advanced) return;
|
||||
advanced = true;
|
||||
connectNext();
|
||||
}
|
||||
socket.binaryType = 'arraybuffer';
|
||||
socket.onmessage = function (event) {
|
||||
if (transportId !== state.transportId) return;
|
||||
if (typeof event.data !== 'string') return;
|
||||
var msg;
|
||||
try { msg = JSON.parse(event.data); } catch (e) { return; }
|
||||
if (msg.type === 'ready') {
|
||||
ready = true;
|
||||
attachMedia(socket, state.codec, msg.mime, transportId);
|
||||
} else if (msg.type === 'error') {
|
||||
setStatus(msg.message);
|
||||
socket.close();
|
||||
}
|
||||
};
|
||||
socket.onerror = function () {
|
||||
if (!ready) advance();
|
||||
};
|
||||
socket.onclose = function () {
|
||||
if (!state.open || transportId !== state.transportId) return;
|
||||
if (!ready && !state.socket) {
|
||||
advance();
|
||||
} else if (ready && state.socket === socket) {
|
||||
setStatus('实时截图已断开');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
connectNext();
|
||||
}
|
||||
|
||||
window.alasStartLivePreview = function (instance, codec) {
|
||||
start(instance, codec);
|
||||
};
|
||||
|
||||
window.alasStopLivePreview = function () {
|
||||
state.open = false;
|
||||
cleanupTransport();
|
||||
var panel = ensurePanel();
|
||||
panel.style.display = 'none';
|
||||
};
|
||||
|
||||
window.alasToggleLivePreview = function (instance) {
|
||||
if (state.open) {
|
||||
window.alasStopLivePreview();
|
||||
} else {
|
||||
window.alasStartLivePreview(instance, state.codec);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// ============================================================
|
||||
// 公告系统
|
||||
// ============================================================
|
||||
|
||||
@ -308,9 +308,7 @@ async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
|
||||
config = AzurLaneConfig(inst)
|
||||
device = Device(config)
|
||||
image = device.screenshot()
|
||||
# ALAS uses BGR (OpenCV format), convert to RGB for PIL
|
||||
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
image_pil = Image.fromarray(image_rgb)
|
||||
image_pil = Image.fromarray(image)
|
||||
|
||||
buffered = BytesIO()
|
||||
image_pil.save(buffered, format="JPEG")
|
||||
|
||||
@ -602,7 +602,7 @@ class NemuIpc(Platform):
|
||||
def screenshot_nemu_ipc(self):
|
||||
image = self.nemu_ipc.screenshot()
|
||||
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGB)
|
||||
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
|
||||
cv2.flip(image, 0, dst=image)
|
||||
return image
|
||||
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
import cv2
|
||||
from starlette.responses import JSONResponse, HTMLResponse
|
||||
from starlette.routing import Route
|
||||
from starlette.routing import Route, WebSocketRoute
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
from module.logger import logger
|
||||
|
||||
def api_cl1_stats(request):
|
||||
@ -36,8 +46,255 @@ def serve_obs_overlay(request):
|
||||
except Exception as e:
|
||||
return HTMLResponse(f"Error loading obs overlay: {e}", status_code=500)
|
||||
|
||||
|
||||
def _get_ffmpeg_path():
|
||||
ffmpeg = shutil.which("ffmpeg")
|
||||
if ffmpeg:
|
||||
return ffmpeg
|
||||
try:
|
||||
import imageio_ffmpeg
|
||||
return imageio_ffmpeg.get_ffmpeg_exe()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _video_stream_command(ffmpeg, codec, width, height, fps):
|
||||
bitrate = "800k"
|
||||
bufsize = "1600k"
|
||||
base = [
|
||||
ffmpeg,
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-f",
|
||||
"rawvideo",
|
||||
"-pix_fmt",
|
||||
"rgb24",
|
||||
"-s",
|
||||
f"{width}x{height}",
|
||||
"-r",
|
||||
str(fps),
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-an",
|
||||
]
|
||||
if codec == "h265":
|
||||
return base + [
|
||||
"-c:v",
|
||||
"libx265",
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
"-b:v",
|
||||
bitrate,
|
||||
"-maxrate",
|
||||
bitrate,
|
||||
"-bufsize",
|
||||
bufsize,
|
||||
"-x265-params",
|
||||
f"log-level=error:keyint={fps}:min-keyint={fps}:scenecut=0",
|
||||
"-tag:v",
|
||||
"hvc1",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-f",
|
||||
"mp4",
|
||||
"-movflags",
|
||||
"empty_moov+default_base_moof+frag_keyframe",
|
||||
"pipe:1",
|
||||
]
|
||||
|
||||
return base + [
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
"-tune",
|
||||
"zerolatency",
|
||||
"-b:v",
|
||||
bitrate,
|
||||
"-maxrate",
|
||||
bitrate,
|
||||
"-bufsize",
|
||||
bufsize,
|
||||
"-profile:v",
|
||||
"baseline",
|
||||
"-level",
|
||||
"3.1",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-g",
|
||||
str(fps),
|
||||
"-keyint_min",
|
||||
str(fps),
|
||||
"-sc_threshold",
|
||||
"0",
|
||||
"-f",
|
||||
"mp4",
|
||||
"-movflags",
|
||||
"empty_moov+default_base_moof+frag_keyframe",
|
||||
"pipe:1",
|
||||
]
|
||||
|
||||
|
||||
async def ws_live_screenshot(websocket):
|
||||
await websocket.accept()
|
||||
|
||||
instance = websocket.query_params.get("instance", "alas")
|
||||
codec = websocket.query_params.get("codec", "h264").lower()
|
||||
if codec not in ("h264", "h265"):
|
||||
codec = "h264"
|
||||
try:
|
||||
fps = int(websocket.query_params.get("fps", "5"))
|
||||
except ValueError:
|
||||
fps = 5
|
||||
fps = max(1, min(fps, 15))
|
||||
try:
|
||||
target_width = int(websocket.query_params.get("width", "640"))
|
||||
except ValueError:
|
||||
target_width = 640
|
||||
target_width = max(320, min(target_width, 1280))
|
||||
|
||||
ffmpeg = _get_ffmpeg_path()
|
||||
if not ffmpeg:
|
||||
await websocket.send_text(json.dumps({
|
||||
"type": "error",
|
||||
"message": "ffmpeg not found. Install ffmpeg or imageio-ffmpeg to use H264/H265 live preview.",
|
||||
}))
|
||||
await websocket.close()
|
||||
return
|
||||
|
||||
stop_event = threading.Event()
|
||||
out_queue = queue.Queue(maxsize=16)
|
||||
proc = None
|
||||
|
||||
try:
|
||||
from module.webui.fake_pil_module import remove_fake_pil_module
|
||||
remove_fake_pil_module()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
from module.config.config import AzurLaneConfig
|
||||
from module.device.device import Device
|
||||
|
||||
if "ALAS_CONFIG_NAME" not in os.environ:
|
||||
os.environ["ALAS_CONFIG_NAME"] = instance
|
||||
|
||||
config = AzurLaneConfig(instance)
|
||||
device = Device(config)
|
||||
first = device.screenshot()
|
||||
src_height, src_width = first.shape[:2]
|
||||
target_height = int(round(target_width * src_height / src_width))
|
||||
if target_height % 2:
|
||||
target_height += 1
|
||||
size = (target_width, target_height)
|
||||
|
||||
mime = (
|
||||
'video/mp4; codecs="hvc1.1.6.L93.B0"'
|
||||
if codec == "h265"
|
||||
else 'video/mp4; codecs="avc1.42E01E"'
|
||||
)
|
||||
await websocket.send_text(json.dumps({
|
||||
"type": "ready",
|
||||
"codec": codec,
|
||||
"mime": mime,
|
||||
"width": target_width,
|
||||
"height": target_height,
|
||||
"fps": fps,
|
||||
}))
|
||||
|
||||
proc = subprocess.Popen(
|
||||
_video_stream_command(ffmpeg, codec, target_width, target_height, fps),
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
|
||||
def normalize_frame(image):
|
||||
if image.shape[1] != target_width or image.shape[0] != target_height:
|
||||
image = cv2.resize(image, size, interpolation=cv2.INTER_AREA)
|
||||
if not image.flags["C_CONTIGUOUS"]:
|
||||
image = image.copy()
|
||||
return image
|
||||
|
||||
def writer():
|
||||
frame_interval = 1 / fps
|
||||
next_frame = time.perf_counter()
|
||||
image = first
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
proc.stdin.write(normalize_frame(image).tobytes())
|
||||
proc.stdin.flush()
|
||||
except Exception:
|
||||
break
|
||||
next_frame += frame_interval
|
||||
sleep_for = next_frame - time.perf_counter()
|
||||
if sleep_for > 0:
|
||||
stop_event.wait(sleep_for)
|
||||
if stop_event.is_set():
|
||||
break
|
||||
try:
|
||||
image = device.screenshot()
|
||||
except Exception as e:
|
||||
out_queue.put(("error", str(e)))
|
||||
break
|
||||
try:
|
||||
proc.stdin.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def reader():
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
chunk = proc.stdout.read(32768)
|
||||
except Exception:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
out_queue.put(("data", chunk))
|
||||
out_queue.put(("eof", None))
|
||||
|
||||
def stderr_reader():
|
||||
try:
|
||||
err = proc.stderr.read().decode("utf-8", errors="replace").strip()
|
||||
except Exception:
|
||||
err = ""
|
||||
if err and not stop_event.is_set():
|
||||
out_queue.put(("error", err[-1000:]))
|
||||
|
||||
threading.Thread(target=writer, daemon=True).start()
|
||||
threading.Thread(target=reader, daemon=True).start()
|
||||
threading.Thread(target=stderr_reader, daemon=True).start()
|
||||
|
||||
while not stop_event.is_set():
|
||||
kind, payload = await asyncio.to_thread(out_queue.get)
|
||||
if kind == "data":
|
||||
await websocket.send_bytes(payload)
|
||||
elif kind == "error":
|
||||
await websocket.send_text(json.dumps({"type": "error", "message": payload}))
|
||||
break
|
||||
else:
|
||||
break
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"ws_live_screenshot error: {e}")
|
||||
try:
|
||||
await websocket.send_text(json.dumps({"type": "error", "message": str(e)}))
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
stop_event.set()
|
||||
if proc is not None:
|
||||
try:
|
||||
proc.kill()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
api_routes = [
|
||||
Route("/api/cl1_stats", api_cl1_stats),
|
||||
Route("/api/ap_timeline", api_ap_timeline),
|
||||
Route("/obs", serve_obs_overlay),
|
||||
WebSocketRoute("/ws/live_screenshot", ws_live_screenshot),
|
||||
]
|
||||
|
||||
@ -1419,6 +1419,13 @@ class AlasGUI(Frame):
|
||||
"log-bar-btns",
|
||||
[
|
||||
put_scope("log_scroll_btn"),
|
||||
put_button(
|
||||
label="截图预览",
|
||||
onclick=lambda: run_js(
|
||||
f"window.alasToggleLivePreview({json.dumps(self.alas_name)});"
|
||||
),
|
||||
color="off",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -1757,6 +1764,13 @@ class AlasGUI(Frame):
|
||||
"log-bar-btns",
|
||||
[
|
||||
put_scope("log_scroll_btn"),
|
||||
put_button(
|
||||
label="截图预览",
|
||||
onclick=lambda: run_js(
|
||||
f"window.alasToggleLivePreview({json.dumps(self.alas_name)});"
|
||||
),
|
||||
color="off",
|
||||
),
|
||||
put_scope("dashboard_btn"),
|
||||
],
|
||||
),
|
||||
@ -2168,6 +2182,13 @@ class AlasGUI(Frame):
|
||||
"log-bar-btns",
|
||||
[
|
||||
put_scope("log_scroll_btn"),
|
||||
put_button(
|
||||
label="截图预览",
|
||||
onclick=lambda: run_js(
|
||||
f"window.alasToggleLivePreview({json.dumps(self.alas_name)});"
|
||||
),
|
||||
color="off",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ scipy
|
||||
pillow
|
||||
opencv-python>=4.8.0
|
||||
imageio
|
||||
imageio-ffmpeg
|
||||
adbutils
|
||||
uiautomator2
|
||||
uiautomator2cache
|
||||
@ -30,7 +31,8 @@ typing_extensions>=4.8.0
|
||||
pydantic>=2.5.0
|
||||
rapidocr
|
||||
onnxruntime-directml>=1.24.4; sys_platform == "win32"
|
||||
onnxruntime>=1.24.4; sys_platform != "win32"
|
||||
onnxruntime>=1.24.4; sys_platform == "linux"
|
||||
onnxruntime @ https://files.pythonhosted.org/packages/fb/aa/04530bd38e31e26970fa1212346d76cf81705dc16a8ee5e6f4fb24634c11/onnxruntime-1.25.1-cp314-cp314-macosx_14_0_arm64.whl; sys_platform == "darwin" and platform_machine == "arm64"
|
||||
watchdog>=2.0.0
|
||||
numba
|
||||
pip
|
||||
@ -41,4 +43,4 @@ importlib_metadata>=8.0.0
|
||||
openai
|
||||
mcp==1.23.0
|
||||
sse-starlette==3.0.3
|
||||
pygments>=2.20.0
|
||||
pygments>=2.20.0
|
||||
|
||||
@ -95,6 +95,8 @@ idna==3.7
|
||||
# requests
|
||||
imageio==2.26.0
|
||||
# via -r requirements-in.txt
|
||||
imageio-ffmpeg==0.6.0
|
||||
# via -r requirements-in.txt
|
||||
importlib-metadata==8.0.0
|
||||
# via -r requirements-in.txt
|
||||
importlib-resources==6.0.0
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile requirements-in.txt --python-platform macos --python-version 3.14 --output-file requirements-macos.txt
|
||||
# uv pip compile requirements-in.txt --python-platform aarch64-apple-darwin --python-version 3.14 --output-file requirements-macos.txt
|
||||
adbutils==2.12.0
|
||||
# via
|
||||
# -r requirements-in.txt
|
||||
@ -81,6 +81,8 @@ idna==3.7
|
||||
# requests
|
||||
imageio==2.37.3
|
||||
# via -r requirements-in.txt
|
||||
imageio-ffmpeg==0.6.0
|
||||
# via -r requirements-in.txt
|
||||
importlib-metadata==8.0.0
|
||||
# via -r requirements-in.txt
|
||||
importlib-resources==6.0.0
|
||||
@ -111,8 +113,6 @@ mcp==1.23.0
|
||||
# via -r requirements-in.txt
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
mpmath==1.3.0
|
||||
# via sympy
|
||||
msgpack==1.1.2
|
||||
# via zerorpc
|
||||
numba==0.65.0
|
||||
@ -133,7 +133,7 @@ omegaconf==2.3.0
|
||||
# via rapidocr
|
||||
onepush==1.8.0
|
||||
# via -r requirements-in.txt
|
||||
onnxruntime==1.24.4
|
||||
onnxruntime @ https://files.pythonhosted.org/packages/fb/aa/04530bd38e31e26970fa1212346d76cf81705dc16a8ee5e6f4fb24634c11/onnxruntime-1.25.1-cp314-cp314-macosx_14_0_arm64.whl
|
||||
# via -r requirements-in.txt
|
||||
openai==2.31.0
|
||||
# via -r requirements-in.txt
|
||||
@ -254,8 +254,6 @@ starlette==0.49.1
|
||||
# via
|
||||
# -r requirements-in.txt
|
||||
# mcp
|
||||
sympy==1.14.0
|
||||
# via onnxruntime
|
||||
tornado==6.5.5
|
||||
# via pywebio
|
||||
tqdm==4.67.3
|
||||
|
||||
@ -24,7 +24,7 @@ decorator==5.1.1 # via retry
|
||||
deprecated==1.2.13 # via uiautomator2
|
||||
deprecation==2.1.0 # via adbutils
|
||||
distro==1.9.0 # via openai
|
||||
filelock==3.20.3 # via uiautomator2
|
||||
filelock==3.20.3 # via uiautomator2
|
||||
flatbuffers==25.12.19 # via onnxruntime-directml
|
||||
fonttools==4.62.1 # via matplotlib
|
||||
future==0.18.3 # via zerorpc
|
||||
@ -37,6 +37,7 @@ httpx==0.28.1 # via mcp, openai
|
||||
httpx-sse==0.4.3 # via mcp
|
||||
idna==3.7 # via anyio, httpx, requests
|
||||
imageio==2.26.0 # via -r requirements-in.txt
|
||||
imageio-ffmpeg==0.6.0 # via -r requirements-in.txt
|
||||
importlib-metadata==8.0.0 # via -r requirements-in.txt
|
||||
importlib-resources==6.0.0 # via -r requirements-in.txt
|
||||
inflection==0.5.1 # via -r requirements-in.txt
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user