diff --git a/Bin/slip b/Bin/slip new file mode 100755 index 0000000..114c77b --- /dev/null +++ b/Bin/slip @@ -0,0 +1,354 @@ +#! /bin/bash + +set -eo pipefail + +VERSION="2.0.4" + +# Sane defaults in case of not being set in the config / config not existing. +CREDENTIALS_FILE="$HOME/.config/slip/credentials" +RECORD_PIDFILE="/tmp/slip_record.pid" +IMAGES="$HOME/Pictures" +VIDEOS="$HOME/Videos" +GIF_SCALE="640" +GIF_FPS=15 +SOUND=0 +NOUPLOAD=0 + +DMENU_CMD="rofi -dmenu -p slip" + +# Load config. +CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/slip/config" +if [ -f $CONFIG ]; then + source $CONFIG +else + echo "No config was found - using default options. Please copy the example configuration to ~/.config/slip/config from https://github.com/Toqozz/slip" +fi + +CLIENT_ID="abd3a90bbfb65e9" + +# Dmenu prompts. +DMENU_OPTS="screenshot +video +gif +exit" +DMENU_RECORD_OPTS="stop +cancel" +DMENU_UPLOAD_OPTS_BIG="upload (gfycat) +delete +exit" +DMENU_UPLOAD_OPTS="upload (imgur) +upload (gfycat) +delete +exit" + +# Maximum size for upload (kb). +# Gfycat does not have a file limit. +MAX_GIF_SIZE_IMGUR=10000 + +function usage() { + echo "" + echo " slip [ -h | -v | -s | -r | -a | -q ] -nu" + echo " Uploads images taken with ffmpeg via slop to imgur. Quick video recording." + echo " That's all." + echo "" + echo " -h | --help show this help" + echo " -v | --version show version" + echo " -s | --screenshot take a screenshot (skip dmenu)" + echo " -g | --gif take a gif (skip dmenu)" + echo " -r | --record take a video (skip dmenu)" + echo " -nu | --no-upload don't upload the result" + echo " -q | --stop stop recording a video / gif (skip dmenu) (only when recording)" + echo "" +} + +function upload_imgur() { + file="$1" + + if [ -f "$file" ]; then + if [ -f "$CREDENTIALS_FILE" ]; then + # We have an auth token to use. + check_auth_credentials + if [ $? -ne 1 ]; then + # Token valid. + curl -sH "Authorization: Bearer ${access_token}" -F "image=@$file" "https://api.imgur.com/3/upload" + fi + else + # Imgur needs to know what program is using its services. + curl -sH "Authorization: Client-ID ${CLIENT_ID}" -F "image=@$file" "https://api.imgur.com/3/upload" + fi + else + echo "File does not exist, what happened?" + fi +} + +function upload_gfycat() { + local file="$1" + + local result=$(curl -s -XPOST https://api.gfycat.com/v1/gfycats -H "Content-Type: application/json") + local gfyname=$(parse "gfycat" $result) + curl -s "https://filedrop.gfycat.com/$gfyname" --upload-file "$file" + + # No new line so we can append it easily. + echo -n "$gfyname" +} + +function check_auth_credentials() { + token_expire_time=0 + if [ -f "${CREDENTIALS_FILE}" ]; then + source "${CREDENTIALS_FILE}" + fi + + if [ ! -z ${access_token} ]; then + current_time="$(date +%s)" + preemptive_refresh_time="$((10*60))" + expired="$((current_time > (token_expire_time - preemptive_refresh_time)))" + + if [ "${expired}" -eq "0" ]; then + #token has expired, get a new one. + echo 'The authorization token has expired, please get a new one.' + return 1; + fi + else + return 1; + fi +} + +function clip_clear() { + # Clear x cliboard selection. + xsel -bc # Ctrl-v / Shift-Insert. + # xsel -pc # Middle click. +} + +# Run slop and get the geometry line. +function slop_geom() { + if [ "$1" = "image" ]; then + slop -f '%wx%h+%x+%y' + else + slop -f '%wx%h|%x,%y' # | sed -e s/+/\|/ -e s/+/,/ + fi + #elif [ "$1" = "video" ]; then + #slop -f '%wx%h+%x+%y' | sed -e s/+/\|/ -e s/+/,/ + #slop | sed -n 5p | sed -e s/G=// -e s/+/\|/ -e s/+/,/ + #elif [ "$1" = "gif" ]; then + #slop -f '%wx%h+%x+%y' | sed -e s/+/\|/ -e s/+/,/ +} + +# Take the shot (or start the video.) +function shot() { + if [ "$1" = "image" ]; then + extension=".png" # img-2016-04-16-153906.png + filename="$IMAGES/img-`date +%Y-%m-%d-%H%M%S`$extension" # .png, .jpg + maim -g "$2" $filename + # TODO, do we want more dependencies? (optional dependencies?) + #ffmpeg -f x11grab -video_size "$2" -i "$DISPLAY+$3" -vframes 1 $filename &> /dev/null + elif [ "$1" = "video" ]; then + extension=".mkv" # vid-2016-04-16-153906.mkv + filename="$VIDEOS/vid-`date +%Y-%m-%d-%H%M%S`$extension" # .mkv, .mp4 + if [ $SOUND == 0 ]; then + ffmpeg -f x11grab -video_size "$2" -framerate 60 -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $filename & + else + ffmpeg -f pulse -i 1 -f x11grab -video_size "$2" -framerate 60 -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $filename & + fi + echo "$! vid $filename" > "$RECORD_PIDFILE" + elif [ "$1" = "gif" ]; then + extension=".gif" # gif-2016-04-16-153906.gif + tmpfilename="/tmp/gif-`date +%Y-%m-%d-%H%M%S-tmp`.mkv" + filename="$VIDEOS/gif-`date +%Y-%m-%d-%H%M%S`$extension" # .gif + # Record a video to be converted to gif. + ffmpeg -f x11grab -video_size "$2" -framerate $GIF_FPS -i "$DISPLAY+$3" -loglevel quiet -c:v libx264 -preset ultrafast $tmpfilename & + #ffmpeg -f x11grab -video_size "$2" -framerate 15 -i "$DISPLAY+$3" -vf scale="$GIF_SCALE:-1" $filename &> /dev/null & + + # We need to access this information later. + ratio=$(awk -F"x" '{ print int($1/$2) }' <<< "$2") # We only really need a binary answer. + if [ "$ratio" -ge 1 ]; then echo "$! gif $tmpfilename $filename 0" > "$RECORD_PIDFILE" + else echo "$! gif $tmpfilename $filename 1" > "$RECORD_PIDFILE" + fi + fi +} + +function kill_ffmpeg() { + # Kill ffmpeg (stopping the recording.) + kill $1 || echo "Failed to kill ffmpeg, did it crash? Removing $RECORD_PIDFILE anyway." + # Remove the pid file so that slip can be used as normal again. + rm "$RECORD_PIDFILE" +} + +function convert_to_gif() { + local tfn=$1 + local pfn=$2 + local fn=$3 + local wh=$4 # 0 = width larger than height, 1 = height larger than width + + local ratio + # If the width is larger than the height, we want to scale with the width, otherwise scale with the height. + if [ $wh -eq 0 ]; then + ratio="$GIF_SCALE:-1" + else + ratio="-1:$GIF_SCALE" + fi + + notify "Converting to gif…" + # Give enough time to save the file. + sleep 1 && ffmpeg -i "$tfn" -loglevel quiet -vf fps=$GIF_FPS,scale="$ratio":flags=lanczos,palettegen "$pfn" && + ffmpeg -i "$tfn" -i "$pfn" -loglevel quiet -filter_complex "fps=$GIF_FPS,scale=$ratio:flags=lanczos[x];[x][1:v]paletteuse" "$fn" && + + echo "$fn" +} + +function stop_rec() { + local choice + + local pid=$1 # Process id (for killing ffmpeg). + local tfn=$3 # Temp file name (for gifs). + local fn=$4 # File name for the gif/vid we're saving. + local pfn="${tfn}-palette.png" # Palette file name (for gif rendering). + + # Stop recording. + kill_ffmpeg "$pid" + + # When processing a gif, we canvert it straight away and then upload. + # NOTE: gfycat does not require the video to be converted, but we convert it anyway to store + # the .gif file. + # Is this actually a good idea? We could just store the .mkv instead, but then we lose the ability to store .gif. + if [ "$2" = "gif" ]; then + fn=$(convert_to_gif "$tfn" "$pfn" "$fn" "$5") + size=$(du -k $fn | cut -f1) + + if [ $NOUPLOAD == 0 ]; then + if [ "$size" -lt "${MAX_GIF_SIZE_IMGUR}" ]; then + choice=$($DMENU_CMD <<< $DMENU_UPLOAD_OPTS) + else + choice=$($DMENU_CMD <<< $DMENU_UPLOAD_OPTS_BIG) + fi + + if [ "$choice" = "upload (gfycat)" ]; then + clip_clear + name=$(upload_gfycat "$tfn") + url="https://gfycat.com/$name" + notify "Uploading to $url ..." + echo "$url" + echo -n "$url" | xsel -bi # Read to clipboard. + elif [ "$choice" = "upload (imgur)" ]; then + clip_clear + output=$(upload_imgur "$fn") + url=$(parse "imgur" "$output") + notify "$url" + echo "$url" + echo -n "$url" | xsel -bi # Read to clipboard. + elif [ "$choice" = "delete" ]; then + rm $fn + fi + fi + else + notify "$fn" + fi +} + +# Parse x,y -- but also imgur. +function parse() { + if [ "$1" = "geometryx" ]; then + awk -F"|" '{ print $1 }' <<< "$2" + elif [ "$1" = "geometry+" ]; then + awk -F"|" '{ print $2 }' <<< "$2" + elif [ "$1" = "imgur" ]; then + jq -r ".data.link" <<< "$2" + #sed -e 's/.*\"link\":"\([^"]*\).*/\1/' -e 's/\\//g' <<< "$2" + #sed -e 's/\"link\":"\([^"]*\)/\1/' -e 's/\\//g' <<< "$2" + elif [ "$1" = "gfycat" ]; then + jq -r ".gfyname" <<< "$2" + fi +} + +function notify() { + notify-send -t 7000 "slip" "$1" +} + +function main() { + if [ "$1" = "screenshot" ]; then + # Run slop and get geometry from it. + # maim naturally supports slop's output coordinates. + geometry=$(slop_geom "image") + # Take the shot. + shot "image" "$geometry" + # Parse imgur json into link. + + # If noupload is not set. + if [ $NOUPLOAD == 0 ]; then + #echo "uploading..." + # Clear cliboard before doing anything, so we can copy link to it later. + clip_clear + output=$(upload_imgur "$filename") + url=$(parse "imgur" "$output") + # Notify user that upload has finished. + notify "$url" + echo "$url" + # Read to clipboard, removing trailing newline. + echo -n "$url" | xsel -bi + else + notify "$filename" + fi + + # echo "$url" | xsel -pi # Read to primary. + elif [ "$1" = "video" ]; then + geometry=$(slop_geom "video") + wxh=$(parse "geometryx" $geometry) + off=$(parse "geometry+" $geometry) + shot "video" "$wxh" "$off" + elif [ "$1" = "gif" ]; then + geometry=$(slop_geom "gif") + wxh=$(parse "geometryx" $geometry) + off=$(parse "geometry+" $geometry) + shot "gif" "$wxh" "$off" + elif [ "$1" = "stop" ]; then + # Get info of recording process. + local info + # Get infor from the pidfile. + info=$(cat "$RECORD_PIDFILE") + # Stop with parameters (" ") + stop_rec $info + exit 0 + else + exit 0 + fi +} + +# Dependencies. +depends="curl +jq +maim +slop +ffmpeg" +while read line +do + if ! type $line &> /dev/null ; then + echo "$line not found, expect unexpected or breakage." + fi +done <<< "$depends" + +# Main. +# Yes, we could call this stuff with integers, but this is much clearer. +if [ "$1" = "-nu" -o "$1" = "--no-upload" -o "$2" = "-nu" -o "$2" = "--no-upload" ]; then + NOUPLOAD=1 +fi + +if [ "$1" = "-h" -o "$1" = "--help" ]; then + usage + exit 0 +elif [ "$1" = "-v" -o "$1" = "--version" ]; then + echo "Version: $VERSION" + exit 0 +elif [ "$1" = "-s" -o "$1" = "--screenshot" ]; then + main "screenshot" +elif [ "$1" = "-g" -o "$1" = "--gif" ]; then + main "gif" +elif [ "$1" = "-r" -o "$1" = "--record" ]; then + main "video" +elif [ "$1" = "-q" -o "$1" = "--stop" ]; then + main "stop" +elif [ $# == 0 ]; then + if [ -a "$RECORD_PIDFILE" ]; then + main $($DMENU_CMD <<< "$DMENU_RECORD_OPTS") + else + main $($DMENU_CMD <<< "$DMENU_OPTS") + fi +fi diff --git a/config/i3/config b/config/i3/config index 4a99b07..d135542 100644 --- a/config/i3/config +++ b/config/i3/config @@ -204,7 +204,10 @@ bindsym Mod1+Control+q exec "~/Bin/lock.sh" # File browser bindsym $mod+f exec "rofi -modi 'fb:~/.local/share/rofi/rofi-file-browser.sh' -show fb" +# Find file in user directory bindsym $mod+x exec "rofi -modi 'find:~/.local/share/rofi/finder.sh' -show find" +# Shop top +bindsym $mod+t exec "rofi -show top -modi top" # Multimedia keys bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume `pactl info|grep Sink|cut -d: -f 2` +5% @@ -223,6 +226,7 @@ bindsym Shift+XF86AudioNext exec dbus-send --print-reply --dest=org.mpris.MediaP # Take Screenshot bindsym Print exec ~/Bin/screenshooter.sh +bindsym $mod+Print exec ~/Bin/slip #colors border back text indicator #client.focused #0088CC #0088CC #ffffff #dddddd diff --git a/config/rofi/config.top b/config/rofi/config.top new file mode 100644 index 0000000..4035f52 --- /dev/null +++ b/config/rofi/config.top @@ -0,0 +1,3 @@ +[general] +sorting=1 +ordering=1 diff --git a/config/slip/config b/config/slip/config new file mode 100644 index 0000000..7a98294 --- /dev/null +++ b/config/slip/config @@ -0,0 +1,12 @@ +DMENU_CMD="rofi -dmenu" +RECORD_PIDFILE="/tmp/slip_record.pid" +IMAGES="${HOME}/Pictures" +VIDEOS="${HOME}/Videos" +DMENU_PROMPT="slip" + +GIF_SCALE="640" +GIF_FPS="15" + +SOUND=0 + +NOUPLOAD=1 diff --git a/dotfiles/Xresources b/dotfiles/Xresources index aefd953..7e0ab19 100644 --- a/dotfiles/Xresources +++ b/dotfiles/Xresources @@ -113,7 +113,7 @@ URxvt.keysym.Control-Left: \033[1;5D rofi.lines: 12 rofi.modi: combi -rofi.combi-modi: window,ssh,run,drun +rofi.combi-modi: window,ssh,run,drun,emoji rofi.font: Droid Sans Mono 8 rofi.terminal: tilix