# Notes: # - Please keep this file working for both BASH and ZSH. :) ## Variables ## # Environment Usability # export PATH="$PATH:~/bin-shared:~/.bin-shared" # PS1 Colors # colors=`tput colors` if (( colors >= 256 )); then export PURPLE="`tput setaf 55`" export ORANGE="`tput setaf 208`" export GREEN="`tput setaf 34`" export RED="`tput setaf 196`" export SCARLET="`tput setaf 160`" export YELLOW="`tput setaf 226`" export GRAY="`tput setaf 243`" elif (( colors == 16 )); then export PURPLE="`tput setaf 13`" export ORANGE="`tput setaf 11`" export GREEN="`tput setaf 02`" export RED="`tput setaf 09`" export SCARLET="`tput setaf 01`" export YELLOW="`tput setaf 11`" export GRAY="`tput setaf 08`" else export PURPLE="`tput setaf 05`" export ORANGE="`tput setaf 03`" export GREEN="`tput setaf 02`" export RED="`tput setaf 05`" export SCARLET="`tput setaf 01`" export YELLOW="`tput setaf 03`" export GRAY="`tput setaf 07`" fi export RESET="`tput sgr0`" export BOLD="`tput bold`" ## Aliases ## # Quickies # alias reload-bash="source ~/.bashrc" alias bash-reload="reload-bash" alias shell-reload="reload-bash" alias reload-shell="reload-bash" alias reload="reload-bash" alias l='ls ' alias ll='ls -alh ' alias lh='ls -ash ' function clones { export CLONES_PATH="$HOME/Clones" if [[ -d /sdcard ]]; then export CLONES_PATH="/sdcard/Library/Clones" fi mkdir -pv "$CLONES_PATH" cd "$CLONES_PATH" } alias cl="clones" alias cdl="cl" alias cdc="cl" alias library="clones && cd .." alias lib="library" # Networking # alias scan="nmap -A -p- --script=vuln " # Trash Related # export TRASH_ROOT="/" if [[ -d "/sdcard" ]]; then # Android / Termux export TRASH_ROOT="/sdcard" else # Desktop export TRASH_ROOT="$HOME" fi export TRASH="$TRASH_ROOT/TRASH" alias trash="cd $TRASH" alias clean-trash="bash -c 'rm -rfv "$TRASH"/*'; clean-trashed" alias trash-clean="clean-trash" alias check-trash='du -h $TRASH | sort -h; find "$TRASH_ROOT" -name .Trash"*" -exec du -h {} \; -exec mv -v {} "$TRASH"/ \;' alias trash-check="check-trash" alias check-trashed='find "$TRASH_ROOT" -name ".trashed*" -exec du -h {} \; -exec mv -v {} "$TRASH"/ \; | sort -h' alias clean-trashed='find "$TRASH_ROOT" -name ".trashed*" -exec du -h {} \; -delete | sort -h' ## Functions ## # Related specifically to this project. # branch="$BRANCH" if [[ -z "$branch" ]]; then branch="$PROD_GIT_BRANCH" fi if [[ -z "$branch" ]]; then branch="dev" fi function update-shared-old { log "`date` - Reloading the '~/.rc_shared' file from env-shared.\n" if [[ -n "$1" ]]; then branch="$1" fi dir="shared-rc-deleteme" git clone https://git.hyperling.com/me/env-shared \ $dir --branch=$branch mv -v $dir/rc_shared.sh ~/.rc_shared local_bin="$HOME/bin" remote_bin="shared-rc-deleteme/bin-shared" if [[ -d "$local_bin" ]]; then log "\n`date` - Found '$local_bin', adding scripts.\n" mv -v $remote_bin/*.sh "$local_bin"/ refactor_script="refactor_music_library.sh" wget -O "$refactor_script" \ https://git.hyperling.com/me/shell-music-refactor-library/raw/branch/main/refactor_music_library.sh mv -v $refactor_script "$local_bin"/ chmod 755 -Rc "$local_bin" else echo "Did not find '$local_bin', did not copy scripts." fi log "\n`date` - Done! Removing git clone.\n" mv -v $dir ~/TRASH/"$dir-`date "+%Y%m%d-%H%M%S"`" log "\n`date` - Complete!" } export PROJECT_SHARED="$HOME/.git-env-shared" function update-shared2 { # Refresh the local env-shared repo. if [[ -d $PROJECT_SHARED ]]; then log "`date` - Updating '$PROJECT_SHARED'.\n" git -C "$PROJECT_SHARED" switch "$branch" git -C "$PROJECT_SHARED" pull --recurse-submodules || \ ( echo "Failed to pull project, exiting." && return 1 ) else log "`date` - Downloading '$PROJECT_SHARED'.\n" git clone https://git.hyperling.com/me/env-shared \ "$PROJECT_SHARED" --branch=$branch fi # Tha main piece! Reload the shared RC file. cp -v "$PROJECT_SHARED"/rc_shared.sh ~/.rc_shared cp -v "$PROJECT_SHARED"/vimrc.vim ~/.vimrc # Copy the scripts from the shared bin to the home bin. local_bin="$HOME/bin" remote_bin="$PROJECT_SHARED/bin-shared" if [[ -d "$local_bin" ]]; then log "\n`date` - Found '$local_bin', adding scripts.\n" cp -v "$remote_bin"/*.sh "$local_bin"/ refactor_script="refactor_music_library.sh" wget -O "$refactor_script" \ https://git.hyperling.com/me/shell-music-refactor-library/raw/branch/main/refactor_music_library.sh mv -v $refactor_script "$local_bin"/ chmod 755 -Rc "$local_bin" else echo "Did not find '$local_bin', did not copy scripts." fi log "\n`date` - Complete!" } alias shared-update2="update-shared2" alias reload-shared2="update-shared2" alias shared-reload2="update-shared2" alias update-shared="update-shared2" alias shared-update="update-shared" alias reload-shared="update-shared" alias shared-reload="update-shared" function reload-all { log "`date` - Reloading BASH" reload-bash log "\n`date` - Reloading Main (Termux/Ansible)" if [[ -d /sdcard ]]; then reload-termux else scm.sh fi reload-bash log "\n`date` - Reloading Shared" reload-shared reload-bash } alias all-reload="reload-all" function update-all { log "`date` - Updating env projects..." reload-all log "\n\n\n`date` - Running system updates..." update -y if [[ $(type -t update-sdk 2>/dev/null 1>&2) ]]; then log "\n\n\n`date` - Updating SDKs..." update-sdk fi log "\n\n\n`date` - Done with update-all!" } alias all-update="update-all" PROJECT_SHARED_OLD01="$HOME/.env-shared" if [[ -d "$PROJECT_SHARED_OLD01" ]]; then log "`date` - Removing old project name '$PROJECT_SHARED_OLD01'." mv -v "$PROJECT_SHARED_OLD01" "$TRASH"/env-shared-deleteme | grep -v ".git" fi # Shortcuts # function send-master { send_master.sh; } alias push-master="send-master" alias sync-master="send-master" function pull-clone { pull_clone.sh; } alias get-clone="pull-clone" alias sync-clone="pull-clone" alias send-sync="send-master" alias push-sync="send-master" alias pull-sync="pull-clone" alias get-sync="pull-clone" function sync-sync { cat <<- EOF Command not implemented, too dangerous guessing the state of the folder. Please use push-sync and pull-sync in the correct order for its current state. EOF } function goodbye { update -y; bye; } function clone { rsync -auPhz --delete --exclude '.gradle' --exclude 'app/build' "$@" } function now { date "+%Y%m%d-%H%M%S"; } function today { date "+%Y%m%d"; } function log { echo -e "$1"; } function blog { echo -e "\n\n$1\n\n"; } function pull { if [[ -d .git ]]; then echo "Recognized git project, pulling latest changes." git pull --recurse-submodules else echo "Not a git repo, attempting to sync with SFTP server." pull-clone fi } function push { if [[ -d .git ]]; then echo "Recognized git project, promoting branch to stage and prod." git push && eval load-stage && eval load-prod else echo "Not a git repo, attempting to sync with SFTP server." send-master fi } function set-variable { var="$1" val="$2" echo -n "export $var=\"$val\" - " export $var="$val" status="$?" if [[ "$val" == "${!var}" ]]; then echo "Success!" return 0 fi echo "Failed with status '$status', $var is '${!var}'." return $status } alias set-var="set-variable " # Enhance Bin Scripts # # MASTER and CLONE folders. function send-masters { echo -e "`date` - Looping through MASTER directories." ls | grep "\-MASTER" | while read dir; do echo -e "\n`date` - Found '$dir'..." cd $dir send-master cd .. done echo -e "\n`date` - Done checking for MASTER directories." } alias push-masters="send-masters" alias sync-masters="send-masters" alias push-all="send-masters" alias send-all="send-masters" function pull-clones { echo -e "`date` - Looping through clone directories." ls | grep "\-clone" | while read dir; do echo -e "\n`date` - Found '$dir'..." cd $dir pull-clone cd .. done echo -e "\n`date` - Done checking for clone directories." } alias get-clones="pull-clones" alias sync-clones="pull-clones" alias pull-all="pull-clones" alias get-all="pull-clones" function sync-all { push-masters echo -e "\n" pull-clones echo -e "\n`date` - Skipping SYNC folders! Please do their loads separately." ls | grep "\-SYNC" | while read dir; do echo "- $dir" done echo -e "`date` - Done.\n" } function sync-all-loop { typeset -i sleep_time count if [[ "$1" ]]; then sleep_time="$1" fi if [[ -z "$sleep_time" || "$sleep_time" == 0 ]]; then sleep_time="300" fi log "`date` - Sleep timer set for '$sleep_time' seconds." count=0 while true; do sync-all count=$(( count + 1 )) echo -e "\n\n*** Sleeping for '$sleep_time' after run# '$count'! ***\n\n" sleep $sleep_time done } # SYNC folders. function send-syncs { echo -e "`date` - Looping through SYNC directories." ls | grep "\-SYNC" | while read dir; do echo -e "\n`date` - Found '$dir'..." cd $dir send-master cd .. done echo -e "\n`date` - Done checking for SYNC directories." } alias push-syncs="send-syncs" function pull-syncs { echo -e "`date` - Looping through SYNC directories." ls | grep "\-SYNC" | while read dir; do echo -e "\n`date` - Found '$dir'..." cd $dir pull-clone cd .. done echo -e "\n`date` - Done checking for SYNC directories." } alias get-syncs="pull-syncs" function sync-syncs { cat <<- EOF Command not implemented, too dangerous guessing the state of the folder. Please use push-syncs and pull-syncs in the correct order for its current state. EOF } # Audio # # Video # # Optimize the bitrate and audio levels for an edited video. function process-video-usage { echo "USAGE: process-video oldFile newFile [videoBitrate] [audioBitrate] [sizeRating] [numPasses] [forceBitrate]" echo -n "Purpose: Call ffmpeg with preferred video posting settings. " echo -n "Bitrates default to 2000k and 192k, size is 720, passes is 1, and force is N." echo "These work well on Odysee and are fairly small as backups." echo "Examples:" echo "- Create a small file for quick streaming." echo " process-video youcut.mp4 20240210.mp4 1200k 128k 480" echo "- Create a larger file for something like YouTube." echo " process-video youcut.mp4 20240210_1080p.mp4 5000k 256k 1080" } function process-video { # Parameters file="$1" newfile="$2" video="$3" audio="$4" size="$5" passes="$6" force="$7" # Validations if [[ -z $file || ! -e $file ]]; then echo "ERROR: Original file '$file' does not exist." >&2 process-video-usage return 1 fi if [[ -z $newfile ]]; then echo "ERROR: New file's name must be provided." >&2 process-video-usage return 1 elif [[ -e $newfile ]]; then echo "ERROR: New file '$newfile' already exists." >&2 du -h "$newfile" process-video-usage return 1 fi echo "`date` - Converting '$file' to '$newfile'." if [[ -z $video ]]; then video="2000k" fi if [[ $force == "Y" ]]; then video="-b:v $video" else video="-b:v $video -minrate 0 -maxrate $video -bufsize $video" fi if [[ -z $audio ]]; then audio="192k" fi audio="-b:a $audio" if [[ -z $size ]]; then size="720" fi # Lanczos is good for upscaling, but this is usually used for downscaling. if [[ ! -z "$PV_VERTICAL_ASPECT_RATIO" ]]; then size="-filter:v scale=$size:-1" #:flags=lanczos" else size="-filter:v scale=-1:$size" #:flags=lanczos" fi if [[ -z $passes ]]; then passes=1 fi pass="" if [[ $passes != 1 || ! -z "$PV_2PASS" ]]; then passes=2 pass="-pass 2" fi v2="" if [[ ! -z "$PV_V2" ]]; then v2="-crf 18 -preset veryslow -c:a aac -ar 48000" #-profile:v high - Letting ffmpeg use the default profile. fi time="" if [[ ! -z "$PV_TIME" ]]; then time="`which time`" fi quiet="-hide_banner -loglevel level+warning" if [[ ! -z "$PV_PROGRESS" ]]; then quiet="-hide_banner -loglevel level+info" fi if [[ ! -z "$PV_VERBOSE" ]]; then quiet="-loglevel level+verbose" fi ## Main ## # More information on two-pass processing with ffmpeg # https://cinelerra-gg.org/download/CinelerraGG_Manual/Two_pass_Encoding_with_FFmp.html if [[ $passes == 2 ]]; then set -x $time ffmpeg -nostdin $quiet \ -i "$file" $size $video $audio \ -filter:a "dynaudnorm=f=33:g=65:p=0.66:m=33.3" \ -vcodec libx264 -movflags +faststart \ $v2 \ -pass 1 -f mp4 /dev/null -y status=$? set +x echo "`date` - Done with the first pass." if [[ $status != 0 ]]; then echo "Received unsuccessful status, exiting." return 1 fi fi set -x && $time ffmpeg -nostdin $quiet \ -i "$file" $size $video $audio \ -filter:a "dynaudnorm=f=33:g=65:p=0.66:m=33.3" \ -vcodec libx264 -movflags +faststart \ $v2 \ $pass "$newfile" status="$?" set +x echo "`date` - Done with the final pass." if [[ $passes == 2 && $status == 0 ]]; then mv -v ffmpeg2pass*.log* ~/TRASH/ fi echo "`date` - Syncing data." sync sleep 10 sync if [[ -s $newfile ]]; then echo "`date` - Getting file sizes." du -h "$file" du -h "$newfile" else echo "ERROR: New file not created or has a 0 size." >&2 fi unset PV_VERTICAL_ASPECT_RATIO unset PV_V2 unset PV_TIME unset PV_PROGRESS unset PV_VERBOSE unset PV_2PASS echo -e "\n`date` - Finished with status '$status'." return $status } alias pv="process-video" alias fn='basename `pwd`' # Folder Name alias qpv='pv ./raw/YouCut*.mp4 `fn`.mp4' # Quick Process Video alias set-pv-vertical='set-var PV_VERTICAL_ASPECT_RATIO "y" ' alias set-pv-time='set-var PV_TIME "y" ' alias set-pv-progress='set-var PV_PROGRESS "y" ' alias set-pv-verbose='set-var PV_VERBOSE "y" ' alias set-pv-2pass='set-var PV_2PASS "y" ' alias set-pv-v2='set-var PV_V2 "y" ' alias set-pv-v2='set-var PV_YIFY "y" ' alias set-pv-vert='set-pv-vertical ' alias set-pv2='set-pv-v2; set-pv-2pass; set-pv-progress ' alias pvs='set-pv-vert; pv ' alias pv2='set-pv2; pv ' alias pv2s='set-pv2; set-pv-vert; pv ' alias pvs2='pv2s ' alias qpv2='set-pv2; qpv ' # 20260713 - Attempt at compressing videos as well as done for torrented movies. function pv-yify { # ffmpeg -i input.mkv -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: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 output.mp4 input="$1" output="$2" ratio="$3" audio="$4" if [[ -z "$input" ]]; then cat <<- EOF Usage: pv-yify INPUT_FILE [OUTPUT_FILE] [ASPECT_RATIO] Examples: - Create file raw.mp4.pv-yify.mp4 without changing aspect ratio: $ pv-yify raw.mp4 - Compress a video and ensures it is 720p: $ pv-yify raw.mp4 compressed.mp4 1280:720 - Compress a video ensuring that it is 720 pixels tall: $ pv-yify raw.mp4 vertical.mp4 720:-1 EOF fi if [[ -z "$output" ]]; then output="$input.pv-yify.mp4" fi echo -e "\n`date` - Converting '$input' with YIFY-like compression to '$output'." if [[ -f "$output" ]]; then echo -e "\n`date` - Moving '$output' to TRASH." mv -v "$output" ~/TRASH/ fi if [[ -n "$ratio" ]]; then if [[ "$ratio" != *":"* ]]; then echo "ERROR: Optional ratio parameter needs to be like '-1:720' (horizontal) or '720:-1' (vertical)". return 1 fi size="-filter:v scale=$ratio" fi if [[ -z "$audio" ]]; then audio="128k" fi echo -e "\n`date` - Starting FFMPEG...\n" set -x ffmpeg -i "$input" \ -hide_banner -loglevel level+warning \ -c:v libx264 -crf 27 -c:a aac -ar 44100 -b:a "$audio" -map 0 \ -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: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 \ $ratio \ "$output" set +x echo -e "\n`date` - Finished FFMPEG." echo -e "\n`date` - Syncing and checking file sizes." sync sleep 5 ls -lh "$input" "$output" echo -e "\n`date` - Done!" } alias yify="pv-yify " alias yify-pv="pv-yify " function set-pv-all { #set-var PV_VERTICAL_ASPECT_RATIO "y" set-var PV_V2 "y" set-var PV_TIME "y" set-var PV_PROGRESS "y" set-var PV_VERBOSE "y" set-var PV_2PASS "y" } alias set-pv-everything='set-pv-all ' function unset-pv-all { unset PV_VERTICAL_ASPECT_RATIO unset PV_V2 unset PV_TIME unset PV_PROGRESS unset PV_VERBOSE unset PV_2PASS } alias unset-pv-everything='unset-pv-all ' # Allow converting video to audio and other smaller # tasks than what process-video is intended to do. function basic-process-usage { echo "basic-process INPUT OUTPUT NORMALIZE [EXTRA]" echo -n "Pass a file through ffmpeg with the option" echo "to easily normalize the audio with a Y." echo "Examples:" echo "- Normalize audio on a video." echo " basic-process video.mp4 normalized.mp4 Y" echo "- Convert a video to audio at 192k." echo " basic-process video.mp4 audio.mp3 N '-b:a 192k'" } function basic-process { # Parameters input="$1" output="$2" typeset -u normalize normalize="$3" extra="$4" echo "`date` - Starting basic-process" # Validations if [[ -z $input || ! -e $input ]]; then echo "ERROR: Input file '$input' does not exist." >&2 basic-process-usage return 1 fi if [[ -z $output ]]; then echo "ERROR: Output file's name must be provided." >&2 basic-process-usage return 1 elif [[ -e $output ]]; then echo "ERROR: Output file '$output' already exists." >&2 du -h "$output" basic-process-usage return 1 fi if [[ $normalize == "Y" ]]; then echo "Normalize set to TRUE." normal="-filter:a dynaudnorm=f=33:g=65:p=0.66:m=33.3" else echo "No audio normalization is being done." fi # Main echo "`date` - Converting '$input' to '$output'." set -x ffmpeg -nostdin -hide_banner -loglevel quiet \ -i "$input" $extra $normal "$output" status=$? set +x if [[ $status != 0 ]]; then echo "`date` - WARNING: ffmpeg exited with status '$status'." >&2 fi # Finish if [[ ! -s $output ]]; then echo "`date` - ERROR: Output '$output' not created or has 0 size." >&2 return 1 fi sync echo "`date` - '$output' has been created successfully." sleep 3 du -h "$input" du -h "$output" echo "`date` - Finished basic-process" return 0 } alias bp="basic-process" alias bv="bp" # Function to automatically append the Y. function fix-audio { basic-process "$1" "$2" Y; } function eq { fix-audio "$1" "$2"; } # Function to easily turn a video to audio without typing the audio name. function convert-to-audio { video="$1" audio="$2" if [[ -z "$2" || "$2" == "Y" ]]; then audio="${1//mp4/mp3}" fi volume="" if [[ ( "$2" == "Y" && -z "$3" ) || "$3" == "Y" ]]; then $volume="Y" fi basic-process "$video" "$audio" $volume } alias v2a="convert-to-audio" alias vta="v2a" alias pull-audio="v2a" alias pa="v2a" # Run all the different codecs, wrappers, bitrates, etc on a file. function pv-test { echo -e "Welcome! Let's get your tests set up...\n" ## Input ## filename_in="$1" while [[ -z "$filename_in" ]]; do read -p "Please provide the filename: " filename_in done # Existing Functions # declare -l pv{,2}="n" typeset -l test_pv{,2} read -p "Create a test with current 'pv' function? (y/N) " test_pv if [[ "$test_pv" == "y"* ]]; then pv="y" echo " great, will run pv" else echo " skipping" fi echo " " read -p "Create a test with current 'pv2' function? (y/N) " test_pv2 if [[ "$test_pv2" == "y"* ]]; then pv2="y" echo " great, will run pv2" else echo " skipping" fi echo " " # Loop Data # declare -l {vpreset,crf,vbr,abr,achannel,afreq,over,under}s="default" echo -e " * * Loop Settings * * \n" basename="`basename "$filename_in"`" basename_in="" read -p "Would you like to specify a basename? [$basename] " basename_in if [[ -n "$basename_in" ]]; then basename="$basename_in" fi echo -e " basename: '$basename'\n" typeset -i size_in read -p "What display ratio would you like to use? [720] " size_in if (( $size_in == 0 )); then size_in=720 fi echo -e " size: '$size_in'\n" vcodecs="libsvtav1" read -p "Please provide the video codecs to test: [$vcodecs] " test_vcodecs if [[ -n "$test_vcodecs" ]]; then vcodecs="$test_vcodecs" fi echo -e " video codecs: '$vcodecs'\n" echo "Please provide any presets to use." echo "(if testing both H264 and AV1, please select from: vsl slr slw med fst fsr vft)" read -p "Add any presets? [$vpresets] " test_vpresets if [[ -n "$test_vpresets" ]]; then vpresets="$test_vpresets" fi echo -e " presets: '$vpresets'\n" read -p "Please provide any CRFs to test: [default] " test_crfs if [[ -n "$test_crfs" ]]; then crfs="$test_crfs" fi echo -e " CRFs: '$crfs'\n" read -p "Please provide any video bitrates to test: [default] " test_vbrs if [[ -n "$test_vbrs" ]]; then vbrs="$test_vbrs" fi echo -e " video bitrates: '$vbrs'\n" if [[ "$vcodecs" == *"libsvtav1"* ]]; then read -p "Please provide any over percentages for libsvtav1: [default] " test_overs if [[ -n "$test_overs" ]]; then overs="$test_overs" fi echo -e " over percentages: '$overs'\n" read -p "Please provide any under percentages for libsvtav1: [default] " test_unders if [[ -n "$test_unders" ]]; then unders="$test_unders" fi echo -e " under percentages: '$unders'\n" fi acodecs="libopus" read -p "Please provide the audio codecs to test: [$acodecs] " test_acodecs if [[ -n "$test_acodecs" ]]; then acodecs="$test_acodecs" fi echo -e " audio codecs: '$acodecs'\n" read -p "Please provide any audio bitrates to test: [default] " test_abrs if [[ -n "$test_abrs" ]]; then abrs="$test_abrs" fi echo -e " audio bitrates: '$abrs'\n" read -p "Test both mono and stereo audio channels? (y/N) " test_achannels if [[ "$test_achannels" == "y"* ]]; then achannels="1 2" # 1 is mono, 2 is stereo fi echo -e " audio channels: '$achannels'\n" read -p "Test both 48 and 44.1 kHz audio frequencies? (y/N) " test_afreqs if [[ "$test_afreqs" == "y"* ]]; then afreqs="48000 44100" # 48 typically found in videos, 44.1 in audio. fi echo -e " audio frequencies: '$afreqs'\n" normalize="" typeset -u normalize_in read -p "Would you like to normalize the audio? (Y/n) " normalize_in if [[ "$normalize_in" != "N"* && $normalize_in != "F"* ]]; then normalize="-filter:a dynaudnorm=f=33:g=65:p=0.66:m=33.3" fi echo -e " normalize: '$normalize'\n" exts="mkv" read -p "Please provide the containers to test with: [$exts] " test_exts if [[ -n "$test_exts" ]]; then exts="$test_exts" fi echo -e " extensions: '$exts'\n" bitrate_check="" typeset -u bitrate_in read -p "Would you like to append the bitrate to the filename? (Y/n) " bitrate_in if [[ "$bitrate_in" != "N"* && $bitrate_in != "F"* ]]; then bitrate_check="Y" fi echo -e " bitrate_check: '$bitrate_check'\n" # Final Check # echo -e " * * Confirmation * * \n" ffprobe -i "$filename_in" echo -e "\n * Testing against '$filename_in' above. * \n" typeset -i guess guess=$(( \ `wc -w <<< $vcodecs` * `wc -w <<< $vpresets` * `wc -w <<< $crfs` \ * `wc -w <<< $vbrs` * `wc -w <<< $overs` * `wc -w <<< $unders` \ * `wc -w <<< $acodecs` * `wc -w <<< $abrs` \ * `wc -w <<< $achannels` * `wc -w <<< $afreqs` \ )) if (( $guess > 0 )); then plural="" if (( $guess != 1 )); then plural="s" fi echo -e "Expecting ${guess} loop${plural}.\n" else echo "ERROR: Something went wrong, got 0 loops, exiting." return 1 fi typeset -u okay read -p "Does everything look okay? (Y/n) " okay if [[ "$okay" == "N" || "$okay" == "F"* ]]; then echo " Not okay, aborting." return 1 fi echo -e "Sounds good, here we go!\n" ## Main ## if [[ "$pv" == "y" ]]; then echo " * Starting pv... * " filename_pv="$(basename "${filename_in}-pv.mkv")" if [[ -e "$filename_pv" ]]; then mv -v "$filename_pv" ~/TRASH fi pv "$filename_in" "$filename_pv" echo -e " * Testing with 'pv' complete! * \n" fi if [[ "$pv2" == "y" ]]; then echo " * Starting pv2... * " filename_pv2="$(basename "${filename_in}-pv2.mkv")" if [[ -e "$filename_pv2" ]]; then mv -v "$filename_pv2" ~/TRASH fi pv "$filename_in" "$filename_pv2" echo -e " * Testing with 'pv2' complete! * \n" fi typeset -i count unset vcodec vpreset crf vbr acodec abr ext for vcodec in $vcodecs; do for vpreset in $vpresets; do for crf in $crfs; do for vbr in $vbrs; do for over in $overs; do for under in $unders; do for acodec in $acodecs; do for abr in $abrs; do for achannel in $achannels; do for afreq in $afreqs; do for ext in $exts; do (( count++ )) preset="" if [[ "$vpreset" != "default" ]]; then if [[ "$vcodec" == "libsvtav1" ]]; then # Scale goes from 0 to 13, 0 being like x264's "placebo", above 8 being destructive. # Each step down is about 2x as slow, so 6 to 4 is ~4x slower, 6 to 2 is ~16x slower. case $vpreset in vsl) preset="2" ;; # best quality; quite slow, but still feasible slr) preset="3" ;; # nearly the same as 4 but twice as slow slw) preset="4" ;; # great quality; decent speed med) preset="6" ;; # good quality; great speed fst) preset="8" ;; # default; quite fast fsr) preset="9" ;; # rough drafts only vft) preset="10" ;; # real time encoding for livestreams *) preset="$vpreset" ;; esac else case $vpreset in vsl) preset="veryslow" ;; slr) preset="slower" ;; slw) preset="slow" ;; med) preset="medium" ;; # default fst) preset="fast" ;; fsr) preset="faster" ;; vft) preset="veryfast" ;; *) preset="$vpreset" ;; esac fi if [[ -n "$preset" ]]; then preset="-preset $preset" fi fi bitrate="" if [[ "$crf" != "default" ]]; then bitrate="-crf $crf" fi declare {o,u}fn="" if [[ "$vbr" != "default" ]]; then if [[ "$vcodec" == "libsvtav1" ]]; then #bitrate="$bitrate -svtav1-params rc=1:tbr=$vbr:overshoot-pct=0:undershoot-pct=100" declare {o,u}="" if [[ "$over" != "default" ]]; then o=":overshoot-pct=${over}" ofn="-o${over}" fi if [[ "$under" != "default" ]]; then u=":undershoot-pct=${under}" ufn="-u${under}" fi if [[ "$crf" == "default" ]]; then # Variable Bitrate, rate passed should be the TARGET, not the max. # Can control the params which allow the target to go up and down, but defaults are okay. # Seems better to not try and force this to imitate "-b:v -minrate -maxrate -bufsize" # Will definitely want to use a slower preset to ensure the quality stays consistent. # Can be done like this if not changing over or under. #bitrate="-b:v $vbr" # Prefer this method so we know we're doing VBR (rc=1) not CBR (rc=2). # rc=1, over defaults to 25, under defaults to 50 # rc=2, over defaults to 25, under defaults to 25 bitrate="-svtav1-params rc=1:tbr=${vbr}${o}${u}" else # Capped CRF, when both a CRF and bitrate are passed. # Better to use high CRF with low BR than high BR with low CRF (produces artifacts/tearing). bitrate="-svtav1-params rc=0:crf=${crf}:mbr=${vbr}${o}${u}" fi else bitrate="$bitrate -b:v $vbr -minrate 0 -maxrate $vbr -bufsize $vbr" fi fi fidelity="" if [[ "$abr" != "default" ]]; then fidelity="-b:a $abr" fi channel="" if [[ "$achannel" != "default" ]]; then channel="-ac $achannel" fi frequency="" if [[ "$afreq" != "default" ]]; then if [[ "$acodec" == "libopus" ]]; then case $afreq in 48000) ;; 24000) ;; 16000) ;; 12000) ;; 8000) ;; *) echo -e "ERROR: libopus cannot do this, skipping frequency ${afreq}." continue ;; esac fi frequency="-ar $afreq" fi ext=".$ext" newfile="_${size_in}p_${vcodec}-${vpreset}-${crf}-${vbr}${ofn}${ufn}_${acodec}-${abr}-${achannel}-${afreq}${ext}" echo -e " * [Loop $count/$guess] Parameters for this test: $newfile * \n" if [[ -e "$newfile" ]]; then mv -v "$newfile" ~/TRASH/ echo -e "Removed unfinished version of this file.\n" fi if [[ `ls *"${newfile//$ext/}"*"$ext"* 2>/dev/null` ]]; then echo -e "Completed version of this file already exists, skipping.\n" continue fi echo "$(date) - Starting run..." start=$SECONDS set -x ffmpeg -nostdin -hide_banner -loglevel level+warning \ -i "$filename_in" \ -filter:v scale=-1:$size_in \ -c:v $vcodec \ $preset \ $bitrate \ -c:a $acodec \ $fidelity \ $channel \ $frequency \ $normalize \ -movflags +faststart \ "$newfile" status="$?" set +x finish=$SECONDS minutes=$(( (finish - start) / 60 )) seconds=$(( (finish - start) % 60 )) printf -v padded_mins "%03d" "$minutes" printf -v padded_secs "%02d" "$seconds" bitrate=""; bitrate_padded=""; if [[ "$bitrate_check" == "Y" ]]; then j="" for i in `ffprobe "$newfile" 2>&1 | grep bitrate`; do # Previous loop was the 'bitrate' label, so get bitrate now. if [[ "$j" == 'bitrate:' ]]; then bitrate="$i" #printf -v bitrate_padded "%05d" "$bitrate" fi # Previous loop was the bitrate, so get the units now. if [[ "$j" == "$bitrate" ]]; then #bitrate="_""$bitrate_padded${i//\//p}" bitrate="_""$bitrate${i//\//p}" fi j="$i" done fi if [[ "$status" == 0 && -s "$newfile" ]]; then mv -v "$newfile" "${basename}_${padded_mins}m${padded_secs}s${newfile//$ext/$bitrate$ext}" else echo "ERROR: ffmpeg exited with status $status, not renaming file." fi echo -e "\n * Finished in ${minutes}m${seconds}s! * \n" done done done done done done done done done done done ## Finish ## plural="" if (( $count != 1 )); then plural="s" fi echo "Completed tests after ${count} loop${plural}! :)" } # 20260914 - After playing with pv-test for a while. function pv3 { org="$1" out="$2" echo "Processing '$org' to '$out' using AV1 and OPUS." ## Versions ## # v0 scale=-1:720 preset=0 # took 20 minutes where preset 2 was under 2 minutes crf=18 vbr=2000k # this is creating 2Mbps files, not maxxing at 2. overshoot=0 undershoot=100 # v1 preset=2 # WAY faster and still great. crf=28 # much smaller filesize # v2 # Try to mimic h264 better. vbr=1000k overshoot=100 # v3 # Fix weird aspect ratios. scale="1280:720,setsar=1" # v4 # Assuming it was just a weird Pixel phone video issue. scale=-1:720 # Remove CRF since we are using VBR. crf="" # Still not 100% on these but seems like a good ballpark. preset=6 vbr=800k overshoot=50 undershoot=100 # Add audio bitrate. abr=128k # v5 # 800k had some visible degradation compared to pv. Up it a bit more. vbr=1200k preset=2 ## Main ## echo -e "\n * Original File * \n" ffprobe -i "$org" echo -e "\n * Starting Processing *\n" date set -x ffmpeg -nostdin -hide_banner -loglevel level+warning \ -i "$org" \ -c:v libsvtav1 \ -preset $preset \ -filter:v scale=$scale \ -svtav1-params rc=1:tbr=${vbr}:overshoot-pct=${overshoot}:undershoot-pct=${undershoot} \ -c:a libopus \ -filter:a dynaudnorm=f=33:g=65:p=0.66:m=33.3 \ -b:a $abr \ -movflags +faststart \ "$out" status="$?" set +x # -crf $crf \ if [[ "$status" != 0 || ! -s "$out" ]]; then echo "Failed, deleting output." mv -v "$out" ~/TRASH/ return $status fi echo -e "\n * Success! *" } # 20260915 - Stack 2 videos for comparing quality. TBD: Move to Ansible? function vcompare { vid1="$1" vid2="$2" if [[ -z $vid1 || -z $vid2 ]]; then echo "ERROR: Missing vid1 ('$vid1') or vid2 ('$vid2'). " return 1 fi orientation="$3" if [[ -z $orientation || ($orientation != "h" && $orientation != "v") ]]; then echo -n "NOTICE: Received '$orientation' for orientation, using vertical instead. " orientation="v" echo "'$orientation'" fi mpv --lavfi-complex="[vid1][vid2]${orientation}stack[vo];[aid1][aid2]amix[ao]" \ "$vid1" \ --external-file="$vid2" } alias vcomp="vcompare " ### Web ### function download-website { website="$1" if [[ -z "$website" ]]; then echo "ERROR: Website not provided. $website" return fi if [[ "$website" != "http"* ]]; then website="http://$website" fi # https://simpleit.rocks/linux/how-to-download-a-website-with-wget-the-right-way/ # Does not simplify the links for offline usage. # wget --wait=2 \ # --level=inf \ # --limit-rate=20K \ # --recursive \ # --page-requisites \ # --user-agent=Mozilla \ # --no-parent \ # --convert-links \ # --adjust-extension \ # --no-clobber \ # -e robots=off \ # $website && # https://www.digitalcitizen.life/how-to-download-entire-website-for-offline-viewing/ # This one is supposed to work too and is much smaller. # wget --mirror \ # --convert-links \ # --adjust-extension \ # --page-requisites \ # --no-parent \ # $website && # Combine the two by using mirow and removing the rate limit. wget --mirror \ --wait=2 \ --level=inf \ --recursive \ --page-requisites \ --user-agent=Mozilla \ --no-parent \ --convert-links \ --adjust-extension \ --no-clobber \ -e robots=off \ --timeout=15 \ --tries=0 \ --continue \ --waitretry=5 \ --random-wait \ $website && { echo -e "\nSuccess!" } || { echo -e "\nERROR: Command reported a failure. (Error code '$?')" echo "This may mean that the URL was not converted to local links." echo "Please check the error and contents and try again if needed." } } alias website-download="download-website" alias curl-site="download-website" alias pull-site="download-website" alias site-pull="download-website" alias curl-www="download-website" alias pull-www="download-website" alias www-pull="download-website" function run-site { python3 -m http.server } alias run-html="run-site" alias exec-site="run-site" alias exec-html="run-site" alias site-host="run-site" alias host-site="run-site" # TBD: Move this to a different section? function git-check { dir="`pwd`" if [[ -n "$1" ]]; then dir="$1" fi echo "Checking '$dir' for git changes." ls -d "$dir"/* | while read project; do if [[ ! -d $project ]]; then continue fi echo -e "\n*** `basename $project` ***" cd $project if [[ -d .git ]]; then git ls-remote --exit-code --heads origin dev dev_exists="$?" if [[ "$dev_exists" == 0 && "$dir" != *".wiki" ]]; then git switch dev elif [[ "$dev_exists" == 2 || "$dir" == *".wiki" ]]; then git switch main else echo "ERROR: Unknown status for dev_exists, '$dev_exists'." continue fi git pull git push else echo "Not a Git project, skipping!" fi done echo -e "\nDone!" } # TBD: Move this to a different section? function check-colors { min=0 max=256 unset i echo "Current terminal supports '`tput colors`' colors." echo "Displaying examples..." for (( i = min; i <= max; i++ )); do tput setaf $i printf "%03d" "$i" tput sgr0 if (( i < max )); then printf " " fi done printf "\n" } alias color-check="check-colors" alias show-colors="check-colors" alias test-colors="check-colors" alias colors="check-colors" alias term-colors="check-colors" alias display-colors="check-colors" ## Finalize ## # Export all functions! # eval "$(declare -F | grep -v _ | sed 's/-f /-fx /')" # Run update checker in background. if [[ "$USER" != "root" ]]; then ( if [[ -d "$PROJECT_SHARED" ]]; then git -C "$PROJECT_SHARED" fetch >/dev/null 2>&1 \ && git -C "$PROJECT_SHARED" status \ | grep "is behind" \ | while read status; do title=TBD message=TBD success=0 reload-shared || success=1 if [[ "$success" == 0 ]]; then title="env-shared Updated" message="Reloaded automatically, please reload BASH. :)" else title="env-shared Updates Available" message="Update with 'reload-shared'." fi if [[ -d /sdcard ]]; then termux-notification \ -t "$title" \ -c "$message" else notify-send "$title" "$message" fi done fi >> "$HOME"/Reports/env-shared.log.txt 2>&1 & ) fi ## Complete! ## [[ $(whoami) != "root" ]] && echo "`date` - Shared RC Config - Complete!" export _LOADED_SHARED_RC=true