From 707eba3ead2e3d1255cff6805a9ab1263c059c5c Mon Sep 17 00:00:00 2001 From: Ville Rantanen Date: Fri, 8 Oct 2021 11:19:58 +0300 Subject: [PATCH] transferring several commands from private repo --- av/ffmpeg-magic | 51 +++++++ av/ffmpeg-parser | 292 +++++++++++++++++++++++++++++++++++++++ av/ffmpeg-screencapture | 155 +++++++++++++++++++++ av/video-compress | 216 +++++++++++++++++++++++++++++ bin/2fa-menu | 1 + bin/2fa-view | 1 - bin/ffmpeg-magic | 1 + bin/ffmpeg-parser | 1 + bin/ffmpeg-screencapture | 1 + bin/rclone-mount | 1 + bin/ssh-nosave | 1 + bin/ssh-scan-lan | 1 + bin/sshfs-mount | 1 + bin/video-compress | 1 + shell/2fa-menu | 101 ++++++++++++++ shell/2fa-view | 288 -------------------------------------- web/rclone-mount | 129 +++++++++++++++++ web/ssh-nosave | 9 ++ web/ssh-scan-lan | 71 ++++++++++ web/sshfs-mount | 107 ++++++++++++++ 20 files changed, 1140 insertions(+), 289 deletions(-) create mode 100755 av/ffmpeg-magic create mode 100755 av/ffmpeg-parser create mode 100755 av/ffmpeg-screencapture create mode 100755 av/video-compress create mode 120000 bin/2fa-menu delete mode 120000 bin/2fa-view create mode 120000 bin/ffmpeg-magic create mode 120000 bin/ffmpeg-parser create mode 120000 bin/ffmpeg-screencapture create mode 120000 bin/rclone-mount create mode 120000 bin/ssh-nosave create mode 120000 bin/ssh-scan-lan create mode 120000 bin/sshfs-mount create mode 120000 bin/video-compress create mode 100755 shell/2fa-menu delete mode 100755 shell/2fa-view create mode 100755 web/rclone-mount create mode 100755 web/ssh-nosave create mode 100755 web/ssh-scan-lan create mode 100755 web/sshfs-mount diff --git a/av/ffmpeg-magic b/av/ffmpeg-magic new file mode 100755 index 0000000..2077168 --- /dev/null +++ b/av/ffmpeg-magic @@ -0,0 +1,51 @@ +#!/bin/bash + +if [[ "$1" = "-h" ]]; then + echo This script prints out various ffmpeg commands + exit +fi + + +cat <<'EOF' | md-color +# Basic Good Quality +a) -hide_banner -q:v 0 -q:a 0 +b) -s hd720 -g 250 -c:v libx264 -crf 23 -c:a aac -movflags faststart -strict -2 +YIFY) -c:v libx264 -crf 27 -x264-params cabac=1:ref=5:analyse=0x133:me=umh:subme=9:chroma-me=1:deadzone-inter=21:deadzone-intra=11:b-adapt=2:rc-lookahead=60:vbv-maxrate=10000:vbv-bufsize=10000:qpmax=69:bframes=5:b-adapt=2:direct=auto:crf-max=51:weightp=2:merange=24:chroma-qp-offset=-1:sync-lookahead=2:psy-rd=1.00,0.15:trellis=2:min-keyint=23:partitions=all -c:a aac -ar 44100 -b:a 128k -map 0 +# Smooth slow motion + -filter:v "minterpolate='mi_mode=mci:mc_mode=aobmc:vsbmc=1:fps=120'",setpts=1.5*PTS +# BCS + unsharp (works also on -filter_complex) + -vf unsharp,"eq=contrast=1.4:brightness=0.1:saturation=1.5" +# More filters to try + histeq=strength=0.1,normalize=blackpt=red:whitept=yellow:smoothing=150,hqdn3d=4:4:3:3" +# Trim + -vcodec copy -acodec copy -ss 00:01:30 -to 00:02:30 + -vcodec copy -acodec copy -ss 50 -to 65 +# Vertical2Horizontal + -filter_complex "[0:v]scale=ih*16/9:-1,boxblur=luma_radius=min(h\,w)/20:luma_power=1:chroma_radius=min(cw\,ch)/20:chroma_power=1[bg];[bg][0:v]overlay=(W-w)/2:(H-h)/2,crop=h=iw*9/16" +# Rotate 0=90CCW+flip,1=90CW,2=90CCW,3=90CW+flip. example 180 deg. + -vf "transpose=2,transpose=2" +# Antishake-Stabilize +in=videofile.mp4 +ffmpeg -i "$in" -vf vidstabdetect=stepsize=32:shakiness=10:accuracy=10:result="$in".transforms.trf -f null - +ffmpeg -i "$in" -vf vidstabtransform=input="$in".transforms.trf:zoom=0:smoothing=10,unsharp=5:5:0.8:3:3:0.4 -q:v 0 "$in".stabilized.mp4 +# To Stills + -vf fps=10 "$1".%06d.png +# From Stills + -r 1/5 -start_number 0 -i img%03d.png -r 30 -pix_fmt yuv420p +# Segment + -segment_time 00:15:00 -f segment -reset_timestamps 1 "$in.%03d.480p.mp4" +# Stacking + -i input0 -i input1 -filter_complex vstack=inputs=2 + -i input0 -i input1 -filter_complex h stack=inputs=2 +# Chroma key + -f lavfi -i color=c=green:s=1920x1080 -i input.mp4 -filter_complex "[1:v]chromakey=0x297141:0.1:0.0[ckout];[0:v][ckout]overlay[o]" -map [o] -map 1:a +# View live + -pix_fmt yuv420p -f matroska - | ffplay -i - +# Concatenate + find . -type f -printf "file '%P'\n" | sort -V > filelist.txt + ffmpeg -f concat -safe 0 -i filelist.txt -c copy concatenated.mp4 +# Audio encode + ffmpeg -i file.wav -codec:a libmp3lame -qscale:a 2 file.mp3 +# Audio map + ffmpeg -i input.mp4 -i input.mp3 -c copy -map 0:0 -map 1:1 -shortest out.mp4 +EOF diff --git a/av/ffmpeg-parser b/av/ffmpeg-parser new file mode 100755 index 0000000..9f935a9 --- /dev/null +++ b/av/ffmpeg-parser @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +import os +import time +import parse +from ansi import cursor +from datetime import datetime + + +class Chopper: + def __init__(self, buf): + self.buffer = buf + self.memory = bytearray() + self.eol1 = b"\r" + self.eol2 = b"\n" + self.leneol = len(self.eol1) + + def read(self): + line = bytearray() + while True: + c = self.buffer.read(1) + if c: + line += c + if line[-self.leneol :] == self.eol1: + break + if line[-self.leneol :] == self.eol2: + break + else: + break + return bytes(line).decode("utf-8") + + +class Progress: + def __init__(self): + self.started = time.time() + self.duration = None + self.framedata = {} + self.parsable = ( + "frame", + "fps", + "bitrate", + "total_size", + "speed", + "out_time_ms", + ) + self.inputs = [] + self.inputs_full = [] + self.input = "NA" + self.input_size = "NA" + self.outputs = [] + self.output = "NA" + + def parse(self, line): + + if not self.parse_frame(line): + print(line.rstrip()) + self.parse_input(line) + self.parse_output(line) + self.parse_duration(line) + + self.print() + + def parse_input(self, line): + + # Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'VID_20210804_141045.mp4': + parsed = parse.parse("Input #{:d}, {}, from '{}':", line.strip()) + try: + self.inputs.append(os.path.basename(parsed[2])) + self.inputs_full.append(parsed[2]) + self.input = ", ".join(self.inputs) + self.input_size = ", ".join( + [self._mbstr(os.stat(i).st_size) for i in self.inputs_full] + ) + except Exception: + return + + def parse_output(self, line): + + parsed = parse.parse("Output #{:d}, {}, to '{}':", line.strip()) + try: + self.outputs.append(os.path.basename(parsed[2])) + self.output = ", ".join(self.outputs) + except Exception: + return + + def parse_duration(self, line): + + if self.duration: + return + parsed = parse.parse( + "Duration: {:d}:{:d}:{:d}.{:d}, start: {}, bitrate: {} kb/s", line.strip() + ) + # ~ Duration: 00:00:48.21, start: 0.000000, bitrate: 17780 kb/s + try: + self.duration = 3600 * parsed[0] + 60 * parsed[1] + parsed[2] + except Exception: + return + + def parse_frame(self, line): + + try: + values = len(line.strip().split("=")) + if values > 2: # and line.startswith("frame="): + return True + if values != 2: + return False + parsed = parse.parse("{}={}", line.strip()) + if not parsed: + return False + if parsed[0] in self.parsable: + if parsed[0] == "out_time_ms": + self.framedata["out_time_s"] = float(parsed[1]) / 1000000 + else: + self.framedata[parsed[0]] = parsed[1] + except Exception as e: + print(e) + return False + + return True + + def print(self): + + if len(self.framedata) == 0: + return + + self.framedata["time_elapsed"] = int(time.time() - self.started) + if "out_time_s" in self.framedata: + self.framedata["percent_done"] = round( + 100 * self.framedata["out_time_s"] / self.duration, 1 + ) + try: + if self.framedata["percent_done"] > 100: + self.framedata["time_remaining"] = "NA" + else: + self.framedata["time_remaining"] = int( + ( + 100 + * float(time.time() - self.started) + / self.framedata["percent_done"] + ) + - self.framedata["time_elapsed"] + ) + except Exception: + self.framedata["time_remaining"] = "NA" + try: + self.framedata["projected_size"] = int( + ( + 100 + * float(self.framedata["total_size"]) + / self.framedata["percent_done"] + ) + ) + except Exception: + self.framedata["time_remaining"] = "NA" + else: + self.framedata["percent_done"] = "NA" + self.framedata["time_remaining"] = "NA" + self.framedata["projected_size"] = "NA" + + try: + msg = """{cl}==== Q to exit =============== +{cl}Input: {input_file} +{cl}Output: {output_file} +{cl}Progress: {progress}% Elapsed: H{elapsed} +{cl}Finished in: H{left} +{cl}Frame: {frame} = {out_time} +{cl}Source duration: {duration} +{cl}Processing speed: FPS {fps} / {speed} +{cl}Bitrate: {bitrate} +{cl}File size: {total_size}Mb -> {projected_size}Mb (Input: {input_size}Mb) +{cl}{progress_bar}\r{up}""".format( + input_file=self.input, + input_size=self.input_size, + output_file=self.output, + progress=self.framedata["percent_done"], + progress_bar=self._progress_bar(self.framedata["percent_done"]), + elapsed=self._timestr(self.framedata["time_elapsed"]), + left=self._timestr(self.framedata["time_remaining"]), + duration=self._timestr(self.duration), + out_time=self._timestr(self.framedata["out_time_s"]), + frame=self.framedata.get("frame", "NA"), + fps=self.framedata.get("fps", "NA"), + bitrate=self.framedata["bitrate"], + speed=self.framedata["speed"], + total_size=self._mbstr(self.framedata["total_size"]), + projected_size=self._mbstr(self.framedata["projected_size"]), + up=cursor.up(10), + cl=cursor.erase_line(), + ) + + sys.stdout.write(msg) + sys.stdout.flush() + except Exception as e: + pass + + def finish(self): + + for i in range(len(self.framedata) + 3): + sys.stdout.write("\n") + sys.stdout.flush() + + def _timestr(self, sec): + + try: + hours = int(sec) // 3600 % 24 + minutes = int(sec) // 60 % 60 + seconds = int(sec) % 60 + return "{:02d}:{:02d}:{:02d}".format(hours, minutes, seconds) + except Exception: + return sec + + def _mbstr(self, b): + + try: + return "{:.1f}".format((float(b) / (1024 ** 2))) + except Exception: + return b + + def _progress_bar(self, p): + + try: + done_chars = int(float(p) * 30 / 100) + todo_chars = int(30 - done_chars) + return (">" * done_chars + "-" * todo_chars)[0:30] + except Exception: + return ">" * 30 + + +def parse_output(args): + return args[-1] + + +def parse_overwrite(args): + return "-y" in args + + +def ask_for_overwrite(commands, path): + + print("File {} exists. Overwrite or break? ([y]/n)".format(path)) + answer = input() + if answer == "n": + sys.exit(1) + + commands.insert(-1, "-y") + return commands + + +if __name__ == "__main__": + commands = sys.argv[1:] + if len(commands) == 1: + if commands[0] == "-h": + print("This command passes all arguments to FFMPEG, and parses output to readable format with progress") + sys.exit(0) + try: + has_overwrite = parse_overwrite(commands) + if not has_overwrite: + output_file = parse_output(commands) + output_exists = os.path.exists(output_file) + if output_exists: + commands = ask_for_overwrite(commands, output_file) + + process = subprocess.Popen( + ["", "-progress", "pipe:2", "-hide_banner"] + commands, + executable="ffmpeg", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + chopper = Chopper(process.stdout) + progress = Progress() + + started = time.time() + duration = None + while True: + output = chopper.read() + progress.parse(output) + if not output: + if process.poll() != None: + break + + rc = process.poll() + progress.finish() + print("Exit code: {}".format(rc)) + sys.exit(rc) + except Exception as e: + print(e) + process.terminate() + progress.finish() + while process.poll() != None: + time.sleep(1) + sys.exit(1) diff --git a/av/ffmpeg-screencapture b/av/ffmpeg-screencapture new file mode 100755 index 0000000..9ff67fb --- /dev/null +++ b/av/ffmpeg-screencapture @@ -0,0 +1,155 @@ +#!/bin/bash + +_help() { + echo 'Commands: + -o [filename] -s [size] -p [pos] -r [framerate] -d [delay] -i + default filename: '$filename' + default size: '$size' + default pos: '$pos' + default framerate: '$framerate' + default delay: '$delay' + -i for interactive selection using "slop" + --shell to record current terminal + + Hit "q" when finished! +' +} +_helpexit(){ + _help + exit +} +function animate() { + x0=$( tput cols ) + pre=$( qolop eval "_qPos save; _qPos abs 1 $x0; _qCol R;" ) + post=$( qolop eval "_qCol Z; _qPos restore;" ) + while true; do + { + printf "%s*%s" "$pre" "$post" + sleep 1 + printf "%s %s" "$pre" "$post" + sleep 1 + } >&2 + done +} + +printf -v filename "grab-%(%FT%H-%M)T.mp4" +size=$( xdpyinfo | awk "/dimensions/{print \$2}" ) +pos="0,0" +framerate=15 +delay=0 +interact=0 +new_shell=0 +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-h"* ]] && _helpexit + [[ ${!i} = "--h"* ]] && _helpexit + [[ ${!i} = "help" ]] && _helpexit +done +for (( i=1; i<=$#; i++ )); do + j=$(( $i + 1 )) + if [[ ${!i} = "-o" ]]; then + filename="${!j}" + shift + fi + if [[ ${!i} = "-s" ]]; then + size="${!j}" + shift + fi + if [[ ${!i} = "-p" ]]; then + pos="${!j}" + shift + fi + if [[ ${!i} = "-r" ]]; then + framerate="${!j}" + shift + fi + if [[ ${!i} = "-d" ]]; then + delay="${!j}" + shift + fi + if [[ ${!i} = "-i" ]]; then + interact=1 + fi + if [[ ${!i} = "--shell" ]]; then + new_shell=1 + fi + +done + +if [[ $new_shell -eq 0 ]]; then + + if [[ $interact -eq 1 ]]; then + slop=$( slop -b 10 -c 1.0,0.8,0.1,0.8 ) + size=$( echo $slop | cut -d+ -f1 ) + pos=$( echo $slop | cut -d+ -f2-3 ) + pos=${pos/+/,} + else + convert -size 256x256 xc:red PNG8:- | timeout 5 feh --geometry $size+${pos//,/+} - + fi +else + eval $(xwininfo -id $(xdotool getactivewindow) | + sed -n -e "s/^ \+Absolute upper-left X: \+\([0-9]\+\).*/x=\1/p" \ + -e "s/^ \+Absolute upper-left Y: \+\([0-9]\+\).*/y=\1/p" \ + -e "s/^ \+Width: \+\([0-9]\+\).*/w=\1/p" \ + -e "s/^ \+Height: \+\([0-9]\+\).*/h=\1/p" ) + size="${w}x${h}" + pos="${x},${y}" +fi + + +if [[ $delay -gt 0 ]]; then + echo Waiting.. for $delay seconds + sleep $delay +fi + + +echo Grabbing: $filename ${size}+$pos + +rm -f "$filename" + +if [[ $new_shell -eq 1 ]]; then + + echo Starting to record in 5 seconds, or press enter + read -t 5 foo || true + x0=$( tput cols ) + pre=$( qolop eval "_qPos save; _qPos abs 1 $x0; _qCol R;" ) + post=$( qolop eval "_qCol Z; _qPos restore;" ) + animate & + animpid=$! + trap 'trap - SIGTERM && kill -- $animpid' EXIT SIGINT SIGTERM + ffmpeg \ + -video_size $size \ + -framerate $framerate \ + -f x11grab \ + -i $DISPLAY+${pos} \ + -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" \ + -c:v libx264 -crf 23 -profile:v baseline -level 3.0 -pix_fmt yuv420p \ + -an \ + -movflags faststart \ + -loglevel panic \ + -hide_banner \ + "$filename" & + ffpid=$! + clear + bash + kill -INT $ffpid + trap - EXIT SIGINT SIGTERM + kill $animpid > /dev/null 2>&1 + #animate_clear + printf "%s %s" "$pre" "$post" >&2 + +else + ffmpeg \ + -video_size $size \ + -framerate $framerate \ + -f x11grab \ + -i $DISPLAY+${pos} \ + -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" \ + -c:v libx264 -crf 23 -profile:v baseline -level 3.0 -pix_fmt yuv420p \ + -an \ + -movflags faststart \ + -loglevel panic \ + -hide_banner \ + "$filename" +fi + +echo "Saved to: $filename" diff --git a/av/video-compress b/av/video-compress new file mode 100755 index 0000000..0918901 --- /dev/null +++ b/av/video-compress @@ -0,0 +1,216 @@ +#!/bin/bash + +. qolop + +get_toggles() { + toggles=$( + echo "$@" | \ + smenu -m "Toggle with t" -n 10 -t 1 -a m:7,bu i:8 c:2 t:7/0 ct:2/0 -T ' ' + # -m title + # -n rows + # -t columns + # -a styless: m title, i items, c cursor, t tagged, ct cursor on tagged + # -T multiselect + ) +} + +get_choice() { + choice=$( + echo "$@" | \ + smenu -n 10 -t 1 -a c:10 -s "$lastchoice" + # -m title + # -n rows + # -t columns + # -a styless: m title, i items, c cursor, t tagged, ct cursor on tagged + # -T multiselect + ) +} + +get_resize() { + title Resize + echo "$resize" + echo "" + + get_choice no-resize hd480 hd720 hd1080 640x360 356x200 manual + case $choice in + no-resize) resize="";; + manual) + echo "Type resolution WxH" + read -e size + resize="-s $size" + ;; + *) resize="-s $choice";; + esac +} + +get_compression() { + title Compression + echo "$compression" + echo "" + + get_choice x264 copy similar YIFY + case $choice in + x264) + echo Video quality: 23 = good, 18 = insane, 30 = poor + read -e -i "$crf" crf + compression="-g 250 -c:v libx264 -crf $crf" + ;; + copy) compression="-vcodec copy";; + similar) compression="-c:v libx264 -q:v 0";; + YIFY) + compression="$yify_compression" + audio="" + ;; + *) return;; + esac +} + +get_audio() { + title Audio + echo "$audio" + echo "" + + get_choice aac copy similar no-audio + lastchoice="$choice" + case $choice in + aac) + echo Audio quality: 64 poor, 128 good + read -e -i "$abit" abit + audio="-c:a aac -ar 44100 -b:a ${abit}k -strict -2" + ;; + copy) audio="-acodec copy";; + similar) audio="-q:a 0";; + no-audio) audio="-an";; + *) return;; + esac +} + +title() { + _qCol Y S; echo ""; echo "$1":; _qCol z +} + +helpexit() { + echo " + echo convert with ffmpeg: [preset] inputfile [outputfile] + + Pressing q will stop, and you can use the command from the screen. + + Presets do no ask questions: + -low crf:27 abit:64k size:hd480 + -medium crf:23 abit:96k + -high crf:18 abit:128k + -yify + -custom:crf:abit:[resolution] + + -y overwrite existing files + " + exit +} + +# Defaults +resize="" +crf=23 +abit=128 +yify_compression="-c:v libx264 -crf 26 -x264-params cabac=1:ref=5:analyse=0x133:me=umh:subme=9:chroma-me=1:deadzone-inter=21:deadzone-intra=11:b-adapt=2:rc-lookahead=60:vbv-maxrate=10000:vbv-bufsize=10000:qpmax=69:bframes=5:b-adapt=2:direct=auto:crf-max=51:weightp=2:merange=24:chroma-qp-offset=-1:sync-lookahead=2:psy-rd=1.00,0.15:trellis=2:min-keyint=23:partitions=all -c:a aac -ar 44100 -b:a 128k -map 0" +preset="compress" +overwrite="" + +if [[ -z "$1" ]]; then helpexit; fi +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-h" ]] && helpexit + [[ ${!i} = "-low" ]] && { + FORCE=1 + crf=27 + abit=64 + resize="-s hd480" + preset="low" + shift 1 + } + [[ ${!i} = "-med"* ]] && { + FORCE=1 + crf=23 + abit=96 + preset="med" + shift 1 + } + [[ ${!i} = "-high" ]] && { + FORCE=1 + crf=18 + abit=128 + preset="high" + shift 1 + } + [[ ${!i} = "-yify" ]] && { + FORCE=1 + YIFY_PRESET=1 + preset="yify" + shift 1 + } + [[ ${!i} = "-custom"* ]] && { + FORCE=1 + options="${!i}" + option_array=( ${options//:/ } ) + crf=${option_array[1]} + abit=${option_array[2]} + resize=${option_array[3]} + if [[ -n "$resize" ]]; then + resize="-s $resize" + fi + shift 1 + } + [[ ${!i} = "-y" ]] && { + overwrite="-y" + shift 1 + } +done + +compression="-g 250 -c:v libx264 -crf $crf" +audio="-c:a aac -ar 44100 -b:a ${abit}k -strict -2" +if [[ "$YIFY_PRESET" -eq 1 ]]; then + compression="$yify_compression" + audio="" +fi +inputfile="$1" +outputfile="$2" +inputname="${inputfile%.*}" +if [[ -z "$outputfile" ]]; then outputfile="${inputname}.${preset}.mp4";fi +if [[ ! -e "$inputfile" ]]; then echo "Inputfile '$inputfile' missing"; exit 1; fi + +ffprobe -hide_banner "$inputfile" + +while true; do + command="ffmpeg-parser -i \"$inputfile\" $resize $compression $audio -movflags faststart $overwrite \"$outputfile\"" + if [[ $FORCE -eq 1 ]]; then + choice=EXECUTE + else + title Command + echo "# $command" + echo "" + get_choice EXECUTE resize compression audio + fi + + case $choice in + EXECUTE) + title Execute + echo "# $command" + echo "" + echo -n "Frames: " + ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 "$inputfile" + echo "" + setterm --linewrap off + trap "setterm --linewrap on" 0 1 9 15 + eval "$command" + ec=$? + setterm --linewrap on + echo "# $command" + exit $? + ;; + + resize) get_resize;; + compression) get_compression;; + audio) get_audio;; + + *) exit;; + esac +done + diff --git a/bin/2fa-menu b/bin/2fa-menu new file mode 120000 index 0000000..38049b4 --- /dev/null +++ b/bin/2fa-menu @@ -0,0 +1 @@ +../shell/2fa-menu \ No newline at end of file diff --git a/bin/2fa-view b/bin/2fa-view deleted file mode 120000 index f263e72..0000000 --- a/bin/2fa-view +++ /dev/null @@ -1 +0,0 @@ -../shell/2fa-view \ No newline at end of file diff --git a/bin/ffmpeg-magic b/bin/ffmpeg-magic new file mode 120000 index 0000000..6f851da --- /dev/null +++ b/bin/ffmpeg-magic @@ -0,0 +1 @@ +../av/ffmpeg-magic \ No newline at end of file diff --git a/bin/ffmpeg-parser b/bin/ffmpeg-parser new file mode 120000 index 0000000..196a901 --- /dev/null +++ b/bin/ffmpeg-parser @@ -0,0 +1 @@ +../av/ffmpeg-parser \ No newline at end of file diff --git a/bin/ffmpeg-screencapture b/bin/ffmpeg-screencapture new file mode 120000 index 0000000..f92b741 --- /dev/null +++ b/bin/ffmpeg-screencapture @@ -0,0 +1 @@ +../av/ffmpeg-screencapture \ No newline at end of file diff --git a/bin/rclone-mount b/bin/rclone-mount new file mode 120000 index 0000000..c6a8a2a --- /dev/null +++ b/bin/rclone-mount @@ -0,0 +1 @@ +../web/rclone-mount \ No newline at end of file diff --git a/bin/ssh-nosave b/bin/ssh-nosave new file mode 120000 index 0000000..1594100 --- /dev/null +++ b/bin/ssh-nosave @@ -0,0 +1 @@ +../web/ssh-nosave \ No newline at end of file diff --git a/bin/ssh-scan-lan b/bin/ssh-scan-lan new file mode 120000 index 0000000..b3d7a00 --- /dev/null +++ b/bin/ssh-scan-lan @@ -0,0 +1 @@ +../web/ssh-scan-lan \ No newline at end of file diff --git a/bin/sshfs-mount b/bin/sshfs-mount new file mode 120000 index 0000000..65a915a --- /dev/null +++ b/bin/sshfs-mount @@ -0,0 +1 @@ +../web/sshfs-mount \ No newline at end of file diff --git a/bin/video-compress b/bin/video-compress new file mode 120000 index 0000000..f87ed88 --- /dev/null +++ b/bin/video-compress @@ -0,0 +1 @@ +../av/video-compress \ No newline at end of file diff --git a/shell/2fa-menu b/shell/2fa-menu new file mode 100755 index 0000000..5656e41 --- /dev/null +++ b/shell/2fa-menu @@ -0,0 +1,101 @@ +#!/bin/bash +_help() { + echo ' +2FA code viewer + +Usage: +- Create a (possible encrypted) folder for your service keys +- When the service presents you the QRCode, you typically can + view the actual code as text. It looks something like: + I5XXIY3IMEWCA3LBMRSSA6LPOUQGY33PNMQQU=== +- Store this piece of text in a file: "Service name".key +- Run 2fa-menu command in the folder + +Viewer: +- Codes and service names are printed on the screen + + ' + + exit +} + +for (( i=1; i<=$#; i++ )); do + value=${!i} + j=$(( i + 1 )) + [[ "$value" = "--help" ]] && _help + [[ "${value}" = "-"* ]] && { + [[ "$value" =~ -.*h ]] && { _help; } + continue + } +done + +which oathtool &> /dev/null || { + echo Missing oathtool + exit 1 +} +which smenu &> /dev/null || { + echo Missing smenu + exit 1 +} + +timeadjust=0 +update_frequency=30 + +shopt -s nullglob + +_exit() { + clear + exit +} +_get_code() { + oathtool -b --totp $( cat "$1" | tr -dc '[:print:]' ) +} +_print_codes() { + echo "Refresh" + even=0 + for k in *key; do + name=${k%.key} + name=${name//_/ } + printf "%-${PAD_LENGTH}s [ %s ] \n" \ + "$name" \ + $( _get_code "$k" ) + even=$(( 1 - $even )) + done +} + + +while true; do + clear + PAD_LENGTH=0 + for key in *key; do + name=${key%.key} + if [[ ${#name} -gt $PAD_LENGTH ]]; then + PAD_LENGTH=${#name} + fi + done + + set -e + new_codes=$( _print_codes ) + set +e + codes=$new_codes + i=$(( ( $( date +%s ) - $timeadjust ) % $update_frequency )) + left=$(( $update_frequency - $i )) + when=$( date -d "now + $left seconds" +"%H:%M:%S" ) + selected=$( echo -e "$codes" | smenu \ + -M -m "Codes change in ${left}s at $when" -W $'\n' \ + -t 1 -n 30 -a c:0/2 i:3 \ + ) + + if [[ -z "$selected" ]]; then + exit + fi + if [[ "$selected" = "Refresh" ]]; then + continue + fi + code=$( echo "$selected" | rev | cut -d" " -f2 | rev ) + echo Copying $code to clipboard + echo -n $code | xclip -selection clipboard 2> /dev/null + echo -n $code | xclip 2> /dev/null + countdown -n 15 + exit +done diff --git a/shell/2fa-view b/shell/2fa-view deleted file mode 100755 index e4852b2..0000000 --- a/shell/2fa-view +++ /dev/null @@ -1,288 +0,0 @@ -#!/bin/bash -_help() { - echo ' -2FA code viewer - -Usage: -- Create a (possible encrypted) folder for your service keys -- When the service presents you the QRCode, you typically can - view the actual code as text. It looks something like: - I5XXIY3IMEWCA3LBMRSSA6LPOUQGY33PNMQQU=== -- Store this piece of text in a file: "Service name".key -- Run 2fa-view command in the folder - -Viewer: -- Codes and service names are printed on the screen -- The upper timer shows the 30 second interval when - the code changes -- The lower timer shows time left before the program exits - -Options: - --light to disable animations - --color X to prefer a color for the skull. Choose color from: - k r g b c m y w K R G B C M Y W - ' - - - exit -} - -for (( i=1; i<=$#; i++ )); do - value=${!i} - j=$(( i + 1 )) - [[ "$value" = "--help" ]] && _help - [[ "$value" = "--light" ]] && { LIGHT=1; continue; } - [[ "$value" = "--color" ]] && { - TFA_COLOR_TITLE="${!j}" - ((i++)) - continue; - } - [[ "${value}" = "-"* ]] && { - [[ "$value" =~ -.*h ]] && { _help; } - continue - } -done - -which oathtool &> /dev/null || { - echo Missing oathtool - exit 1 -} - -timeadjust=0 -update_frequency=30 -exit_timeout=120 -if [ "$1" = "light" ]; then LIGHT=1; fi -shopt -s nullglob -. qolop - -picture=" ..;;;::;:;::;;:;,. - .:::::;;;:;:::::::;;;;::;:;:. - .::::;;::;::;:;::::::::::;:;::;::. - ,::::;:::::::;;;::;:;;::::;::::::::. - ';:::::::::::::::;;;;;;;:::;;::;;;:.. - ';;::::',',',;:::;:;;;,'.;::::;;:;... - .'. . .,:,.. .. - ' @ .::;:;. @ .. - .:' .;: .::. .::. - ..,;;;:;:;;:' ,;,'';;,..,,' - . .;;. .. .,::, . - ';:;;;::;;:::..::: - ,:::...;:. . .,, - .. -" -picture_colors="kkKKwwbm$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE$TFA_COLOR_TITLE" -picture_color_codes=() -for x in $( seq 0 $(( ${#picture_colors} - 1 )) ); do - picture_color_codes+=( $( _qCol ${picture_colors:$x:1} ) ) -done -picture_eye=$( _qCol R ) - -_skull() { - if [ "$LIGHT" = 1 ]; then return; fi - _qPos abs 4 1 - for x in $( seq 1 ${#picture} ); do - chr="${picture:$x:1}" - if [ "$chr" = " " ]; then _qPos right 1; continue; fi - color="${picture_color_codes[$(( $RANDOM % ${#picture_colors} ))]}" - if [ "$chr" = "@" ]; then color="$picture_eye"; chr="o"; fi - printf "%s%s" \ - "$color" \ - "$chr" - done - - _qCol z -} - -flame=( ) -for x in $( seq 1 $(( 44 * 8 )) ); do - flame+=( 0 ) -done -flame_mask_array=( \ - "11111000000000000000000000000000000000001111" \ - "11000000000000000000000000000000000000000001" \ - "00000000000000000000000000000000000000000000" \ - "00000000000000000000000000000000000000000000" \ - "10000000000001110110111111001110000000000001" \ - "11100000010111111111011110111101100000000111" \ - "11100000011111111111111111111111110000000111" \ - "11110001111111111111111111111111110100011111" \ -) -flame_mask=( ) -for y in {0..7}; do - for x in {0..43}; do - flame_mask+=( ${flame_mask_array[$y]:$x:1} ) - done -done -flame_chars=" .:##@" -flame_colors="zzzmryYY" -flame_color_codes=() -for x in $( seq 0 $(( ${#flame_colors} - 1 )) ); do - flame_color_codes+=( $( _qCol ${flame_colors:$x:1} ) ) -done -flame_next=$( _qPos right 1 ) -flame_corner=$( _qPos abs 1 1 ) - -_flames() { - if [ "$LIGHT" = 1 ]; then return; fi - width=43 - height=7 - seq_x=$( seq 0 $width ) - for x in $seq_x; do - pos=$(( ( $height ) * ( $width + 1 ) + $x )) - flame[$pos]=$(( $RANDOM % 256 )) - done - - for y in $( seq 0 $(( $height - 1 )) ); do - for x in $seq_x; do - if [ $x -eq $width ]; then continue; fi - if [ $x -eq 0 ]; then continue; fi - pos=$(( $y * ( $width + 1 ) + $x )) - pos_under=$(( ( $y + 1 ) * ( $width + 1 ) + $x )) - new_value=$(( ( ( \ - ${flame[$(( $pos_under ))]} + \ - ${flame[$(( $pos_under + 1 ))]} + \ - ${flame[$(( $pos_under - 1 ))]} \ - ) * 17 ) / 48 )) - - if [ $new_value -gt 255 ]; then - new_value=255 - fi - flame[$pos]=$new_value - done - done - flame_buffer="${flame_corner}${flame_next}${flame_next}${flame_next}" - for x in $( seq 0 $(( ( $width + 1 ) * ( $height + 1 ) - 1 )) ); do - if [ $(( $x % ( $width + 1 ) )) -eq $width ]; then flame_buffer+="\n${flame_next}${flame_next}${flame_next}"; fi - if [ ${flame_mask[$x]} -eq 1 ]; then flame_buffer+="${flame_next}"; continue; fi - print_value=$(( flame[$x] / 32 )) - flame_buffer+="${flame_color_codes[${print_value}]}${flame_chars:${print_value}:1}" - done - printf "$flame_buffer" - _qPos abs 1 1 -} - -_stars() { - if [ "$LIGHT" = 1 ]; then return; fi - wall_chars=" ,.'\`" - wall_colors="kkkKKKzzzr" - wall_color_codes=() - for x in $( seq 0 $(( ${#wall_colors} - 1 )) ); do - wall_color_codes+=( $( _qCol ${wall_colors:$x:1} ) ) - done - - lines=$(( 25 + $KEY_FILES )) - _qPos abs 1 1 - seq_x=$( seq 1 50 ) - for y in $( seq 1 $lines ); do - for x in $seq_x; do - chr="${wall_chars:$(( $RANDOM % ${#wall_chars} )):1}" - if [ "$chr" = " " ]; then _qPos right 1; continue; fi - color="${wall_color_codes[$(( $RANDOM % ${#wall_colors} ))]}" - printf "%s%s" \ - "$color" \ - "$chr" - done - printf "\n" - done - _qCol z -} - -_exit() { - clear - _skull - _qPos down 4 - exit -} -_get_code() { - oathtool -b --totp $( cat "$1" | tr -dc '[:print:]' ) -} -_print_codes() { - #~ printf 'L%.0s' $( seq -1 $update_frequency ) - if [ "$LIGHT" = 1 ]; then - _qPos abs 3 1; - else - _qPos abs 20 1 - fi - even=0 - for k in *key; do - if [ $even -eq 0 ]; then - _qCol z Y - else - _qCol z G - fi - name=${k%.key} - name=${name//_/ } - _qPos right 6 - printf "[ %s ]%s %s \n" \ - $( _get_code "$k" ) \ - $( _qCol s ) \ - "$name" - even=$(( 1 - $even )) - done - _qCol z -} - -_show_clock() { - for foo in {1..1}; do - i=$(( ( $( date +%s ) - $timeadjust ) % $update_frequency )) - l=$(( $update_frequency - $i )) - if [ "$LIGHT" = 1 ]; then - _qPos abs $(( $KEY_FILES + 5 )) 1 - else - _qPos abs $(( $KEY_FILES + 22 )) 1 - fi - _qCol CLREND z g;_qPos right 5; date - # seconds to code update - _qCol z g - printf "\r"; _qPos right 4; printf "["; - printf ' %.0s' $( seq 1 $update_frequency ) - printf ']\r'; _qPos right 5 - if [ $l -gt 8 ]; then _qCol z G; - elif [ $l -gt 3 ]; then _qCol z Y; - else _qCol z R; - fi - printf '=%.0s' $( seq 1 $l ) - _qCol z c - # seconds to this client shutdown - left=$(( $update_frequency * ( $exit_timeout - $SECONDS ) / $exit_timeout )) - printf "\n\r"; _qPos right 4; printf "["; - printf ' %.0s' $( seq 1 $update_frequency ) - printf ']\r'; _qPos right 5 - if [ $left -gt 8 ]; then _qCol z c; - elif [ $left -gt 3 ]; then _qCol z y; - else _qCol z r; - fi - printf '#%.0s' $( seq 1 $left ) - _qCol z; printf "\n" - _qCol CLREND - # die if key pressed, or client open for exit_timeout length - wait_for_second=$SECONDS - for i in {1..10}; do - read -t 0.1 foo - [[ "$?" -eq 0 ]] && _exit - [[ $SECONDS -gt $exit_timeout ]] && _exit - _flames - if [ $SECONDS -gt $wait_for_second ]; then break; fi - done - done -} -if [ -f "$1" ]; then _get_code "$1"; _get_code "$1" | xclip; exit; fi -for key in *key; do ((KEY_FILES++)); done -clear -_print_codes -_skull -_flames; _flames; _flames; _flames - -while :;do - set -e - new_codes=$( _print_codes ) - set +e - [[ ! "$new_codes" = "$codes" ]] && { - # print codes only if they change, this keeps selection in shell intact - _stars - codes=$new_codes - echo -e "$codes" - echo '' - } - _show_clock -done diff --git a/web/rclone-mount b/web/rclone-mount new file mode 100755 index 0000000..74b774c --- /dev/null +++ b/web/rclone-mount @@ -0,0 +1,129 @@ +#!/bin/bash + + +_helpexit() { + printf "Usage: %s [-u] remote:[path] + + remote from rclone config. Note, all remotes end with : + -u will unmount all FUSE.rclone paths if no path given! + +" "$( basename $0 )" + echo Current mounts: + cat /proc/mounts | grep fuse.rclone | awk '{ print $1 "\t" $2 }' + exit 1 +} + +_menu() { + echo Current mounts: + cat /proc/mounts | grep fuse.rclone | awk '{ print $1 "\t" $2 }' + _askpass + choice=$( rclone --ask-password=false listremotes | \ + sort | \ + smenu -t 1 -a c:0/2 i:3 -n 25 -m "Select remote" \ + -N -D n:1 i:1 ) + + echo $choice + if [[ -z "$choice" ]]; then + _helpexit + fi + remote="$choice" +} + +_askpass() { + if [[ -n "$RCLONE_CONFIG_PASS" ]]; then + # password already set + return + fi + if grep -q ^"# Encrypted rclone configuration File" "$HOME/.config/rclone/rclone.conf"; then + read -s -p "rclone config password: " RCLONE_CONFIG_PASS + export RCLONE_CONFIG_PASS + else + RCLONE_CONFIG_PASS="not-required" + true + fi +} + +_check_mount() { + if [[ -d "$valid_name" ]]; then + device1=$( stat -c "%d" ~/mnt/"$valid_name" ) + device2=$( stat -c "%d" ~/mnt ) + else + device1=valid + device2=valid + fi +} + +[[ -z "$1" ]] && _menu +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-h" ]] && _helpexit + [[ ${!i} = "--help" ]] && _helpexit +done +unmount=false +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-u" ]] && { unmount=true; continue; } + if [[ -z "$remote" ]]; then + remote="${!i}" + fi +done + +mkdir -p ~/mnt + +valid_name=$( echo "${remote%:}" | sed -e 's/:/-/g' | sed -e 's/[^A-Za-z0-9._@-]//g' ) + +if [[ "$unmount" = true ]]; then + cd ~/mnt + if [[ -n "$remote" ]]; then + fusermount -u -z ~/mnt/$valid_name + rmdir --ignore-fail-on-non-empty ~/mnt/$valid_name &>/dev/null + else + # no path, unmount all + cat /proc/mounts | grep fuse.rclone | awk '{ print $2 }' | while read dir; do + echo Unmounting $dir + fusermount -u -z "$dir" + rmdir --ignore-fail-on-non-empty "$dir" &>/dev/null + done + fi + exit +fi + +if [[ -z "$remote" ]]; then + echo "No remote given" + _helpexit +fi + +_check_mount +tries=20 + +if [[ $device1 = $device2 ]]; then + echo "Mounting $remote in ~/mnt/$valid_name" + _askpass + cd ~/mnt + mkdir -p "$valid_name" + + echo "" + rclone --ask-password=false mount --daemon "${remote}" ~/mnt/"$valid_name" + if [[ $? -gt 0 ]]; then + tries=1 + fi + echo "Waiting for mount to finish ..." +else + echo "~/mnt/$valid_name is already mounted" + exit +fi + +while [[ $tries -gt 0 ]]; do + read -t 1 -n 1 key + _check_mount + if [[ $device1 = $device2 ]]; then + tries=$(( $tries - 1 )) + else + df -h "$valid_name" + exit 0 + fi +done + +echo $remote was not mounted +fusermount -u -z ~/mnt/$valid_name &>/dev/null +rmdir --ignore-fail-on-non-empty ~/mnt/$valid_name &>/dev/null +exit 1 + diff --git a/web/ssh-nosave b/web/ssh-nosave new file mode 100755 index 0000000..a554933 --- /dev/null +++ b/web/ssh-nosave @@ -0,0 +1,9 @@ +#!/bin/bash + +if [[ "$1" = "-h" ]]; then + echo "Set options to not use host key checking, and pass all commands to 'ssh'." + exit +fi + + +exec ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$@" diff --git a/web/ssh-scan-lan b/web/ssh-scan-lan new file mode 100755 index 0000000..21d2a99 --- /dev/null +++ b/web/ssh-scan-lan @@ -0,0 +1,71 @@ +#!/bin/bash + +if [[ "$1" = "-h" ]]; then + echo This scans your LAN for SSH servers. + echo 'Requires: nmap; ifconfig or nmcli' + exit +fi + +PATH=$PATH:/sbin/:/usr/sbin + +which nmap &>/dev/null || { + echo nmap required + exit 1 +} + +which ifconfig &>/dev/null && { + IPLIST="ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*'" + ifmask() { + ifconfig | grep $1 | \ + grep -Eo 'netmask (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' + } +} +which nmcli &>/dev/null && { + IPLIST="nmcli | grep -Eo 'inet4 ([0-9]*\.){3}[0-9]*'" + ifmask() { + cidr=$( nmcli | grep 'inet4 ' | grep $1 | \ + grep -Eo '([0-9]*$)' ) + value=$(( 0xffffffff ^ ((1 << (32 - $cidr)) - 1) )) + echo "$(( (value >> 24) & 0xff )).$(( (value >> 16) & 0xff )).$(( (value >> 8) & 0xff )).$(( value & 0xff ))" + } +} +servers_found="" + +while read myip; do + ip_base=$( echo $myip | cut -d. -f 1-3 ) + + mymask=$( ifmask $myip ) + + IFS=. read -r i1 i2 i3 i4 <<< "$myip" + IFS=. read -r m1 m2 m3 m4 <<< "$mymask" + + f1=$((i1 & m1)) + f2=$((i2 & m2)) + f3=$((i3 & m3)) + f4=$(((i4 & m4)+1)) + l1=$((i1 & m1 | 255-m1)) + l2=$((i2 & m2 | 255-m2)) + l3=$((i3 & m3 | 255-m3)) + l4=$(((i4 & m4 | 255-m4)-1)) + + first_ip="$f1.$f2.$f3.$f4" + last_ip="$l1.$l2.$l3.$l4" + + printf "IP: %15s/%-15s scan: %15s - %-15s\n" \ + "$myip" "$mymask" "$first_ip" "$last_ip" + + while read line; do + if [[ "$line" = "#"* ]]; then continue; fi + ip=$( echo $line | grep -Eo '([0-9]*\.){3}[0-9]*' ) + if [[ $ip = $myip ]]; then continue; fi + ssh_server=$( ssh-keyscan -p 22 -t rsa -T 1 "$ip" 2>&1 | grep ^# | sed 's/:\([0-9]\+\)/ -p \1/' ) + printf -v servers_found "%s%s\n" "$servers_found" "$ssh_server" + done < <( nmap -T5 -sP "$f1-$l1.$f2-$l2.$f3-$l3.$f4-$l4" -oG - ) + +done < <( eval $IPLIST | \ + grep -Eo '([0-9]*\.){3}[0-9]*' | \ + grep -v 127.0.0.1 | \ + grep -v ^172. ) + +echo "SSH Servers found:" +printf "%s" "$servers_found" | sort -n | grep -v "^$" diff --git a/web/sshfs-mount b/web/sshfs-mount new file mode 100755 index 0000000..cc3fbfb --- /dev/null +++ b/web/sshfs-mount @@ -0,0 +1,107 @@ +#!/bin/bash + + +_helpexit() { + printf "Usage: %s [-u] [-nosave] host [path] + + host can be from ssh config + path is the remote path. Defaults to remote home. + -u will unmount all FUSE.sshfs paths if no path given! + -nosave will not use host key checking, nor save it anywhere. potentially dangerous. + +" "$( basename $0 )" + echo Current mounts: + cat /proc/mounts | grep fuse.sshfs | awk '{ print $1 "\t" $2 }' + exit 1 +} + +_menu() { + echo Current mounts: + cat /proc/mounts | grep fuse.sshfs | awk '{ print $1 "\t" $2 }' + choice=$( grep -Pi "^host ([^*]+)$" $HOME/.ssh/config | \ + sed 's/host //i' | tr ' ' '\n' | sort | \ + smenu -t 1 -a c:0/2 i:3 -n 25 -m "Select server" \ + -N -D n:1 i:1 ) + + echo $choice + if [[ -z "$choice" ]]; then + _helpexit + fi + host="$choice" +} + + +[[ -z "$1" ]] && _menu +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-h" ]] && _helpexit + [[ ${!i} = "--help" ]] && _helpexit +done +unmount=false +nosave=false +for (( i=1; i<=$#; i++ )); do + [[ ${!i} = "-u" ]] && { unmount=true; continue; } + [[ ${!i} = "-nosave" ]] && { nosave=true; continue; } + if [[ -z "$host" ]]; then + host="${!i}" + else + localpath="${!i}" + fi +done + +mkdir -p ~/mnt + +valid_name=$( echo "$host" | sed -e 's/[^A-Za-z0-9._@-]//g') +path=:"${localpath}" + +if [[ "$nosave" = true ]]; then + NOSAVE="-o ssh_command='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'" +fi + +if [[ "$unmount" = true ]]; then + cd ~/mnt + if [[ -n "$host" ]]; then + fusermount -u -z ~/mnt/$valid_name + rmdir --ignore-fail-on-non-empty ~/mnt/$valid_name &>/dev/null + else + # no path, unmount all + cat /proc/mounts | grep fuse.sshfs | awk '{ print $2 }' | while read dir; do + echo Unmounting $dir + fusermount -u -z "$dir" + rmdir --ignore-fail-on-non-empty "$dir" &>/dev/null + done + fi + exit +fi + +if [[ -z "$host" ]]; then + echo "No host given" + _helpexit +fi + +if [[ -d "$valid_name" ]]; then + device1=$( stat -c "%d" "$valid_name" ) + device2=$( stat -c "%d" . ) +else + device1=valid + device2=valid +fi + +if [[ $device1 = $device2 ]]; then + echo "Mounting $valid_name$path in ~/mnt/$valid_name" + cd ~/mnt + mkdir -p "$valid_name" + + eval sshfs \ + -o reconnect \ + -o ServerAliveInterval=45,ServerAliveCountMax=2 \ + $NOSAVE \ + -o follow_symlinks "$valid_name$path" "$valid_name" + if [[ $? -gt 0 ]]; then + rmdir "$valid_name" + else + df -h "$valid_name" + fi +else + echo "~/mnt/$valid_name is already mounted" +fi +