├── LICENSE ├── Makefile ├── README.md ├── config └── config.conf ├── gsu └── gsu.1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2020 Jesse Bryan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | MANDIR ?= $(PREFIX)/share/man 3 | SYSCONFDIR ?= /etc 4 | 5 | all: 6 | @echo "Run 'make install' to install gsu." 7 | 8 | install: 9 | @echo "Making directories..." 10 | mkdir -p $(DESTDIR)$(PREFIX)/bin 11 | mkdir -p $(DESTDIR)$(MANDIR)/man1 12 | mkdir -p $(DESTDIR)$(SYSCONFDIR)/gsu 13 | 14 | @echo -e "\nInstalling binaries..." 15 | sed "s|SYSCONFDIR|$(SYSCONFDIR)/gsu|g" < gsu > $(DESTDIR)$(PREFIX)/bin/gsu 16 | chmod 755 $(DESTDIR)$(PREFIX)/bin/gsu 17 | 18 | @echo -e "\nInstalling man page and config file..." 19 | cp -p gsu.1 $(DESTDIR)$(MANDIR)/man1/gsu.1 20 | cp -p config/config.conf $(DESTDIR)$(SYSCONFDIR)/gsu/config.conf 21 | 22 | uninstall: 23 | @echo "Removing files..." 24 | rm -rf $(DESTDIR)$(PREFIX)/bin/gsu 25 | rm -rf $(DESTDIR)$(MANDIR)/man1/gsu.1 26 | rm -rf $(DESTDIR)$(SYSCONFDIR)/gsu 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gsu 2 | 3 | gsu (General Screenshot Utility) allows for easy capturing and uploading of images, video, and gifs from your desktop. 4 | 5 | ## Features 6 | 7 | * Screenshot capture. 8 | * Video capture. 9 | * GIF capture. 10 | * Built-in uploading to uguu.se, streamable, and gfycat. 11 | * Custom uploading capability if the above services don't suit your fancy. 12 | * dmenu and rofi integration. 13 | * A clean and easy to understand CLI. 14 | 15 | ## Usage 16 | 17 | ``` 18 | Usage: gsu [OPTION]... SOURCE 19 | A general screenshot and upload utility for images, video, and gifs. 20 | 21 | SOURCE defaults to $XDG_CONFIG_HOME/gsu/imgs if nothing is provided. 22 | The most common location for $XDG_CONFIG_HOME is ~/.config. 23 | 24 | GENERAL OPTIONS 25 | -h, --help Show the help menu. 26 | -v, --version Show the current version. 27 | -l, --list-displays List your current displays and their resolutions. 28 | -u, --upload Upload after running the utility. 29 | --hide Minimize the current window while capturing. 30 | This is useful when running with --terminal to hide 31 | the newly opened term. 32 | --terminal Open interactive commands in a new terminal.. 33 | SET THIS IF RUNNING FROM ANYTHING OTHER THAN A TERM, 34 | i.e. xbindkeys 35 | --notify Send a libnotify notification of the output. 36 | 37 | UTILITY OPTIONS 38 | -r, --dmenu Select the one of the below options from dmenu or rofi. 39 | -s, --screenshot Take a screenshot. (default) 40 | -m, --video Record a video of the screen. 41 | -g, --gif Record a video and convert it to the gif format. 42 | 43 | CAPTURE OPTIONS 44 | -n, --nocursor Hide the cursor. 45 | -d, --display NUM|TYPE Set the selection to a specific display. 46 | Can read from an argument or from stdin. 47 | Values: number of display, 'active', 'all' (default) 48 | 49 | VIDEO OPTIONS 50 | -c, --countdown NUM Countdown before recording. 51 | ``` 52 | 53 | ## Installation 54 | 55 | ### Package Manager 56 | 57 | * [Arch Linux (AUR): gsu](https://aur.archlinux.org/packages/gsu/) 58 | * [Arch Linux (AUR): gsu-git](https://aur.archlinux.org/packages/gsu-git/) 59 | 60 | ### Manual 61 | 62 | To install gsu's required dependencies, run the following command in accordance to your distro. 63 | 64 | #### Arch Linux 65 | 66 | ``` 67 | $ sudo pacman -S curl jq xdotool maim slop ffmpeg 68 | ``` 69 | 70 | #### Ubuntu 20.04 LTS/Debian 11 71 | 72 | ``` 73 | $ sudo apt install curl jq xdotool maim slop ffmpeg 74 | ``` 75 | 76 | #### Older Debian-based (Ubuntu 19.10/Debian 10 or prior) 77 | 78 | maim, slop, and FFmpeg all have bugs in earlier versions that make gsu unusable in certain scenarios. Therefore, they must all be installed via source. 79 | 80 | Afterwards, run the following command to install the remaining required dependencies. 81 | 82 | ``` 83 | $ sudo apt install curl jq xdotool 84 | ``` 85 | 86 | #### Other Linux or BSD 87 | 88 | Install `curl`, `jq`, `xdotool`, `maim`, `slop`, and `ffmpeg` via source or your package manager of choice. *If installing by package manager, ensure that the following required versions are met.* 89 | 90 | *maim*: 5.x or later 91 | *slop*: 7.x or later 92 | *FFmpeg*: 4.2.x or later 93 | 94 | --- 95 | 96 | You can optionally install `xsel` if you want automatic URL clipboard pasting after the utility finishes an upload. 97 | 98 | After installing the dependents, run the following commands to install gsu. 99 | 100 | ``` 101 | $ curl -o gsu.tar.gz https://codeload.github.com/winneon/gsu/tar.gz/1.3.2 102 | $ tar xvf gsu.tar.gz && cd gsu-1.3.2 103 | $ sudo make install 104 | ``` 105 | 106 | ## Configuration 107 | 108 | Run the utility at least once, i.e. `gsu --help`. The configuration file will be generated in `$XDG_CONFIG_HOME/gsu` if it didn't already exist. The most common location is `~/.config/gsu`. 109 | 110 | ## Troubleshooting 111 | 112 | Here are some frequent problems users may face and their respective solutions. If you have more solutions, feel free to send in an issue or pull request. 113 | 114 | ### The outputted capture is completely black or another solid color. 115 | 116 | Make sure that you are running your DE/WM under the latest version of X11. Wayland is untested. To check, run the following command. 117 | 118 | ``` 119 | $ echo $XDG_SESSION_TYPE 120 | ``` 121 | 122 | If you are running X11 and the problem persists, try disabling OpenGL hardware-based acceleration by adding `--no-opengl` to your gsu command. 123 | 124 | ### The outputted capture has black/white sections or artifacts. 125 | 126 | This is a problem with older versions of maim. Upgrade maim to 5.x or later or install via source. 127 | 128 | ### The capture session won't end when capturing a video or gif. 129 | 130 | Versions of FFmpeg prior to 4.2.x have a bug where an `x11grab` capture sessions won't end unless forcibly killed by the system. Upgrade FFmpeg to 4.2.x or install via source. 131 | 132 | ### There is no audio in the captured video. 133 | 134 | PulseAudio is the default audio capture device. If you do not use PulseAudio, adjust your audio capture device in the configuration file. 135 | 136 | If you do use PulseAudio, use a program like `pavucontrol` to change the input device to a monitor of your audio output device. If you use PAVUcontrol, the resulting setup should look similar to below. 137 | 138 | ![pavucontrol example](http://i.imgur.com/qbN5741.png) 139 | 140 | ## Credits 141 | 142 | * [Dalton Nell](https://github.com/naelstrof) for maim and slop, used for screenshot creation and selections 143 | * [The FFmpeg team](https://ffmpeg.org/) for FFmpeg, used for video and gif creation 144 | * [Daniel Stenberg](https://github.com/bagder) for curl, used for uploading 145 | * [Stephen Dolan](https://github.com/stedolan) for jq, used for parsing JSON 146 | -------------------------------------------------------------------------------- /config/config.conf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Terminal. Uncomment your terminal to use, 4 | # or uncomment and edit one of the values to use your own terminal. 5 | # 6 | # If uncommented, be sure that the terminal you are providing is installed. 7 | # If --terminal is provided, a new terminal using this variable will be opened. 8 | # Use the string "CMD" to refer to the command that is run (gsu). 9 | # 10 | # gnome-terminal: 11 | #GSU_TERM=(gnome-terminal -- CMD) 12 | # 13 | # urxvt: 14 | #GSU_TERM=(urxvt -e bash -c "'CMD'") 15 | 16 | # Audio input device. If empty, gsu defaults to pulseaudio. 17 | # See https://www.ffmpeg.org/ffmpeg-devices.html#Input-Devices for a list 18 | # of valid input devices. 19 | AUDIO="" 20 | 21 | # Streamable email/username. 22 | # REQUIRED for video upload without a custom upload command. 23 | STREAMABLE_USER="" 24 | 25 | # Streamable password. 26 | # REQUIRED for video upload without a custom upload command. 27 | STREAMABLE_PASS="" 28 | 29 | # Custom screenshot upload command. Leave empty to upload to uguu.se. 30 | # Use the string "GSUFILEOUT" to refer to your gsu operand. 31 | S_UPLOAD="" 32 | 33 | # Custom video upload command. Leave empty to upload to streamable. 34 | # Use the string "GSUFILEOUT" to refer to your gsu operand. 35 | V_UPLOAD="" 36 | 37 | # Custom gif upload command. Leave empty to upload to gfycat. 38 | # Use the string "GSUFILEOUT" to refer to your gsu operand. 39 | G_UPLOAD="" 40 | -------------------------------------------------------------------------------- /gsu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION="1.3.2" 4 | config_path="${XDG_CONFIG_HOME:-${HOME}/.config}/gsu" 5 | 6 | notify() { 7 | echo -e "$1" 8 | 9 | if [[ ! -z "$notify_arg" ]] && type -p notify-send >/dev/null; then 10 | notify-send "gsu" "$1" 11 | fi 12 | } 13 | 14 | print_help() { 15 | echo "\ 16 | Usage: gsu [OPTION]... SOURCE 17 | A general screenshot and upload utility for images, video, and gifs. 18 | 19 | SOURCE defaults to \$XDG_CONFIG_HOME/gsu/imgs if nothing is provided. 20 | The most common location for \$XDG_CONFIG_HOME is ~/.config. 21 | 22 | GENERAL OPTIONS 23 | -h, --help Show the help menu. 24 | -v, --version Show the current version. 25 | -l, --list-displays List your current displays and their resolutions. 26 | -u, --upload Upload after running the utility. 27 | --terminal Open interactive commands in a new terminal.. 28 | SET THIS IF RUNNING FROM ANYTHING OTHER THAN A TERM, 29 | i.e. xbindkeys 30 | --hide Minimize the current window while capturing. 31 | This is useful when running with --terminal to hide 32 | the newly opened term. 33 | --notify Send a libnotify notification of the output. 34 | 35 | UTILITY OPTIONS 36 | -r, --dmenu Select the one of the below options from dmenu or rofi. 37 | -s, --screenshot Take a screenshot. (default) 38 | -m, --video Record a video of the screen. 39 | -g, --gif Record a video and convert it to the gif format. 40 | 41 | CAPTURE OPTIONS 42 | -n, --no-cursor Hide the cursor. 43 | -d, --display NUM|TYPE Set the selection to a specific display. 44 | Can read from an argument or from stdin. 45 | Values: number of display, 'active', 'all' (default) 46 | 47 | SCREENSHOT OPTIONS 48 | -o, --no-opengl Disable OpenGL hardware-accelerated capture. 49 | 50 | VIDEO OPTIONS 51 | -c, --countdown NUM Countdown before recording." 52 | 53 | exit 0 54 | } 55 | 56 | print_version() { 57 | echo "gsu $VERSION" 58 | exit 0 59 | } 60 | 61 | print_displays() { 62 | local monitors=$(xrandr | grep -o "[0-9]*x[0-9]*[+-][0-9]*[+-][0-9]*") 63 | 64 | if [[ -z "$monitors" ]]; then 65 | echo "gsu: unable to find any displays" 66 | exit 1 67 | fi 68 | 69 | readarray -t monitors <<< "$monitors" 70 | local displays="" 71 | 72 | for ((i = 0; i < ${#monitors[@]}; i++)); do 73 | ((num = i + 1)) 74 | displays="${displays}\n${num}: ${monitors[$i]}" 75 | done 76 | 77 | displays=$(sed 's/\\n//' <<< $displays) 78 | echo -e "$displays" 79 | 80 | exit 0 81 | } 82 | 83 | print_not_int() { 84 | regex=$(echo "$1" | sed -e "s/^-\{1,2\}//") 85 | 86 | echo "gsu: value for '$regex' is not an integer" 87 | exit 1 88 | } 89 | 90 | print_invalid_option() { 91 | regex=$(echo "$1" | sed -e "s/^-\{1,2\}//") 92 | 93 | echo "\ 94 | gsu: invalid option -- '$regex' 95 | Try 'gsu --help' for more information." 96 | 97 | exit 1 98 | } 99 | 100 | print_invalid_operand() { 101 | echo "\ 102 | gsu: invalid operand -- '$1' 103 | Valid characters: alphanumeric, dot, slash, underscore, dash." 104 | 105 | exit 1 106 | } 107 | 108 | print_missing_deps() { 109 | local args="" 110 | 111 | for arg in "$@"; do 112 | args="$args '$arg'" 113 | done 114 | 115 | args=$(sed 's/ //' <<< $args) 116 | echo "gsu: missing required dependency(s) -- $args" 117 | 118 | exit 1 119 | } 120 | 121 | print_manually_quit() { 122 | echo "gsu: manually quit from user action" 123 | exit 1 124 | } 125 | 126 | get_args() { 127 | local prev_arg="" 128 | 129 | while [[ "$1" ]]; do 130 | case "$1" in 131 | "-h" | "--help") print_help ;; 132 | "-v" | "--version") print_version ;; 133 | "-l" | "--list-displays") print_displays ;; 134 | "-u" | "--upload") upload="$1" ;; 135 | 136 | "--hide") 137 | if ! type -p xdotool >/dev/null; then 138 | print_missing_deps "xdotool" 139 | fi 140 | 141 | hide="$1" 142 | ;; 143 | 144 | "--terminal") 145 | if [[ -z "$GSU_TERM" ]]; then 146 | notify "\ 147 | gsu: \$GSU_TERM is not set 148 | Set this variable in '$config_path/config.conf'." 149 | 150 | exit 1 151 | fi 152 | 153 | terminal_arg="$1" 154 | ;; 155 | 156 | "--notify") notify_arg="$1" ;; 157 | 158 | "-r" | "--dmenu") 159 | if type -p dmenu >/dev/null; then 160 | dmenu_type=(dmenu) 161 | fi 162 | 163 | if type -p rofi >/dev/null; then 164 | dmenu_type=(rofi -dmenu -p "gsu > ") 165 | fi 166 | 167 | if [[ -z "$dmenu_type" ]]; then 168 | print_missing_deps "dmenu or rofi" 169 | else 170 | rofi_arg="$1" 171 | fi 172 | ;; 173 | 174 | "-s" | "--screenshot") set_type "screenshot" ;; 175 | "-m" | "--video") set_type "video" ;; 176 | "-g" | "--gif") set_type "gif" ;; 177 | "-n" | "--no-cursor") nocursor="-u"; draw_mouse="-draw_mouse 0" ;; 178 | 179 | "-d" | "--display") 180 | if [[ -t 0 ]]; then 181 | display="$2" 182 | else 183 | display=$(cat) 184 | display="${display:0:1}" 185 | 186 | if [[ -z "$display" ]]; then 187 | notify "gsu: display is empty, assuming quit from dmenu or rofi" 188 | exit 1 189 | fi 190 | fi 191 | ;; 192 | 193 | "-o" | "--no-opengl") noopengl="--noopengl" ;; 194 | 195 | "-c" | "--countdown") 196 | if ! [[ $2 =~ ^[0-9]+$ ]]; then 197 | print_not_int "$1" 198 | fi 199 | 200 | countdown_enable="$2" 201 | ;; 202 | 203 | -*) print_invalid_option "$1" ;; 204 | 205 | *) 206 | case "$prev_arg" in 207 | "-d" | "--display" | "-c" | "--countdown") ;; 208 | *) 209 | if [[ "$1" =~ ^[a-zA-Z0-9_.\/-]+$ ]]; then 210 | operand="$1" 211 | else 212 | print_invalid_operand "$1" 213 | fi 214 | ;; 215 | esac 216 | ;; 217 | esac 218 | 219 | prev_arg="$1" 220 | shift 221 | done 222 | } 223 | 224 | get_config() { 225 | mkdir -p "$config_path" 226 | 227 | if [[ ! -f "$config_path/config.conf" ]]; then 228 | if [[ -f "SYSCONFDIR/config.conf" ]]; then 229 | cp "SYSCONFDIR/config.conf" "$config_path" 230 | elif [[ -f "config/config.conf" ]]; then 231 | cp "config/config.conf" "$config_path" 232 | else 233 | echo "gsu: unable to find the config file" 234 | exit 1 235 | fi 236 | fi 237 | 238 | source "$config_path/config.conf" 239 | } 240 | 241 | get_command() { 242 | local MONW="0" 243 | local MONH="0" 244 | local MONX="0" 245 | local MONY="0" 246 | 247 | if [[ ! -z "$display" ]]; then 248 | if ! type -p xrandr >/dev/null; then 249 | print_missing_deps "xrandr" 250 | fi 251 | 252 | if ! type -p xdotool >/dev/null; then 253 | print_missing_deps "xdotool" 254 | fi 255 | 256 | local MONITORS=$(xrandr | grep -o "[0-9]*x[0-9]*[+-][0-9]*[+-][0-9]*") 257 | local XMOUSE=$(xdotool getmouselocation | awk -F "[: ]" '{print $2}') 258 | local YMOUSE=$(xdotool getmouselocation | awk -F "[: ]" '{print $4}') 259 | 260 | readarray -t MONITORS <<< "$MONITORS" 261 | 262 | for ((i = 0; i < ${#MONITORS[@]}; i++)); do 263 | local mon="${MONITORS[$i]}" 264 | 265 | local TEMPMONW=$(echo -e $mon | awk -F "[x+]" '{print $1}') 266 | local TEMPMONH=$(echo -e $mon | awk -F "[x+]" '{print $2}') 267 | local TEMPMONX=$(echo -e $mon | awk -F "[x+]" '{print $3}') 268 | local TEMPMONY=$(echo -e $mon | awk -F "[x+]" '{print $4}') 269 | 270 | case "$display" in 271 | "active") 272 | if (($XMOUSE >= $TEMPMONX)); then 273 | if (($XMOUSE <= $TEMPMONX + $TEMPMONW)); then 274 | if (($YMOUSE >= $TEMPMONY)); then 275 | if (($YMOUSE <= $TEMPMONY + $TEMPMONH)); then 276 | MONW=$TEMPMONW 277 | MONH=$TEMPMONH 278 | MONX=$TEMPMONX 279 | MONY=$TEMPMONY 280 | fi 281 | fi 282 | fi 283 | fi 284 | ;; 285 | 286 | *) 287 | if [[ "$display" =~ ^[0-9]+$ ]]; then 288 | if (($display == $i + 1)); then 289 | MONW=$TEMPMONW 290 | MONH=$TEMPMONH 291 | MONX=$TEMPMONX 292 | MONY=$TEMPMONY 293 | fi 294 | else 295 | if (($TEMPMONW > $MONW)); then 296 | ((MONW=$MONW + $TEMPMONW)) 297 | fi 298 | 299 | if (($TEMPMONH > $MONH)); then 300 | ((MONH=$MONH + $TEMPMONH)) 301 | fi 302 | 303 | if (($TEMPMONX + $TEMPMONW >= $MONW)); then 304 | ((MONW=$TEMPMONW + $TEMPMONX)) 305 | fi 306 | 307 | if (($TEMPMONY + $TEMPMONH >= $MONH)); then 308 | ((MONH=$TEMPMONH + $TEMPMONY)) 309 | fi 310 | fi 311 | ;; 312 | esac 313 | done 314 | 315 | if [[ "$MONW" == "0" ]] && [[ "$MONH" == "0" ]] && [[ "$MONX" == "0" ]] && [[ "$MONY" == "0" ]]; then 316 | notify "gsu: unable to find your display" 317 | exit 1 318 | fi 319 | fi 320 | 321 | check_minimize 322 | 323 | case "$type" in 324 | "screenshot") 325 | if [[ -z "$display" ]]; then 326 | cmd="$(maim $noopengl $nocursor -s "$operand" 2>&1>/dev/null)" 327 | else 328 | cmd="$(maim $noopengl $nocursor -g "${MONW}x${MONH}+${MONX}+${MONY}" "$operand" 2>&1>/dev/null)" 329 | fi 330 | ;; 331 | 332 | "video" | "gif") 333 | if [[ -z "$display" ]]; then 334 | read -r X Y W H G ID < <(slop -f "%x %y %w %h %g %i") 335 | 336 | if [[ -z "$X" ]] || [[ -z "$Y" ]] || [[ -z "$W" ]] || [[ -z "$H" ]]; then 337 | check_maximize 338 | print_manually_quit 339 | fi 340 | fi 341 | 342 | if [[ -z "$AUDIO" ]]; then 343 | if ! type -p pulseaudio >/dev/null; then 344 | notify "\ 345 | gsu: no audio device set, default device 'pulse' cannot be found 346 | Either install pulseaudio, or add your audio device to `$config_path/config.conf`." 347 | check_maximize 348 | 349 | exit 1 350 | fi 351 | 352 | AUDIO="pulse" 353 | fi 354 | 355 | if [[ ! -z "$countdown_enable" ]]; then 356 | check_maximize 357 | countdown "$countdown_enable" "Handing control over to ffmpeg in COUNTNUM seconds." 358 | check_minimize 359 | fi 360 | 361 | echo "Press [q] or [Ctrl+C] to stop recording." 362 | 363 | if [[ -z "$display" ]]; then 364 | cmd=$(ffmpeg -y -f alsa -i $AUDIO -r 30 -f x11grab -show_region 1 $draw_mouse -s "$W"x"$H" -i :0.0+$X,$Y -c:v libx264 -crf 18 -preset:v ultrafast -c:a aac -b:a 192k -loglevel error "$output" 2>&1>/dev/null) 365 | else 366 | cmd=$(ffmpeg -y -f alsa -i $AUDIO -r 30 -f x11grab -show_region 1 $draw_mouse -s "$MONW"x"$MONH" -i :0.0+$MONX,$MONY -c:v libx264 -crf 18 -preset:v ultrafast -c:a aac -b:a 192k -loglevel error "$output" 2>&1>/dev/null) 367 | fi 368 | ;; 369 | esac 370 | 371 | check_maximize 372 | } 373 | 374 | set_type() { 375 | if [[ -z "$type" ]]; then 376 | type="$1" 377 | fi 378 | } 379 | 380 | check_minimize() { 381 | if [[ ! -z "$hide" ]]; then 382 | xdotool_id=$(xdotool getactivewindow) 383 | xdotool windowminimize $xdotool_id 384 | sleep 0.5 385 | fi 386 | } 387 | 388 | check_maximize() { 389 | if [[ ! -z "$hide" ]] && [[ ! -z "$xdotool_id" ]]; then 390 | xdotool windowactivate $xdotool_id 391 | fi 392 | } 393 | 394 | countdown() { 395 | for ((i = $1; i > 0; i--)); do 396 | echo -ne "\r${2//COUNTNUM/$i}" 397 | sleep 1 398 | done 399 | 400 | echo 401 | } 402 | 403 | log() { 404 | if [[ ! -d "$config_path/logs" ]]; then 405 | mkdir -p "$config_path/logs" 406 | fi 407 | 408 | echo "$1" >"$config_path/logs/$(date +'%Y.%m.%d-%H.%M.%S.%N').log" 409 | } 410 | 411 | check_valid_exts() { 412 | local filename="${operand##*/}" 413 | local extension="${filename##*.}" 414 | 415 | IFS="," read -ra exts <<< "$1" 416 | 417 | for ext in "${exts[@]}"; do 418 | if [[ "$extension" == "$ext" ]]; then 419 | return 420 | fi 421 | done 422 | 423 | notify "gsu: invalid $type file extension -- '$extension'" 424 | exit 1 425 | } 426 | 427 | take_screenshot() { 428 | if ! type -p maim >/dev/null; then 429 | print_missing_deps "maim" 430 | fi 431 | 432 | get_command 433 | 434 | if [[ ! -z "$cmd" ]]; then 435 | if [[ "$cmd" == *"keystroke or right-click"* ]]; then 436 | print_manually_quit 437 | fi 438 | 439 | log "$cmd" 440 | 441 | notify "\ 442 | gsu: maim returned an error 443 | Check the most recent log in '$config_path/logs'." 444 | 445 | exit 1 446 | fi 447 | } 448 | 449 | take_video() { 450 | local output="$operand" 451 | 452 | if ! type -p slop >/dev/null; then 453 | print_missing_deps "slop" 454 | fi 455 | 456 | if ! type -p ffmpeg >/dev/null; then 457 | print_missing_deps "ffmpeg" 458 | fi 459 | 460 | if [[ "$type" == "gif" ]]; then 461 | output="$operand.mp4" 462 | fi 463 | 464 | get_command 465 | 466 | if [[ -z "$cmd" ]]; then 467 | echo "Stopped recording." 468 | else 469 | log "$cmd" 470 | 471 | notify "\ 472 | gsu: ffmpeg returned an error 473 | Check the most recent log in '$config_path/logs'." 474 | 475 | exit 1 476 | fi 477 | } 478 | 479 | make_gif() { 480 | if ! type -p ffmpeg >/dev/null; then 481 | print_missing_deps "ffmpeg" 482 | fi 483 | 484 | echo "Generating your GIF. This could take awhile depending on how large the selection was." 485 | local cmd=$(ffmpeg -y -i "$operand.mp4" -vf "fps=30,palettegen" -loglevel error "/tmp/palette.png" 2>&1>/dev/null) 486 | 487 | if [[ -z "$cmd" ]]; then 488 | cmd=$(ffmpeg -y -i "$operand.mp4" -i "/tmp/palette.png" -filter_complex "fps=30,scale=flags=lanczos[x];[x][1:v]paletteuse" -loglevel error "$operand") 489 | 490 | if [[ -z "$cmd" ]]; then 491 | return 492 | fi 493 | fi 494 | 495 | log "$cmd" 496 | 497 | notify "\ 498 | gsu: ffmpeg returned an error 499 | Check the most recent log in '$config_path/logs'." 500 | 501 | exit 1 502 | } 503 | 504 | upload() { 505 | pushd $(dirname $operand) &>/dev/null 506 | local absolute="$PWD/$(basename $operand)" 507 | popd &>/dev/null 508 | 509 | if ! type -p jq >/dev/null; then 510 | print_missing_deps "jq" 511 | fi 512 | 513 | local cmd 514 | 515 | case "$type" in 516 | "screenshot") 517 | if [[ -z "$S_UPLOAD" ]]; then 518 | cmd=$(curl --progress-bar -F "randomname=true" -F "file=@$absolute" "https://uguu.se/api.php?d=upload-tool") 519 | else 520 | cmd=$(eval "${S_UPLOAD//GSUFILEOUT/$absolute}") 521 | fi 522 | ;; 523 | 524 | "video") 525 | if [[ -z "$V_UPLOAD" ]]; then 526 | if [[ -z "$STREAMABLE_USER" ]] || [[ -z "$STREAMABLE_PASS" ]]; then 527 | notify "\ 528 | gsu: invalid streamable credentials 529 | Fill in your credentials in '$config_path/config.conf' and try again." 530 | 531 | exit 1 532 | else 533 | cmd=$(curl --progress-bar -u "$STREAMABLE_USER:$STREAMABLE_PASS" -F "file=@$absolute" "https://api.streamable.com/upload" | jq -r ".shortcode") 534 | 535 | if [[ "${#cmd}" == 5 ]]; then 536 | cmd="https://streamable.com/$cmd" 537 | fi 538 | fi 539 | else 540 | cmd=$(eval "${V_UPLOAD//GSUFILEOUT/$absolute}") 541 | fi 542 | ;; 543 | 544 | "gif") 545 | if [[ -z "$G_UPLOAD" ]]; then 546 | local cmd=$(curl -s -X POST -H "Content-Type: application/json" "https://api.gfycat.com/v1/gfycats") 547 | 548 | if echo "$cmd" | jq -e ".gfyname" &>/dev/null; then 549 | local output=$(echo "$cmd" | jq -r ".gfyname") 550 | cp "$absolute" "$output" 551 | 552 | cmd=$(curl --progress-bar -w "%{http_code}" --upload-file "$output" "https://filedrop.gfycat.com") 553 | rm "$output" 554 | 555 | if [[ "$cmd" == "200" ]]; then 556 | echo "Gfycat is processing..." 557 | local count=0 558 | 559 | while [[ "$(curl -s https://api.gfycat.com/v1/gfycats/fetch/status/$output | jq -e -r '.task')" != "complete" ]]; do 560 | ((count++)) 561 | 562 | if [[ $count == 300 ]]; then 563 | notify "gsu: gfycat timed out while processing" 564 | exit 1 565 | fi 566 | 567 | sleep 1 568 | done 569 | 570 | cmd="https://gfycat.com/$output" 571 | else 572 | notify "gsu: gfycat returned an invalid http code -- '$cmd'" 573 | exit 1 574 | fi 575 | fi 576 | else 577 | cmd=$(eval "${G_UPLOAD//GSUFILEOUT/$absolute}") 578 | fi 579 | ;; 580 | esac 581 | 582 | if type -p xsel >/dev/null && [[ "$cmd" == "http"* ]]; then 583 | echo -n "$cmd" | xsel -ib 584 | fi 585 | 586 | notify "$cmd" 587 | } 588 | 589 | remove_args() { 590 | local args=() 591 | 592 | while [[ "$1" ]]; do 593 | if [[ "$1" == "--terminal" ]]; then 594 | shift 595 | else 596 | args+=("$1") 597 | shift 598 | fi 599 | done 600 | 601 | echo "${args[@]}" 602 | } 603 | 604 | main() { 605 | get_config 606 | get_args "$@" 607 | 608 | if [[ -z "$terminal_arg" ]]; then 609 | if [[ ! -z "$rofi_arg" ]]; then 610 | case $(echo -e "Take a screenshot\nRecord a video\nRecord a video and convert it to the gif format" | "${dmenu_type[@]}") in 611 | "Take a screenshot") set_type "screenshot" ;; 612 | "Record a video") set_type "video" ;; 613 | "Record a video and convert it to the gif format") set_type "gif" ;; 614 | *) print_manually_quit ;; 615 | esac 616 | fi 617 | 618 | set_type "screenshot" 619 | 620 | if [[ -z "$operand" ]]; then 621 | local ext="" 622 | 623 | case "$type" in 624 | "screenshot") ext=".png" ;; 625 | "video") ext=".mp4" ;; 626 | "gif") ext=".gif" ;; 627 | esac 628 | 629 | mkdir -p "$config_path/imgs" 630 | operand="$config_path/imgs/$(date +"%F.%H-%M-%S.%N")$ext" 631 | fi 632 | 633 | case "$type" in 634 | "screenshot") 635 | if [[ -z "$S_UPLOAD" ]]; then 636 | check_valid_exts "png,jpg" 637 | fi 638 | 639 | take_screenshot 640 | ;; 641 | 642 | "video") 643 | if [[ -z "$V_UPLOAD" ]]; then 644 | check_valid_exts "mp4,mkv,mov" 645 | fi 646 | 647 | take_video 648 | ;; 649 | 650 | "gif") 651 | if [[ -z "$G_UPLOAD" ]]; then 652 | check_valid_exts "gif" 653 | fi 654 | 655 | take_video 656 | make_gif 657 | ;; 658 | esac 659 | 660 | if [[ ! -z "$upload" ]]; then 661 | upload 662 | else 663 | notify "gsu: saved to '$operand'" 664 | fi 665 | else 666 | pushd $(dirname ${BASH_SOURCE[0]}) &>/dev/null 667 | local absolute="$PWD/$(basename ${BASH_SOURCE[0]})" 668 | popd &>/dev/null 669 | 670 | local args=$(remove_args "$@") 671 | 672 | for ((i = 0; i < ${#GSU_TERM[@]}; i++)); do 673 | if [[ "${GSU_TERM[$i]}" == *"CMD"* ]]; then 674 | GSU_TERM[$i]="${GSU_TERM[$i]//CMD/$absolute $args}" 675 | fi 676 | done 677 | 678 | eval "${GSU_TERM[@]}" 679 | fi 680 | 681 | exit 0 682 | } 683 | 684 | main "$@" 685 | -------------------------------------------------------------------------------- /gsu.1: -------------------------------------------------------------------------------- 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.11. 2 | .TH GSU "1" "December 2019" "gsu 1.3.2" "User Commands" 3 | .SH NAME 4 | gsu \- manual page for gsu 1.3.2 5 | .SH SYNOPSIS 6 | .B gsu 7 | [\fI\,OPTION\/\fR]... \fI\,SOURCE\/\fR 8 | .SH DESCRIPTION 9 | A general screenshot and upload utility for images, video, and gifs. 10 | .PP 11 | SOURCE defaults to \fI\,$XDG_CONFIG_HOME/gsu/imgs\/\fP if nothing is provided. 12 | The most common location for $XDG_CONFIG_HOME is ~/.config. 13 | .PP 14 | GENERAL OPTIONS 15 | .TP 16 | \fB\-h\fR, \fB\-\-help\fR 17 | Show the help menu. 18 | .TP 19 | \fB\-v\fR, \fB\-\-version\fR 20 | Show the current version. 21 | .TP 22 | \fB\-l\fR, \fB\-\-list\-displays\fR 23 | List your current displays and their resolutions. 24 | .TP 25 | \fB\-u\fR, \fB\-\-upload\fR 26 | Upload after running the utility. 27 | .TP 28 | \fB\-\-terminal\fR 29 | Open interactive commands in a new terminal.. 30 | SET THIS IF RUNNING FROM ANYTHING OTHER THAN A TERM, 31 | i.e. xbindkeys 32 | .TP 33 | \fB\-\-hide\fR 34 | Minimize the current window while capturing. 35 | This is useful when running with \fB\-\-terminal\fR to hide 36 | the newly opened term. 37 | .TP 38 | \fB\-\-notify\fR 39 | Send a libnotify notification of the output. 40 | .PP 41 | UTILITY OPTIONS 42 | .TP 43 | \fB\-r\fR, \fB\-\-dmenu\fR 44 | Select the one of the below options from dmenu or rofi. 45 | .TP 46 | \fB\-s\fR, \fB\-\-screenshot\fR 47 | Take a screenshot. (default) 48 | .TP 49 | \fB\-m\fR, \fB\-\-video\fR 50 | Record a video of the screen. 51 | .TP 52 | \fB\-g\fR, \fB\-\-gif\fR 53 | Record a video and convert it to the gif format. 54 | .PP 55 | CAPTURE OPTIONS 56 | .TP 57 | \fB\-n\fR, \fB\-\-no\-cursor\fR 58 | Hide the cursor. 59 | .TP 60 | \fB\-d\fR, \fB\-\-display\fR NUM|TYPE 61 | Set the selection to a specific display. 62 | Can read from an argument or from stdin. 63 | Values: number of display, 'active', 'all' (default) 64 | .PP 65 | SCREENSHOT OPTIONS 66 | .TP 67 | \fB\-o\fR, \fB\-\-no\-opengl\fR 68 | Disable OpenGL hardware\-accelerated capture. 69 | .PP 70 | VIDEO OPTIONS 71 | .TP 72 | \fB\-c\fR, \fB\-\-countdown\fR NUM 73 | Countdown before recording. 74 | .SH "SEE ALSO" 75 | The full documentation for 76 | .B gsu 77 | is maintained as a Texinfo manual. If the 78 | .B info 79 | and 80 | .B gsu 81 | programs are properly installed at your site, the command 82 | .IP 83 | .B info gsu 84 | .PP 85 | should give you access to the complete manual. 86 | --------------------------------------------------------------------------------