Local, video-only Windows screen recording for Python 3.11 or newer. The recorder supports three explicit operating modes and can be controlled through Python, an interactive CLI, or an optional localhost REST API.
| Mode | Use case | Save behavior |
|---|---|---|
normal |
One manually controlled recording | Start, optionally pause/resume, stop, then save one file |
interval |
Long-running recording with durable parts | Save numbered files automatically at the configured interval |
replay |
Retrospective incident capture | Keep a rolling window, 60 seconds by default, and save the recent past on demand |
Core package:
py -3.11 -m pip install .With the optional REST API:
py -3.11 -m pip install ".[http]"For development:
py -3.11 -m pip install -e ".[dev,http]"FFmpeg is required at runtime. Configure ffmpeg_path, set AUTOSCREENREC_FFMPEG, place ffmpeg.exe at tools/ffmpeg/bin/ffmpeg.exe, or make it available through PATH.
FFmpeg itself publishes source code. The official FFmpeg download page links to third-party providers of ready-to-run Windows builds, including gyan.dev and BtbN. Always review the license of the exact build you redistribute. The recorder does not download FFmpeg during normal operation.
The Windows builds page linked by FFmpeg documents WinGet packages. One documented option is the Essentials build:
winget install "FFmpeg (Essentials Build)"A full build is also listed:
winget install ffmpegAfter installation, open a new terminal and verify:
ffmpeg -versionThe same Windows builds provider documents:
choco install ffmpegThen verify:
ffmpeg -versionA documented Essentials package is:
scoop install ffmpeg-essentialsThen verify:
ffmpeg -version- Open the official FFmpeg download page:
https://ffmpeg.org/download.html. - Follow one of the linked Windows-build providers.
- Download and extract the selected build.
- Locate
ffmpeg.exeinside the extractedbindirectory. - Either configure its absolute path in Python or place the executable at the repository-local location
tools/ffmpeg/bin/ffmpeg.exe.
Explicit configuration is the most deterministic approach:
from pathlib import Path
from automated_screen_recorder import RecordingConfig
config = RecordingConfig(
output_path=Path(r"C:\Recordings\demo.mp4"),
ffmpeg_path=Path(r"C:\Tools\ffmpeg\bin\ffmpeg.exe"),
)Alternatively, set the environment variable used by the recorder:
$env:AUTOSCREENREC_FFMPEG = "C:\Tools\ffmpeg\bin\ffmpeg.exe"The recorder searches for FFmpeg in this order:
RecordingConfig.ffmpeg_pathAUTOSCREENREC_FFMPEGtools/ffmpeg/bin/ffmpeg.exe- the process
PATH
The discovered executable is validated with:
ffmpeg -versionIf FFmpeg cannot be found or validated, recording startup fails instead of silently downloading another executable.
from pathlib import Path
from automated_screen_recorder import RecordingConfig, RecordingMode, ScreenRecorder
config = RecordingConfig(
output_path=Path(r"C:\Recordings\normal.mp4"),
mode=RecordingMode.NORMAL,
)
with ScreenRecorder() as recorder:
recorder.start_recording(config)
recorder.stop_recording()
result = recorder.save_recording()CLI:
autoscreenrec C:\Recordings\normal.mp4 --mode normalPress Ctrl+C to stop and save.
config = RecordingConfig(
output_path=Path(r"C:\Recordings\shift.mp4"),
mode=RecordingMode.INTERVAL,
interval_seconds=300,
)Output names:
shift.part000001.mp4
shift.part000002.mp4
shift.part000003.mp4
CLI:
autoscreenrec C:\Recordings\shift.mp4 `
--mode interval `
--interval-seconds 300Replay mode continuously captures short temporary segments. Old segments are deleted as the configured window advances. The default replay window is 60 seconds.
config = RecordingConfig(
output_path=Path(r"C:\Recordings\incident.mp4"),
mode=RecordingMode.REPLAY,
replay_buffer_seconds=60,
replay_segment_seconds=2,
)
with ScreenRecorder() as recorder:
recorder.start_recording(config)
# Called when something worth retaining just happened.
result = recorder.save_replay_buffer()
print(result.output_path)
# Capture continues after the save.
recorder.cancel_recording()Default output names are numbered so repeated saves do not overwrite each other:
incident.replay000001.mp4
incident.replay000002.mp4
Replay data is rotated in short segments. The temporary on-disk buffer may exceed the requested replay window by at most approximately one configured segment. When a replay is saved, FFmpeg is asked to retain only the configured tail duration. Stream-copy trimming can begin on a nearby keyframe, so exact frame-level duration is not guaranteed.
Smaller replay_segment_seconds values reduce timing granularity but increase process finalization and restart overhead. The default is 2 seconds.
autoscreenrec C:\Recordings\incident.mp4 `
--mode replay `
--buffer-seconds 60 `
--segment-seconds 2The process then accepts:
save
save C:\Recordings\custom-incident.mp4
status
pause
resume
quit
cancel
savewrites the current replay window and continues buffering.quitstops and saves the remaining replay window.cancelstops and removes temporary data. Previously committed replay outputs remain.
Start the local API:
autoscreenrec-http --host 127.0.0.1 --port 8765Start replay mode:
$Body = @{
request_id = "start-replay-0001"
config = @{
output_path = "C:\Recordings\incident.mp4"
mode = "replay"
replay_buffer_seconds = 60
replay_segment_seconds = 2
create_output_directory = $true
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8765/recordings/start" `
-ContentType "application/json" `
-Body $BodySave the recent replay window without stopping:
$Body = @{
request_id = "save-replay-0001"
} | ConvertTo-Json
Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8765/recordings/save-replay" `
-ContentType "application/json" `
-Body $Body| Method | Path | Purpose |
|---|---|---|
POST |
/recordings/start |
Start any configured mode |
POST |
/recordings/pause |
Pause active capture |
POST |
/recordings/resume |
Resume capture |
POST |
/recordings/stop |
Stop capture |
POST |
/recordings/save |
Save a stopped session |
POST |
/recordings/save-replay |
Save the current replay window and continue |
POST |
/recordings/cancel |
Cancel and clean temporary artifacts |
GET |
/recordings/status |
Return state, mode, buffer duration, and outputs |
GET |
/displays |
Enumerate displays |
GET |
/docs |
Swagger API Doc |
GET |
/redoc |
ReDoc API Doc |
| Setting | Default | Meaning |
|---|---|---|
mode |
normal |
normal, interval, or replay |
interval_seconds |
None |
Required only in interval mode |
replay_buffer_seconds |
60 |
Retrospective window, 5 to 3600 seconds |
replay_segment_seconds |
2 |
Replay rotation granularity, 0.5 to 30 seconds |
frame_rate |
30 |
Requested capture rate, 1 to 120 |
display_index |
0 |
Selected monitor |
overwrite |
False |
Allow destination replacement |
create_output_directory |
False |
Create a missing output parent |
stop_timeout_seconds |
10 |
Graceful FFmpeg stop timeout |
Replay-aware status adds:
modebuffered_secondssaved_output_pathsnext_rotation_in_seconds
- Python does not buffer video frames.
- FFmpeg owns capture and encoding.
- Normal mode does not run a rotation worker.
- Interval and replay modes rotate finalized segments on a monotonic schedule.
- Replay mode keeps a bounded set of temporary files instead of unbounded video history.
- Saving a replay finalizes the current segment, writes the snapshot, and starts another segment. This introduces a short capture gap.
- All final destinations use staging and commit behavior to avoid presenting a partially copied file as complete.
Normal processing remains local. Capture commands explicitly disable audio with -an. The REST API is unauthenticated and defaults to loopback. Do not expose it directly to an untrusted network.
python -m ruff check .
python -m ruff format --check .
python -m mypy src
python -m pytest --cov=automated_screen_recorderUnit tests use a deterministic fake backend. Real Windows capture still requires an interactive desktop and FFmpeg.
- Replay saves use stream-copy trimming and are therefore keyframe-aligned rather than guaranteed frame-exact.
- Segment rotation and on-demand replay saving introduce a brief capture gap.
- Secure desktops, protected surfaces, and disconnected sessions can prevent usable capture.
- Hardware encoders depend on the machine, driver, and FFmpeg build.