Crash-safe screen recorder for Linux (Wayland-first).
If the app crashes mid-recording, your footage survives.
Many recorders write the .mp4 index only on clean shutdown — if the app
crashes mid-recording, the whole file is often lost. CastMane is built
around a single principle: a crash must never cost you the footage.
Verified: a recording killed with
kill -9remains fully playable (h264,ffprobe-checked). See Crash-safety.
Download the AppImage from the Releases page, then:
chmod +x CastMane-*.AppImage
./CastMane-*.AppImageThat's it — no Python, venv, or pip setup. The AppImage bundles Python, Qt, and all PyPI dependencies.
| Recording | Dark theme | Audio settings |
|---|---|---|
![]() |
![]() |
![]() |
| Area | Capability |
|---|---|
| Full-screen | Native Wayland capture via xdg-desktop-portal ScreenCast + GStreamer, lossless MP4 remux on stop |
| X11 fallback | ffmpeg (x11grab) backend, selected automatically |
| Crash-safe | Fragmented container, .part staging, atomic rename, graceful shutdown, encoder watchdog |
| Minimal UI | Record / Stop / Pause, elapsed timer, status line |
| Pause & quality | Freeze the encoder mid-take; Draft / Balanced / High / Custom presets |
| Source choice | Full-screen, single window, or ask every time |
| Audio | Optional mic or system-sound capture (AAC) with per-source mixer gain |
| Camera | OBS-style studio preview: drag the PiP anywhere, scroll to resize, burn it into the recording |
| Replay buffer | Keep the last N seconds on disk, save the moment after it happened |
| Shortcuts | Configurable Record / Pause / Replay / Folder keys, headless --record CLI |
| Output | Timestamped castmane_YYYY-MM-DD_HH-MM-SS.mp4 or .mkv files in ~/Videos/CastMane, recent-recordings list |
| Component | Wayland backend | X11 backend |
|---|---|---|
| OS | Linux, Python >= 3.10 | Linux, Python >= 3.10 |
| Video pipeline | gst-launch-1.0 with pipewiresrc, openh264enc, matroskamux |
ffmpeg with x11grab |
| System service | xdg-desktop-portal with a ScreenCast backend (KDE/GNOME provide one) |
— |
| Audio (optional) | Pulse/PipeWire source (pactl list short sources) |
Pulse source |
Install the host tools (the AppImage bundles everything else):
# Fedora / KDE (tested on Fedora 44, KDE Plasma, Wayland)
sudo dnf install ffmpeg gstreamer1 gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugin-openh264
# Ubuntu / Debian
sudo apt install ffmpeg gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-uglyMissing something? The app shows a friendly popup telling you exactly what to install.
chmod +x CastMane-*.AppImage
./CastMane-*.AppImage./packaging/build-appimage.sh
./dist/CastMane-*.AppImagepython3 -m venv .venv && source .venv/bin/activate
pip install -e .
python3 -m castmaneNo system Python packages or compiler toolchain required: the Wayland backend
drives the portal over D-Bus (dbus-next) and runs GStreamer as a subprocess,
so PyGObject is not needed.
- Start the app. The status line shows the active backend, e.g.
Ready [portal (Wayland)]. - Pick a source: Full-screen, Window, or Ask / Select region. On Wayland the system dialog lists monitors or windows; on X11, Ask opens a drag-to-select frame.
- Press Record. On Wayland the system screen-share dialog appears — pick the monitor or window and confirm (you have 180 seconds).
- Press Stop. The finished file lands in
~/Videos/CastMane.
The interface is English with the light theme by default. Open the
Settings tab to switch language (EN / TR) or theme (Light /
Dark / Ocean / Midnight / Forest / Dracula / Nord) —
both apply instantly and are remembered on next launch.
In code: AppConfig(language="tr", theme="dark").
Recovering an interrupted recording: on the next launch the app
offers to Restore leftover *.mkv.part (Wayland) or *.mp4.part
(X11) files next to your finished videos — or Delete / Later.
Restored files play as-is (on Wayland, optionally remux to MP4 yourself:
ffmpeg -i file.mkv.part -c copy file.mp4).
| Layer | Mechanism |
|---|---|
| Container | MKV staging (matroskamux tolerates missing timestamps), MP4 via lossless ffmpeg -c copy remux on clean stop |
| File handling | Write to X.mp4.part, os.replace() to X.mp4 only on clean stop |
| Shutdown | SIGINT -> wait -> SIGTERM -> SIGKILL escalation |
| Monitoring | Encoder watchdog reports early exits with the last log lines |
Reproduce the proof yourself
Kill a live MKV recording, then remux:
gst-launch-1.0 videotestsrc is-live=true ! videoconvert ! openh264enc \
! h264parse ! matroskamux ! filesink location=proof.mkv.part &
sleep 4; kill -9 $!
ffprobe -v error -show_entries stream=codec_name proof.mkv.part
ffmpeg -y -v error -i proof.mkv.part -c copy proof.mp4
ffprobe -v error -show_entries stream=codec_name,duration proof.mp4Pause & quality presets
Press Pause mid-recording to freeze the encoder (SIGSTOP); the timer
stops and the paused stretch is left out of the footage. Resume continues
into the same file. Stopping while paused resumes first, so the trailer is
always written cleanly.
from castmane.config import AppConfig
cfg = AppConfig()
cfg.apply_quality_preset("high") # "draft" | "balanced" | "high" | "custom"| Preset | fps | Bitrate |
|---|---|---|
| Draft | 24 | 4 Mbps |
| Balanced | 30 | 8 Mbps |
| High | 60 | 16 Mbps |
| Custom | your FPS (1–120) | your bitrate (0.5–50 Mbps) |
Capture source
from castmane.config import AppConfig
cfg = AppConfig(capture_mode="window") # "fullscreen" | "window" | "ask"
# X11 fixed region (0 = full screen):
# cfg = AppConfig(region_x=100, region_y=200, region_w=1280, region_h=720)
# X11 single window (id from `xwininfo`):
# cfg = AppConfig(capture_mode="window", window_id=123456)On Wayland, window / ask switch the portal dialog from monitors to
windows (or both). On X11, ask lets you drag the region with the mouse
(ffmpeg -select_region), while region_* / window_id give scriptable
control. Crash-safety is unchanged: region and window recordings still go
through the .part + atomic-rename pipeline.
Audio
Tick Record audio in the window and pick the source — Mic, Desktop (system sound), or Both (mixed into one track) — or in code:
from castmane.config import AppConfig
cfg = AppConfig(audio_enabled=True) # default mic
# system sound instead:
# cfg = AppConfig(audio_enabled=True, audio_mode="desktop")
# mic + system sound mixed:
# cfg = AppConfig(audio_enabled=True, audio_mode="both")
# explicit monitor device (skips auto-detection):
# cfg = AppConfig(audio_enabled=True, audio_mode="desktop",
# audio_desktop_source="alsa_output.pci-0000_00_1f.3.analog-stereo.monitor")List devices with pactl list short sources. "Desktop" auto-detects the
first .monitor source. Audio is encoded as AAC
(avenc_aac on Wayland, aac on X11) into the same crash-safe file, so a
kill -9 recording and its MP4 remux both keep the audio track.
Quiet system sound? Monitor sources have no mic-style input boost, so system audio can sound much fainter than mic recordings. Raise the gain in the window (%) or in code (
AppConfig(audio_gain=2.0)~ +6 dB,3.0~ +9.5 dB). Stay at or below5.0to avoid clipping.
src/castmane/
|-- app.py # Qt main window (Record/Stop, timer, status)
|__ config.py # Output dir, fps, bitrate, fragment settings
+-- recorder.py # Crash-safe core: FfmpegX11Recorder + finalize_part()
+-- backends/__init__.py # create_backend(): portal on Wayland, ffmpeg on X11
`-- backends/portal.py # PortalRecorder: ScreenCast D-Bus + gst-launch pipe
(MKV record, MP4 remux on stop)
Portal signatures are discovered from the broker at runtime
(session_handle type, Start argument order), so the client keeps working
across xdg-desktop-portal versions.
- Full-screen recording (Wayland + X11)
- Crash-safe output pipeline
- Audio capture (mic / desktop / both, selectable in the UI)
- Window / region selection
- Pause / resume, configurable quality presets
- One-file AppImage distribution
Issues and pull requests are welcome. Please keep recordings-related test
evidence (ffprobe output, steps to reproduce) in bug reports about lost or
corrupt footage — crash-safety is the point of this app.
GPL-3.0-or-later. See LICENSE.




