#!/bin/sh usage() { echo "Asks for recording type via dmenu." echo "If there is already a running instance, user will be prompted to end it. " echo "" echo "Usage: ${0##*/} [-h] [-a|--audio|audio] [-k|--kill|kill] [-v|--video|video] [-s|--screencast|screencast]" echo "" echo "Options:" echo " - audio : Records only audio" echo " - kill : Kills existing recording" echo " - video : Records only screen" echo " - screencast : Records both audio and screen" } getdim() { xrandr | grep -oP '(?<=current ).*(?=,)' | tr -d ' '; } updateicon() { echo "$1" >/tmp/recordingicon pkill -RTMIN+24 "${STATUSBAR:-dwmblocks}" } killrecording() { recpid="$(cat /tmp/recordingpid)" kill -15 "$recpid" rm -f /tmp/recordingpid updateicon "" pkill -RTMIN+24 "${STATUSBAR:-dwmblocks}" } getmonitor() { xrandr | awk ' /^[^ ]+ connected/ { name=$1 if (match($0, /([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+)/, a)) { printf "%s %s %s+%s\n", name, a[1] "x" a[2], a[3], a[4] } } ' } selectmonitor() { map=$(getmonitor) [ -n "$map" ] || exit 1 names=$(printf "%s\n" "$map" | awk '{print $1}') monitor_count=$(printf "%s\n" "$names" | wc -l) if [ "$monitor_count" -ge 2 ]; then options=$(printf "%s\nall" "$names") choice=$(printf "%s\n" "$options" | dmenu -p "Select monitor to record:") [ -n "$choice" ] || exit 1 if [ "$choice" = "all" ]; then echo "all" else line=$(printf "%s\n" "$map" | awk -v mon="$choice" '$1 == mon') [ -n "$line" ] || exit 1 res=$(echo "$line" | awk '{print $2}') pos=$(echo "$line" | awk '{print $3}') echo "$res+$pos" fi else echo "all" fi } screencast() { ffmpeg -y \ -f x11grab \ -framerate 30 \ -s "$(getdim)" \ -i "$DISPLAY" \ -r 24 \ -use_wallclock_as_timestamps 1 \ -f alsa -thread_queue_size 1024 -i default \ -c:v h264 \ -crf 0 -preset ultrafast -c:a aac \ "$recordings/screencast-$(date '+%y%m%d-%H%M-%S').mp4" & echo $! >/tmp/recordingpid updateicon "⏺️🎙️" } screencastselected() { geometry=$(selectmonitor) || exit 1 if [ "$geometry" = "all" ]; then screencast else ffmpeg -y \ -f x11grab \ -framerate 30 \ -video_size "${geometry%%+*}" \ -i "$DISPLAY+${geometry#*+}" \ -r 24 \ -use_wallclock_as_timestamps 1 \ -f alsa -thread_queue_size 1024 -i default \ -c:v h264 \ -crf 0 -preset ultrafast -c:a aac \ "$recordings/screencast-$(date '+%y%m%d-%H%M-%S').mp4" & echo $! >/tmp/recordingpid updateicon "⏺️🎙️" fi } video() { ffmpeg \ -f x11grab \ -framerate 30 \ -s "$(getdim)" \ -i "$DISPLAY" \ -c:v libx264 -qp 0 -r 30 \ "$recordings/video-$(date '+%y%m%d-%H%M-%S').mkv" & echo $! >/tmp/recordingpid updateicon "⏺️" } videoselected() { slop -f "%x %y %w %h" >/tmp/slop read -r X Y W H /tmp/recordingpid updateicon "⏺️" } webcamselect() { cameras=$( v4l2-ctl --list-devices | awk ' BEGIN { RS=""; FS="\n" } { name = $1; sub(/ \(.*$/, "", name); gsub(/^[ \t]+|[ \t]+$/, "", name); for (i=2; i<=NF; i++) { if ($i ~ /\/dev\/video/) { gsub(/^[ \t]+/, "", $i); print name "|" $i; break; } } }' ) names=$(echo "$cameras" | cut -d '|' -f1) choice=$(echo "$names" | dmenu -i -p 'Choose a camera:') camera=$(echo "$cameras" | awk -F '|' -v sel="$choice" '$1 == sel {print $2}') } webcamhidef() { [ -z "$camera" ] && exit ffmpeg \ -display_hflip \ -f v4l2 \ -i "$camera" \ -video_size 1920x1080 \ "$recordings/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & echo $! >/tmp/recordingpid updateicon "🎥" } webcam() { [ -z "$camera" ] && exit ffmpeg \ -display_hflip \ -f v4l2 \ -i "$camera" \ -video_size 640x480 \ "$recordings/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & echo $! >/tmp/recordingpid updateicon "🎥" } audio() { ffmpeg \ -f alsa -i default \ -c:a flac \ "$recordings/audio-$(date '+%y%m%d-%H%M-%S').flac" & echo $! >/tmp/recordingpid updateicon "🎙️" } askrecording() { choice=$(printf "screencast\\nscreencast selected\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:") case "$choice" in screencast) screencast ;; "screencast selected") screencastselected ;; audio) audio ;; video) video ;; "video selected") videoselected ;; webcam) webcamselect && webcam ;; "webcam (hi-def)") webcamselect && webcamhidef ;; *) exit ;; esac } asktoend() { response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?") [ -z "$response" ] && exit [ "$response" = "Yes" ] && killrecording } recordings="${XDG_VIDEOS_DIR:-$HOME/Videos}/recordings" [ -d "$recordings" ] || mkdir -p "$recordings" case "$1" in -h | --help | help) usage && exit 0 ;; -a | --audio | audio) audio ;; -k | --kill | kill) killrecording ;; -s | --screencast | screencast) screencast ;; -ss | --screencast-selected | "screencast selected") screencastselected ;; -v | --video | video) video ;; -vs | --video-selected | "video selected") videoselected ;; *) ([ -f /tmp/recordingpid ] && asktoend && exit) || askrecording ;; esac