A collection of scripts
1#!/bin/sh
2# shellcheck disable=SC1090,SC1091,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/home/cfg/scr/config.sh
28. "$SCR_CFG_DIR/config.sh" || {
29 printf '%s\n' "${0##*/}: failed to source $SCR_CFG_DIR/config.sh" 1>&2; exit 1
30}
31
32# Usage statement for the script
33usage() {
34 printf '%s\n' "usage: ${0##*/} action [options]" \
35 "actions:" \
36 "\taud - audio" \
37 "\tpic - picture" \
38 "\trec - record" \
39 "options:" \
40 "\t-a - record desktop audio (aud,rec)" \
41 "\t-c - copy image to clipboard (pic)" \
42 "\t-h - display this message" \
43 "\t-m - record microphone audio (aud,rec)" \
44 "\t-o - output to use (pic,rec)" \
45 "\t-d - all displays (pic)"
46}
47
48# Determine the action to run
49case $1 in
50 aud ) action="scr_aud" ;;
51 pic ) action="scr_pic" ;;
52 rec ) action="scr_rec" ;;
53 *h|*help ) usage; exit 0 ;;
54 * ) printf '%s\n' "${0##*/}: $1: invalid action" 1>&2; usage 1>&2; exit 1 ;;
55esac
56shift
57
58# Determine options to run with based on arguments
59# (I need to stop)
60for flag in "$@"
61do
62 # Make sure arguments start with '-' and are atleast 2 characters long
63 case $flag in
64 - ) continue ;;
65 -- ) break ;;
66 -* ) ;;
67 * ) continue;;
68 esac
69
70 # Split arguments to be 1 character long and determine options to use
71 args=${flag#-}
72
73 while [ "$args" ]
74 do
75 a=${args%${args#?}}
76
77 case $a in
78 a ) desktop_audio=true ;;
79 c ) copy_clipboard=true ;;
80 d ) output=all ;; # Kinda redundent lol
81 h ) usage; exit 0 ;;
82 m ) microphone=true ;;
83 o ) aargs=$*
84 output=${aargs##*${flag}}; output=${output#\ }; output=${output%%\ *}
85 [ "${output}" ] || printf '%s\n' "${0##*/}: -o: missing output" 1>&2 ;;
86 * ) printf '%s\n' "${0##*/}: -$a: invalid argument" 1>&2
87 usage 1>&2; exit 1 ;;
88 esac
89
90 args=${args#?}
91 done
92done
93unset args arg
94
95# Simple function to print out an error message and exit
96die() {
97 printf '%s\n' "${0##*/}: $*" 1>&2
98 exit 1
99}
100
101# Record Audio
102scr_aud() {
103 # Create the directory to store audio recordings if it does not already exist
104 [ -d "$scr_aud_dir" ] || \
105 { mkdir -p "$scr_aud_dir" || die "failed to make directory: $scr_aud_dir"; }
106
107 # Create the directory to store logs if it does not already exist
108 [ -d "$SCR_CACHE_DIR" ] || \
109 { mkdir -p "$SCR_CACHE_DIR" || \
110 die "failed to make directory: $SCR_CACHE_DIR"; }
111
112 filename="$scr_aud_dir/$aud_filename"
113
114 # Require atleast one of the arguments: -a or -m
115 [ "$microphone" ] || [ "$desktop_audio" ] || \
116 { die "aud: argument -a or -m is required to record audio"; }
117
118 # Set ffmpeg options based on script options
119 [ "$microphone" ] && { args="-f pulse -i $aud_source"; }
120 [ "$desktop_audio" ] && { args="$args -f pulse -i $aud_sink"; }
121 [ "$microphone" ] && [ "$desktop_audio" ] && \
122 { args="$args -filter_complex amix=inputs=2"; }
123
124 # Pressing Ctrl+C will exit the script instead of just ffmpeg.
125 # Intercept Ctrl+C and do nothing.
126 trap '' INT
127
128 # shellcheck disable=SC2086
129 # Word splitting is favorable here
130 ffmpeg $args "$filename" > "$SCR_CACHE_DIR/aud.log" 2>&1 &
131 aud_pid=$!
132 printf '%s' "Press Ctrl+C to stop recording. " 1>&2
133 wait $aud_pid
134
135 # Reset the trap
136 trap - INT
137
138 printf '\n%s\n' "$filename"
139}
140
141# Take a screenshot
142scr_pic() {
143 # Create directories if they do not already exist
144 [ -d "$scr_pic_dir" ] || \
145 { mkdir -p "$scr_pic_dir" || die "failed to make directory: $scr_pic_dir"; }
146
147 [ -d "$SCR_CACHE_DIR" ] || \
148 { mkdir -p "$SCR_CACHE_DIR" || \
149 die "failed to create directory: $SCR_CACHE_DIR"; }
150
151 filename="$scr_pic_dir/$pic_filename"
152
153 if [ "$output" = "all" ]
154 then
155 # Grim will screenshot all monitors by default
156 grim "$filename" > "$SCR_CACHE_DIR/pic.log" 2>&1
157 else
158 # Get the geometry of the screenshot from the user and take the screenshot
159 if [ "$output" ]; then set -- -o "$output"; else set -- -g "$(slurp)"; fi
160 grim "$@" "$filename" > "$SCR_CACHE_DIR/pic.log" 2>&1
161 fi
162
163
164 # Copy the image to the system clipboard
165 $copy_clipboard && { wl-copy <"$filename" > "$SCR_CACHE_DIR/copy.log" 2>&1; }
166
167 printf '%s\n' "$filename"
168}
169
170scr_rec() {
171 # Create directories if they do not already exist
172 [ -d "$scr_rec_dir" ] || \
173 { mkdir -p "$scr_rec_dir" || die "failed to make directory: $scr_pic_dir"; }
174
175 [ -d "$SCR_CACHE_DIR" ] || \
176 { mkdir -p "$SCR_CACHE_DIR" || \
177 die "failed to make directory: $SCR_CACHE_DIR"; }
178
179 filename="$scr_rec_dir/$rec_filename"
180
181 # Set wf-recorder arguments based on script options
182 [ "$microphone" ] && args="-a$aud_source"
183 [ "$desktop_audio" ] && args="-a$aud_sink"
184
185 # If both microphone and desktop_audio is set, create a loopback devices pointing to
186 # scr_inputs. wf-record does not support multiple audio devices. This is how they
187 # recomend you record two devices at the same time.
188 [ "$microphone" ] && [ "$desktop_audio" ] && {
189 unload_pulse_modules=true
190 null_sink=$(pactl load-module module-null-sink sink_name=aud_both)
191 lb_desk=$(pactl load-module module-loopback sink=aud_both source="$aud_sink")
192 lb_mic=$(pactl load-module module-loopback sink=aud_both source="$aud_source")
193 args="-aaud_both.monitor"
194 }
195
196 # Pressing Ctrl+C will exit the script instead of just wf-recorder.
197 # Intercept Ctrl+C and exit wf-recorder instead of the script
198 trap '' INT
199
200 if [ "$output" ]; then set -- -o "$output"; else set -- -g "$(slurp)"; fi
201 # Word splitting is favorable here
202 # shellcheck disable=SC2086
203 wf-recorder $args $rec_extraflags "$@" -f "$filename" > "$SCR_CACHE_DIR/rec.log" 2>&1 &
204 rec_pid=$!
205 printf '%s' "Press Ctrl+C to stop recording. " 1>&2
206 wait $rec_pid
207
208 # Reset the trap
209 trap - INT
210
211 # Clean up pulseaudio modules that the script created
212 [ "$unload_pulse_modules" ] && {
213 pactl unload-module "$lb_mic"
214 pactl unload-module "$lb_desk"
215 pactl unload-module "$null_sink"
216 }
217
218 printf '\n%s\n' "$filename"
219}
220
221$action
222
223# Run post scripts
224# Scripts in the directory `$SCR_CFG_DIR/scripts` will
225# take in `$action` as `$1` and `$filename` as `$2`.
226# Sample scripts can be found in my dotfiles at:
227# https://github.com/yemouu/setup/blob/master/home/cfg/scr/scripts
228for i in "$SCR_CFG_DIR/scripts/"*
229do
230 [ -x "$i" ] && {
231 printf '%s\n' "# $i --- START" >> "$SCR_CACHE_DIR/scripts.log"
232 $i "$action" "$filename" > "$SCR_CACHE_DIR/scripts.log" 2>&1
233 printf '%s\n' "# $i --- END" >> "$SCR_CACHE_DIR/scripts.log"
234 }
235done