屏幕录制功能

This commit is contained in:
root 2025-08-20 08:54:36 +08:00
parent f754072e08
commit 0e5923def3
2 changed files with 299 additions and 2 deletions

297
backend/screen_recorder.py Normal file
View File

@ -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()

View File

@ -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'
}
}