最后活跃于 1728564236

Windows 中使用 PowerShell 进行录屏的脚本

修订 bf205a17de2b04820474d1270abd321f37d3f0da

README.md 原始文件

Windows录屏脚本

Windows 中使用 PowerShell 进行录屏的脚本,只在用户活动的时(鼠标点击或移动或敲打键盘)才进行录制

准备

  • 安装 ffmpeg 用来将截屏的png转换为视频. 如果不需要视频只要图片可以不装

用法

  1. 用powsershell执行 screenshot.ps1 开始截屏生成png文件.
  2. 停止powershell,执行 make.cmd 开始导出视频.
  3. generate-input-list.py 可选的python脚本用来重新生成duration.txt当你删除了部分png文件时.

说明

  • duration.txt 是给ffmpeg合成视频时用的时间轴文件,里面指明了每个png图片的播放时长。如果删除了部分png图片,需要使用generate-input-list.py来重新生成它
generate-input-list.py 原始文件
1import os
2from datetime import datetime
3
4time_format = '%Y%m%d_%H%M%S%f'
5
6# 获取当前目录下所有的png文件名,无扩展名
7png_files = [f.split('.')[0] for f in os.listdir('./shots') if f.endswith('.png')]
8
9# 解析文件名并排序
10sorted_files = sorted(png_files, key=lambda x: datetime.strptime(x, time_format))
11
12# 计算时间间隔并生成输入文件
13with open('./shots/duration.txt', 'w') as f:
14 for i in range(len(sorted_files)):
15 current_file = sorted_files[i]
16 current_time = datetime.strptime(current_file, time_format)
17
18 if i != 0:
19 prev_file = sorted_files[i-1]
20 prev_time = datetime.strptime(prev_file, time_format)
21 duration = (current_time - prev_time).total_seconds()
22 f.write(f"duration {duration}\n")
23
24 f.write(f"file '{current_file}.png'\n")
25
26print("输入文件列表已生成为 duration.txt")
make.cmd 原始文件
1ffmpeg -f concat -i ./shots/duration.txt -pix_fmt yuv420p -c:v libx264 -r 5 output.mp4
screenshot.ps1 原始文件
1# 定义保存截图的文件夹路径为当前脚本所在目录下的shots文件夹
2$folderPath = Join-Path -Path $PSScriptRoot -ChildPath "shots"
3if (-not (Test-Path -Path $folderPath)) {
4 New-Item -ItemType Directory -Path $folderPath
5}
6
7# 导入用户32.dll,用于检测鼠标键盘活动
8Add-Type @"
9 using System;
10 using System.Runtime.InteropServices;
11 public class UserActivity {
12 [DllImport("user32.dll")]
13 public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
14
15 [StructLayout(LayoutKind.Sequential)]
16 public struct LASTINPUTINFO {
17 public uint cbSize;
18 public uint dwTime;
19 }
20 }
21"@
22
23# 检测用户是否活跃
24function IsUserActive {
25 $lastInput = New-Object UserActivity+LASTINPUTINFO
26 $lastInput.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($lastInput)
27
28 if ([UserActivity]::GetLastInputInfo([ref]$lastInput)) {
29 $idleTime = [Math]::Floor([TimeSpan]::FromMilliseconds([Environment]::TickCount - $lastInput.dwTime).TotalSeconds)
30 # 如果空闲时间小于5秒,认为用户是活跃的
31 return ($idleTime -lt 1)
32 }
33 return $false
34}
35
36$durationTxt = Join-Path -Path $folderPath -ChildPath "duration.txt"
37$fileMatches = if (Test-Path -Path $durationTxt) { (Get-Content -Path $durationTxt -Tail 3 | Select-String -Pattern "^file '(.*\.png)'$").Matches } else { @() }
38$script:lastScreenshotFile = if ($fileMatches.Count -gt 0) { $fileMatches[0].Groups[1].Value } else { "" }
39# Write-Host "lastScreenshotFile: $lastScreenshotFile"
40$script:isFirstRun = $true
41$script:lastScreenshotTime = if ($script:lastScreenshotFile -ne "") { [DateTime]::ParseExact($script:lastScreenshotFile.Substring(0, 18), "yyyyMMdd_HHmmssfff", $null) } else { [DateTime]::MinValue }
42
43# 截屏函数
44function TakeScreenshot {
45 $now = Get-Date
46 $timestamp = $now.ToString("yyyyMMdd_HHmmssfff")
47 $filePath = Join-Path -Path $folderPath -ChildPath "$timestamp.png"
48
49 Add-Type -AssemblyName System.Windows.Forms
50 Add-Type -AssemblyName System.Drawing
51 $bitmap = New-Object System.Drawing.Bitmap([System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width, [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height)
52 $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
53 # Capture the screen
54 $graphics.CopyFromScreen(0, 0, 0, 0, $bitmap.Size)
55
56 # Draw the cursor
57 $cursorPosition = [System.Windows.Forms.Cursor]::Position
58 $cursorRectangle = New-Object System.Drawing.Rectangle($cursorPosition, [System.Drawing.Size]::new(32, 32)) # Adjust size if needed
59 # [System.Windows.Forms.Cursor]::Current.Draw($graphics, $cursorRectangle)
60 [System.Windows.Forms.Cursors]::Arrow.Draw($graphics, $cursorRectangle)
61
62 $bitmap.Save($filePath, [System.Drawing.Imaging.ImageFormat]::Png)
63
64 # Add filename to duration.txt
65 if ($script:lastScreenshotFile -ne "") {
66 $duration = ([TimeSpan]::FromTicks(($now - $script:lastScreenshotTime).Ticks)).TotalSeconds
67 $durationStr = "duration {0:F3}" -f $duration
68 if ($script:isFirstRun) {
69 $lastDuration = Get-Content -Path $durationTxt -Tail 1 | Select-String -Pattern "^duration [\d\.]+$"
70 if ($lastDuration) {
71 # remove last line
72 $lines = Get-Content -Path $durationTxt
73 $lines = $lines[0..($lines.Length - 2)]
74 $lines += $durationStr
75 $lines | Set-Content -Path $durationTxt
76 } else {
77 Add-Content -Path $durationTxt -Value $durationStr
78 }
79 $script:isFirstRun = $false
80 } else {
81 Add-Content -Path $durationTxt -Value $durationStr
82 }
83 }
84 Add-Content -Path $durationTxt -Value "file '$timestamp.png'"
85
86 Write-Host "Screenshot saved: $timestamp.png"
87 [console]::beep(1000, 500) # 播放提示音
88
89 $script:lastScreenshotTime = $now
90 $script:lastScreenshotFile = "$timestamp.png"
91}
92
93# Delay 3 seconds to start
94Start-Sleep -Seconds 3
95
96# 主循环
97while ($true) {
98 try {
99 if (IsUserActive) {
100 TakeScreenshot
101 }
102 Start-Sleep -Seconds 1
103 } catch {
104 Write-Host "An error occurred: $_"
105 Start-Sleep -Seconds 5 # 出错时等待5秒再继续
106 }
107}