#!/bin/sh # shellcheck disable=SC1090,SC1091,SC2154 # SC1090 & SC2154 # The files sourced are user generated files that should contain the needed # variables for the script to function correctly. It should be safe to ignore # these warnings. # Required Commands # ffmpeg - needed for aud # grim - needed for pic # mkdir - create missing directories # pactl - create loopback devices for multi-device audio recording in wf-recorder # slurp - make a selection # wf-recorder - needed for rec # wl-copy - copy images to clipboard # This script is intended for use on wayland; however, `scr aud` should work fine without # wayland. # Set required variables if needed [ "$SCR_CFG_DIR" ] || SCR_CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/scr" [ "$SCR_CACHE_DIR" ] || SCR_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/scr" # Source the configuration file # A sample configuration can be found in my dotfiles at: # https://github.com/yemouu/setup/blob/master/home/cfg/scr/config.sh # TODO: put an example cofiguration in this repo . "$SCR_CFG_DIR/config.sh" || { printf '%s\n' "${0##*/}: failed to source $SCR_CFG_DIR/config.sh" 1>&2; exit 1 } # Usage statement for the script usage() { printf '%b\n' "usage: ${0##*/} action [options]" \ "actions:" \ "\taud - audio" \ "\tpic - picture" \ "\trec - record" \ "options:" \ "\t-a - record desktop audio (aud,rec)" \ "\t-c - copy image to clipboard (pic)" \ "\t-h - display this message" \ "\t-m - record microphone audio (aud,rec)" \ "\t-o - output to use (pic,rec)" \ "\t-d - all displays (pic)" } # Determine the action to run case $1 in aud ) action="scr_aud" ;; pic ) action="scr_pic" ;; rec ) action="scr_rec" ;; *h|*help ) usage; exit 0 ;; * ) printf '%s\n' "${0##*/}: $1: invalid action" 1>&2; usage 1>&2; exit 1 ;; esac shift # Determine options to run with based on arguments # (I need to stop) for flag in "$@" do # Make sure arguments start with '-' and are atleast 2 characters long case $flag in - ) continue ;; -- ) break ;; -* ) ;; * ) continue;; esac # Split arguments to be 1 character long and determine options to use args=${flag#-} while [ "$args" ] do a=${args%"${args#?}"} case $a in a ) desktop_audio=true ;; c ) copy_clipboard=true ;; d ) output=all ;; # Kinda redundent lol h ) usage; exit 0 ;; m ) microphone=true ;; o ) aargs=$* output=${aargs##*"${flag}"}; output=${output#\ }; output=${output%%\ *} [ "${output}" ] || printf '%s\n' "${0##*/}: -o: missing output" 1>&2 ;; * ) printf '%s\n' "${0##*/}: -$a: invalid argument" 1>&2 usage 1>&2; exit 1 ;; esac args=${args#?} done done unset args arg # Simple function to print out an error message and exit die() { printf '%s\n' "${0##*/}: $*" 1>&2 exit 1 } # Record Audio scr_aud() { # Create the directory to store audio recordings if it does not already exist [ -d "$scr_aud_dir" ] || \ { mkdir -p "$scr_aud_dir" || die "failed to make directory: $scr_aud_dir"; } # Create the directory to store logs if it does not already exist [ -d "$SCR_CACHE_DIR" ] || \ { mkdir -p "$SCR_CACHE_DIR" || \ die "failed to make directory: $SCR_CACHE_DIR"; } filename="$scr_aud_dir/$aud_filename" # Require atleast one of the arguments: -a or -m [ "$microphone" ] || [ "$desktop_audio" ] || \ { die "aud: argument -a or -m is required to record audio"; } # Set ffmpeg options based on script options [ "$microphone" ] && { args="-f pulse -i $aud_source"; } [ "$desktop_audio" ] && { args="$args -f pulse -i $aud_sink"; } [ "$microphone" ] && [ "$desktop_audio" ] && \ { args="$args -filter_complex amix=inputs=2"; } # Pressing Ctrl+C will exit the script instead of just ffmpeg. # Intercept Ctrl+C and do nothing. trap '' INT # shellcheck disable=SC2086 # Word splitting is favorable here ffmpeg $args "$filename" > "$SCR_CACHE_DIR/aud.log" 2>&1 & aud_pid=$! printf '%s' "Press Ctrl+C to stop recording. " 1>&2 wait $aud_pid # Reset the trap trap - INT printf '\n%s\n' "$filename" } # Take a screenshot scr_pic() { # Create directories if they do not already exist [ -d "$scr_pic_dir" ] || \ { mkdir -p "$scr_pic_dir" || die "failed to make directory: $scr_pic_dir"; } [ -d "$SCR_CACHE_DIR" ] || \ { mkdir -p "$SCR_CACHE_DIR" || \ die "failed to create directory: $SCR_CACHE_DIR"; } filename="$scr_pic_dir/$pic_filename" if [ "$output" = "all" ] then # Grim will screenshot all monitors by default grim "$filename" > "$SCR_CACHE_DIR/pic.log" 2>&1 else # Get the geometry of the screenshot from the user and take the screenshot # shellcheck disable=SC2086 if [ "$output" ]; then set -- -o "$output"; else set -- -g "$(slurp ${SLURP_ARGS})"; fi grim "$@" "$filename" > "$SCR_CACHE_DIR/pic.log" 2>&1 fi # Copy the image to the system clipboard $copy_clipboard && { wl-copy <"$filename" > "$SCR_CACHE_DIR/copy.log" 2>&1; } printf '%s\n' "$filename" } scr_rec() { # Create directories if they do not already exist [ -d "$scr_rec_dir" ] || \ { mkdir -p "$scr_rec_dir" || die "failed to make directory: $scr_pic_dir"; } [ -d "$SCR_CACHE_DIR" ] || \ { mkdir -p "$SCR_CACHE_DIR" || \ die "failed to make directory: $SCR_CACHE_DIR"; } filename="$scr_rec_dir/$rec_filename" # Set wf-recorder arguments based on script options [ "$microphone" ] && args="-a$aud_source" [ "$desktop_audio" ] && args="-a$aud_sink" # If both microphone and desktop_audio is set, create a loopback devices pointing to # scr_inputs. wf-record does not support multiple audio devices. This is how they # recomend you record two devices at the same time. [ "$microphone" ] && [ "$desktop_audio" ] && { unload_pulse_modules=true null_sink=$(pactl load-module module-null-sink sink_name=aud_both) lb_desk=$(pactl load-module module-loopback sink=aud_both source="$aud_sink") lb_mic=$(pactl load-module module-loopback sink=aud_both source="$aud_source") args="-aaud_both.monitor" } # Pressing Ctrl+C will exit the script instead of just wf-recorder. # Intercept Ctrl+C and exit wf-recorder instead of the script trap '' INT # shellcheck disable=SC2086 if [ "$output" ]; then set -- -o "$output"; else set -- -g "$(slurp ${SLURP_ARGS})"; fi # Word splitting is favorable here # shellcheck disable=SC2086 wf-recorder $args $rec_extraflags "$@" -f "$filename" > "$SCR_CACHE_DIR/rec.log" 2>&1 & rec_pid=$! printf '%s' "Press Ctrl+C to stop recording. " 1>&2 wait $rec_pid # Reset the trap trap - INT # Clean up pulseaudio modules that the script created [ "$unload_pulse_modules" ] && { pactl unload-module "$lb_mic" pactl unload-module "$lb_desk" pactl unload-module "$null_sink" } printf '\n%s\n' "$filename" } $action # Run post scripts # Scripts in the directory `$SCR_CFG_DIR/scripts` will # take in `$action` as `$1` and `$filename` as `$2`. # Sample scripts can be found in my dotfiles at: # https://github.com/yemouu/setup/blob/master/home/cfg/scr/scripts for i in "$SCR_CFG_DIR/scripts/"* do [ -x "$i" ] && { printf '%s\n' "# $i --- START" >> "$SCR_CACHE_DIR/scripts.log" $i "$action" "$filename" > "$SCR_CACHE_DIR/scripts.log" 2>&1 printf '%s\n' "# $i --- END" >> "$SCR_CACHE_DIR/scripts.log" } done