Initial VRC OSC commit
This commit is contained in:
167
src/screen/screen_actions.py
Normal file
167
src/screen/screen_actions.py
Normal file
@@ -0,0 +1,167 @@
|
||||
import pyautogui
|
||||
import pygetwindow as gw
|
||||
|
||||
from common.config_loader import loadOcrConfig
|
||||
from common.project_paths import RUNTIME_DIR
|
||||
|
||||
SCREENSHOT_PATH = RUNTIME_DIR / "latest_screenshot.png"
|
||||
OCR_SCREENSHOT_PATH = RUNTIME_DIR / "latest_ocr.png"
|
||||
|
||||
EXCLUDED_WINDOW_KEYWORDS = [
|
||||
"Google Chrome",
|
||||
"Microsoft Edge",
|
||||
"Mozilla Firefox",
|
||||
"Visual Studio Code",
|
||||
"PowerShell",
|
||||
"Windows Terminal",
|
||||
"ChatGPT",
|
||||
]
|
||||
|
||||
|
||||
def log(level, message):
|
||||
from datetime import datetime
|
||||
|
||||
now = datetime.now().strftime("%H:%M:%S")
|
||||
print(f"[{level}] {now} {message}", flush=True)
|
||||
|
||||
|
||||
def findVrchatWindow():
|
||||
windows = gw.getAllWindows()
|
||||
candidates = []
|
||||
|
||||
for window in windows:
|
||||
title = window.title or ""
|
||||
normalized = title.strip().lower()
|
||||
if not normalized:
|
||||
continue
|
||||
if any(keyword.lower() in normalized for keyword in EXCLUDED_WINDOW_KEYWORDS):
|
||||
continue
|
||||
|
||||
if normalized == "vrchat":
|
||||
candidates.append((0, window))
|
||||
continue
|
||||
if normalized.startswith("vrchat "):
|
||||
candidates.append((1, window))
|
||||
continue
|
||||
if normalized.startswith("vrchat"):
|
||||
candidates.append((2, window))
|
||||
|
||||
if not candidates:
|
||||
visible_titles = []
|
||||
for window in windows:
|
||||
title = (window.title or "").strip()
|
||||
if title:
|
||||
visible_titles.append(title)
|
||||
|
||||
log("ERROR", "VRChatウィンドウが見つかりません")
|
||||
log("CHECK", "現在検出できるウィンドウ")
|
||||
for title in visible_titles[:30]:
|
||||
print(title, flush=True)
|
||||
|
||||
raise RuntimeError("VRChatウィンドウが見つかりません")
|
||||
|
||||
candidates = sorted(
|
||||
candidates,
|
||||
key=lambda item: (item[0], -(item[1].width * item[1].height)),
|
||||
)
|
||||
window = candidates[0][1]
|
||||
|
||||
if window.width <= 0 or window.height <= 0:
|
||||
raise RuntimeError("VRChatウィンドウサイズが不正です")
|
||||
|
||||
return window
|
||||
|
||||
|
||||
def getVrchatRegion():
|
||||
window = findVrchatWindow()
|
||||
|
||||
try:
|
||||
if window.isMinimized:
|
||||
window.restore()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
left = int(window.left)
|
||||
top = int(window.top)
|
||||
width = int(window.width)
|
||||
height = int(window.height)
|
||||
|
||||
if width <= 0 or height <= 0:
|
||||
raise RuntimeError("VRChatウィンドウ範囲が不正です")
|
||||
|
||||
log("INFO", f"VRChat window title={window.title}")
|
||||
log("INFO", f"VRChat region left={left} top={top} width={width} height={height}")
|
||||
return left, top, width, height
|
||||
|
||||
|
||||
def clampRatio(value):
|
||||
return max(0.0, min(1.0, value))
|
||||
|
||||
|
||||
def getOcrRegion():
|
||||
left, top, width, height = getVrchatRegion()
|
||||
config = loadOcrConfig()
|
||||
|
||||
# Default to the lower central area. It is much faster than OCRing the
|
||||
# whole VRChat window and usually contains chat/subtitle text.
|
||||
crop_left = clampRatio(config["crop_left"])
|
||||
crop_top = clampRatio(config["crop_top"])
|
||||
crop_right = clampRatio(config["crop_right"])
|
||||
crop_bottom = clampRatio(config["crop_bottom"])
|
||||
|
||||
if crop_right <= crop_left:
|
||||
crop_left = 0.0
|
||||
crop_right = 1.0
|
||||
|
||||
if crop_bottom <= crop_top:
|
||||
crop_top = 0.0
|
||||
crop_bottom = 1.0
|
||||
|
||||
region_left = left + int(width * crop_left)
|
||||
region_top = top + int(height * crop_top)
|
||||
region_width = max(1, int(width * (crop_right - crop_left)))
|
||||
region_height = max(1, int(height * (crop_bottom - crop_top)))
|
||||
|
||||
log(
|
||||
"INFO",
|
||||
f"OCR crop left={region_left} top={region_top} width={region_width} height={region_height}",
|
||||
)
|
||||
return region_left, region_top, region_width, region_height
|
||||
|
||||
|
||||
def captureRegionTo(image_path, region):
|
||||
image_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
left, top, width, height = region
|
||||
|
||||
image = pyautogui.screenshot(region=(left, top, width, height))
|
||||
image.save(image_path)
|
||||
|
||||
log("ACTION", f"captureVrchatWindow saved={image_path}")
|
||||
return image_path
|
||||
|
||||
|
||||
def captureVrchatWindowTo(image_path):
|
||||
return captureRegionTo(
|
||||
image_path,
|
||||
getVrchatRegion(),
|
||||
)
|
||||
|
||||
|
||||
def captureVrchatWindow():
|
||||
return captureVrchatWindowTo(SCREENSHOT_PATH)
|
||||
|
||||
|
||||
def captureOcrRegion():
|
||||
return captureRegionTo(
|
||||
OCR_SCREENSHOT_PATH,
|
||||
getOcrRegion(),
|
||||
)
|
||||
|
||||
|
||||
def captureScreen():
|
||||
return captureVrchatWindow()
|
||||
|
||||
|
||||
def takeScreenshot():
|
||||
return captureVrchatWindow()
|
||||
Reference in New Issue
Block a user