From 0e5923def3f73b2106aeee9c3a7291beeb9a337f Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Wed, 20 Aug 2025 08:54:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B1=8F=E5=B9=95=E5=BD=95=E5=88=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/screen_recorder.py | 297 ++++++++++++++++++++++ frontend/src/renderer/src/services/api.js | 4 +- 2 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 backend/screen_recorder.py diff --git a/backend/screen_recorder.py b/backend/screen_recorder.py new file mode 100644 index 00000000..478c839e --- /dev/null +++ b/backend/screen_recorder.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +屏幕录制工具 +支持录制当前屏幕并保存为视频文件 +""" + +import cv2 +import numpy as np +import pyautogui +import threading +import time +from datetime import datetime +import os + +class ScreenRecorder: + def __init__(self, output_dir="recordings", fps=20, quality=80, region=None): + """ + 初始化屏幕录制器 + + Args: + output_dir (str): 输出目录 + fps (int): 帧率 + quality (int): 视频质量 (1-100) + region (tuple): 录制区域 (x, y, width, height),None表示全屏录制 + """ + self.output_dir = output_dir + self.fps = fps + self.quality = quality + self.recording = False + self.paused = False + self.video_writer = None + self.thread = None + self.region = region + + # 创建输出目录 + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # 获取屏幕尺寸 + self.screen_size = pyautogui.size() + print(f"屏幕尺寸: {self.screen_size}") + + # 设置录制区域 + if self.region: + x, y, width, height = self.region + # 确保区域在屏幕范围内 + x = max(0, min(x, self.screen_size[0] - 1)) + y = max(0, min(y, self.screen_size[1] - 1)) + width = min(width, self.screen_size[0] - x) + height = min(height, self.screen_size[1] - y) + self.region = (x, y, width, height) + self.record_size = (width, height) + print(f"录制区域: {self.region}") + else: + self.record_size = self.screen_size + print("录制模式: 全屏录制") + + def start_recording(self, filename=None): + """ + 开始录制 + + Args: + filename (str): 输出文件名,如果为None则自动生成 + """ + if self.recording: + print("录制已在进行中") + return + + if filename is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"screen_record_{timestamp}.mp4" + + self.output_path = os.path.join(self.output_dir, filename) + + # 设置视频编码器 + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + self.video_writer = cv2.VideoWriter( + self.output_path, + fourcc, + self.fps, + self.record_size + ) + + self.recording = True + self.paused = False + + # 在新线程中开始录制 + self.thread = threading.Thread(target=self._record_loop) + self.thread.daemon = True + self.thread.start() + + print(f"开始录制: {self.output_path}") + + def _record_loop(self): + """ + 录制循环 + """ + while self.recording: + if not self.paused: + # 截取屏幕 + if self.region: + # 区域录制 + x, y, width, height = self.region + screenshot = pyautogui.screenshot(region=(x, y, width, height)) + else: + # 全屏录制 + screenshot = pyautogui.screenshot() + + # 转换为numpy数组 + frame = np.array(screenshot) + + # 转换颜色格式 (RGB -> BGR) + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + + # 写入视频文件 + self.video_writer.write(frame) + + # 控制帧率 + time.sleep(1.0 / self.fps) + + def pause_recording(self): + """ + 暂停录制 + """ + if not self.recording: + print("当前没有在录制") + return + + self.paused = not self.paused + status = "暂停" if self.paused else "继续" + print(f"录制{status}") + + def stop_recording(self): + """ + 停止录制 + """ + if not self.recording: + print("当前没有在录制") + return + + self.recording = False + self.paused = False + + # 等待录制线程结束 + if self.thread: + self.thread.join() + + # 释放视频写入器 + if self.video_writer: + self.video_writer.release() + self.video_writer = None + + print(f"录制完成: {self.output_path}") + + def set_region(self, region): + """ + 设置录制区域 + + Args: + region (tuple): 录制区域 (x, y, width, height),None表示全屏录制 + """ + if self.recording: + print("录制进行中,无法更改区域设置") + return False + + self.region = region + + # 重新计算录制区域 + if self.region: + x, y, width, height = self.region + # 确保区域在屏幕范围内 + x = max(0, min(x, self.screen_size[0] - 1)) + y = max(0, min(y, self.screen_size[1] - 1)) + width = min(width, self.screen_size[0] - x) + height = min(height, self.screen_size[1] - y) + self.region = (x, y, width, height) + self.record_size = (width, height) + print(f"录制区域已设置: {self.region}") + else: + self.record_size = self.screen_size + print("录制模式已设置: 全屏录制") + + return True + + def get_status(self): + """ + 获取录制状态 + + Returns: + dict: 包含录制状态信息的字典 + """ + return { + 'recording': self.recording, + 'paused': self.paused, + 'output_path': getattr(self, 'output_path', None), + 'screen_size': self.screen_size, + 'record_size': self.record_size, + 'region': self.region, + 'fps': self.fps + } + +def main(): + """ + 主函数 - 命令行界面 + """ + recorder = ScreenRecorder() + + print("\n=== 屏幕录制工具 ===") + print("命令:") + print(" start [filename] - 开始录制") + print(" pause - 暂停/恢复录制") + print(" stop - 停止录制") + print(" status - 查看状态") + print(" region x y w h - 设置录制区域 (x, y, 宽度, 高度)") + print(" fullscreen - 设置全屏录制") + print(" center w h - 设置居中区域录制 (宽度, 高度)") + print(" quit - 退出程序") + print("\n输入命令:") + + while True: + try: + command = input("> ").strip().split() + + if not command: + continue + + cmd = command[0].lower() + + if cmd == "start": + filename = command[1] if len(command) > 1 else None + recorder.start_recording(filename) + + elif cmd == "pause": + recorder.pause_recording() + + elif cmd == "stop": + recorder.stop_recording() + + elif cmd == "status": + status = recorder.get_status() + print(f"录制状态: {'进行中' if status['recording'] else '已停止'}") + print(f"暂停状态: {'是' if status['paused'] else '否'}") + print(f"输出文件: {status['output_path']}") + print(f"屏幕尺寸: {status['screen_size']}") + print(f"录制尺寸: {status['record_size']}") + print(f"录制区域: {status['region'] if status['region'] else '全屏'}") + print(f"帧率: {status['fps']} FPS") + + elif cmd == "region": + if len(command) != 5: + print("用法: region x y width height") + continue + try: + x, y, w, h = map(int, command[1:5]) + if recorder.set_region((x, y, w, h)): + print(f"录制区域已设置: ({x}, {y}, {w}, {h})") + except ValueError: + print("请输入有效的数字") + + elif cmd == "fullscreen": + if recorder.set_region(None): + print("已设置为全屏录制") + + elif cmd == "center": + if len(command) != 3: + print("用法: center width height") + continue + try: + w, h = map(int, command[1:3]) + screen_w, screen_h = recorder.screen_size + x = (screen_w - w) // 2 + y = (screen_h - h) // 2 + if recorder.set_region((x, y, w, h)): + print(f"居中录制区域已设置: ({x}, {y}, {w}, {h})") + except ValueError: + print("请输入有效的数字") + + elif cmd == "quit": + if recorder.recording: + recorder.stop_recording() + print("程序退出") + break + + else: + print("未知命令") + + except KeyboardInterrupt: + if recorder.recording: + recorder.stop_recording() + print("\n程序退出") + break + except Exception as e: + print(f"错误: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/frontend/src/renderer/src/services/api.js b/frontend/src/renderer/src/services/api.js index c0070b67..1b3a18be 100644 --- a/frontend/src/renderer/src/services/api.js +++ b/frontend/src/renderer/src/services/api.js @@ -13,7 +13,7 @@ api.interceptors.request.use( if (window.electronAPI) { config.baseURL = window.electronAPI.getBackendUrl() } else { - config.baseURL = 'http://192.168.1.58:5000' + config.baseURL = 'http://localhost:5000' } // 只为需要发送数据的请求设置Content-Type @@ -607,7 +607,7 @@ export const getBackendUrl = () => { if (window.electronAPI) { return window.electronAPI.getBackendUrl() } else { - return 'http://192.168.1.58:5000' + return 'http://localhost:5000' } }