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