148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
"""screenCaptureNoConfirm
|
||
pywebview 通过js跳过确认直接获取屏幕媒体流
|
||
"""
|
||
|
||
import webview
|
||
import threading
|
||
import time
|
||
|
||
# -------------------------- 1. PyWebView配置 --------------------------
|
||
class ScreenCaptureAPI:
|
||
def __init__(self):
|
||
self.window = None
|
||
|
||
def set_window(self, window):
|
||
self.window = window
|
||
|
||
def auto_trigger_capture(self):
|
||
"""后台自动触发屏幕采集(模拟用户点击)"""
|
||
# 延迟1秒(等待页面加载完成)
|
||
time.sleep(1)
|
||
# 执行JS自动点击按钮,触发屏幕采集
|
||
self.window.evaluate_js("document.getElementById('autoCaptureBtn').click();")
|
||
|
||
# -------------------------- 2. 前端HTML(核心:自动触发+无感采集) --------------------------
|
||
HTML_CONTENT = """
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>自动获取屏幕流</title>
|
||
<style>
|
||
* {margin: 0; padding: 0; box-sizing: border-box;}
|
||
body {padding: 20px; background: #f5f7fa; font-family: Arial;}
|
||
.video-container {
|
||
width: 100%; max-width: 1600px;
|
||
border: 1px solid #ddd; border-radius: 8px;
|
||
overflow: hidden; background: #000;
|
||
margin-top: 20px;
|
||
}
|
||
#localVideo {
|
||
width: 100%; height: auto;
|
||
display: block; object-fit: contain;
|
||
}
|
||
/* 隐藏触发按钮(仅用于自动点击) */
|
||
#autoCaptureBtn {
|
||
position: absolute; top: -9999px; left: -9999px;
|
||
opacity: 0; pointer-events: none;
|
||
}
|
||
.status {
|
||
margin: 10px 0; color: #666; font-size: 14px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 隐藏的自动触发按钮(用于模拟用户点击) -->
|
||
<button id="autoCaptureBtn" onclick="captureScreen()"></button>
|
||
|
||
<div class="status" id="status">状态:等待自动采集...</div>
|
||
<div class="video-container">
|
||
<video id="localVideo" autoplay muted playsinline></video>
|
||
</div>
|
||
|
||
<script>
|
||
const videoEl = document.getElementById('localVideo');
|
||
const statusEl = document.getElementById('status');
|
||
let screenStream = null;
|
||
|
||
// 核心:获取屏幕媒体流(必须由用户手势触发)
|
||
async function captureScreen() {
|
||
try {
|
||
statusEl.textContent = "状态:请求屏幕权限...";
|
||
// 调用Screen Capture API(系统级弹窗无法跳过)
|
||
screenStream = await navigator.mediaDevices.getDisplayMedia({
|
||
video: {
|
||
cursor: "always", // 显示鼠标光标
|
||
displaySurface: "monitor" // 优先选择整个显示器
|
||
},
|
||
audio: false // 可选:是否采集音频
|
||
});
|
||
|
||
// 将流绑定到video标签
|
||
videoEl.srcObject = screenStream;
|
||
statusEl.textContent = "状态:屏幕采集成功!";
|
||
|
||
// 监听流结束事件(用户停止共享)
|
||
screenStream.getVideoTracks()[0].addEventListener('ended', () => {
|
||
statusEl.textContent = "状态:屏幕采集已停止";
|
||
videoEl.srcObject = null;
|
||
});
|
||
|
||
} catch (error) {
|
||
statusEl.textContent = `状态:采集失败 - ${error.message}`;
|
||
console.error("屏幕采集错误:", error);
|
||
}
|
||
}
|
||
|
||
// 页面加载完成后,通知Python端可以自动触发
|
||
window.onload = async () => {
|
||
// 告知Python页面已加载,可触发自动点击
|
||
await window.pywebview.api.page_loaded();
|
||
};
|
||
</script>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
# -------------------------- 3. 主程序入口 --------------------------
|
||
if __name__ == "__main__":
|
||
# 初始化API
|
||
api = ScreenCaptureAPI()
|
||
|
||
# 创建pywebview窗口(关键配置:WebView2引擎+权限豁免)
|
||
window = webview.create_window(
|
||
title="自动获取屏幕流",
|
||
html=HTML_CONTENT,
|
||
width=1400,
|
||
height=900,
|
||
resizable=True,
|
||
# confirm_close=True,
|
||
# 额外权限配置(WebView2)
|
||
# webview_settings={
|
||
# "web_security": False, # 关闭跨域限制(本地运行需开启)
|
||
# "allow_displaying_insecure_content": True,
|
||
# "allow_running_insecure_content": True
|
||
# },
|
||
js_api=api
|
||
)
|
||
|
||
api.set_window(window)
|
||
|
||
# 定义页面加载完成后的回调(自动触发采集)
|
||
def on_page_loaded():
|
||
# 启动线程自动触发点击
|
||
trigger_thread = threading.Thread(target=api.auto_trigger_capture, daemon=True)
|
||
trigger_thread.start()
|
||
|
||
# 暴露API给前端
|
||
api.page_loaded = on_page_loaded
|
||
|
||
# 启动pywebview
|
||
try:
|
||
webview.start(
|
||
private_mode=False, # 关键:关闭私有模式,授予本地权限
|
||
debug=True,
|
||
http_server=True
|
||
)
|
||
except KeyboardInterrupt:
|
||
print("程序已终止") |