import collectionsimport datetimeimport globimport osimport randomimport shutilimport signalimport subprocessimport sysimport tempfileimport timefrom typing import Optional
import cv2import numpy as npimport pyautoguiimport pygetwindow as gwimport win32api, win32confrom mss import mss
PRIMARY_BUTTON_IMAGES = [‘revive_btn.png’, ‘claim_btn.png’]SECONDARY_BUTTON_IMAGES = [‘1.png’, ‘2.png’, ‘3.png’, ‘4.png’]SECONDARY_PROBABILITY = 0.50PRIMARY_PAUSE_SEC = 10
WINDOW_TITLE = ‘Bounce Ball’CLICK_INTERVAL_SEC = 0.5
CAPTURE_W, CAPTURE_H = 1200, 700 # зона, которую сравниваем (из окна)CHECK_INTERVAL_SEC = 5 # раз в 5 с берём кадрFREEZE_CHECKS_LIMIT = 10 # подряд «тихих» кадров → фризDIFF_THRESHOLD = 2.0 # среднее |Δ| яркости 0-255
RECORD_FPS = 24BUFFER_SECONDS = 2500VIDEO_BITRATE = ‘6M’VIDEO_CODEC = ‘h264_nvenc’
sct = mss()run_dir: strscreenshot_index: int = 0
def move_and_click(x: int, y: int):win32api.SetCursorPos((x, y))time.sleep(0.05)win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)time.sleep(0.05)win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)time.sleep(0.05)
def locate_any(images, confidence=0.7):for image_path in images:try:location = pyautogui.locateCenterOnScreen(image_path,confidence=confidence,grayscale=True)except pyautogui.ImageNotFoundException:location = Noneif location is not None:return location, image_pathreturn None, None
def save_screenshot(region):global screenshot_indexscreenshot_index += 1path = os.path.join(run_dir, f"{screenshot_index}.png")pyautogui.screenshot(path, region=region)print(f"[INFO] Сохранён скриншот: {path}")
def start_ffmpeg(x, y, w, h):tmp_dir = tempfile.mkdtemp(prefix=“bb_ffmpeg_”)out_pattern = rf"{tmp_dir}\ring_%02d.mp4" # будет ring_00.mp4
cmd = [
"ffmpeg", "-loglevel", "error", # или "warning", если нужно больше логов
"-f", "gdigrab",
"-framerate", str(RECORD_FPS),
"-offset_x", str(x),
"-offset_y", str(y),
"-video_size", f"{w}x{h}",
"-draw_mouse", "0",
"-show_region", "0",
"-i", "desktop", # если хотите по title: "-i", f"title={WINDOW_TITLE}"
"-an", # аудио отключено
"-c:v", VIDEO_CODEC,
"-preset", "p5" if VIDEO_CODEC.endswith("nvenc") else "ultrafast",
"-b:v", VIDEO_BITRATE,
"-pix_fmt", "nv12" if VIDEO_CODEC.endswith("nvenc") else "yuv420p",
"-f", "segment",
"-segment_time", str(BUFFER_SECONDS),
"-segment_wrap", "1",
"-reset_timestamps", "1",
out_pattern
]
print("[INFO] FFmpeg cmd:", " ".join(cmd)) # можно закомментировать
try:
proc = subprocess.Popen(cmd, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
except FileNotFoundError:
sys.exit("FFmpeg не найден. Добавьте ffmpeg.exe в PATH.")
return proc, tmp_dir, out_pattern
def stop_ffmpeg(proc: subprocess.Popen):if proc.poll() is None:proc.send_signal(signal.CTRL_BREAK_EVENT)try:proc.wait(timeout=3)except subprocess.TimeoutExpired:proc.kill()
def grab_frame(x, y, w, h):raw = sct.grab({“top”: y, “left”: x, “width”: w, “height”: h})gray = cv2.cvtColor(np.array(raw)[:, :, :3], cv2.COLOR_BGR2GRAY)return gray
def main_loop():global run_dir
run_dir = os.path.join(os.getcwd(),
datetime.datetime.now().strftime("run_%Y-%m-%d_%H-%M-%S"))
os.makedirs(run_dir, exist_ok=True)
print(f"[INFO] Каталог: {run_dir}")
windows = gw.getWindowsWithTitle(WINDOW_TITLE)
if not windows:
sys.exit(f"[ERROR] Окно «{WINDOW_TITLE}» не найдено")
game_win = windows[0]
try:
game_win.activate()
except Exception:
pass
x0, y0, w, h = game_win.left, game_win.top, game_win.width, game_win.height
rx1 = x0 + int(w * 0.10); ry1 = y0 + int(h * 0.30)
rx2 = x0 + int(w * 0.70); ry2 = y0 + int(h * 0.70)
ff_proc, ff_dir, ff_pattern = start_ffmpeg(x0, y0, w, h)
last_frame = grab_frame(x0, y0, CAPTURE_W, CAPTURE_H)
static_hits = 0
next_check_t = time.monotonic() + CHECK_INTERVAL_SEC
print(f"[INFO] Запись {RECORD_FPS} fps. Детектор фриза: "
f"{FREEZE_CHECKS_LIMIT}×<{DIFF_THRESHOLD} за {CHECK_INTERVAL_SEC}s")
time.sleep(3)
try:
while True:
now = time.monotonic()
if now >= next_check_t:
cur_frame = grab_frame(x0, y0, CAPTURE_W, CAPTURE_H)
diff = cv2.absdiff(cur_frame, last_frame).mean()
if diff < DIFF_THRESHOLD:
static_hits += 1
print(f"[FREEZE?] Δ={diff:.2f} hit {static_hits}/{FREEZE_CHECKS_LIMIT}")
if static_hits >= FREEZE_CHECKS_LIMIT:
print("[FREEZE] Обнаружен фриз, выходим из цикла.")
break
else:
static_hits = 0
last_frame = cur_frame
next_check_t += CHECK_INTERVAL_SEC
location, img_name = locate_any(PRIMARY_BUTTON_IMAGES)
if location:
if os.path.basename(img_name) == 'claim_btn.png':
save_screenshot((x0, y0, w, h))
move_and_click(location.x, location.y)
print(f"[CLICK] {img_name} @({location.x},{location.y})")
time.sleep(PRIMARY_PAUSE_SEC) # ← Пауза 5 с
else:
if random.random() < SECONDARY_PROBABILITY:
paths = SECONDARY_BUTTON_IMAGES[:]
random.shuffle(paths)
sec_location, sec_img = locate_any(paths)
if sec_location:
move_and_click(sec_location.x, sec_location.y)
print(f"[CLICK] secondary {sec_img} "
f"@({sec_location.x},{sec_location.y})")
time.sleep(CLICK_INTERVAL_SEC)
continue
rand_x, rand_y = random.randint(rx1, rx2), random.randint(ry1, ry2)
move_and_click(rand_x, rand_y)
print(f"[CLICK] random ({rand_x},{rand_y})")
time.sleep(CLICK_INTERVAL_SEC)
finally:
stop_ffmpeg(ff_proc)
try:
last_seg = max(glob.glob(os.path.join(ff_dir, "*.mp4")),
key=os.path.getmtime)
shutil.copy2(last_seg, os.path.join(run_dir, "freeze_capture.mp4"))
print(f"[INFO] Сегмент скопирован → {run_dir}\\freeze_capture.mp4")
except (ValueError, FileNotFoundError):
print("[WARN] Видео-файл не найден.")
shutil.rmtree(ff_dir, ignore_errors=True)
if name == “main”:main_loop()
something like that