A collection of scripts
1#!/bin/sh
2# shellcheck disable=SC1090,SC2154
3
4# SC1090 & SC2154
5# The files sourced are user generated files that should contain the needed
6# variables for the script to function correctly. It should be safe to ignore
7# these warnings.
8
9# Required Commands
10# ffmpeg - needed for aud
11# grim - needed for pic
12# mkdir - create missing directories
13# pactl - create loopback devices for multi-device audio recording in wf-recorder
14# slurp - make a selection
15# wf-recorder - needed for rec
16# wl-copy - copy images to clipboard
17
18# This script is intended for use on wayland; however, `scr aud` should work fine without
19# wayland.
20
21# Set required variables if needed
22[ "$SCR_CFG_DIR" ] || SCR_CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/scr"
23[ "$SCR_CACHE_DIR" ] || SCR_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/scr"
24
25# Source the configuration file
26# A sample configuration can be found in my dotfiles at:
27# https://github.com/yemouu/setup/blob/master/root/home/cfg/scr/config.sh
28. "$SCR_CFG_DIR/config.sh" || \
29 { printf '%s\n' "${0##*/}: failed to source $SCR_CONFIG_DIR/config.sh" 1>&2; exit 1; }
30
31# Usage statement for the script
32usage() {
33 printf '%s\n' "usage: ${0##*/} action [options]" \
34 "actions:" \
35 " aud - audio" \
36 " pic - picture" \
37 " rec - record" \
38 "options:" \
39 " -a - record desktop audio" \
40 " -c - copy image to clipboard" \
41 " -h - display this message" \
42 " -m - record microphone audio"
43}
44
45# Determine the action to run
46case $1 in
47 aud ) action="scr_aud" ;;
48 pic ) action="scr_pic" ;;
49 rec ) action="scr_rec" ;;
50 *h|*help ) usage; exit 0 ;;
51 * ) printf '%s\n' "${0##*/}: $1: invalid action" 1>&2; usage 1>&2; exit 1 ;;
52esac
53shift
54
55# Determine options to run with based on arguments
56for flag in "$@"
57do
58 # Make sure arguments start with '-' and are atleast 2 characters long
59 case $flag in
60 - ) continue ;;
61 -- ) break ;;
62 -* ) ;;
63 * ) continue;;
64 esac
65
66 # Split arguments to be 1 character long and determine options to use
67 flag=${flag#-}
68 while [ "$flag" ]
69 do
70 a=${flag%${flag#?}}
71 case $a in
72 a ) desktop_audio=true ;;
73 c ) copy_clipboard=true ;;
74 h ) usage; exit 0 ;;
75 m ) microphone=true ;;
76 * ) printf '%s\n' "${0##*/}: -$a: invalid argument" 1>&2
77 usage 1>&2; exit 1 ;;
78 esac
79 flag=${flag#?}
80 done
81done
82unset args arg
83
84# Simple function to print out an error message and exit
85die() {
86 printf '%s\n' "${0##*/}: $*" 1>&2
87 exit 1
88}
89
90# Record Audio
91scr_aud() {
92 # Create the directory to store audio recordings if it does not already exist
93 [ -d "$scr_aud_dir" ] || \
94 { mkdir -p "$scr_aud_dir" || die "failed to make directory: $scr_aud_dir"; }
95
96 # Create the directory to store logs if it does not already exist
97 [ -d "$SCR_CACHE_DIR" ] || \
98 { mkdir -p "$SCR_CACHE_DIR" || \
99 die "failed to make directory: $SCR_CACHE_DIR"; }
100
101 filename="$scr_aud_dir/$aud_filename"
102
103 # Require atleast one of the arguments: -a or -m
104 [ "$microphone" ] || [ "$desktop_audio" ] || \
105 { die "aud: argument -a or -m is required to record audio"; }
106
107 # Set ffmpeg options based on script options
108 [ "$microphone" ] && { args="-f pulse -i $aud_source"; }
109 [ "$desktop_audio" ] && { args="$args -f pulse -i $aud_sink"; }
110 [ "$microphone" ] && [ "$desktop_audio" ] && \
111 { args="$args -filter_complex amix=inputs=2"; }
112
113 # Pressing Ctrl+C will exit the script instead of just wf-recorder.
114 # Intercept Ctrl+C and exit wf-recorder instead of the script
115 trap 'kill -2 $aud_pid' INT
116
117 # shellcheck disable=SC2086
118 # Word splitting is favorable here
119 ffmpeg $args "$filename" > "$SCR_CACHE_DIR/aud.log" 2>&1 &
120 aud_pid=$!
121 printf '%s' "Press Ctrl+C to stop recording. " 1>&2
122 wait $aud_pid
123
124 # Reset the trap
125 trap - INT
126
127 printf '\n%s\n' "$filename"
128}
129
130# Take a screenshot
131scr_pic() {
132 # Create directories if they do not already exist
133 [ -d "$scr_pic_dir" ] || \
134 { mkdir -p "$scr_pic_dir" || die "failed to make directory: $scr_pic_dir"; }
135
136 [ -d "$SCR_CACHE_DIR" ] || \
137 { mkdir -p "$SCR_CACHE_DIR" || \
138 die "failed to create directory: $SCR_CACHE_DIR"; }
139
140 filename="$scr_pic_dir/$pic_filename"
141
142 # Get the geometry of the screenshot from the user and take the screenshot
143 grim -g "$(slurp)" "$filename" > "$SCR_CACHE_DIR/pic.log" 2>&1
144
145 # Copy the image to the system clipboard
146 $copy_clipboard && { wl-copy <"$filename" > "$SCR_CACHE_DIR/copy.log" 2>&1; }
147
148 printf '%s\n' "$filename"
149}
150
151scr_rec() {
152 # Create directories if they do not already exist
153 [ -d "$scr_rec_dir" ] || \
154 { mkdir -p "$scr_rec_dir" || die "failed to make directory: $scr_pic_dir"; }
155
156 [ -d "$SCR_CACHE_DIR" ] || \
157 { mkdir -p "$SCR_CACHE_DIR" || \
158 die "failed to make directory: $SCR_CACHE_DIR"; }
159
160 filename="$scr_rec_dir/$rec_filename"
161
162 # Set wf-recorder arguments based on script options
163 [ "$microphone" ] && args="-a$aud_source"
164 [ "$desktop_audio" ] && args="-a$aud_sink"
165
166 # If both microphone and desktop_audio is set, create a loopback devices pointing to
167 # scr_inputs. wf-record does not support multiple audio devices. This is how they
168 # recomend you record two devices at the same time.
169 [ "$microphone" ] && [ "$desktop_audio" ] && {
170 unload_pulse_modules=true
171 null_sink=$(pactl load-module module-null-sink sink_name=aud_both)
172 lb_desk=$(pactl load-module module-loopback sink=aud_both source="$aud_sink")
173 lb_mic=$(pactl load-module module-loopback sink=aud_both source="$aud_source")
174 args="-aaud_both.monitor"
175 }
176
177 # Pressing Ctrl+C will exit the script instead of just wf-recorder.
178 # Intercept Ctrl+C and exit wf-recorder instead of the script
179 trap 'kill -2 $rec_pid' INT
180
181 # Word splitting is favorable here
182 # shellcheck disable=SC2086
183 wf-recorder $args -g "$(slurp)" -f "$filename" > "$SCR_CACHE_DIR/rec.log" 2>&1 &
184 rec_pid=$!
185 printf '%s' "Press Ctrl+C to stop recording. " 1>&2
186 wait $rec_pid
187
188 # Reset the trap
189 trap - INT
190
191 # Clean up pulseaudio modules that the script created
192 [ "$unload_pulse_modules" ] && {
193 pactl unload-module "$lb_mic"
194 pactl unload-module "$lb_desk"
195 pactl unload-module "$null_sink"
196 }
197
198 printf '\n%s\n' "$filename"
199}
200
201$action