diff options
Diffstat (limited to 'dotfiles/system/.local/bin')
100 files changed, 3885 insertions, 0 deletions
| diff --git a/dotfiles/system/.local/bin/AAXtoMP3 b/dotfiles/system/.local/bin/AAXtoMP3 new file mode 100755 index 0000000..adc91ef --- /dev/null +++ b/dotfiles/system/.local/bin/AAXtoMP3 @@ -0,0 +1,908 @@ +#!/usr/bin/env bash + + +# ======================================================================== +# Command Line Options + +# Usage Synopsis. +usage=$'\nUsage: AAXtoMP3 [--flac] [--aac] [--opus ] [--single] [--level <COMPRESSIONLEVEL>] +[--chaptered] [-e:mp3] [-e:m4a] [-e:m4b] [--authcode <AUTHCODE>] [--no-clobber] +[--target_dir <PATH>] [--complete_dir <PATH>] [--validate] [--loglevel <LOGLEVEL>] +[--keep-author <N>] [--author <AUTHOR>] [--{dir,file,chapter}-naming-scheme <STRING>]   +[--use-audible-cli-data] [--continue <CHAPTERNUMBER>] {FILES}\n' +codec=libmp3lame            # Default encoder. +extension=mp3               # Default encoder extension. +level=-1                    # Compression level. Can be given for mp3, flac and opus. -1 = default/not specified. +mode=chaptered              # Multi file output +auth_code=                  # Required to be set via file or option. +targetdir=                  # Optional output location.  Note default is basedir of AAX file. +dirNameScheme=              # Custom directory naming scheme, default is $genre/$author/$title +customDNS=0 +fileNameScheme=             # Custom file naming scheme, default is $title +customFNS=0 +chapterNameScheme=          # Custom chapter naming scheme, default is '$title-$(printf %0${#chaptercount}d $chapternum) $chapter' (BookTitle-01 Chapter 1) +customCNS=0 +completedir=                # Optional location to move aax files once the decoding is complete. +container=mp3               # Just in case we need to change the container.  Used for M4A to M4B +VALIDATE=0                  # Validate the input aax file(s) only.  No Transcoding of files will occur +loglevel=1                  # Loglevel: 0: Show progress only; 1: default; 2: a little more information, timestamps; 3: debug +noclobber=0                 # Default off, clobber only if flag is enabled +continue=0                  # Default off, If set Transcoding is skipped and chapter splitting starts at chapter continueAt. +continueAt=1                # Optional chapter to continue splitting the chapters. +keepArtist=-1               # Default off, if set change author metadata to use the passed argument as field +authorOverride=             # Override the author, ignoring the metadata            +audibleCli=0                # Default off, Use additional data gathered from mkb79/audible-cli +aaxc_key=                   # Initialize variables, in case we need them in debug_vars +aaxc_iv=                    # Initialize variables, in case we need them in debug_vars + +# ----- +# Code tip Do not have any script above this point that calls a function or a binary.  If you do +# the $1 will no longer be a ARGV element.  So you should only do basic variable setting above here. +# +# Process the command line options.  This allows for un-ordered options. Sorta like a getops style +while true; do +  case "$1" in +                      # Flac encoding +    -f | --flac       ) codec=flac; extension=flac; mode=single; container=flac;        shift ;; +                      # Apple m4a music format. +    -a | --aac        ) codec=copy; extension=m4a; mode=single; container=m4a;          shift ;; +                      # Ogg Format +    -o | --opus       ) codec=libopus; extension=opus; container=ogg;                   shift ;; +                      # If appropriate use only a single file output. +    -s | --single     ) mode=single;                                                    shift ;; +                      # If appropriate use only a single file output. +    -c | --chaptered  ) mode=chaptered;                                                 shift ;; +                      # This is the same as --single option. +    -e:mp3            ) codec=libmp3lame; extension=mp3; mode=single; container=mp3;    shift ;; +                      # Identical to --acc option. +    -e:m4a            ) codec=copy; extension=m4a; mode=single; container=mp4;          shift ;; +                      # Similar to --aac but specific to audio books +    -e:m4b            ) codec=copy; extension=m4b; mode=single; container=mp4;          shift ;; +                      # Change the working dir from AAX directory to what you choose. +    -t | --target_dir ) targetdir="$2";                                                 shift 2 ;; +                      # Use a custom directory naming scheme, with variables. +    -D | --dir-naming-scheme ) dirNameScheme="$2"; customDNS=1;                         shift 2 ;; +                      # Use a custom file naming scheme, with variables. +    -F | --file-naming-scheme ) fileNameScheme="$2"; customFNS=1;                       shift 2 ;; +                       # Use a custom chapter naming scheme, with variables. +    --chapter-naming-scheme ) chapterNameScheme="$2"; customCNS=1;                      shift 2 ;; +                      # Move the AAX file to a new directory when decoding is complete. +    -C | --complete_dir ) completedir="$2";                                             shift 2 ;; +                      # Authorization code associate with the AAX file(s) +    -A | --authcode   ) auth_code="$2";                                                 shift 2 ;; +                      # Don't overwrite the target directory if it already exists +    -n | --no-clobber ) noclobber=1;                                                    shift ;; +                      # Extremely verbose output. +    -d | --debug      ) loglevel=3;                                                     shift ;; +                      # Set loglevel. +    -l | --loglevel   ) loglevel="$2";                                                  shift 2 ;; +                      # Validate ONLY the aax file(s) No transcoding occurs +    -V | --validate   ) VALIDATE=1;                                                     shift ;; +                      # continue splitting chapters at chapter continueAt +    --continue        ) continueAt="$2"; continue=1;                                    shift 2 ;; +                      # Use additional data got with mkb79/audible-cli +    --use-audible-cli-data ) audibleCli=1;                                              shift ;; +                      # Compression level +    --level           ) level="$2";                                                     shift 2 ;; +                      # Keep author number n +    --keep-author     ) keepArtist="$2";                                                shift 2 ;; +                      # Author override +    --author          ) authorOverride="$2";                                            shift 2 ;; +                      # Command synopsis. +    -h | --help       ) printf "$usage" $0 ;                                            exit ;; +                      # Standard flag signifying the end of command line processing. +    --                )                                                                 shift; break ;; +                      # Anything else stops command line processing. +    *                 )                                                                 break ;; + +  esac +done + +# ----- +# Empty argv means we have nothing to do so lets bark some help. +if [ "$#" -eq 0 ]; then +  printf "$usage" $0 +  exit 1 +fi + +# Setup safer bash script defaults. +set -o errexit -o noclobber -o nounset -o pipefail + +# ======================================================================== +# Utility Functions + +# ----- +# debug +# debug "Some longish message" +debug() { +  if [ $loglevel == 3 ] ; then +    echo "$(date "+%F %T%z") DEBUG ${1}" +  fi +} + +# ----- +# debug dump contents of a file to STDOUT +# debug "<full path to file>" +debug_file() { +  if [ $loglevel == 3 ] ; then +    echo "$(date "+%F %T%z") DEBUG" +    echo "=Start==========================================================================" +    cat "${1}" +    echo "=End============================================================================" +  fi +} + +# ----- +# debug dump a list of internal script variables to STDOUT +# debug_vars "Some Message" var1 var2 var3 var4 var5 +debug_vars() { +  if [ $loglevel == 3 ] ; then +    msg="$1"; shift ; # Grab the message +    args=("$@")       # Grab the rest of the args + +    # determine the length of the longest key +    l=0 +    for (( n=0; n<${#args[@]}; n++ )) ; do +      (( "${#args[$n]}" > "$l" )) && l=${#args[$n]} +    done + +    # Print the Debug Message +    echo "$(date "+%F %T%z") DEBUG ${msg}" +    echo "=Start==========================================================================" + +    # Using the max length of a var name we dynamically create the format. +    fmt="%-"${l}"s = %s\n" + +    for (( n=0; n<${#args[@]}; n++ )) ; do +      eval val="\$${args[$n]}"              ; # We save off the value of the var in question for ease of coding. + +      echo "$(printf "${fmt}" ${args[$n]} "${val}" )" +    done +    echo "=End============================================================================" +  fi +} + +# ----- +# log +log() { +  if [ "$((${loglevel} > 1))" == "1" ] ; then +    echo "$(date "+%F %T%z") ${1}" +  else +    echo "${1}" +  fi +} + +# ----- +#progressbar produces a progressbar in the style of +# process: |#######             |  XX% (part/total unit) +# which is gonna be overwritten by the next line. + +progressbar() { +  #get input +  part=${1} +  total=${2} + +  #compute percentage and make print_percentage the same length regardless of the number of digits. +  percentage=$((part*100/total)) +  if [ "$((percentage<10))" = "1" ]; then print_percentage="  $percentage" +  elif [ "$((percentage<100))" = "1" ]; then print_percentage=" $percentage" +  else print_percentage="$percentage"; fi + +  #draw progressbar with one # for every 5% and blank spaces for the missing part. +  progressbar="" +  for (( n=0; n<(percentage/5); n++ )) ; do progressbar="$progressbar#"; done +  for (( n=0; n<(20-(percentage/5)); n++ )) ; do progressbar="$progressbar "; done + +  #print progressbar +  echo -ne "Chapter splitting: |$progressbar| $print_percentage% ($part/$total chapters)\r" +} +# Print out what we have already after command line processing. +debug_vars "Command line options as set" codec extension mode container targetdir completedir auth_code keepArtist authorOverride audibleCli + +# ======================================================================== +# Variable validation + +if [ $(uname) = 'Linux' ]; then +    GREP="grep" +    FIND="find" +    SED="sed" +else +    GREP="ggrep" +    FIND="gfind" +    SED="gsed" +fi + + +# ----- +# Detect which annoying version of grep we have +if ! [[ $(type -P "$GREP") ]]; then +  echo "$GREP (GNU grep) is not in your PATH" +  echo "Without it, this script will break." +  echo "On macOS, you may want to try: brew install grep" +  exit 1 +fi + +# ----- +# Detect which annoying version of find we have +if ! [[ $(type -P "$FIND") ]]; then +  echo "$FIND (GNU find) is not in your PATH" +  echo "Without it, this script will break." +  echo "On macOS, you may want to try: brew install findutils" +  exit 1 +fi + +# ----- +# Detect which annoying version of sed we have +if ! [[ $(type -P "$SED") ]]; then +  echo "$SED (GNU sed) is not in your PATH" +  echo "Without it, this script will break." +  echo "On macOS, you may want to try: brew install gnu-sed" +  exit 1 +fi + +# ----- +# Detect ffmpeg and ffprobe +if [[ "x$(type -P ffmpeg)" == "x" ]]; then +  echo "ERROR ffmpeg was not found on your env PATH variable" +  echo "Without it, this script will break." +  echo "INSTALL:" +  echo "MacOS:           brew install ffmpeg" +  echo "Ubuntu:          sudo apt-get update; sudo apt-get install ffmpeg libav-tools x264 x265 bc" +  echo "Ubuntu (20.04):  sudo apt-get update; sudo apt-get install ffmpeg x264 x265 bc" +  echo "RHEL:            yum install ffmpeg" +  exit 1 +fi + +# ----- +# Detect ffmpeg and ffprobe +if [[ "x$(type -P ffprobe)" == "x" ]]; then +  echo "ERROR ffprobe was not found on your env PATH variable" +  echo "Without it, this script will break." +  echo "INSTALL:" +  echo "MacOS:   brew install ffmpeg" +  echo "Ubuntu:  sudo apt-get update; sudo apt-get install ffmpeg libav-tools x264 x265 bc" +  echo "RHEL:    yum install ffmpeg" +  exit 1 +fi + + +# ----- +# Detect if we need mp4art for cover additions to m4a & m4b files. +if [[ "x${container}" == "xmp4" && "x$(type -P mp4art)" == "x" ]]; then +  echo "WARN mp4art was not found on your env PATH variable" +  echo "Without it, this script will not be able to add cover art to" +  echo "m4b files. Note if there are no other errors the AAXtoMP3 will" +  echo "continue. However no cover art will be added to the output." +  echo "INSTALL:" +  echo "MacOS:   brew install mp4v2" +  echo "Ubuntu:  sudo apt-get install mp4v2-utils" +fi + +# ----- +# Detect if we need mp4chaps for adding chapters to m4a & m4b files. +if [[ "x${container}" == "xmp4" && "x$(type -P mp4chaps)" == "x" ]]; then +  echo "WARN mp4chaps was not found on your env PATH variable" +  echo "Without it, this script will not be able to add chapters to" +  echo "m4a/b files. Note if there are no other errors the AAXtoMP3 will" +  echo "continue. However no chapter data will be added to the output." +  echo "INSTALL:" +  echo "MacOS:   brew install mp4v2" +  echo "Ubuntu:  sudo apt-get install mp4v2-utils" +fi + +# ----- +# Detect if we need mediainfo for adding description and narrator +if [[ "x$(type -P mediainfo)" == "x" ]]; then +  echo "WARN mediainfo was not found on your env PATH variable" +  echo "Without it, this script will not be able to add the narrator" +  echo "and description tags. Note if there are no other errors the AAXtoMP3" +  echo "will continue. However no such tags will be added to the output." +  echo "INSTALL:" +  echo "MacOS:   brew install mediainfo" +  echo "Ubuntu:  sudo apt-get install mediainfo" +fi + +# ----- +# Obtain the authcode from either the command line,  local directory or home directory. +# See Readme.md for details on how to acquire your personal authcode for your personal +# audible AAX files. +if [ -z $auth_code ]; then +  if [ -r .authcode ]; then +    auth_code=`head -1 .authcode` +  elif [ -r ~/.authcode ]; then +    auth_code=`head -1 ~/.authcode` +  fi +fi + +# ----- +# Check the target dir for if set if it is writable +if [[ "x${targetdir}" != "x"  ]]; then +  if [[ ! -w "${targetdir}" || ! -d "${targetdir}" ]] ; then +    echo "ERROR Target Directory does not exist or is not writable: \"$targetdir\"" +    echo "$usage" +    exit 1 +  fi +fi + +# ----- +# Check the completed dir for if set if it is writable +if [[ "x${completedir}" != "x"  ]]; then +  if [[ ! -w "${completedir}" || ! -d "${completedir}" ]] ; then +    echo "ERROR Complete Directory does not exist or is not writable: \"$completedir\"" +    echo "$usage" +    exit 1 +  fi +fi + +# ----- +# Check whether the loglevel is valid +if [ "$((${loglevel} < 0 || ${loglevel} > 3 ))" = "1" ]; then +  echo "ERROR loglevel has to be in the range from 0 to 3!" +  echo "      0: Show progress only" +  echo "      1: default" +  echo "      2: a little more information, timestamps" +  echo "      3: debug" +  echo "$usage" +  exit 1 +fi +# ----- +# If a compression level is given, check whether the given codec supports compression level specifiers and whether the level is valid. +if [ "${level}" != "-1" ]; then +  if [ "${codec}" == "flac" ]; then +    if [ "$((${level} < 0 || ${level} > 12 ))" = "1" ]; then +      echo "ERROR Flac compression level has to be in the range from 0 to 12!" +      echo "$usage" +      exit 1 +    fi +  elif [ "${codec}" == "libopus" ]; then +    if [ "$((${level} < 0 || ${level} > 10 ))" = "1" ]; then +      echo "ERROR Opus compression level has to be in the range from 0 to 10!" +      echo "$usage" +      exit 1 +    fi +  elif [ "${codec}" == "libmp3lame" ]; then +    if [ "$((${level} < 0 || ${level} > 9 ))" = "1" ]; then +      echo "ERROR MP3 compression level has to be in the range from 0 to 9!" +      echo "$usage" +      exit 1 +    fi +  else +    echo "ERROR This codec doesnt support compression levels!" +    echo "$usage" +    exit 1 +  fi +fi + +# ----- +# Clean up if someone hits ^c or the script exits for any reason. +trap 'rm -r -f "${working_directory}"' EXIT + +# ----- +# Set up some basic working files ASAP.  Note the trap will clean this up no matter what. +working_directory=`mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir'` +metadata_file="${working_directory}/metadata.txt" + +# ----- +# Validate the AAX and extract the metadata associated with the file. +validate_aax() { +  local media_file +  media_file="$1" + +  # Test for existence +  if [[ ! -r "${media_file}" ]] ; then +    log "ERROR File NOT Found: ${media_file}" +    return 1 +  else +    if [[ "${VALIDATE}" == "1" ]]; then +      log "Test 1 SUCCESS: ${media_file}" +    fi +  fi + +  # Clear the errexit value we want to capture the output of the ffprobe below. +  set +e errexit + +  # Take a look at the aax file and see if it is valid. If the source file is aaxc, we give ffprobe additional flags +  output="$(ffprobe -loglevel warning ${decrypt_param} -i "${media_file}" 2>&1)" + +  # If invalid then say something. +  if [[ $? != "0" ]] ; then +    # No matter what lets bark that something is wrong. +    log "ERROR: Invalid File: ${media_file}" +  elif [[ "${VALIDATE}" == "1" ]]; then +    # If the validate option is present then lets at least state what is valid. +    log "Test 2 SUCCESS: ${media_file}" +  fi + +  # This is a big test only performed when the --validate switch is passed. +  if [[ "${VALIDATE}" == "1" ]]; then +    output="$(ffmpeg -hide_banner ${decrypt_param} -i "${media_file}" -vn -f null - 2>&1)" +    if [[ $? != "0" ]] ; then +      log "ERROR: Invalid File: ${media_file}" +    else +      log "Test 3 SUCCESS: ${media_file}" +    fi +  fi + +  # Dump the output of the ffprobe command. +  debug "$output" + +  # Turn it back on.  ffprobe is done. +  set -e errexit +} + +validate_extra_files() { +  local extra_media_file extra_find_command +  extra_media_file="$1" +  # Bash trick to delete, non greedy, from the end up until the first '-' +  extra_title="${extra_media_file%-*}" + +  # Using this is not ideal, because if the naming scheme is changed then +  # this part of the script will break +  # AAX file: BookTitle-LC_128_44100_stereo.aax +  # Cover file: BookTitle_(1215).jpg +  # Chapter file: BookTitle-chapters.json + +  # Chapter +  extra_chapter_file="${extra_title}-chapters.json" + +  # Cover +  extra_dirname="$(dirname "${extra_media_file}")" +  extra_find_command='$FIND "${extra_dirname}" -maxdepth 1 -regex ".*/${extra_title##*/}_([0-9]+)\.jpg"' +  # We want the output of the find command, we will turn errexit on later +  set +e errexit +  extra_cover_file="$(eval ${extra_find_command})" +  extra_eval_comm="$(eval echo ${extra_find_command})" +  set -e errexit + +  if [[ "${aaxc}" == "1" ]]; then +    # bash trick to get file w\o extention (delete from end to the first '.') +    extra_voucher="${extra_media_file%.*}.voucher" +    if [[ ! -r "${extra_voucher}" ]] ; then +      log "ERROR File NOT Found: ${extra_voucher}" +      return 1 +    fi +    aaxc_key=$(jq -r '.content_license.license_response.key' "${extra_voucher}") +    aaxc_iv=$(jq -r '.content_license.license_response.iv' "${extra_voucher}") +  fi + +  debug_vars "Audible-cli variables" extra_media_file extra_title extra_chapter_file extra_cover_file extra_find_command extra_eval_comm extra_dirname extra_voucher aaxc_key aaxc_iv + +  # Test for chapter file existence +  if [[ ! -r "${extra_chapter_file}" ]] ; then +    log "ERROR File NOT Found: ${extra_chapter_file}" +    return 1 +  fi +  if [[ "x${extra_cover_file}" == "x" ]] ; then +    log "ERROR Cover File NOT Found" +    return 1 +  fi + +  debug "All expected audible-cli related file are here" +} + +# ----- +# Inspect the AAX and extract the metadata associated with the file. +save_metadata() { +  local media_file +  media_file="$1" +  ffprobe -i "$media_file" 2> "$metadata_file" +  if [[ $(type -P mediainfo) ]]; then +    echo "Mediainfo data START" >> "$metadata_file" +    # Mediainfo output is structured like ffprobe, so we append it to the metadata file and then parse it with get_metadata_value() +    # mediainfo "$media_file" >> "$metadata_file" +    # Or we only get the data we are intrested in: +    # Description +    echo "Track_More :" "$(mediainfo --Inform="General;%Track_More%" "$media_file")" >> "$metadata_file" +    # Narrator +    echo "nrt :" "$(mediainfo --Inform="General;%nrt%" "$media_file")" >> "$metadata_file" +    # Publisher +    echo "pub :" "$(mediainfo --Inform="General;%pub%" "$media_file")" >> "$metadata_file" +    echo "Mediainfo data END" >> "$metadata_file" +  fi +  if [[ "${audibleCli}" == "1" ]]; then +    # If we use data we got with audible-cli, we delete conflicting chapter infos +    $SED -i '/^    Chapter #/d' "${metadata_file}" +    # Some magic: we parse the .json generated by audible-cli. +    # to get the output structure like the one generated by ffprobe,  +    # we use some characters (#) as placeholder, add some new lines, +    # put a ',' after the start value, we calculate the end of each chapter +    # as start+length, and we convert (divide) the time stamps from ms to s. +    # Then we delete all ':' since they make a filename invalid. +    jq -r '.content_metadata.chapter_info.chapters[] | "Chapter # start: \(.start_offset_ms/1000), end: \((.start_offset_ms+.length_ms)/1000) \n#\n# Title: \(.title)"' "${extra_chapter_file}" \ +      | tr -d ':' >> "$metadata_file" +  fi +  debug "Metadata file $metadata_file" +  debug_file "$metadata_file" +} + +# ----- +# Reach into the meta data and extract a specific value. +#   This is a long pipe of transforms. +#   This finds the first occurrence of the key : value pair. +get_metadata_value() { +  local key +  key="$1" +        # Find the key in the meta data file                                  # Extract field value     # Remove the following /'s  "(Unabridged)  <blanks> at start end and multiples. +  echo "$($GREP --max-count=1 --only-matching "${key} *: .*" "$metadata_file" | cut -d : -f 2- | $SED -e 's#/##g;s/ (Unabridged)//;s/^[[:blank:]]\+//g;s/[[:blank:]]\+$//g' | $SED 's/[[:blank:]]\+/ /g')" +} + +# ----- +# specific variant of get_metadata_value bitrate is important for transcoding. +get_bitrate() { +  get_metadata_value bitrate | $GREP --only-matching '[0-9]\+' +} + +# Save the original value, since in the for loop we overwrite +# $audibleCli in case the file is aaxc. If the file is the +# old aax, reset the variable to be the one passed by the user +originalAudibleCliVar=$audibleCli +# ======================================================================== +# Main Transcode Loop +for aax_file +do +  # If the file is in aaxc format, set the proper variables +  if [[ ${aax_file##*.} == "aaxc" ]]; then +    # File is the new .aaxc +    aaxc=1 +    audibleCli=1 +  else +    # File is the old .aax +    aaxc=0 +    # If some previous file in the loop are aaxc, the $audibleCli variable has been overwritten, so we reset it to the original one +    audibleCli=$originalAudibleCliVar +  fi + +  debug_vars "Variables set based on file extention" aaxc originalAudibleCliVar audibleCli + +  # No point going on if no authcode found and the file is aax. +  # If we use aaxc as input, we do not need it +  # if the string $auth_code is null and the format is not aaxc; quit. We need the authcode +  if [ -z $auth_code ] && [ "${aaxc}" = "0" ]; then +    echo "ERROR Missing authcode, can't decode $aax_file" +    echo "$usage" +    exit 1 +  fi + +  # Validate the input aax file.  Note this happens no matter what. +  # It's just that if the validate option is set then we skip to next file. +  # If however validate is not set and we proceed with the script any errors will +  # case the script to stop. + +  # If the input file is aaxc, we need to first get the audible_key and audible_iv +  # We get them in the function validate_extra_files + +  if [[ ${audibleCli} == "1" ]] ; then +    # If we have additional files (obtained via audible-cli), be sure that they +    # exists and they are in the correct location. +    validate_extra_files "${aax_file}" +  fi + +  # Set the needed params to decrypt the file. Needed in all command that require ffprobe or ffmpeg +  # After validate_extra_files, since the -audible_key and -audible_iv are read in that function +  if [[ ${aaxc} == "1" ]] ; then +    decrypt_param="-audible_key ${aaxc_key} -audible_iv ${aaxc_iv}" +  else +    decrypt_param="-activation_bytes ${auth_code}" +  fi + +  validate_aax "${aax_file}" +  if [[ ${VALIDATE} == "1" ]] ; then +    # Don't bother doing anything else with this file. +    continue +  fi + +  # ----- +  # Make sure everything is a variable.  Simplifying Command interpretation +  save_metadata "${aax_file}" +  genre=$(get_metadata_value genre) +  if [ "x${authorOverride}" != "x" ]; then +    #Manual Override +    artist="${authorOverride}" +    album_artist="${authorOverride}" +  else +    if [ "${keepArtist}" != "-1" ]; then +      # Choose artist from the one that are present in the metadata. Comma separated list of names +      #                                                                 remove leading space;     'C. S. Lewis' -> 'C.S. Lewis' +      artist="$(get_metadata_value artist | cut -d',' -f"$keepArtist" | $SED -E 's|^ ||g; s|\. +|\.|g; s|((\w+\.)+)|\1 |g')" +      album_artist="$(get_metadata_value album_artist | cut -d',' -f"$keepArtist" | $SED -E 's|^ ||g; s|\. +|\.|g; s|((\w+\.)+)|\1 |g')" +    else +      # The default +      artist=$(get_metadata_value artist) +      album_artist="$(get_metadata_value album_artist)" +    fi +  fi +  title=$(get_metadata_value title) +  title=${title:0:128} +  bitrate="$(get_bitrate)k" +  album="$(get_metadata_value album)" +  album_date="$(get_metadata_value date)" +  copyright="$(get_metadata_value copyright)" + +  # Get more tags with mediainfo +  if [[ $(type -P mediainfo) ]]; then +    narrator="$(get_metadata_value nrt)" +    description="$(get_metadata_value Track_More)" +    publisher="$(get_metadata_value pub)" +  else +    narrator="" +    description="" +    publisher="" +  fi + +  # Define the output_directory +  if [ "${customDNS}" == "1" ]; then +    currentDirNameScheme="$(eval echo "${dirNameScheme}")" +  else +    # The Default +    currentDirNameScheme="${genre}/${artist}/${title}" +  fi + +  # If we defined a target directory, use it. Otherwise use the location of the AAX file +  if [ "x${targetdir}" != "x" ] ; then +    output_directory="${targetdir}/${currentDirNameScheme}/" +  else +    output_directory="$(dirname "${aax_file}")/${currentDirNameScheme}/" +  fi + +  # Define the output_file +  if [ "${customFNS}" == "1" ]; then +    currentFileNameScheme="$(eval echo "${fileNameScheme}")" +  else +    # The Default +    currentFileNameScheme="${title}" +  fi +  output_file="${output_directory}/${currentFileNameScheme}.${extension}" + +  if [[ "${noclobber}" = "1" ]] && [[ -d "${output_directory}" ]]; then +    log "Noclobber enabled but directory '${output_directory}' exists. Exiting to avoid overwriting" +    exit 0 +  fi +  mkdir -p "${output_directory}" + +  if [ "$((${loglevel} > 0))" = "1" ]; then +    # Fancy declaration of which book we are decoding.  Including the AUTHCODE. +    dashline="----------------------------------------------------" +    log "$(printf '\n----Decoding---%s%s--%s--' "${title}" "${dashline:${#title}}" "${auth_code}")" +    log "Source: ${aax_file}" +  fi + +  # Big long DEBUG output. Fully describes the settings used for transcoding. +  # Note this is a long debug command. It's not critical to operation. It's purely for people debugging +  # and coders wanting to extend the script. +  debug_vars "Book and Variable values" title auth_code aaxc aaxc_key aaxc_iv mode aax_file container codec bitrate artist album_artist album album_date genre copyright narrator description publisher currentDirNameScheme output_directory currentFileNameScheme output_file metadata_file working_directory + + +  # Display the total length of the audiobook in format hh:mm:ss +  # 10#$var force base-10 interpretation. By default it's base-8, so values like 08 or 09 are not octal numbers +  total_length="$(ffprobe -v error ${decrypt_param} -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${aax_file}" | cut -d . -f 1)" +  hours="$((total_length/3600))" +  if [ "$((hours<10))" = "1" ]; then hours="0$hours"; fi +  minutes="$((total_length/60-60*10#$hours))" +  if [ "$((minutes<10))" = "1" ]; then minutes="0$minutes"; fi +  seconds="$((total_length-3600*10#$hours-60*10#$minutes))" +  if [ "$((seconds<10))" = "1" ]; then seconds="0$seconds"; fi +  log "Total length: $hours:$minutes:$seconds" +   +  # If level != -1 specify a compression level in ffmpeg. +  compression_level_param="" +  if [ "${level}" != "-1" ]; then +    compression_level_param="-compression_level ${level}" +  fi +   +  # ----- +  if [ "${continue}" == "0" ]; then +    # This is the main work horse command.  This is the primary transcoder. +    # This is the primary transcode. All the heavy lifting is here. +    debug 'ffmpeg -loglevel error -stats ${decrypt_param} -i "${aax_file}" -vn -codec:a "${codec}" -ab ${bitrate} -map_metadata -1 -metadata title="${title}" -metadata artist="${artist}" -metadata album_artist="${album_artist}" -metadata album="${album}" -metadata date="${album_date}" -metadata track="1/1" -metadata genre="${genre}" -metadata copyright="${copyright}" "${output_file}"' +    </dev/null ffmpeg -loglevel error \ +      -stats \ +      ${decrypt_param} \ +      -i "${aax_file}" \ +      -vn \ +      -codec:a "${codec}" \ +      ${compression_level_param} \ +      -ab ${bitrate} \ +      -map_metadata -1 \ +      -metadata title="${title}" \ +      -metadata artist="${artist}" \ +      -metadata album_artist="${album_artist}" \ +      -metadata album="${album}" \ +      -metadata date="${album_date}" \ +      -metadata track="1/1" \ +      -metadata genre="${genre}" \ +      -metadata copyright="${copyright}" \ +      -metadata description="${description}" \ +      -metadata composer="${narrator}" \ +      -metadata publisher="${publisher}" \ +      -f ${container} \ +      "${output_file}" +    if [ "$((${loglevel} > 0))" == "1" ]; then +      log "Created ${output_file}." +    fi +    # ----- +  fi +  # Grab the cover art if available. +  cover_file="${output_directory}/cover.jpg" +  extra_crop_cover='' +  if [ "${continue}" == "0" ]; then +    if [ "${audibleCli}" == "1" ]; then +      # We have a better quality cover file, copy it. +      if [ "$((${loglevel} > 1))" == "1" ]; then +        log "Copy cover file to ${cover_file}..." +      fi +      cp "${extra_cover_file}" "${cover_file}" + +      # We now set a variable, ${extra_crop_cover}, which contains an additional +      # ffmpeg flag. It crops the cover so the width and the height is divisible by two. +      # Since the standard (in the aax file) image resolution is 512, we set the flag +      # only if we use a custom cover art. +      extra_crop_cover='-vf crop=trunc(iw/2)*2:trunc(ih/2)*2' +    else +      # Audible-cli not used, extract the cover from the aax file +      if [ "$((${loglevel} > 1))" == "1" ]; then +        log "Extracting cover into ${cover_file}..." +      fi +      </dev/null ffmpeg -loglevel error -activation_bytes "${auth_code}" -i "${aax_file}" -an -codec:v copy "${cover_file}" +    fi +  fi + +  # ----- +  # If mode=chaptered, split the big converted file by chapter and remove it afterwards. +  # Not all audio encodings make sense with multiple chapter outputs (see options section) +  if [ "${mode}" == "chaptered" ]; then +    # Playlist m3u support +    playlist_file="${output_directory}/${currentFileNameScheme}.m3u" +    if [ "${continue}" == "0" ]; then +      if [ "$((${loglevel} > 0))" == "1" ]; then +        log "Creating PlayList ${currentFileNameScheme}.m3u" +      fi +      echo '#EXTM3U' > "${playlist_file}" +    fi + +    # Determine the number of chapters. +    chaptercount=$($GREP -Pc "Chapter.*start.*end" $metadata_file) +    if [ "$((${loglevel} > 0))" == "1" ]; then +      log "Extracting ${chaptercount} chapter files from ${output_file}..." +      if [ "${continue}" == "1" ]; then +        log "Continuing at chapter ${continueAt}:" +      fi +    fi +    chapternum=1 +    #start progressbar for loglevel 0 and 1 +    if [ "$((${loglevel} < 2))" == "1" ]; then +      progressbar 0 ${chaptercount} +    fi +    # We pipe the metadata_file in read. +    # Example of the section that we are interested in: +    # +    # Chapter #0:0: start 0.000000, end 1928.231474 +    # Metadata: +    # title           : Chapter 1 +    # +    # Then read the line in these variables: +    # first            Chapter +    # _                #0:0: +    # _                start +    # chapter_start    0.000000, +    # _                end +    # chapter_end      1928.231474 +    while read -r -u9 first _ _ chapter_start _ chapter_end +    do +      # Do things only if the line starts with 'Chapter' +      if [[ "${first}" = "Chapter" ]]; then +        # The next line (Metadata:...) gets discarded +        read -r -u9 _ +        # From the line 'title  :  Chapter 1' we save the third field and those after in chapter +        read -r -u9 _ _ chapter + +        # The formatting of the chapters names and the file names. +        # Chapter names are used in a few place. +        # Define the chapter_file +        if [ "${customCNS}" == "1" ]; then +          chapter_title="$(eval echo "${chapterNameScheme}")" +        else +          # The Default +          chapter_title="${title}-$(printf %0${#chaptercount}d $chapternum) ${chapter}" +        fi +        chapter_file="${output_directory}/${chapter_title}.${extension}" + +        # Since the .aax file allready got converted we can use +        # -acodec copy, which is much faster than a reencodation. +        # Since there is an issue when using copy on flac, where +        # the duration of the chapters gets shown as if they where +        # as long as the whole audiobook. +        chapter_codec="" +        if test "${extension}" = "flac"; then +          chapter_codec="flac "${compression_level_param}"" +        else +          chapter_codec="copy" +        fi + +        #Since there seems to be a bug in some older versions of ffmpeg, which makes, that -ss and -to +        #have to be apllied to the output file, this makes, that -ss and -to get applied on the input for +        #ffmpeg version 4+ and on the output for all older versions. +        split_input="" +        split_output="" +        if [ "$(($(ffmpeg -version | $SED -E 's/[^0-9]*([0-9]).*/\1/g;1q') > 3))" = "1" ]; then +          split_input="-ss ${chapter_start%?} -to ${chapter_end}" +        else +          split_output="-ss ${chapter_start%?} -to ${chapter_end}" +        fi + +        # Big Long chapter debug +        debug_vars "Chapter Variables:" cover_file chapter_start chapter_end chapternum chapter chapterNameScheme chapter_title chapter_file +        if [ "$((${continueAt} > ${chapternum}))" = "0" ]; then +          # Extract chapter by time stamps start and finish of chapter. +          # This extracts based on time stamps start and end. +          if [ "$((${loglevel} > 1))" == "1" ]; then +            log "Splitting chapter ${chapternum}/${chaptercount} start:${chapter_start%?}(s) end:${chapter_end}(s)" +          fi +          </dev/null ffmpeg -loglevel quiet \ +            -nostats \ +            ${split_input} \ +            -i "${output_file}" \ +            -i "${cover_file}" \ +            ${extra_crop_cover} \ +            ${split_output} \ +            -map 0:0 \ +            -map 1:0 \ +            -acodec ${chapter_codec} \ +            -metadata:s:v title="Album cover" \ +            -metadata:s:v comment="Cover (Front)" \ +            -metadata track="${chapternum}" \ +            -metadata title="${chapter}" \ +            -metadata:s:a title="${chapter}" \ +            -metadata:s:a track="${chapternum}" \ +            -map_chapters -1 \ +            -f ${container} \ +            "${chapter_file}" +          # ----- +          if [ "$((${loglevel} < 2))" == "1" ]; then +            progressbar ${chapternum} ${chaptercount} +          fi +          # OK lets get what need for the next chapter in the Playlist m3u file. +          # Playlist creation. +          duration=$(echo "${chapter_end} - ${chapter_start%?}" | bc) +          echo "#EXTINF:${duration%.*},${title} - ${chapter}" >>  "${playlist_file}" +          echo "${chapter_title}.${extension}" >> "${playlist_file}" +        fi +        chapternum=$((chapternum + 1 )) +      fi +    done 9< "$metadata_file" + +    # Clean up of working directory stuff. +    rm "${output_file}" +    if [ "$((${loglevel} > 1))" == "1" ]; then +      log "Done creating chapters for ${output_directory}." +    else +      #ending progress bar +      echo "" +    fi +  else +    # Perform file tasks on output file. +    # ---- +    # ffmpeg seems to copy only chapter position, not chapter names. +    if [[ ${container} == "mp4" && $(type -P mp4chaps) ]]; then +      ffprobe -i "${aax_file}" -print_format csv -show_chapters 2>/dev/null | awk -F "," '{printf "CHAPTER%02d=%02d:%02d:%02.3f\nCHAPTER%02dNAME=%s\n", NR, $5/60/60, $5/60%60, $5%60, NR, $8}' > "${output_directory}/${currentFileNameScheme}.chapters.txt" +      mp4chaps -i "${output_file}" +    fi +  fi + +  # ----- +  # Announce that we have completed the transcode +  if [ "$((${loglevel} > 0))" == "1" ]; then +    log "Complete ${title}" +  fi +  # Lastly get rid of any extra stuff. +  rm "${metadata_file}" + +  # Move the aax file if the decode is completed and the --complete_dir is set to a valid location. +  # Check the target dir for if set if it is writable +  if [[ "x${completedir}" != "x"  ]]; then +    if [ "$((${loglevel} > 0))" == "1" ]; then +      log "Moving Transcoded ${aax_file} to ${completedir}" +    fi +    mv "${aax_file}" "${completedir}" +  fi + +done diff --git a/dotfiles/system/.local/bin/airplanemodetoggle b/dotfiles/system/.local/bin/airplanemodetoggle new file mode 100755 index 0000000..c98e144 --- /dev/null +++ b/dotfiles/system/.local/bin/airplanemodetoggle @@ -0,0 +1,33 @@ +#!/bin/bash + +if [ "$(printf "On\\nOff" | dmenu -i -p "Set airplane mode:")" = "On" ] +then +    notify-send "Airplane Mode"  "Turning on airplane mode...." +    sudo systemctl stop  bluetooth.service +    sudo systemctl stop  expressvpn.service +    sudo systemctl stop  sshd.service +    sudo systemctl stop  syncthing@cjennings.service +    sudo systemctl stop  avahi-daemon.service +    sudo systemctl stop  cronie.service +    sudo systemctl stop  cups.service             +    sudo ip link set wlp170s0 down +    sudo systemctl stop  wpa_supplicant.service +    sudo systemctl stop  NetworkManager.service +    sudo nmcli radio all off +    sudo powertop --auto-tune +    notify-send "Airplane Mode" "Airplane mode is now on." +else +    notify-send "Airplane Mode" "Turning off airplane mode....." +    sudo nmcli radio wifi on +    sudo systemctl start  NetworkManager.service +    sudo systemctl start  wpa_supplicant.service +    sudo ip link set wlp170s0 up +    sudo systemctl start  bluetooth.service +    sudo systemctl start  expressvpn.service +    sudo systemctl start  sshd.service +    sudo systemctl start  syncthing@cjennings.service +    sudo systemctl start  avahi-daemon.service +    sudo systemctl start  cronie.service +    sudo systemctl start  cups.service             +    notify-send "Airplane Mode" "Airplane mode is now off." +fi diff --git a/dotfiles/system/.local/bin/audioselect b/dotfiles/system/.local/bin/audioselect new file mode 100755 index 0000000..13ffc36 --- /dev/null +++ b/dotfiles/system/.local/bin/audioselect @@ -0,0 +1,70 @@ +#!/bin/sh +# Craig Jennings +# convenience script to switch audio devices +# need bluez and bluez-utils + +# DEVICE MAC ADDRESSES +marshall_earbuds_device="00:25:D1:1B:39:CA" +marshall_headset_device="9C:0D:AC:05:1E:C9" + +# SINKS (Audio Out) +marshall_headset_sink="bluez_output.9C_0D_AC_05_1E_C9.a2dp-sink" +marshall_earbuds_sink="bluez_output.00_25_D1_1B_39_CA.a2dp_sink" +builtin_sink="alsa_output.pci-0000_00_1f.3.analog-stereo" +jabra_510_sink="alsa_output.usb-0b0e_Jabra_SPEAK_510_USB_1C48F9C067D5020A00-00.analog-stereo" +steelseries_sink="alsa_output.usb-SteelSeries_SteelSeries_Arctis_7-00.stereo-game" +emberton_sink="bluez_sink.04_21_44_89_D0_BE.a2dp_sink" + +# SOURCES (Audio In) +jabra_510_source="alsa_input.usb-0b0e_Jabra_SPEAK_510_USB_1C48F9C067D5020A00-00.mono-fallback" +builtin_source="alsa_input.pci-0000_00_1f.3.analog-stereo" +steelseries_source="alsa_input.usb-SteelSeries_SteelSeries_Arctis_7-00.mono-chat" + +CHOICES="Cancel\nToggle Mute Speaker\nToggle Mute Mic\nMarshall Headset & Jabra Mic\nMarshall Headset & Default Mic\nMarshall Earbuds & Jabra Mic\nJabra Speaker & Mic\nBuilt-In Audio" + +CHOSEN=$(echo -e "$CHOICES" | dmenu -l 10) + +case "$CHOSEN" in +"Toggle Mute Speaker") +	pactl set-sink-mute 0 toggle +	dwmstatus +	;; +"Toggle Mute Mic") +    pactl set-source-mute 0 toggle +	dwmstatus +	;; +"Marshall Headset & Jabra Mic") +    bluetooth power on +    bluetoothctl connect $marshall_headset_device +    pactl set-default-sink $marshall_headset_sink +    pactl set-default-source $jabra_510_source +    ;; +"Marshall Headset & Default Mic") +    bluetooth power on +    bluetoothctl connect $marshall_headset_device +    pactl set-default-sink $marshall_headset_sink +    pactl set-default-source $builtin_source +    ;; +"Marshall Earbuds & Jabra Mic") +    bluetooth power on +    bluetoothctl connect $marshall_earbuds_device +    pactl set-default-sink $marshall_earbuds_sink +    pactl set-default-source $jabra_510_source +    ;; +"Built-In Audio") +    pactl set-default-sink $builtin_audio_sink +    pactl set-default-source $builtin_audio_source +    ;; +"Jabra Speaker & Mic") +    pactl set-default-sink $jabra_510_sink +    pactl set-default-source $jabra_510_source +    ;; +"Emberton & Built-In") +    pactl set-default-sink $emberton_sink +    pactl set-default-source  $builtin_audio_source +    ;; +"Steelseries Headset") +    pactl set-default-sink $steelseries_sink +    pactl set-default-source $steelseries_source +    ;; +esac diff --git a/dotfiles/system/.local/bin/battery_monitor b/dotfiles/system/.local/bin/battery_monitor new file mode 100755 index 0000000..7c6e013 --- /dev/null +++ b/dotfiles/system/.local/bin/battery_monitor @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# battery_monitor +# Intended to be run via .xinitrc +# - Exit automatically if no battery (desktop) +# - When below 15%, warn user of low battery +# - When below 10%, suspend within 10 seconds if not charging +# +# Craig Jennings <c@cjennings.net> + +# check if acpi is installed +if ! command -v acpi &> /dev/null; then +	echo "acpi is not installed. Cannot continue. Exiting...." +	exit 1 +fi + +# exit if a battery exists +if [ ! -d "/sys/class/power_supply/BAT0" ] && [ ! -d "/sys/class/power_supply/BAT1" ]; then +	echo "Acpi is installed but no battery detected. Assuming this is a desktop and exiting...." +	exit 1 +fi + +while true; do +	# Get the current battery percentage using acpi +	battery_percentage=$(acpi -b | awk -F ', ' '{print $2}' | tr -d '%') +	# battery_percentage=$(acpi -b | awk -F ', ' '{print $2}' | sed 's/%//') + +	# When below 10%, suspend within 10 seconds if not charging +	if [ "$battery_percentage" -lt 11 ] && ! acpi -a | grep -q "on-line" ; then +		# Send a notification of sleeping in 10 seconds +		notify-send "Critical Battery" "Battery is at $battery_percentage%. System entering sleep in 30 seconds." + +		# sleep for 10 seconds, then abort if charging +		sleep 30 + +		# Check if the system is charging (AC adapter connected) +		if acpi -a | grep -q "on-line"; then +			notify-send "Charging" "The system is now charging. No action taken." +		else +			notify-send "Critical Battery" "Putting the system to sleep." +			sudo systemctl suspend +		fi +	fi + +	# When below 15%, warn user +	if [ "$battery_percentage" -lt 15 ] && ! acpi -a | grep -q "on-line" ; then +		# Send a notification using notify-send and dunst +		notify-send "Low Battery" "Battery is at $battery_percentage%. System will automatically sleep at 10%." +	fi + +	# Sleep for 5 minutes before checking again +	sleep 300 +done diff --git a/dotfiles/system/.local/bin/bookfind b/dotfiles/system/.local/bin/bookfind new file mode 100755 index 0000000..c5cc1bc --- /dev/null +++ b/dotfiles/system/.local/bin/bookfind @@ -0,0 +1,5 @@ +#!/bin/sh +# allows user to open a calibre book using dmenu  + +find  ~/Library/ -type f \( -iname \*.pdf -o -iname \*.epub \) | dmenu -i -l 20 -p "Choose an ebook:"  + diff --git a/dotfiles/system/.local/bin/brightness b/dotfiles/system/.local/bin/brightness new file mode 100755 index 0000000..9142f33 --- /dev/null +++ b/dotfiles/system/.local/bin/brightness @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Craig Jennings <c@cjennings.net> +# DWM convenience script for changing backlight +# depends on xbacklight + +increment=10 + +case $1 in +	"max") +		sudo xbacklight -set 100%; +		;; +	"min") +		sudo xbacklight -set 5%; +		;; +	"up") +		# get current setting as an int +		current=$( printf "%.0f" "$(xbacklight -get)" ) + +		# add the increment +		newvalue=$(("$current" + "$increment")) + +		# don't let the brightness go above 100 +		[ "$newvalue" -ge 100 ] && 	newvalue=100; + +		# set the value +		xbacklight -set "$newvalue"; +		;; +	"down") +		current=$( printf "%.0f" "$(xbacklight -get)" ) +		newvalue=$(("$current" - "$increment")) +		[ "$newvalue" -le 5 ] && 	newvalue=5; +		xbacklight -set "$newvalue"; +		;; +esac +newvalue=$( printf "%.0f" "$(xbacklight -get)") +notify-send "backlight" "backlight now set to $newvalue" diff --git a/dotfiles/system/.local/bin/bsdnet_bounce b/dotfiles/system/.local/bin/bsdnet_bounce new file mode 100755 index 0000000..e4eec08 --- /dev/null +++ b/dotfiles/system/.local/bin/bsdnet_bounce @@ -0,0 +1,6 @@ +if [ $(id -u) -eq 0 ] +then +    service netif restart && service routing restart +else +    echo "You must be root to run this script" +fi diff --git a/dotfiles/system/.local/bin/build.emacs.aur.sh b/dotfiles/system/.local/bin/build.emacs.aur.sh new file mode 100755 index 0000000..a185437 --- /dev/null +++ b/dotfiles/system/.local/bin/build.emacs.aur.sh @@ -0,0 +1,6 @@ +#!/bin/sh +rm -rf emacs-git +git clone https://aur.archlinux.org/emacs-git.git +cd emacs-git +sed -i 's/^JIT=\(     \)/JIT="YES"/' PKGBUILD +makepkg --syncdeps --install diff --git a/dotfiles/system/.local/bin/build.emacs.src.sh b/dotfiles/system/.local/bin/build.emacs.src.sh new file mode 100755 index 0000000..275a453 --- /dev/null +++ b/dotfiles/system/.local/bin/build.emacs.src.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +EMACSGITREPO=~/code/emacs +TAG=emacs-29.3 + +printf "\n\n... buildin' Emacs time ...\n" + +clone_repo() { +    printf "...grabbing a fresh copy of the source. this takes a minute...\n\n" +    git clone https://github.com/mirrors/emacs.git $EMACSGITREPO +    cd $EMACSGITREPO +} + +nuke_repo() { +    cd $HOME +    printf "...nuking  %s...\n\n" $EMACSGITREPO +    sudo rm -rf $EMACSGITREPO +} + +pull_latest() { +    printf "...okay, but lemme tidy up this mess first...\n\n" +    cd $EMACSGITREPO +    make clean +    printf "...pulling some fresh source code...\n\n" +    git pull +} + +build_emacs() { +	printf "...checking out tag %s...\n\n" $TAG +	git checkout $TAG +    printf "...starting the build...\n\n" +    cd $EMACSGITREPO +    $EMACSGITREPO/autogen.sh +    $EMACSGITREPO/configure --with-x-toolkit=lucid  \ +                --with-modules \ +                --with-xwidgets \ +                --with-treesitter \ +                --with-mailutils \ +                --with-libsystemd \ +                --with-imagemagick  \ +                --with-xml2 \ +                --with-libsystemd \ +                -with-gif --with-jpeg --with-png --with-tiff \ +                CFLAGS='-O2 -march=native' +    make -j$(nproc) +} + +if [ -d "$EMACSGITREPO" ]; then +    printf "\n...one sec. an Emacs repository exists at %s already. Shall I first...\n" $EMACSGITREPO +    printf "(r)efresh the existing repository with any latest commits, or\n" +    printf "(c)lobber what's there and grab a fresh copy of the source, or \n" +    printf "(b)uild what's there?\n\n" +    read -p "Your choice: (r/c/s): " answer +    printf "\n\n" +    case ${answer:0:1} in +        c|C ) +            nuke_repo +            clone_repo +            ;; +        r|R) +            pull_latest +            ;; +        b|B) +            printf "...ok, just buildin'...\n\n" +            ;; +        * ) +            printf "wake up. you didn't choose a valid option. enjoy your day.\n\n" +            exit 1 +            ;; +    esac +else +    printf "...everything checks out, so let's get this rolling....\n" +    clone_repo +fi + +build_emacs diff --git a/dotfiles/system/.local/bin/calibre-install b/dotfiles/system/.local/bin/calibre-install new file mode 100755 index 0000000..42b007c --- /dev/null +++ b/dotfiles/system/.local/bin/calibre-install @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# remember location +export SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# download and install latest calibre +sudo -v && wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sudo sh /dev/stdin + +# install current plugins and config +# tar -xf $SRCDIR/../assets/calibre.tar.gz -C ~/.config/ diff --git a/dotfiles/system/.local/bin/colorpick b/dotfiles/system/.local/bin/colorpick new file mode 100755 index 0000000..b5e1aff --- /dev/null +++ b/dotfiles/system/.local/bin/colorpick @@ -0,0 +1,6 @@ +#!/bin/sh +# displays colorpicker app +# turns cursor into crosshairs, adds preview at bottom left +# selected color is added to the clipboard + +colorpicker --short --one-shot --preview | xsel -b diff --git a/dotfiles/system/.local/bin/cron/README.md b/dotfiles/system/.local/bin/cron/README.md new file mode 100644 index 0000000..fa0c354 --- /dev/null +++ b/dotfiles/system/.local/bin/cron/README.md @@ -0,0 +1,11 @@ +# Important Note + +These cronjobs have components that require information about your current display to display notifications correctly. + +When you add them as cronjobs, I recommend you precede the command with commands as those below: + +``` +export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $USER)/bus; export DISPLAY=:0; . $HOME/.zprofile;  then_command_goes_here +``` + +This ensures that notifications will display, xdotool commands will function and environmental variables will work as well. diff --git a/dotfiles/system/.local/bin/cron/checkup b/dotfiles/system/.local/bin/cron/checkup new file mode 100755 index 0000000..bd3c634 --- /dev/null +++ b/dotfiles/system/.local/bin/cron/checkup @@ -0,0 +1,17 @@ +#!/bin/sh + +# Syncs repositories and downloads updates, meant to be run as a cronjob. + +notify-send "π¦ Repository Sync" "Checking for package updates..." + +sudo pacman -Syyuw --noconfirm || notify-send "Error downloading updates. + +Check your internet connection, if pacman is already running, or run update manually to see errors." +pkill -RTMIN+8 "${STATUSBAR:-dwmblocks}" + +if pacman -Qu | grep -v "\[ignored\]" +then +	notify-send "π Repository Sync" "Updates available. Click statusbar icon (π¦) for update." +else +	notify-send "π¦ Repository Sync"  "Sync complete. No new packages for update." +fi diff --git a/dotfiles/system/.local/bin/cron/crontog b/dotfiles/system/.local/bin/cron/crontog new file mode 100755 index 0000000..5aba5e6 --- /dev/null +++ b/dotfiles/system/.local/bin/cron/crontog @@ -0,0 +1,6 @@ +#!/bin/sh + +# Toggles all cronjobs off/on. +# Stores disabled crontabs in ~/.consaved until restored. + +([ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved ] && crontab - < "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved  && rm "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && notify-send "π Cronjobs re-enabled.") || ( crontab -l > "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && crontab -r && notify-send "π Cronjobs saved and disabled.") diff --git a/dotfiles/system/.local/bin/debugemacs b/dotfiles/system/.local/bin/debugemacs new file mode 100755 index 0000000..4585be1 --- /dev/null +++ b/dotfiles/system/.local/bin/debugemacs @@ -0,0 +1,4 @@ + #!/bin/sh + EMACS_PID=`pgrep emacs` + cd /home/cjennings/Projects/emacs/src + exec -a debug-emacs $TERM -e gdb /usr/local/bin/emacs $EMACS_PID diff --git a/dotfiles/system/.local/bin/displayselect b/dotfiles/system/.local/bin/displayselect new file mode 100755 index 0000000..f9e8062 --- /dev/null +++ b/dotfiles/system/.local/bin/displayselect @@ -0,0 +1,83 @@ +#!/bin/sh + +# A UI for detecting and selecting all displays. Probes xrandr for connected +# displays and lets user select one to use. User may also select "manual +# selection" which opens arandr. + +twoscreen() { # If multi-monitor is selected and there are two screens. + +    mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?") +    # Mirror displays using native resolution of external display and a scaled +    # version for the internal display +    if [ "$mirror" = "yes" ]; then +        external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:") +        internal=$(echo "$screens" | grep -v "$external") + +        res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" | \ +            tail -n 1 | awk '{print $1}') +        res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" | \ +            tail -n 1 | awk '{print $1}') + +        res_ext_x=$(echo "$res_external" | sed 's/x.*//') +        res_ext_y=$(echo "$res_external" | sed 's/.*x//') +        res_int_x=$(echo "$res_internal" | sed 's/x.*//') +        res_int_y=$(echo "$res_internal" | sed 's/.*x//') + +        scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l) +        scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l) + +        xrandr --output "$external" --auto --scale 1.0x1.0 \ +            --output "$internal" --auto --same-as "$external" \ +            --scale "$scale_x"x"$scale_y" +    else + +        primary=$(echo "$screens" | dmenu -i -p "Select primary display:") +        secondary=$(echo "$screens" | grep -v "$primary") +        direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?") +        xrandr --output "$primary" --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0 +    fi +    } + +morescreen() { # If multi-monitor is selected and there are more than two screens. +	primary=$(echo "$screens" | dmenu -i -p "Select primary display:") +	secondary=$(echo "$screens" | grep -v "$primary" | dmenu -i -p "Select secondary display:") +	direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?") +	tertiary=$(echo "$screens" | grep -v "$primary" | grep -v "$secondary" | dmenu -i -p "Select third display:") +	xrandr --output "$primary" --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | grep -v "$direction")"-of "$primary" --auto +	} + +multimon() { # Multi-monitor handler. +	case "$(echo "$screens" | wc -l)" in +		2) twoscreen ;; +		*) morescreen ;; +	esac ;} + +onescreen() { # If only one output available or chosen. +	xrandr --output "$1" --auto --scale 1.0x1.0 $(echo "$allposs" | grep -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -) +	} + +postrun() { # Stuff to run to clean up. +	setbg		# Fix background if screen size/arangement has changed. +	remaps		# Re-remap keys if keyboard added (for laptop bases) +	{ killall dunst ; setsid -f dunst ;} >/dev/null 2>&1 # Restart dunst to ensure proper location on screen +	} + +# Get all possible displays +allposs=$(xrandr -q | grep "connected") + +# Get all connected screens. +screens=$(echo "$allposs" | awk '/ connected/ {print $1}') + +# If there's only one screen +[ "$(echo "$screens" | wc -l)" -lt 2 ] && +	{ onescreen "$screens"; postrun; notify-send "π» Only one screen detected." "Using it in its optimal settings...";  exit ;} + +# Get user choice including multi-monitor and manual selection: +chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") && +case "$chosen" in +	"manual selection") arandr ; exit ;; +	"multi-monitor") multimon ;; +	*) onescreen "$chosen" ;; +esac + +postrun diff --git a/dotfiles/system/.local/bin/dmenuexitmenu b/dotfiles/system/.local/bin/dmenuexitmenu new file mode 100755 index 0000000..5570364 --- /dev/null +++ b/dotfiles/system/.local/bin/dmenuexitmenu @@ -0,0 +1,12 @@ +#!/bin/sh + +menuitems=("Lock ξ² \nSuspend σ°€ \nLogout σ°© \nReboot σ°ͺ \nShutdown ο \nCancel σ°Ί") +choice=$(echo -e $menuitems | dmenu -nb "#DAA520" -nf "#2E3440" -sb "#2E3440" -sf "#DAA520") + +case "$choice" in +	"Logout σ°© ") loginctl terminate-user $(whoami) ;; +	"Lock ξ² ") slock ;; +	"Suspend σ°€ ") systemctl suspend;; +	"Shutdown ο ") systemctl poweroff;; +	"Reboot σ°ͺ ")  systemctl reboot ;; +esac diff --git a/dotfiles/system/.local/bin/dmenuhandler b/dotfiles/system/.local/bin/dmenuhandler new file mode 100755 index 0000000..1c48f3a --- /dev/null +++ b/dotfiles/system/.local/bin/dmenuhandler @@ -0,0 +1,21 @@ +#!/bin/sh + +# Feed this script a link and it will give dmenu +# some choice programs to use to open it. +feed="${1:-$(printf "%s" | dmenu -p 'Paste URL or file path')}" + +case "$(printf "Copy URL\\nsxiv\\nsetbg\\nPDF\\nbrowser\\nlynx\\nvim\\nmpv\\nmpv loop\\nmpv float\\nqueue download\\nqueue yt-dlp\\nqueue yt-dlp audio" | dmenu -i -p "Open it with?")" in +	"Copy URL") echo "$feed" | xclip -selection clipboard ;; +	mpv) setsid -f mpv -quiet "$feed" >/dev/null 2>&1 ;; +	"mpv loop") setsid -f mpv -quiet --loop "$feed" >/dev/null 2>&1 ;; +	"mpv float") setsid -f "$TERMINAL" -e mpv --geometry=+0-0 --autofit=30%  --title="mpvfloat" "$feed" >/dev/null 2>&1 ;; +	"queue yt-dlp") qndl "$feed" >/dev/null 2>&1 ;; +	"queue yt-dlp audio") qndl "$feed" 'yt-dlp --embed-metadata -icx -f bestaudio/best' >/dev/null 2>&1 ;; +	"queue download") qndl "$feed" 'curl -LO' >/dev/null 2>&1 ;; +	PDF) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && zathura "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")"  >/dev/null 2>&1 ;; +	sxiv) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && sxiv -a "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")"  >/dev/null 2>&1 ;; +	vim) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && setsid -f "$TERMINAL" -e "$EDITOR" "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")"  >/dev/null 2>&1 ;; +	setbg) curl -L "$feed" > $XDG_CACHE_HOME/pic ; xwallpaper --zoom $XDG_CACHE_HOME/pic >/dev/null 2>&1 ;; +	browser) setsid -f "$BROWSER" "$feed" >/dev/null 2>&1 ;; +	lynx) lynx "$feed" >/dev/null 2>&1 ;; +esac diff --git a/dotfiles/system/.local/bin/dmenumount b/dotfiles/system/.local/bin/dmenumount new file mode 100755 index 0000000..3cb1f81 --- /dev/null +++ b/dotfiles/system/.local/bin/dmenumount @@ -0,0 +1,67 @@ +#!/bin/sh + +# Gives a dmenu prompt to mount unmounted drives and Android phones. If +# they're in /etc/fstab, they'll be mounted automatically. Otherwise, you'll +# be prompted to give a mountpoint from already existsing directories. If you +# input a novel directory, it will prompt you to create that directory. + +getmount() { \ +	[ -z "$chosen" ] && exit 1 +        # shellcheck disable=SC2086 +	mp="$(find $1 2>/dev/null | dmenu -i -p "Type in mount point.")" || exit 1 +	test -z "$mp" && exit 1 +	if [ ! -d "$mp" ]; then +		mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?") || exit 1 +		[ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp") +	fi +	} + +mountusb() { \ +	chosen="$(echo "$usbdrives" | dmenu -i -p "Mount which drive?")" || exit 1 +	chosen="$(echo "$chosen" | awk '{print $1}')" +	sudo -A mount "$chosen" 2>/dev/null && notify-send "π» USB mounting" "$chosen mounted." && exit 0 +	alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not ( -path *%s -prune ) ",$3}') +	getmount "/mnt /media /mount /home -maxdepth 5 -type d $alreadymounted" +	partitiontype="$(lsblk -no "fstype" "$chosen")" +	case "$partitiontype" in +		"vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;; +		"exfat") sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)";; +		*) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" "$mp";; +	esac +	notify-send "π» USB mounting" "$chosen mounted to $mp." +	} + +mountandroid() { \ +	chosen="$(echo "$anddrives" | dmenu -i -p "Which Android device?")" || exit 1 +	chosen="$(echo "$chosen" | cut -d : -f 1)" +	getmount "$HOME -maxdepth 3 -type d" +        simple-mtpfs --device "$chosen" "$mp" +	echo "OK" | dmenu -i -p "Tap Allow on your phone if it asks for permission and then press enter" || exit 1 +	simple-mtpfs --device "$chosen" "$mp" +	notify-send "π€ Android Mounting" "Android device mounted to $mp." +	} + +asktype() { \ +	choice="$(printf "USB\\nAndroid" | dmenu -i -p "Mount a USB drive or Android device?")" || exit 1 +	case $choice in +		USB) mountusb ;; +		Android) mountandroid ;; +	esac +	} + +anddrives=$(simple-mtpfs -l 2>/dev/null) +usbdrives="$(lsblk -rpo "name,type,size,mountpoint" | grep 'part\|rom' | awk '$4==""{printf "%s (%s)\n",$1,$3}')" + +if [ -z "$usbdrives" ]; then +	[ -z "$anddrives" ] && echo "No USB drive or Android device detected" && exit +	echo "Android device(s) detected." +	mountandroid +else +	if [ -z "$anddrives" ]; then +		echo "USB drive(s) detected." +		mountusb +	else +		echo "Mountable USB drive(s) and Android device(s) detected." +		asktype +	fi +fi diff --git a/dotfiles/system/.local/bin/dmenumountcifs b/dotfiles/system/.local/bin/dmenumountcifs new file mode 100755 index 0000000..46c2b57 --- /dev/null +++ b/dotfiles/system/.local/bin/dmenumountcifs @@ -0,0 +1,19 @@ +#!/bin/sh +# Gives a dmenu prompt to mount unmounted local NAS shares for read/write. +# Requirements - "%wheel ALL=(ALL) NOPASSWD: ALL" +# +# Browse for mDNS/DNS-SD services using the Avahi daemon... +srvname=$(avahi-browse _smb._tcp -t | awk '{print $4}' | dmenu -i -p "Which NAS?") || exit 1 +notify-send "Searching for network shares..." "Please wait..." +# Choose share disk... +share=$(smbclient -L "$srvname" -N | grep Disk | awk '{print $1}' | dmenu -i -p "Mount which share?") || exit 1 +# Format URL... +share2mnt=//"$srvname".local/"$share" + +sharemount() { +	mounted=$(mount -v | grep "$share2mnt") || ([ ! -d /mnt/"$share" ] && sudo mkdir /mnt/"$share") +	[ -z "$mounted" ] && sudo mount -t cifs "$share2mnt" -o user=nobody,password="",noperm /mnt/"$share" && notify-send "Netshare $share mounted" && exit 0 +	notify-send "Netshare $share already mounted"; exit 1 +} + +sharemount diff --git a/dotfiles/system/.local/bin/dmenurecord b/dotfiles/system/.local/bin/dmenurecord new file mode 100755 index 0000000..b83a7c5 --- /dev/null +++ b/dotfiles/system/.local/bin/dmenurecord @@ -0,0 +1,123 @@ +#!/bin/sh + +# Usage: +# `$0`: Ask for recording type via dmenu +# `$0 screencast`: Record both audio and screen +# `$0 video`: Record only screen +# `$0 audio`: Record only audio +# `$0 kill`: Kill existing recording +# +# If there is already a running instance, user will be prompted to end it. + +updateicon() { \ +	echo "$1" > /tmp/recordingicon +	pkill -RTMIN+9 "${STATUSBAR:-dwmblocks}" +	} + +killrecording() { +	recpid="$(cat /tmp/recordingpid)" +	# kill with SIGTERM, allowing finishing touches. +	kill -15 "$recpid" +	rm -f /tmp/recordingpid +	updateicon "" +	pkill -RTMIN+9 "${STATUSBAR:-dwmblocks}" +	# even after SIGTERM, ffmpeg may still run, so SIGKILL it. +	sleep 3 +	kill -9 "$recpid" +	exit +	} + +screencast() { \ +	ffmpeg -y \ +	-f x11grab \ +	-framerate 60 \ +	-s "$(xdpyinfo | awk '/dimensions/ {print $2;}')" \ +	-i "$DISPLAY" \ +	-f alsa -i default \ +	-r 30 \ + 	-c:v h264 -crf 0 -preset ultrafast -c:a aac \ +	"$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4" & +	echo $! > /tmp/recordingpid +	updateicon "βΊοΈποΈ" +       	} + +video() { ffmpeg \ +	-f x11grab \ +	-s "$(xdpyinfo | awk '/dimensions/ {print $2;}')" \ +	-i "$DISPLAY" \ + 	-c:v libx264 -qp 0 -r 30 \ +	"$HOME/video-$(date '+%y%m%d-%H%M-%S').mkv" & +	echo $! > /tmp/recordingpid +	updateicon "βΊοΈ" +	} + +webcamhidef() { ffmpeg \ +	-f v4l2 \ +	-i /dev/video0 \ +	-video_size 1920x1080 \ +	"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & +	echo $! > /tmp/recordingpid +	updateicon "π₯" +	} + +webcam() { ffmpeg \ +	-f v4l2 \ +	-i /dev/video0 \ +	-video_size 640x480 \ +	"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & +	echo $! > /tmp/recordingpid +	updateicon "π₯" +	} + + +audio() { \ +	ffmpeg \ +	-f alsa -i default \ +	-c:a flac \ +	"$HOME/audio-$(date '+%y%m%d-%H%M-%S').flac" & +	echo $! > /tmp/recordingpid +	updateicon "ποΈ" +	} + +askrecording() { \ +	choice=$(printf "screencast\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:") +	case "$choice" in +		screencast) screencast;; +		audio) audio;; +		video) video;; +		*selected) videoselected;; +		webcam) webcam;; +		"webcam (hi-def)") webcamhidef;; +	esac +	} + +asktoend() { \ +	response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?") && +	[ "$response" = "Yes" ] &&  killrecording +	} + +videoselected() +{ +	slop -f "%x %y %w %h" > /tmp/slop +	read -r X Y W H < /tmp/slop +	rm /tmp/slop + +	ffmpeg \ +	-f x11grab \ +	-framerate 60 \ +	-video_size "$W"x"$H" \ +	-i :0.0+"$X,$Y" \ +	-c:v libx264 -qp 0 -r 30 \ +	"$HOME/box-$(date '+%y%m%d-%H%M-%S').mkv" & +	echo $! > /tmp/recordingpid +	updateicon "βΊοΈ" +} + +case "$1" in +	screencast) screencast;; +	audio) audio;; +	video) video;; +	*selected) videoselected;; +	kill) killrecording;; +	*) ([ -f /tmp/recordingpid ] && asktoend && exit) || askrecording;; +esac diff --git a/dotfiles/system/.local/bin/dmenuumount b/dotfiles/system/.local/bin/dmenuumount new file mode 100755 index 0000000..946d12c --- /dev/null +++ b/dotfiles/system/.local/bin/dmenuumount @@ -0,0 +1,44 @@ +#!/bin/sh + +# A dmenu prompt to unmount drives. +# Provides you with mounted partitions, select one to unmount. +# Drives mounted at /, /boot and /home will not be options to unmount. + +unmountusb() { +	[ -z "$drives" ] && exit +	chosen="$(echo "$drives" | dmenu -i -p "Unmount which drive?")" || exit 1 +	chosen="$(echo "$chosen" | awk '{print $1}')" +	[ -z "$chosen" ] && exit +	sudo -A umount "$chosen" && notify-send "π» USB unmounting" "$chosen unmounted." +	} + +unmountandroid() { \ +	chosen="$(awk '/simple-mtpfs/ {print $2}' /etc/mtab | dmenu -i -p "Unmount which device?")" || exit 1 +	[ -z "$chosen" ] && exit +	sudo -A umount -l "$chosen" && notify-send "π€ Android unmounting" "$chosen unmounted." +	} + +asktype() { \ +	choice="$(printf "USB\\nAndroid" | dmenu -i -p "Unmount a USB drive or Android device?")" || exit 1 +	case "$choice" in +		USB) unmountusb ;; +		Android) unmountandroid ;; +	esac +	} + +drives=$(lsblk -nrpo "name,type,size,mountpoint,label" | awk -F':' '{gsub(/ /,":")}$4!~/\/boot|\/efi|\/home$|SWAP/&&length($4)>1{printf "%s (%s) %s\n",$4,$3,$5}') + +if ! grep simple-mtpfs /etc/mtab; then +	[ -z "$drives" ] && echo "No drives to unmount." &&  exit +	echo "Unmountable USB drive detected." +	unmountusb +else +	if [ -z "$drives" ] +	then +		echo "Unmountable Android device detected." +	       	unmountandroid +	else +		echo "Unmountable USB drive(s) and Android device(s) detected." +		asktype +	fi +fi diff --git a/dotfiles/system/.local/bin/dmenuunicode b/dotfiles/system/.local/bin/dmenuunicode new file mode 100755 index 0000000..b25876f --- /dev/null +++ b/dotfiles/system/.local/bin/dmenuunicode @@ -0,0 +1,18 @@ +#!/bin/sh + +# The famous "get a menu of emojis to copy" script. + +# Get user selection via dmenu from emoji file. +chosen=$(cut -d ';' -f1 ~/.local/share/emoji | dmenu -i -l 30 | sed "s/ .*//") + +# Exit if none chosen. +[ -z "$chosen" ] && exit + +# If you run this command with an argument, it will automatically insert the +# character. Otherwise, show a message that the emoji has been copied. +if [ -n "$1" ]; then +	xdotool type "$chosen" +else +	printf "$chosen" | xclip -selection clipboard +	notify-send "'$chosen' copied to clipboard." & +fi diff --git a/dotfiles/system/.local/bin/dotfiles_pushall b/dotfiles/system/.local/bin/dotfiles_pushall new file mode 100755 index 0000000..3eef2c6 --- /dev/null +++ b/dotfiles/system/.local/bin/dotfiles_pushall @@ -0,0 +1,6 @@ +#!/bin/bash +# Craig Jennings <craigmartinjennings@gmail.com> +# tired of pushing to multiple locations with multiple commands +/usr/bin/git --git-dir="$HOME"/.dotfiles/ --work-tree="$HOME" push origin main && \ +/usr/bin/git --git-dir="$HOME"/.dotfiles/ --work-tree="$HOME" push source main && \ +/usr/bin/git --git-dir="$HOME"/.dotfiles/ --work-tree="$HOME" push github main diff --git a/dotfiles/system/.local/bin/dwmstatus b/dotfiles/system/.local/bin/dwmstatus new file mode 100755 index 0000000..d0cff02 --- /dev/null +++ b/dotfiles/system/.local/bin/dwmstatus @@ -0,0 +1,79 @@ +#!/bin/sh +# displays status of microphone, camera, wifi, free home disk space, and date/time +# uses icons found in nerd fonts here: https://github.com/ryanoasis/nerd-fonts.git + +export DISPLAY=:0 +unset status + +########################################################################## +# MIC                                                                    # +########################################################################## + +# micsymbol_on="ο°" +# micsymbol_off="ο±" + +# amixer get Capture | grep '\[off\]' && mic="$micsymbol_off" || mic="$micsymbol_on" +# status="$mic " + + +########################################################################## +# VOLUME                                                                 # +########################################################################## + +speakersymbol="ξ" +if grep -q "yes" <<< $(pactl get-sink-mute $(pactl get-default-sink)) ; then +	speakersymbol="ξ" +	vol="" +else +	vol=$(echo $(pactl get-sink-volume $(pactl get-default-sink)) | cut -d"/" -f2 | xargs) +fi +status+="$speakersymbol $vol " + + +########################################################################## +# WIFI                                                                   # +########################################################################## + +wifisymbol_on="ξ" +wifisymbol_off="ξ" +# note: assumes we're using network-manager +ssid="$(nmcli -t -f active,ssid dev wifi | grep -E '^yes' | cut -d: -f2)" +wifi="$wifisymbol_off" +if [ "$ssid" != "" ]; then wifi="$wifisymbol_on $ssid"; fi +status+="$wifi " + + +########################################################################## +# BATTERY                                                                # +########################################################################## + +# desktops don't typically have batteries. if no batteries are found, skip this section +if [[ -n $(find /sys/class/power_supply/ -name "BAT?") ]]; then + +      # however, laptops may have multiple batteries, so list them individually +	  for battery in /sys/class/power_supply/BAT?; do +            batstat=$(sed "s/[Dd]ischarging/σ±/;s/[Nn]ot charging/σ°Ή/;s/[Cc]harging/σ°/;s/[Uu]nknown/ξ¦/;s/[Ff]ull/ξ₯/" "$battery"/status) +		    battery_level=$(cat "$battery"/capacity 2>/dev/null) +		    status+="${batstat} ${battery_level}% " +	  done +fi + + +########################################################################## +# /HOME DISK                                                             # +########################################################################## + +# disksymbol="ο" +# disk=$(df -hl | awk '{ if ($6 == "/home") print $4 " free" }') +# status+="$disksymbol $disk " + + +########################################################################## +# DATE / TIME                                                            # +########################################################################## + +# Format Example: Thu Mar 25 03:37 PM CDT +calendarsymbol="ο³" +status+="$calendarsymbol $(/bin/date +'%a %b %d %I:%M %p %Z')" + +xsetroot -name "$status" diff --git a/dotfiles/system/.local/bin/ec b/dotfiles/system/.local/bin/ec new file mode 100755 index 0000000..b409195 --- /dev/null +++ b/dotfiles/system/.local/bin/ec @@ -0,0 +1,2 @@ +#!/bin/sh +emacsclient -c -a "" $1 $2 $3 $4 & diff --git a/dotfiles/system/.local/bin/em b/dotfiles/system/.local/bin/em new file mode 100755 index 0000000..b409195 --- /dev/null +++ b/dotfiles/system/.local/bin/em @@ -0,0 +1,2 @@ +#!/bin/sh +emacsclient -c -a "" $1 $2 $3 $4 & diff --git a/dotfiles/system/.local/bin/et b/dotfiles/system/.local/bin/et new file mode 100755 index 0000000..1c3c4a0 --- /dev/null +++ b/dotfiles/system/.local/bin/et @@ -0,0 +1,2 @@ +#!/bin/sh +emacsclient -c -nw --alternate-editor="" $1 $2 $3 $4 &  diff --git a/dotfiles/system/.local/bin/exitmenu b/dotfiles/system/.local/bin/exitmenu new file mode 100755 index 0000000..2c55b34 --- /dev/null +++ b/dotfiles/system/.local/bin/exitmenu @@ -0,0 +1,16 @@ +#!/bin/sh + +CHOICES="Shutdown\nReboot\nLock\nHibernate\nSuspend\nToggle Airplane Mode\nToggle Powersave Mode" +CHOSEN=$(echo -e "$CHOICES" | dmenu -i) + +case $CHOSEN in +     "Shutdown") shutdown now ;; +     "Reboot") reboot ;; +     "Lock") xscreensaver-command --lock ;; +     "Hiberbate") systemctl hibernate ;; +     "Suspend") systemctl suspend ;; +     "Toggle Airplane Mode") airplanemodetoggle ;; +     "Toggle Powersave Mode") lowpowertoggle &&  notify-send "Battery Status" "$(acpi -b)" ;; +esac + +dwmstatus diff --git a/dotfiles/system/.local/bin/extractaudio b/dotfiles/system/.local/bin/extractaudio new file mode 100755 index 0000000..a665451 --- /dev/null +++ b/dotfiles/system/.local/bin/extractaudio @@ -0,0 +1,2 @@ +#!/bin/sh +ffmpeg -i $1 -q:a 0 -map a $1.mp3 diff --git a/dotfiles/system/.local/bin/gitconfig_defaults b/dotfiles/system/.local/bin/gitconfig_defaults new file mode 100755 index 0000000..c2f18ae --- /dev/null +++ b/dotfiles/system/.local/bin/gitconfig_defaults @@ -0,0 +1,5 @@ +git config --global user.email "craigmartinjennings@gmail.com" +git config --global user.name "Craig Jennings"" +git config --global merge.tool meld +git config --global core.editor "emacsclient -c -a''" +git config --global fetch.prune true diff --git a/dotfiles/system/.local/bin/gruv b/dotfiles/system/.local/bin/gruv new file mode 100755 index 0000000..b3e7f35 --- /dev/null +++ b/dotfiles/system/.local/bin/gruv @@ -0,0 +1,3 @@ +#!/bin/bash +dir=$pwd +for file in $(ls $dir | shuf) ; do mpv --no-video "$file"; done diff --git a/dotfiles/system/.local/bin/ifinstalled b/dotfiles/system/.local/bin/ifinstalled new file mode 100755 index 0000000..c192eba --- /dev/null +++ b/dotfiles/system/.local/bin/ifinstalled @@ -0,0 +1,12 @@ +#!/bin/sh + +# Some optional functions in LARBS require programs not installed by default. I +# use this little script to check to see if a command exists and if it doesn't +# it informs the user that they need that command to continue. This is used in +# various other scripts for clarity's sake. + +for x in "$@"; do +	if ! which "$x" >/dev/null 2>&1 && ! pacman -Qq "$x" >/dev/null 2>&1; then + 		notify-send "π¦ $x" "must be installed for this function." && exit 1 ; +	fi +done diff --git a/dotfiles/system/.local/bin/lfrun b/dotfiles/system/.local/bin/lfrun new file mode 100755 index 0000000..5bb0ba3 --- /dev/null +++ b/dotfiles/system/.local/bin/lfrun @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +cleanup() { +    exec 3>&- +	rm "$FIFO_UEBERZUG" +} + +if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then +	lf "$@" +else +	[ ! -d "$HOME/.cache/lf" ] && mkdir --parents "$HOME/.cache/lf" +	export FIFO_UEBERZUG="$HOME/.cache/lf/ueberzug-$$" +	mkfifo "$FIFO_UEBERZUG" +	ueberzug layer -s <"$FIFO_UEBERZUG" -p json & +	exec 3>"$FIFO_UEBERZUG" +	trap cleanup EXIT +	lf "$@" 3>&- +fi diff --git a/dotfiles/system/.local/bin/lfub b/dotfiles/system/.local/bin/lfub new file mode 100755 index 0000000..9012f50 --- /dev/null +++ b/dotfiles/system/.local/bin/lfub @@ -0,0 +1,24 @@ +#!/bin/sh + +# This is a wrapper script for lb that allows it to create image previews with +# ueberzug. This works in concert with the lf configuration file and the +# lf-cleaner script. + +set -e + +cleanup() { +    exec 3>&- +	rm "$FIFO_UEBERZUG" +} + +if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then +	lf "$@" +else +	[ ! -d "$HOME/.cache/lf" ] && mkdir -p "$HOME/.cache/lf" +	export FIFO_UEBERZUG="$HOME/.cache/lf/ueberzug-$$" +	mkfifo "$FIFO_UEBERZUG" +	ueberzug layer -s <"$FIFO_UEBERZUG" -p json & +	exec 3>"$FIFO_UEBERZUG" +	trap cleanup HUP INT QUIT TERM PWR EXIT +	lf "$@" 3>&- +fi diff --git a/dotfiles/system/.local/bin/linkhandler b/dotfiles/system/.local/bin/linkhandler new file mode 100755 index 0000000..cc971fc --- /dev/null +++ b/dotfiles/system/.local/bin/linkhandler @@ -0,0 +1,26 @@ +#!/bin/sh + +# Feed script a url or file location. +# If an image, it will view in sxiv, +# if a video or gif, it will view in mpv +# if a music file or pdf, it will download, +# otherwise it opens link in browser. + +if [ -z "$1" ]; then +	url="$(xclip -o)" +else +	url="$1" +fi + +case "$url" in +	*mkv|*webm|*mp4|*youtube.com/watch*|*youtube.com/playlist*|*youtu.be*|*hooktube.com*|*bitchute.com*|*videos.lukesmith.xyz*|*odysee.com*) +		setsid -f mpv -quiet "$url" >/dev/null 2>&1 ;; +	*png|*jpg|*jpe|*jpeg|*gif) +		curl -sL "$url" > "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && sxiv -a "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")"  >/dev/null 2>&1 & ;; +	*pdf|*cbz|*cbr) +		curl -sL "$url" > "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && zathura "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")"  >/dev/null 2>&1 & ;; +	*mp3|*flac|*opus|*mp3?source*) +		qndl "$url" 'curl -LO'  >/dev/null 2>&1 ;; +	*) +		[ -f "$url" ] && setsid -f "$TERMINAL" -e "$EDITOR" "$url" >/dev/null 2>&1 || setsid -f "$BROWSER" "$url" >/dev/null 2>&1 +esac diff --git a/dotfiles/system/.local/bin/lkg b/dotfiles/system/.local/bin/lkg new file mode 100755 index 0000000..aa9b3f1 --- /dev/null +++ b/dotfiles/system/.local/bin/lkg @@ -0,0 +1,4 @@ +#!/bin/sh +# Craig Jennings <c@cjennings.net> + +sudo zfs destroy -rR zroot@lkg && sudo zfs snapshot -r zroot@lkg && echo "lkg snapshot reset" || echo "lkg snapshot failed!" diff --git a/dotfiles/system/.local/bin/lkg_rollback b/dotfiles/system/.local/bin/lkg_rollback new file mode 100755 index 0000000..3cf4c59 --- /dev/null +++ b/dotfiles/system/.local/bin/lkg_rollback @@ -0,0 +1,12 @@ +#!/bin/sh + +sudo zfs rollback -rR zroot/var@lkg +sudo zfs rollback -rR zroot/var/log@lkg +sudo zfs rollback -rR zroot/usr@lkg +sudo zfs rollback -rR zroot/usr/home@lkg +sudo zfs rollback -rR zroot/ROOT/default@lkg +sudo zfs rollback -rR zroot/ROOT@lkg +sudo zfs rollback -rR zroot@lkg + +echo ""; echo "rollback complete. rebooting..." +sudo shutdown -r now diff --git a/dotfiles/system/.local/bin/lsbak b/dotfiles/system/.local/bin/lsbak new file mode 100755 index 0000000..7803135 --- /dev/null +++ b/dotfiles/system/.local/bin/lsbak @@ -0,0 +1 @@ +ls -laF /media/backup/renovo diff --git a/dotfiles/system/.local/bin/mkplaylist b/dotfiles/system/.local/bin/mkplaylist new file mode 100755 index 0000000..66b6e9c --- /dev/null +++ b/dotfiles/system/.local/bin/mkplaylist @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# Craig Jennings <c@cjennings.net> +# Basically just a bash wrapper around a find/grep/awk command pipe +# to generate m3u playlists from video or audio files in a directory. + +# One m3u playlist will be placed in the MUSIC_DIR, and another +# will be placed inside each playlist directory. +# It also converts .opus and .ogg files to .m4a for Android playback. + +# Note: +# This script requires the following utilities to be on the path: +# mid3v2 (aur package: python-mutagen) +# tageditor (aur package: tageditor) +# metaflac (aur package: flac) + +set -e + +MUSIC_DIR="$HOME/music" +# REQUIRED_TOOLS=("mid3v2" "tageditor") +REQUIRED_TOOLS=("mid3v2" "metaflac" "tageditor") + +#  ---------------------------- Functions ---------------------------- + +usage () { +    printf "\nUsage: mkplaylist <playlist_name>\n\n" +    printf "mkplaylist - creates an m3u playlist in the $MUSIC_DIR directory\n" +    printf "based the music and video files in directory which m3uplaylist is called.\n\n" +    printf " - this script should be run in the directory containing the music or video files\n" +    printf " - <playlist_name> is mandatory and shouldn't end with '.m3u' extension\n" +    printf " - change the destination ($MUSIC_DIR) by editing this script\n\n" +} + +tag_music_file() { +  while IFS= read -r file; do +    filename=$(basename "$file") +    extension="${filename##*.}" +    artist=$(basename "$file" | cut -d '-' -f 1) +    title=$(basename "$file" | cut -d '-' -f 2- | cut -d '.' -f 1) +    outputfile="$(dirname "$file")/$title.flac" + +    # If file is not already flac, convert it +    if [ "$extension" != "flac" ]; then + +      # Delete all tags using mid3v2 +      mid3v2 --delete-all "$file" +      ffmpeg -i "$file" -vn -c:a flac "$outputfile" +      file="$outputfile"  # Now we're working with the new FLAC file + +    fi + +    # Set artist and song title tags using metaflac +    metaflac --set-tag="ARTIST=$artist" --set-tag="TITLE=$title" "$file" + +   done +} + +# tag_music_file() { +#     while IFS= read -r file; do +#         filename=$(basename "$file") +#         extension="${filename##*.}" +#         artist=$(basename "$file" | cut -d '-' -f 1) +#         title=$(basename "$file" | cut -d '-' -f 2- | cut -d '.' -f 1) +#         outputfile="$(dirname "$file")/$title.flac" + +#         # Delete all tags using mid3v2 +#         mid3v2 --delete-all "$file" + +#         # If file is not already flac, convert it +#         if [ "$extension" != "flac" ]; then +#             ffmpeg -i "$file" -vn -c:a flac "$outputfile" +#             file="$outputfile"  # Now we're working with the new FLAC file +#         fi + +#         # Set artist and song title tags using metaflac +#         metaflac --set-tag="ARTIST=$artist" --set-tag="TITLE=$title" "$file" +#     done +# } + +# tag_music_file() { +#     while IFS= read -r file; do +#         # Extract artist and song title from filename +#         artist=$(basename "$file" | cut -d '-' -f 1) +#         title=$(basename "$file" | cut -d '-' -f 2- | cut -d '.' -f 1) +#         outputfile="$(dirname "$file")/$title.flac" + +#         # Delete all tags using mid3v2 +#         mid3v2 --delete-all "$file" + +#         # Convert to flac and save to new file +#         ffmpeg -i "$file" -vn -c:a flac "$outputfile" + +#         # Set artist and song title tags using metaflac +#         metaflac --set-tag="ARTIST=$artist" --set-tag="TITLE=$title" "$outputfile" +#     done +# } + +# tag_music_file() { +#     while IFS= read -r file; do +#         # Extract artist and song title from filename +#         artist=$(basename "$file" | cut -d '-' -f 1) +#         title=$(basename "$file" | cut -d '-' -f 2 | cut -d '.' -f 1) +#         outputfile="$(dirname "$file")/$title.flac" + +#         # Delete all tags using mid3v2 +#         mid3v2 --delete-all "$file" + +#         # Set artist and song title tags using mid3v2 +#         mid3v2 --artist="$artist" --song="$title" "$file" +#         # Convert to flac and save to new file +#         ffmpeg -i "$file" -vn -c:a flac "$outputfile" +#     done +# } + +# tag_music_file() { +#     while IFS= read -r file; do +#         # Extract artist and song title from filename +#         artist=$(basename "$file" | cut -d '-' -f 1) +#         title=$(basename "$file" | cut -d '-' -f 2 | cut -d '.' -f 1) + +#         # Delete all tags using mid3v2 +#         mid3v2 --delete-all "$file" + +#         # Set artist and song title tags using mid3v2 +#         mid3v2 --artist="$artist" --song="$title" "$file" +#     done +# } + + generate_music_m3u() { +     printf "retagging music files....\n" +     find "$(pwd)" -print | file -if - | grep -E '(audio)' | awk -F: '{print $1}' | tag_music_file + +     printf "generating playlist.'%s'...\n" "$LOCAL_PLAYLIST" +     find "$(pwd)" -print | file -if - | grep -E '(video|audio)' | +         awk -F: '{print $1}' | while read -r line; do basename "$line"; done > "$LOCAL_PLAYLIST" +     printf "generating playlist '%s'....\n" "$MUSIC_PLAYLIST" +     find "$(pwd)" -print | file -if - | grep -E '(video|audio)' | awk -F: '{print $1}' > "$MUSIC_PLAYLIST" +     printf "Done.\n\n" + } + + #  ----------------------------- Script ---------------------------- + + # display usage if user specifically requests it + TYPE=$(tr '[a-z]' '[A-Z]' <<< "$@"); + [ "$TYPE" = "HELP" ] && usage && exit 1 + [ "$TYPE" = "-H" ] &&  usage&& exit 1 + + # check that all necessary tools are installed + for tool in ${REQUIRED_TOOLS[@]}; do +     if ! type "$tool" >/dev/null 2>&1; then +         printf "ERROR: The script requires '%s' but it is not installed or not in PATH.\n" "$tool" +         exit 1 +     fi + done + + # use directory name for playlist name when parameter doesn't exist + if [ $# -eq 0 ] + then +     set -- "$(basename "$PWD")" +     echo "no playlist name entered, so using directory name: '$(basename "$PWD")'" + fi + + # ask to overwrite if the playlist already exists + MUSIC_PLAYLIST="$MUSIC_DIR/$@.m3u" + LOCAL_PLAYLIST="./$@.m3u" + + if [ -f "$MUSIC_PLAYLIST" ]; then +     read -p "$MUSIC_PLAYLIST exists. Overwrite (y/n) " yn +     if [ "$yn" != "y" ] && [ "$yn" != "Y" ]; then +         exit 0 +     fi + fi + + generate_music_m3u diff --git a/dotfiles/system/.local/bin/monitor b/dotfiles/system/.local/bin/monitor new file mode 100755 index 0000000..480e1bd --- /dev/null +++ b/dotfiles/system/.local/bin/monitor @@ -0,0 +1,50 @@ +#!/bin/sh +# Craig Jennings +# convenience script to switch monitors + +# this script assumes there are at most two monitors attached and we want to switch between then +CHOICES="Laptop\nLaptop-Scaled\nHome-Display\nExternal-Auto\nExternal-Scaled\nVirtualbox" + +# laptops always have a monitor connected when running the script. +LAPTOP=$(xrandr -q | grep primary | awk '$2 == "connected" {print $1}') +echo "primary monitor is $LAPTOP" + +# an external monitor will always be a connected monitor that isn't primary +EXTERNAL=$(xrandr -q | grep -v primary | awk '$2 == "connected" {print $1}') + +# start by resetting +xrandr -s 0 + +# disable if called automatically, otherwise you'll want the menu +# if there's only one monitor connected, setup laptop monitor +# if [ -z "$EXTERNAL" ]; then +# 	xrandr -s 0 +#     xrandr --output "$LAPTOP" --auto --dpi 144 --scale 0.6 +# 	exit 0 +# fi + +CHOSEN=$(echo -e "$CHOICES" | dmenu -i) + +case "$CHOSEN" in +"Laptop") +	xrandr --output "$LAPTOP" --auto --output "$EXTERNAL" --off +	;; +"Laptop-Scaled") +	xrandr --output "$LAPTOP" --auto --dpi 144 --scale 0.6 --output "$EXTERNAL" --off +	;; +"External") +	xrandr --output "$EXTERNAL" --auto --dpi 96 --mode 3440x1440 --scale 1.0 --output "$LAPTOP" --off +	;; +"External-Auto") +	xrandr --output "$EXTERNAL" --auto --output "$LAPTOP" --off +	;; +"External-Scaled") +	xrandr --output "$EXTERNAL" --auto --scale 0.6 --output "$LAPTOP" --off +    ;; +"Virtualbox") +    xrandr --output "$LAPTOP" --auto --mode 1920x1080 +    ;; +esac + +# restore the wallpaper after resolution change +nitrogen --restore diff --git a/dotfiles/system/.local/bin/mpd_play_yt_stream b/dotfiles/system/.local/bin/mpd_play_yt_stream new file mode 100755 index 0000000..b53f298 --- /dev/null +++ b/dotfiles/system/.local/bin/mpd_play_yt_stream @@ -0,0 +1,14 @@ +#!/bin/bash +# +MYHOST='127.0.0.1' # or your MPD host + +mpduri="$(yt-dlp -f best -g $1)#" +# mpduri="$(yt-dlp -g $1)#" +# TAG=$(yt-dlp -i --get-filename $1)  +# cadena="{\"title\":\"$TAG\"}" +# echo "$cadena" +# mpduri="$mpduri$cadena" +# echo "$mpduri" +mpc insert "$mpduri" +mpc next +mpc play diff --git a/dotfiles/system/.local/bin/msmtp-enqueue.sh b/dotfiles/system/.local/bin/msmtp-enqueue.sh new file mode 100755 index 0000000..c9beaca --- /dev/null +++ b/dotfiles/system/.local/bin/msmtp-enqueue.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env sh + +QUEUEDIR=$HOME/.msmtpqueue + +# Set secure permissions on created directories and files +umask 077 + +# Change to queue directory (create it if necessary) +if [ ! -d "$QUEUEDIR" ]; then +	mkdir -p "$QUEUEDIR" || exit 1 +fi +cd "$QUEUEDIR" || exit 1 + +# Create new unique filenames of the form +# MAILFILE:  ccyy-mm-dd-hh.mm.ss[-x].mail +# MSMTPFILE: ccyy-mm-dd-hh.mm.ss[-x].msmtp +# where x is a consecutive number only appended if you send more than one +# mail per second. +BASE="$(date +%Y-%m-%d-%H.%M.%S)" +if [ -f "$BASE.mail" ] || [ -f "$BASE.msmtp" ]; then +	TMP="$BASE" +	i=1 +	while [ -f "$TMP-$i.mail" ] || [ -f "$TMP-$i.msmtp" ]; do +		i=$((i + 1)) +	done +	BASE="$BASE-$i" +fi +MAILFILE="$BASE.mail" +MSMTPFILE="$BASE.msmtp" + +# Write command line to $MSMTPFILE +echo "$@" > "$MSMTPFILE" || exit 1 + +# Write the mail to $MAILFILE +cat > "$MAILFILE" || exit 1 + +# If we are online, run the queue immediately. +# Replace the test with something suitable for your site. +#ping -c 1 -w 2 SOME-IP-ADDRESS > /dev/null +#if [ $? -eq 0 ]; then +#	msmtp-runqueue.sh > /dev/null & +#fi + +exit 0 diff --git a/dotfiles/system/.local/bin/msmtp-listqueue.sh b/dotfiles/system/.local/bin/msmtp-listqueue.sh new file mode 100755 index 0000000..cc97c58 --- /dev/null +++ b/dotfiles/system/.local/bin/msmtp-listqueue.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +QUEUEDIR=$HOME/.msmtpqueue + +for i in $QUEUEDIR/*.mail; do +	grep -E -s --colour -h '(^From:|^To:|^Subject:)' "$i" || echo "No mail in queue"; +	echo " " +done diff --git a/dotfiles/system/.local/bin/msmtp-runqueue.sh b/dotfiles/system/.local/bin/msmtp-runqueue.sh new file mode 100755 index 0000000..1200610 --- /dev/null +++ b/dotfiles/system/.local/bin/msmtp-runqueue.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh + +QUEUEDIR="$HOME/.msmtpqueue" +LOCKFILE="$QUEUEDIR/.lock" +MAXWAIT=120 + +OPTIONS=$* + +# eat some options that would cause msmtp to return 0 without sendmail mail +case "$OPTIONS" in  +	*--help*) +	echo "$0: send mails in $QUEUEDIR" +	echo "Options are passed to msmtp" +	exit 0 +	;; +	*--version*) +	echo "$0: unknown version" +	exit 0 +	;; +esac + +# wait for a lock that another instance has set +WAIT=0 +while [ -e "$LOCKFILE" ] && [ "$WAIT" -lt "$MAXWAIT" ]; do +	sleep 1 +	WAIT="$((WAIT + 1))" +done +if [ -e "$LOCKFILE" ]; then +	echo "Cannot use $QUEUEDIR: waited $MAXWAIT seconds for" +	echo "lockfile $LOCKFILE to vanish, giving up." +	echo "If you are sure that no other instance of this script is" +	echo "running, then delete the lock file." +	exit 1 +fi + +# change into $QUEUEDIR  +cd "$QUEUEDIR" || exit 1 + +# check for empty queuedir +if [ "$(echo ./*.mail)" = './*.mail' ]; then +	echo "No mails in $QUEUEDIR" +	exit 0 +fi + +# lock the $QUEUEDIR +touch "$LOCKFILE" || exit 1 + +# process all mails +for MAILFILE in *.mail; do +	MSMTPFILE="$(echo $MAILFILE | sed -e 's/mail/msmtp/')" +	echo "*** Sending $MAILFILE to $(sed -e 's/^.*-- \(.*$\)/\1/' $MSMTPFILE) ..." +	if [ ! -f "$MSMTPFILE" ]; then +		echo "No corresponding file $MSMTPFILE found" +		echo "FAILURE" +		continue +	fi +	msmtp $OPTIONS $(cat "$MSMTPFILE") < "$MAILFILE" +	if [ $? -eq 0 ]; then +		rm "$MAILFILE" "$MSMTPFILE" +		echo "$MAILFILE sent successfully" +	else +		echo "FAILURE" +	fi +done + +# remove the lock +rm -f "$LOCKFILE" + +exit 0 diff --git a/dotfiles/system/.local/bin/open-file-in-eww b/dotfiles/system/.local/bin/open-file-in-eww new file mode 100755 index 0000000..e77899e --- /dev/null +++ b/dotfiles/system/.local/bin/open-file-in-eww @@ -0,0 +1,2 @@ +#!/bin/sh +emacsclient --eval "(eww-open-file \"$1\")" diff --git a/dotfiles/system/.local/bin/opus2mp3 b/dotfiles/system/.local/bin/opus2mp3 new file mode 100755 index 0000000..eef37ed --- /dev/null +++ b/dotfiles/system/.local/bin/opus2mp3 @@ -0,0 +1,3 @@ +#!/bin/sh +# Craig Jennings Monday, April 25, 2022  +for f in *.opus; do ffmpeg -i "$f" -codec:v copy -codec:a libmp3lame -q:a 2 "${f%.opus}.mp3"; done diff --git a/dotfiles/system/.local/bin/project b/dotfiles/system/.local/bin/project new file mode 100755 index 0000000..cf5918d --- /dev/null +++ b/dotfiles/system/.local/bin/project @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +echo "" + +# Check parameter +if [ "$#" -ne 1 ] || [ "$1" != "start" ] && [ "$1" != "end" ]; then +    echo "This script must be called with either 'start' or 'end' as a parameter." +    exit 1 +fi + +CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m" +CLEAR_LINE="\033[1K" + +# Define directories to process +project_dirs="$HOME/projects" +code_dirs="$HOME/code" +sync_dirs="$HOME/sync" + + +# Git pull quietly unless there's an error +git_maybe_pull() { +    git fetch --quiet +    if ! git diff --quiet HEAD FETCH_HEAD; then +        git pull --quiet + +        # clear line and message +        echo -ne "\033[1K" +        echo -e "\\rpulled remote changes into $1" +    fi +} + +# Git stash quietly unless there's an error +git_stash () { +    git stash > /dev/null 2>&1 || \ +        echo "git stash error in $1: $? " +} + +# Git stash pop quietly unless there's an error +git_stash_pop () { +    git stash pop > /dev/null 2>&1 || \ +        echo "git stash error in $1: $? " +} + +# Function to process a directory +process_directory() { +    if [ -d "$1/.git" ]; then +        # Check remote repository +        cd "$1" + +        # skip URLs with http/s URLS as they're directories cloned for reference only +        # skip git directories with no remote repository associated as well +        remote_url=$(git config --get remote.origin.url) +        if [ -n "$remote_url" ]; then +            # if remote URL is http or https or empty, skip the directory +            if [ -z "$remote_url" ] || echo "$remote_url" | grep -E -q "^(http|https)://"; then +                return +            fi + +            # clear line and update directory +            echo -ne "$CLEAR_LINE" +            echo -ne "\\rchecking: $1 " + +            if [ "$2" = "start" ]; then +                if [ -n "$(git status --porcelain)" ]; then +                    # notify user of uncommitted work +                    echo ""; echo ">>>> uncommitted work found in $1"; + +                    # git stash, pull latest files, then pop uncommitted work +                    git_stash "$1" +                    git_maybe_pull "$1" +                    git_stash_pop "$1" +                else +                    # retrieve any latest changes +                    git_maybe_pull "$1" +                fi +            elif [ "$2" = "end" ]; then +                # Check for uncommitted work +                if [ -n "$(git status --porcelain)" ]; then +                    echo ""; echo ">>>> Uncommitted work found in $1. <<<<";  echo "" +                fi +                return  # Skip pulling changes +            fi +        fi +    fi +} + +# Process directories +for directory in "$project_dirs"/*; do +    process_directory "$directory" "$1" +done +for directory in "$sync_dirs"/*; do +    process_directory "$directory" "$1" +done +for directory in "$code_dirs"/*; do +    process_directory "$directory" "$1" +done + +# clear line and message finished +echo -ne "\033[1K" +echo -ne "\\rfinished.\n" diff --git a/dotfiles/system/.local/bin/prompt b/dotfiles/system/.local/bin/prompt new file mode 100755 index 0000000..666434f --- /dev/null +++ b/dotfiles/system/.local/bin/prompt @@ -0,0 +1,8 @@ +#!/bin/sh + +# A dmenu binary prompt script. +# Gives a dmenu prompt labeled with $1 to perform command $2. +# For example: +# `./prompt "Do you want to shutdown?" "shutdown -h now"` + +[ "$(printf "No\\nYes" | dmenu -i -p "$1" -nb darkred -sb red -sf white -nf gray )" = "Yes" ] && $2 diff --git a/dotfiles/system/.local/bin/protonvpn b/dotfiles/system/.local/bin/protonvpn new file mode 100755 index 0000000..0f24068 --- /dev/null +++ b/dotfiles/system/.local/bin/protonvpn @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +flatpak run com.protonvpn.www
\ No newline at end of file diff --git a/dotfiles/system/.local/bin/ps-mem b/dotfiles/system/.local/bin/ps-mem new file mode 100755 index 0000000..b24b003 --- /dev/null +++ b/dotfiles/system/.local/bin/ps-mem @@ -0,0 +1,28 @@ +#!/bin/bash +# Craig Jennings <c@cjennings.net> +# Outputs a process's memory usage in multiple size units. + +# Get a list of all processes +procs=$(ps aux --sort=-%mem | awk '{print $2, $4, $11}' | fzf) + +# Check if a process was selected +if [ -z "$procs" ]; then +	echo "No process selected." +	exit 1 +fi + +# Get the PID of the selected process (first field) +PID=$(echo $procs | awk '{print $1}') + +# Get the process name +PROCNAME=$(ps -p $PID -o comm=) + +# Get the memory usage +KB=$(pmap -x $PID | grep total | awk '{print $4}') + +# Convert to MB and GB +MB=$(echo "scale=2; $KB / 1024" | bc) +GB=$(echo "scale=2; $MB / 1024" | bc) + +# Print the memory usage +printf "$PROCNAME (pid $PID) mem usage: $KB KB | $MB MB | $GB GB\n\n" diff --git a/dotfiles/system/.local/bin/recordnow b/dotfiles/system/.local/bin/recordnow new file mode 100755 index 0000000..4e2d04a --- /dev/null +++ b/dotfiles/system/.local/bin/recordnow @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +# Craig Jennings <c@cjennings.net> + +# Start a screen recording using ffmpeg to capture the entire +# screen along with all audio and the microphone. + +# Make sure that ffmpeg is in the path and the destination directory +# exists. + +LOCATION="$HOME/videos/recordings" +NAME=$(date +'%Y-%m-%d-%H-%M-%S') +echo $NAME + +# create the directory if it doesn't exist +if [ ! -d "$LOCATION" ]; then +  mkdir -p "$LOCATION" +fi + +# error out if ffmpeg isn't installed +if ! command -v ffmpeg &> /dev/null +then +    echo "ERROR: ffmpeg couldn't be found. Please ensure it's installed and added to your PATH." +    exit +fi + +ffmpeg -framerate 30 -f x11grab -i :0.0+ -f pulse -i alsa_input.pci-0000_00_1b.0.analog-stereo -ac 1 -f pulse -i alsa_output.pci-0000_00_1b.0.analog-stereo.monitor -ac 2 "$LOCATION/$NAME".mkv diff --git a/dotfiles/system/.local/bin/refresharchkeys b/dotfiles/system/.local/bin/refresharchkeys new file mode 100755 index 0000000..db1e755 --- /dev/null +++ b/dotfiles/system/.local/bin/refresharchkeys @@ -0,0 +1,6 @@ +#!/bin/sh +sudo rm -R /etc/pacman.d/gnupg +sudo pacman-key --init +sudo pacman-key --populate archlinux +sudo pacman -Sy archlinux-keyring +sudo pacman -Syu diff --git a/dotfiles/system/.local/bin/remaps b/dotfiles/system/.local/bin/remaps new file mode 100755 index 0000000..c95ac84 --- /dev/null +++ b/dotfiles/system/.local/bin/remaps @@ -0,0 +1,11 @@ +#!/bin/sh + +# This script is called on startup to remap keys. +# Decrease key repeat delay to 300ms and increase key repeat rate to 50 per second. +xset r rate 300 50 +# Map the caps lock key to super, and map the menu key to right super. +setxkbmap -option caps:super,altwin:menu_win +# When caps lock is pressed only once, treat it as escape. +killall xcape 2>/dev/null ; xcape -e 'Super_L=Escape' +# Turn off caps lock if on since there is no longer a key for it. +xset -q | grep "Caps Lock:\s*on" && xdotool key Caps_Lock diff --git a/dotfiles/system/.local/bin/resetmimetypes b/dotfiles/system/.local/bin/resetmimetypes new file mode 100755 index 0000000..b744c24 --- /dev/null +++ b/dotfiles/system/.local/bin/resetmimetypes @@ -0,0 +1,273 @@ +# Remove Old Databases +rm ~/.config/mimeapps.list >> /dev/null 2>&1 +rm ~/.local/share/applications/mimeinfo.cache >> /dev/null 2>&1 +rm ~/.local/share/applications/mimeinfo.list >> /dev/null 2>&1 + +# Directory Node: thunar +# Open directory nodes in thunar +xdg-mime default thunar.desktop inode/directory + +# Audio Files: audacious +xdg-mime default audacious.desktop audio/basic +xdg-mime default audacious.desktop audio/flac.wav +xdg-mime default audacious.desktop audio/mp4 +xdg-mime default audacious.desktop audio/mpeg +xdg-mime default audacious.desktop audio/ogg +xdg-mime default audacious.desktop audio/opus +xdg-mime default audacious.desktop audio/vnd.rn-realaudio +xdg-mime default audacious.desktop audio/vnd.wav +xdg-mime default audacious.desktop audio/vorbis +xdg-mime default audacious.desktop audio/x-aiff +xdg-mime default audacious.desktop audio/x-mpegurl + +# Video Files: mpv +# xdg-mime default mpv.desktop application/mpeg4-iod +# xdg-mime default mpv.desktop application/mpeg4-muxcodetable +# xdg-mime default mpv.desktop application/ogg +# xdg-mime default mpv.desktop application/vnd.apple.mpegurl +# xdg-mime default mpv.desktop application/x-quicktime-media-link +# xdg-mime default mpv.desktop application/x-quicktimeplayer +# xdg-mime default mpv.desktop application/x-shockwave-flash +# xdg-mime default mpv.desktop video/avi +# xdg-mime default mpv.desktop video/divx +# xdg-mime default mpv.desktop video/flv +# xdg-mime default mpv.desktop video/mp4 +# xdg-mime default mpv.desktop video/mp4 +# xdg-mime default mpv.desktop video/mp4v-es +# xdg-mime default mpv.desktop video/mpeg +# xdg-mime default mpv.desktop video/mpeg-system +# xdg-mime default mpv.desktop video/msvideo +# xdg-mime default mpv.desktop video/ogg +# xdg-mime default mpv.desktop video/quicktime +# xdg-mime default mpv.desktop video/vnd.divx +# xdg-mime default mpv.desktop video/vnd.mpegurl +# xdg-mime default mpv.desktop video/vnd.rn-realvideo +# xdg-mime default mpv.desktop video/webm +# xdg-mime default mpv.desktop video/x-avi +# xdg-mime default mpv.desktop video/x-flv +# xdg-mime default mpv.desktop video/x-m4v +# xdg-mime default mpv.desktop video/x-matroska +# xdg-mime default mpv.desktop video/x-mpeg +# xdg-mime default mpv.desktop video/x-mpeg-system +# xdg-mime default mpv.desktop video/x-mpeg2 +# xdg-mime default mpv.desktop video/x-ms-wmv +# xdg-mime default mpv.desktop video/x-msvideo +# xdg-mime default mpv.desktop video/x-theora +# xdg-mime default mpv.desktop video/x-theora+ogg +# xdg-mime default mpv.desktop x-content/video-dvd +# xdg-mime default mpv.desktop x-content/video-svcd +# xdg-mime default mpv.desktop x-content/video-vcd + +# Video Files: vlc +xdg-mime default vlc.desktop application/mpeg4-iod +xdg-mime default vlc.desktop application/mpeg4-muxcodetable +xdg-mime default vlc.desktop application/ogg +xdg-mime default vlc.desktop application/vnd.apple.mpegurl +xdg-mime default vlc.desktop application/x-quicktime-media-link +xdg-mime default vlc.desktop application/x-quicktimeplayer +xdg-mime default vlc.desktop application/x-shockwave-flash +xdg-mime default vlc.desktop video/avi +xdg-mime default vlc.desktop video/divx +xdg-mime default vlc.desktop video/flv +xdg-mime default vlc.desktop video/mp4 +xdg-mime default vlc.desktop video/mp4 +xdg-mime default vlc.desktop video/mp4v-es +xdg-mime default vlc.desktop video/mpeg +xdg-mime default vlc.desktop video/mpeg-system +xdg-mime default vlc.desktop video/msvideo +xdg-mime default vlc.desktop video/ogg +xdg-mime default vlc.desktop video/quicktime +xdg-mime default vlc.desktop video/vnd.divx +xdg-mime default vlc.desktop video/vnd.mpegurl +xdg-mime default vlc.desktop video/vnd.rn-realvideo +xdg-mime default vlc.desktop video/webm +xdg-mime default vlc.desktop video/x-avi +xdg-mime default vlc.desktop video/x-flv +xdg-mime default vlc.desktop video/x-m4v +xdg-mime default vlc.desktop video/x-matroska +xdg-mime default vlc.desktop video/x-mpeg +xdg-mime default vlc.desktop video/x-mpeg-system +xdg-mime default vlc.desktop video/x-mpeg2 +xdg-mime default vlc.desktop video/x-ms-wmv +xdg-mime default vlc.desktop video/x-msvideo +xdg-mime default vlc.desktop video/x-theora +xdg-mime default vlc.desktop video/x-theora+ogg +xdg-mime default vlc.desktop x-content/video-dvd +xdg-mime default vlc.desktop x-content/video-svcd +xdg-mime default vlc.desktop x-content/video-vcd + + +# Images: NSXIV +xdg-mime default nsxiv.desktop image/bmp +xdg-mime default nsxiv.desktop image/gif +xdg-mime default nsxiv.desktop image/jpegmix.desktop; +xdg-mime default nsxiv.desktop image/jpg +xdg-mime default nsxiv.desktop image/png +xdg-mime default nsxiv.desktop image/tiff +xdg-mime default nsxiv.desktop image/x-bmp +xdg-mime default nsxiv.desktop image/x-eps +xdg-mime default nsxiv.desktop image/x-ico +xdg-mime default nsxiv.desktop image/x-icon +xdg-mime default nsxiv.desktop image/x-xbitmap +xdg-mime default nsxiv.desktop image/x-xpixmapq + +# Torrent Files: Transmission Remote +xdg-mime default io.github.TransmissionRemoteGtk-gtk.desktop application/x-bittorrent +xdg-mime default io.github.TransmissionRemoteGtk.desktop x-scheme-handler/magnet + +# Web Browser: Chromium Browser +# xdg-mime default chromium.desktop application/rdf+xml +# xdg-mime default chromium.desktop application/rss+xml +# xdg-mime default chromium.desktop application/xhtml+xml +# xdg-mime default chromium.desktop application/xhtml_xml +# xdg-mime default chromium.desktop application/xml +# # xdg-mime default chromium.desktop image/gif +# # xdg-mime default chromium.desktop image/jpeg +# # xdg-mime default chromoum.desktop image/png +# # xdg-mime default chromium.desktop image/webp +# xdg-mime default chromium.desktop text/html  +# xdg-mime default chromium.desktop text/xml +# xdg-mime default chromium.desktop x-scheme-handler/http +# xdg-mime default chromium.desktop x-scheme-handler/https + +# Web Browser: Google-Chrome Browser +# xdg-mime default google-chrome.desktop application/rdf+xml +# xdg-mime default google-chrome.desktop application/rss+xml +# xdg-mime default google-chrome.desktop application/xhtml+xml +# xdg-mime default google-chrome.desktop application/xhtml_xml +# xdg-mime default google-chrome.desktop application/xml +# # xdg-mime default google-chrome.desktop image/gif +# # xdg-mime default google-chrome.desktop image/jpeg +# # xdg-mime default chromoum.desktop image/png +# # xdg-mime default google-chrome.desktop image/webp +# xdg-mime default google-chrome.desktop text/html  +# xdg-mime default google-chrome.desktop text/xml +# xdg-mime default google-chrome.desktop x-scheme-handler/http +# xdg-mime default google-chrome.desktop x-scheme-handler/https + + +# Web Browser: Firefox +xdg-mime default firefox.desktop application/rdf+xml +xdg-mime default firefox.desktop application/rss+xml +xdg-mime default firefox.desktop application/xhtml+xml +xdg-mime default firefox.desktop application/xhtml_xml +xdg-mime default firefox.desktop application/xml +# xdg-mime default firefox.desktop image/gif +# xdg-mime default firefox.desktop image/jpeg +# xdg-mime default firefox.desktop image/png +# xdg-mime default firefox.desktop image/webp +xdg-mime default firefox.desktop text/html  +xdg-mime default firefox.desktop text/xml +xdg-mime default firefox.desktop x-scheme-handler/http +xdg-mime default firefox.desktop x-scheme-handler/https + +# Mobi Ebooks: calibre-ebook-reader +xdg-mime default calibre-ebook-viewer.desktop application/x-mobi8-ebook +xdg-mime default calibre-ebook-viewer.desktop application/x-mobipocket-ebook + +# Text and Source Code: Emacsclient +xdg-mime default emacsclient.desktop application/x-shellscript +xdg-mime default emacsclient.desktop text/english +xdg-mime default emacsclient.desktop text/markdown +xdg-mime default emacsclient.desktop text/plain +xdg-mime default emacsclient.desktop text/x-c +xdg-mime default emacsclient.desktop text/x-c++ +xdg-mime default emacsclient.desktop text/x-c++hdr +xdg-mime default emacsclient.desktop text/x-c++src +xdg-mime default emacsclient.desktop text/x-chdr +xdg-mime default emacsclient.desktop text/x-csrc +xdg-mime default emacsclient.desktop text/x-java +xdg-mime default emacsclient.desktop text/x-makefile +xdg-mime default emacsclient.desktop text/x-moc +xdg-mime default emacsclient.desktop text/x-pascal +xdg-mime default emacsclient.desktop text/x-tcl +xdg-mime default emacsclient.desktop text/x-tex + +# PDF/EPUB: Emacsclient +# xdg-mime default emacsclient.desktop application/eps +# xdg-mime default emacsclient.desktop application/epub+zip +# xdg-mime default emacsclient.desktop application/oxps +# xdg-mime default emacsclient.desktop application/pdf +# xdg-mime default emacsclient.desktop application/postscript +# xdg-mime default emacsclient.desktop application/ps +# xdg-mime default emacsclient.desktop application/x-fictionbook; +# xdg-mime default emacsclient.desktop image/eps +# xdg-mime default emacsclient.desktop image/vnd.djvu +# xdg-mime default emacsclient.desktop image/vnd.djvu+multipage + +# ePUB: Foliate +# xdg-mime default com.github.johnfactotum.Foliate.desktop application/epub+zip + +# PDF ePUB: Zathura +xdg-mime default org.pwmt.zathura.desktop application/eps               +xdg-mime default org.pwmt.zathura.desktop application/epub+zip +xdg-mime default org.pwmt.zathura.desktop application/oxps              +xdg-mime default org.pwmt.zathura.desktop application/pdf               +xdg-mime default org.pwmt.zathura.desktop application/postscript        +xdg-mime default org.pwmt.zathura.desktop application/ps                +xdg-mime default org.pwmt.zathura.desktop application/x-fictionbook;    +xdg-mime default org.pwmt.zathura.desktop image/eps                     +xdg-mime default org.pwmt.zathura.desktop image/vnd.djvu                +xdg-mime default org.pwmt.zathura.desktop image/vnd.djvu+multipage + +# PDF ePUB: Evince +# xdg-mime default org.gnome.Evince.desktop application/eps               +# # xdg-mime default org.gnome.Evince.desktop application/epub+zip          +# xdg-mime default org.gnome.Evince.desktop application/oxps              +# xdg-mime default org.gnome.Evince.desktop application/pdf               +# xdg-mime default org.gnome.Evince.desktop application/postscript        +# xdg-mime default org.gnome.Evince.desktop application/ps                +# # xdg-mime default org.gnome.Evince.desktop application/x-fictionbook;    +# # xdg-mime default org.gnome.Evince.desktop image/eps                     +# xdg-mime default org.gnome.Evince.desktop image/vnd.djvu                +# xdg-mime default org.gnome.Evince.desktop image/vnd.djvu+multipage + +# Comics Files: Zathura +xdg-mime default org.pwmt.zathura.desktop application/vnd.comicbook+zip +xdg-mime default org.pwmt.zathura.desktop application/vnd.comicbook-rar + +# Libreoffice Writer +xdg-mime default libreoffice-writer.desktop application/clarisworks +xdg-mime default libreoffice-writer.desktop application/macwriteii +xdg-mime default libreoffice-writer.desktop application/msword +xdg-mime default libreoffice-writer.desktop application/prs.plucker +xdg-mime default libreoffice-writer.desktop application/rtf +xdg-mime default libreoffice-writer.desktop application/vnd.lotus-wordpro +xdg-mime default libreoffice-writer.desktop application/vnd.ms-word +xdg-mime default libreoffice-writer.desktop application/vnd.ms-word.template.macroEnabled.12 +xdg-mime default libreoffice-writer.desktop application/vnd.ms-works +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text-flat-xml +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text-master +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text-master-template +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text-template +xdg-mime default libreoffice-writer.desktop application/vnd.oasis.opendocument.text-web +xdg-mime default libreoffice-writer.desktop application/vnd.openxmlformats-officedocument +xdg-mime default libreoffice-writer.desktop application/vnd.openxmlformats-officedocument.wordprocessingml.document +xdg-mime default libreoffice-writer.desktop application/vnd.palm +xdg-mime default libreoffice-writer.desktop application/vnd.stardivision.writer-global +xdg-mime default libreoffice-writer.desktop application/vnd.sun.xml.writer +xdg-mime default libreoffice-writer.desktop application/vnd.sun.xml.writer.global +xdg-mime default libreoffice-writer.desktop application/vnd.sun.xml.writer.template +xdg-mime default libreoffice-writer.desktop application/vnd.wordperfect +xdg-mime default libreoffice-writer.desktop application/wordperfect +xdg-mime default libreoffice-writer.desktop application/x-abiword +xdg-mime default libreoffice-writer.desktop application/x-aportisdoc +xdg-mime default libreoffice-writer.desktop application/x-doc +xdg-mime default libreoffice-writer.desktop application/x-extension-txt +xdg-mime default libreoffice-writer.desktop application/x-fictionbook+xml +xdg-mime default libreoffice-writer.desktop application/x-hwp +xdg-mime default libreoffice-writer.desktop application/x-iwork-pages-sffpages +xdg-mime default libreoffice-writer.desktop application/x-mswrite +xdg-mime default libreoffice-writer.desktop application/x-sony-bbeb +xdg-mime default libreoffice-writer.desktop application/x-starwriter +xdg-mime default libreoffice-writer.desktop application/x-t602 +xdg-mime default libreoffice-writer.desktop text/rtf + +# FTP Scheme-Handler: Filezilla +xdg-mime default filezilla.desktop x-scheme-handler/ftp + +# Org-Protocol Scheme-Handler: emacsclient +xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol + +update-desktop-database ~/.local/share/applications/ diff --git a/dotfiles/system/.local/bin/samedir b/dotfiles/system/.local/bin/samedir new file mode 100755 index 0000000..371ec64 --- /dev/null +++ b/dotfiles/system/.local/bin/samedir @@ -0,0 +1,10 @@ +#!/bin/sh + +# Open a terminal window in the same directory as the currently active window. + +PID=$(xprop -id "$(xprop -root | xprop -root | sed -n "/_NET_ACTIVE_WINDOW/ s/^.*# // p")" | sed -n "/PID/ s/^.*= // p") +PID="$(pstree -lpA "$PID")" +PID="${PID##*"${SHELL##*/}"(}" +PID="${PID%%)*}" +cd "$(readlink /proc/"$PID"/cwd)" || return 1 +"$TERMINAL" diff --git a/dotfiles/system/.local/bin/screenshotmenu b/dotfiles/system/.local/bin/screenshotmenu new file mode 100755 index 0000000..c899dfc --- /dev/null +++ b/dotfiles/system/.local/bin/screenshotmenu @@ -0,0 +1,13 @@ +#!/bin/sh +# Requires maim, xdotool, and dmenu +# Uses dmenu to choose the type of screenshot to take,  + +case "$(printf "full screen\\nselected area\\ncurrent window\\nselected area (copy)\\ncurrent window (copy)\\nfull screen (copy)" | dmenu -l 6 -i -p "Screenshot which area?")" in +	"full screen") file="$(date +%Y.%m.%d-%M%S).png" && maim ~/pictures/screenshots/$file  && notify-send "Image selection saved to ~/pictures/screenshots/$file" ;; +	"full screen (5 sec delay)") file="$(date +%Y.%m.%d-%M%S).png" && sleep 5 && maim ~/pictures/screenshots/$file  && notify-send "Image selection saved to ~/pictures/screenshots/$file" ;; +    "selected area") file="$(date +%Y.%m.%d-%M%S).png" && maim -s ~/pictures/screenshots/$file  && notify-send "Image selection saved to ~/pictures/screenshots/$file" ;; +	"current window") maim -i "$(xdotool getactivewindow)" '~/pictures/screenshots/$(date +%Y.%m.%d-%M%S).png'  && notify-send "Image selection saved to ~/pictures/screenshots/" ;; +	"selected area (copy)") maim -s | xclip -selection clipboard -t image/png && notify-send "Image selection copied to clipboard." ;; +	"current window (copy)") maim -i "$(xdotool getactivewindow)" | xclip -selection clipboard -t image/png && notify-send "Image selection copied to clipboard." ;; +	"full screen (copy)") maim | xclip -selection clipboard -t image/png && notify-send "Image copied to clipboard." ;; +esac diff --git a/dotfiles/system/.local/bin/setbg b/dotfiles/system/.local/bin/setbg new file mode 100755 index 0000000..b72dc7d --- /dev/null +++ b/dotfiles/system/.local/bin/setbg @@ -0,0 +1,34 @@ +#!/bin/sh + +# This script does the following: +#	Run by itself, set the wallpaper (at X start). +#	If given a file, set that as the new wallpaper. +#	If given a directory, choose random file in it. +#	If wal is installed, also generates a colorscheme. + +# Location of link to wallpaper link. +bgloc="${XDG_DATA_HOME:-$HOME/.local/share}/bg" + +# Configuration files of applications that have their themes changed by pywal. +dunstconf="${XDG_CONFIG_HOME:-$HOME/.config}/dunst/dunstrc" +zathuraconf="${XDG_CONFIG_HOME:-$HOME/.config}/zathura/zathurarc" + +trueloc="$(readlink -f "$1")" && +case "$(file --mime-type -b "$trueloc")" in +	image/* ) ln -sf "$(readlink -f "$1")" "$bgloc" && notify-send -i "$bgloc" "Changing wallpaper..." ;; +	inode/directory ) ln -sf "$(find "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$bgloc" && notify-send -i "$bgloc" "Random Wallpaper chosen." ;; +	*) notify-send "πΌοΈ Error" "Not a valid image or directory." ; exit 1;; +esac + +# If pywal is installed, use it. +if command -v wal >/dev/null 2>&1 ; then +	wal -n -i "$(readlink -f $bgloc)" -o "${XDG_CONFIG_HOME:-$HOME/.config}/wal/postrun" >/dev/null 2>&1 +# If pywal is removed, return config files to normal. +else +	[ -f "$dunstconf.bak" ] && unlink "$dunstconf" && mv "$dunstconf.bak" "$dunstconf" +	[ -f "$zathuraconf.bak" ] && unlink "$zathuraconf" && mv "$zathuraconf.bak" "$zathuraconf" +fi + +xwallpaper --zoom "$bgloc" +# If running, dwm hit the key to refresh the color scheme. +pidof dwm >/dev/null && xdotool key super+F5 diff --git a/dotfiles/system/.local/bin/ssh-createkeys b/dotfiles/system/.local/bin/ssh-createkeys new file mode 100755 index 0000000..a1c14b6 --- /dev/null +++ b/dotfiles/system/.local/bin/ssh-createkeys @@ -0,0 +1,3 @@ +#!/bin/sh +# use strong passwords   +ssh-keygen -t ed25519 -a 100 diff --git a/dotfiles/system/.local/bin/startdwm b/dotfiles/system/.local/bin/startdwm new file mode 100755 index 0000000..2f2628d --- /dev/null +++ b/dotfiles/system/.local/bin/startdwm @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Craig Jennings <c@cjennings.net> +# starts the dwm process for a login manager +# saves the logfile of any output to a logfile + +dbus-update-activation-environment & + +logdir="$HOME/.local/var/logs" +logfile="$logdir/$(date +%Y-%m-%d_%H.dwm.log)" + +if ! [ -d "$logdir" ]; then +    mkdir -p "$logdir" +fi + +# allow sudo use of display +xhost si:localuser:root + +# merge local configuration for X client aplications +[ -f ~/.Xresources ] && xrdb -merge -I $HOME ~/.Xresources + +# merge xmodmap configuration +[[ -f ~/.Xmodmap ]] && xmodmap ~/.Xmodmap + +# start desktop environment applications +(conky | while read LINE; do xsetroot -name "$LINE"; done) & +xscreensaver --no-splash &  +xautolock -time 5 -locker "xscreensaver-command -activate" & +picom & +sxhkd & +emacs --daemon & +nitrogen --restore & +nm-applet & +blueman-applet & +battery_monitor & +mpd & +caffeine & +protonmail-bridge --no-window & +dunst & +signal-desktop --start-in-tray & +sudo powertop --auto-tune & +flameshot & +# dropbox & +touchpad-app & + +# start dwm +echo "$(date): Starting dwm" >> "$logfile" 2>&1 +exec dwm > $logfile 2>&1 diff --git a/dotfiles/system/.local/bin/starth b/dotfiles/system/.local/bin/starth new file mode 100755 index 0000000..98513d6 --- /dev/null +++ b/dotfiles/system/.local/bin/starth @@ -0,0 +1,9 @@ +#!/bin/sh +# startup file for Hyprland + +export WLR_EGL_NO_MODIFIERS=1 +export XDG_CURRENT_DESKTOP=Hyprland +export XDG_SESSION_TYPE=wayland +export XDG_SESSION_DESKTOP=Hyprland + +exec Hyprland diff --git a/dotfiles/system/.local/bin/statusbar/sb-battery b/dotfiles/system/.local/bin/statusbar/sb-battery new file mode 100755 index 0000000..93cbe08 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-battery @@ -0,0 +1,37 @@ +#!/bin/sh + +# Prints all batteries, their percentage remaining and an emoji corresponding +# to charge status (π for plugged up, π for discharging on battery, etc.). + +case $BLOCK_BUTTON in +	3) notify-send "π Battery module" "π: discharging +π: not charging +β»: stagnant charge +π: charging +β‘: charged +β: battery very low! +- Scroll to change adjust xbacklight." ;; +	4) xbacklight -inc 10 ;; +	5) xbacklight -dec 10 ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +# Loop through all attached batteries and format the info +for battery in /sys/class/power_supply/BAT?*; do +	# If non-first battery, print a space separator. +	[ -n "${capacity+x}" ] && printf " " +	# Sets up the status and capacity +	case "$(cat "$battery/status" 2>&1)" in +		"Full") status="β‘" ;; +		"Discharging") status="π" ;; +		"Charging") status="π" ;; +		"Not charging") status="π" ;; +		"Unknown") status="β»οΈ" ;; +		*) exit 1 ;; +	esac +	capacity="$(cat "$battery/capacity" 2>&1)" +	# Will make a warn variable if discharging and low +	[ "$status" = "π" ] && [ "$capacity" -le 25 ] && warn="β" +	# Prints the info +	printf "%s%s%d%%" "$status" "$warn" "$capacity"; unset warn +done && printf "\\n" diff --git a/dotfiles/system/.local/bin/statusbar/sb-clock b/dotfiles/system/.local/bin/statusbar/sb-clock new file mode 100755 index 0000000..e1ca8c7 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-clock @@ -0,0 +1,29 @@ +#!/bin/sh + +clock=$(date '+%I') + +case "$clock" in +	"00") icon="π" ;; +	"01") icon="π" ;; +	"02") icon="π" ;; +	"03") icon="π" ;; +	"04") icon="π" ;; +	"05") icon="π" ;; +	"06") icon="π" ;; +	"07") icon="π" ;; +	"08") icon="π" ;; +	"09") icon="π" ;; +	"10") icon="π" ;; +	"11") icon="π" ;; +	"12") icon="π" ;; +esac + +case $BLOCK_BUTTON in +	1) notify-send "This Month" "$(cal --color=always | sed "s/..7m/<b><span color=\"red\">/;s|..27m|</span></b>|")" && notify-send "Appointments" "$(calcurse -d3)" ;; +	2) setsid -f "$TERMINAL" -e calcurse ;; +	3) notify-send "π
 Time/date module" "\- Left click to show upcoming appointments for the next three days via \`calcurse -d3\` and show the month via \`cal\` +- Middle click opens calcurse if installed" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +date "+%Y %b %d (%a) $icon%I:%M%p" diff --git a/dotfiles/system/.local/bin/statusbar/sb-cpu b/dotfiles/system/.local/bin/statusbar/sb-cpu new file mode 100755 index 0000000..1572b52 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-cpu @@ -0,0 +1,12 @@ +#!/bin/sh + +case $BLOCK_BUTTON in +	1) notify-send "π₯ CPU hogs" "$(ps axch -o cmd:15,%cpu --sort=-%cpu | head)\\n(100% per core)" ;; +	2) setsid -f "$TERMINAL" -e htop ;; +	3) notify-send "π₯ CPU module " "\- Shows CPU temperature. +- Click to show intensive processes. +- Middle click to open htop." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +sensors | awk '/Core 0/ {print "π‘" $3}' diff --git a/dotfiles/system/.local/bin/statusbar/sb-cpubars b/dotfiles/system/.local/bin/statusbar/sb-cpubars new file mode 100755 index 0000000..297424e --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-cpubars @@ -0,0 +1,44 @@ +#!/bin/sh + +# Module showing CPU load as a changing bars. +# Just like in polybar. +# Each bar represents amount of load on one core since +# last run. + +# Cache in tmpfs to improve speed and reduce SSD load +cache=/tmp/cpubarscache + +case $BLOCK_BUTTON in +	2) setsid -f "$TERMINAL" -e htop ;; +	3) notify-send "πͺ¨ CPU load module" "Each bar represents +one CPU core";; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +# id total idle +stats=$(awk '/cpu[0-9]+/ {printf "%d %d %d\n", substr($1,4), ($2 + $3 + $4 + $5), $5 }' /proc/stat) +[ ! -f $cache ] && echo "$stats" > "$cache" +old=$(cat "$cache") +printf "πͺ¨" +echo "$stats" | while read -r row; do +	id=${row%% *} +	rest=${row#* } +	total=${rest%% *} +	idle=${rest##* } + +	case "$(echo "$old" | awk '{if ($1 == id) +		printf "%d\n", (1 - (idle - $3)  / (total - $2))*100 /12.5}' \ +		id="$id" total="$total" idle="$idle")" in + +		"0") printf "β";; +		"1") printf "β";; +		"2") printf "β";; +		"3") printf "β";; +		"4") printf "β
";; +		"5") printf "β";; +		"6") printf "β";; +		"7") printf "β";; +		"8") printf "β";; +	esac +done; printf "\\n" +echo "$stats" > "$cache" diff --git a/dotfiles/system/.local/bin/statusbar/sb-disk b/dotfiles/system/.local/bin/statusbar/sb-disk new file mode 100755 index 0000000..e947509 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-disk @@ -0,0 +1,23 @@ +#!/bin/sh + +# Status bar module for disk space +# $1 should be drive mountpoint, otherwise assumed /. + +location=${1:-/} + +[ -d "$location" ] || exit + +case $BLOCK_BUTTON in +	1) notify-send "π½ Disk space" "$(df -h --output=target,used,size)" ;; +	3) notify-send "π½ Disk module" "\- Shows used hard drive space. +- Click to show all disk info." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +case "$location" in +	"/home"* ) icon="π " ;; +	"/mnt"* ) icon="πΎ" ;; +	*) icon="π₯";; +esac + +printf "%s: %s\n" "$icon" "$(df -h "$location" | awk ' /[0-9]/ {print $3 "/" $2}')" diff --git a/dotfiles/system/.local/bin/statusbar/sb-doppler b/dotfiles/system/.local/bin/statusbar/sb-doppler new file mode 100755 index 0000000..b5833a7 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-doppler @@ -0,0 +1,279 @@ +#!/bin/sh + +# Show a Doppler RADAR of a user's preferred location. + +secs=600 # Download a new doppler radar if one hasn't been downloaded in $secs seconds. +radarloc="${XDG_CACHE_HOME:-$HOME/.cache}/radar" +doppler="${XDG_CACHE_HOME:-$HOME/.cache}/doppler.gif" + +pickloc() { chosen="$(echo "US: Northeast +US: Southeast +US: PacNorthWest +US: PacSouthWest +US: UpperMissVly +US: SouthMissVly +US: SouthPlains +US: NorthRockies +US: SouthRockies +US: Alaska +US: Carib +US: Hawaii +US: CentGrLakes +US: Conus-Large +US: KABR: Aberdeen, SD +US: KBIS: Bismarck, ND +US: KFTG: Denver/Boulder, CO +US: KDMX: Des Moines, IA +US: KDTX: Detroit, MI +US: KDDC: Dodge City, KS +US: KDLH: Duluth, MN +US: KCYS: Cheyenne, WY +US: KLOT: Chicago, IL +US: KGLD: Goodland, KS +US: KUEX: Hastings, NE +US: KGJX: Grand Junction, CO +US: KGRR: Grand Rapids, MI +US: KMVX: Fargo/Grand Forks, ND +US: KGRB: Green Bay, WI +US: KIND: Indianapolis, IN +US: KJKL: Jackson, KY +US: KARX: La Crosse, WI +US: KILX: Lincoln/Central Illinois, IL +US: KLVX: Louisville, KY +US: KMQT: Marquette +US: KMKX: Milwaukee, WI +US: KMPX: Minneapolis, MN +US: KAPX: Gaylord/Alpena, MI +US: KLNX: North Platte, NE +US: KIWX: N. Webster/Northern, IN +US: KOAX: Omaha, NE +US: KPAH: Paducah, KY +US: KEAX: Pleasant Hill, MO +US: KPUX: Pueblo, CO +US: KDVN: Quad Cities, IA +US: KUDX: Rapid City, SD +US: KRIW: Riverton, WY +US: KSGF: Springfield, MO +US: KLSX: St. LOUIS, MO +US: KFSD: Sioux Falls, IA +US: KTWX: Topeka, KS +US: KICT: Wichita, KS +US: KVWX: Paducah, KY +US: ICAO: Responsible Wfo +US: KLTX: WILMINGTON, NC +US: KCCX: State College/Central, PA +US: KLWX: Sterling, VA +US: KFCX: Blacksburg/Roanoke, VA +US: KRAX: Raleigh/Durham, NC +US: KGYX: Portland, ME +US: KDIX: Mt Holly/Philadelphia, PA +US: KPBZ: Pittsburgh, PA +US: KAKQ: Wakefield, VA +US: KMHX: Morehead City, NC +US: KGSP: Greer/Greenville/Sprtbg, SC +US: KILN: Wilmington/Cincinnati, OH +US: KCLE: Cleveland, OH +US: KCAE: Columbia, SC +US: KBGM: Binghamton, NY +US: KENX: Albany, NY +US: KBUF: Buffalo, NY +US: KCXX: Burlington, VT +US: KCBW: Caribou, ME +US: KBOX: Boston /Taunton, MA +US: KOKX: New York City, NY +US: KCLX: Charleston, SC +US: KRLX: Charleston, WV +US: ICAO: Responsible WFO +US: KBRO: Brownsville, TX +US: KABX: Albuquerque, NM +US: KAMA: Amarillo, TX +US: KFFC: Peachtree City/Atlanta, GA +US: KEWX: Austin/Sanantonio, TX +US: KBMX: Birmingham, AL +US: KCRP: Corpus Christi, TX +US: KFWS: Dallas / Ft. Worth, TX +US: KEPZ: El Paso, TX +US: KHGX: Houston/ Galveston, TX +US: KJAX: Jacksonville, FL +US: KBYX: Key West, FL +US: KMRX: Morristown/knoxville, TN +US: KLBB: Lubbock, TX +US: KLZK: Little Rock, AR +US: KLCH: Lake Charles, LA +US: KOHX: Nashville, TN +US: KMLB: Melbourne, FL +US: KNQA: Memphis, TN +US: KAMX: Miami, FL +US: KMAF: Midland/odessa, TX +US: KTLX: Norman, OK +US: KHTX: Huntsville, AL +US: KMOB: Mobile, AL +US: KTLH: Tallahassee, FL +US: KTBW: Tampa Bay Area, FL +US: KSJT: San Angelo, TX +US: KINX: Tulsa, OK +US: KSRX: Tulsa, OK +US: KLIX: New Orleans/slidell, LA +US: KDGX: Jackson, MS +US: KSHV: Shreveport, LA +US: ICAO: Responsible WFO +US: KLGX: Seattle / Tacoma, WA +US: KOTX: Spokane, WA +US: KEMX: Tucson, AZ +US: KYUX: Phoenix, AZ +US: KNKX: San Diego, CA +US: KMUX: Monterey/san Francisco, CA +US: KHNX: San Joaquin/hanford, CA +US: KSOX: San Diego, CA +US: KATX: Seattle / Tacoma, WA +US: KIWA: Phoenix, AZ +US: KRTX: Portland, OR +US: KSFX: Pocatello, ID +US: KRGX: Reno, NV +US: KDAX: Sacramento, CA +US: KMTX: Salt Lake City, UT +US: KPDT: Pendleton, OR +US: KMSX: Missoula, MT +US: KESX: Las Vegas, NV +US: KVTX: Los Angeles, CA +US: KMAX: Medford, OR +US: KFSX: Flagstaff, AZ +US: KGGW: Glasgow, MT +US: KLRX: Elko, NV +US: KBHX: Eureka, CA +US: KTFX: Great Falls, MT +US: KCBX: Boise, ID +US: KBLX: Billings, MT +US: KICX: Salt Lake City, UT +US: ICAO: Responsible Wfo W/ MSCF +US: PABC: Anchorage, AK +US: PAPD: Fairbanks, AK +US: PHKM: Honolulu, HI +US: PAHG: Anchorage, AK +US: PAKC: Anchorage, AK +US: PAIH: Anchorage, AK +US: PHMO: Honolulu, HI +US: PAEC: Fairbanks, AK +US: TJUA: San Juan, PR +US: PACG: Juneau, AK +US: PHKI: Honolulu, HI +US: PHWA: Honolulu, HI +US: ICAO: Responsible Wfo W/ MSCF +US: KFDR: Norman, OK +US: PGUA: Guam +US: KBBX: Sacramento, CA +US: KFDX: Albuquerque, NM +US: KGWX: Jackson, MS +US: KDOX: Wakefield, VA +US: KDYX: San Angelo, TX +US: KEYX: Las Vegas, NV +US: KEVX: Mobile, AL +US: KHPX: Paducah, KY +US: KTYX: Burlington, VT +US: KGRK: Dallas / Ft. Worth, TX +US: KPOE: Lake Charles, LA +US: KEOX: Tallahassee, FL +US: KHDX: El Paso, TX +US: KDFX: San Antonio, TX +US: KMXX: Birmingham, AL +US: KMBX: Bismarck, ND +US: KVAX: Jacksonville, FL +US: KJGX: Peachtree City/atlanta, GA +US: KVNX: Norman, OK +US: KVBX: Vandenberg Afb: Orcutt, CA +EU: Europe +EU: GB: Great Brittain +EU: SCAN: Scandinavia +EU: ALPS: The Alps +EU: NL: The Netherlands +EU: DE: Germany +EU: SP: Spain +EU: FR: France +EU: IT: Italy +EU: PL: Poland +EU: GR: Greece +EU: TU: Turkey +EU: RU: Russia +EU: BA: Bahrain +EU: BC: Botswana +EU: SE: Republic of Seychelles +EU: HU: Hungary +EU: UK: Ukraine +AF: AF: Africa +AF: WA: West Africa +AF: ZA: South Africa +AF: DZ: Algeria +AF: CE: Canary Islands +AF: NG: Nigeria +AF: TD: Chad +AF: CG: Democratic Republic of Congo +AF: EG: Egypt +AF: ET: Ethiopia +AF: CM: Cameroon +AF: IS: Israel +AF: LY: Libya +AF: MG: Madagascar +AF: MO: Morocco +AF: BW: Namibia +AF: SA: Saudi Arabia +AF: SO: Somalia +AF: SD: Sudan +AF: TZ: Tanzania +AF: TN: Tunisia +AF: ZM: Zambia +AF: KE: Kenya +AF: AO: Angola +DE: BAW: Baden-WΓΌrttemberg +DE: BAY: Bavaria +DE: BBB: Berlin +DE: BBB: Brandenburg +DE: HES: Hesse +DE: MVP: Mecklenburg-Western Pomerania +DE: NIB: Lower Saxony +DE: NIB: Bremen +DE: NRW: North Rhine-Westphalia +DE: RPS: Rhineland-Palatinate +DE: RPS: Saarland +DE: SAC: Saxony +DE: SAA: Saxony-Anhalt +DE: SHH: Schleswig-Holstein +DE: SHH: Hamburg +DE: THU: Thuringia" | dmenu -r -i -l 50 -p "Select a radar to use as default:" | tr "[:lower:]" "[:upper:]")" + +# Ensure user did not escape. +[ -z "$chosen" ] && exit 1 + +# Set continent code and radar code. +continentcode=${chosen%%:*} +radarcode=${chosen#* } radarcode=${radarcode%:*} + +# Print codes to $radarloc file. + printf "%s,%s\\n" "$continentcode" "$radarcode" > "$radarloc" ;} + +getdoppler() { +	cont=$(cut -c -2 "$radarloc") +	loc=$(cut -c 4- "$radarloc") +	notify-send "π¦οΈ Doppler RADAR" "Pulling most recent Doppler RADAR for $loc." +	case "$cont" in +		"US") curl -sL "https://radar.weather.gov/ridge/lite/${loc}_loop.gif" > "$doppler" ;; +		"EU") curl -sL "https://api.sat24.com/animated/${loc}/rainTMC/2/" > "$doppler" ;; +		"AF") curl -sL "https://api.sat24.com/animated/${loc}/rain/2/" > "$doppler" ;; +		"DE") loc="$(echo "$loc" | tr "[:upper:]" "[:lower:]")" +		curl -sL "https://www.dwd.de/DWD/wetter/radar/radfilm_${loc}_akt.gif" > "$doppler" ;; +	esac +} + +showdoppler() { setsid -f mpv --no-osc --loop=inf --no-terminal "$doppler" ;} + +case $BLOCK_BUTTON in +	1) [ ! -f "$radarloc" ] && pickloc && getdoppler +	[ $(($(date '+%s') - $(stat -c %Y "$doppler"))) -gt "$secs" ] && getdoppler +	showdoppler ;; +	2) pickloc && getdoppler && showdoppler ;; +	3) notify-send "πΊοΈ Doppler RADAR module" "\- Left click for local Doppler RADAR. +- Middle click to update RADAR location. +After $secs seconds, new clicks will also automatically update the doppler RADAR."  ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +echo πΊοΈ diff --git a/dotfiles/system/.local/bin/statusbar/sb-forecast b/dotfiles/system/.local/bin/statusbar/sb-forecast new file mode 100755 index 0000000..45584c5 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-forecast @@ -0,0 +1,35 @@ +#!/bin/sh + +# Displays todays precipication chance (β) and daily low (π₯Ά) and high (π). +# Usually intended for the statusbar. + +# If we have internet, get a weather report from wttr.in and store it locally. +# You could set up a shell alias to view the full file in a pager in the +# terminal if desired. This function will only be run once a day when needed. +weatherreport="${XDG_CACHE_HOME:-$HOME/.cache}/weatherreport" +getforecast() { curl -sf "wttr.in/$LOCATION" > "$weatherreport" || exit 1 ;} + +# Some very particular and terse stream manipulation. We get the maximum +# precipitation chance and the daily high and low from the downloaded file and +# display them with coresponding emojis. +showweather() { printf "%s" "$(sed '16q;d' "$weatherreport" | +	grep -wo "[0-9]*%" | sort -rn | sed "s/^/β/g;1q" | tr -d '\n')" +sed '13q;d' "$weatherreport" | grep -o "m\\([-+]\\)*[0-9]\\+" | sed 's/+//g' | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print " π₯Ά" $1 "Β°","π" $2 "Β°"}' ;} + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e less -Srf "$weatherreport" ;; +	2) getforecast && showweather ;; +	3) notify-send "π Weather module" "\- Left click for full forecast. +- Middle click to update forecast. +β: Chance of rain/snow +π₯Ά: Daily low +π: Daily high" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +# The test if our forcecast is updated to the day. If it isn't download a new +# weather report from wttr.in with the above function. +[ "$(stat -c %y "$weatherreport" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] || +	getforecast + +showweather diff --git a/dotfiles/system/.local/bin/statusbar/sb-help-icon b/dotfiles/system/.local/bin/statusbar/sb-help-icon new file mode 100755 index 0000000..8fa4a52 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-help-icon @@ -0,0 +1,17 @@ +#!/bin/sh + +# The clickable help menu. Middle click to restart wm. + +# If dwm is running, use dwm's readme and restart. +pidof dwm >/dev/null && +	READMEFILE=/usr/local/share/dwm/larbs.mom +	restartwm() { pkill -HUP dwm ;} || +		restartwm() { i3 restart ;} + +case $BLOCK_BUTTON in +	1) groff -mom "${READMEFILE:-${XDG_DATA_HOME:-$HOME/.local/share}/larbs/readme.mom}" -Tpdf | zathura - ;; +	2) restartwm ;; +	3) notify-send "β Help module" "\- Left click to open LARBS guide. +- Middle click to refresh window manager." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac; echo "β" diff --git a/dotfiles/system/.local/bin/statusbar/sb-internet b/dotfiles/system/.local/bin/statusbar/sb-internet new file mode 100755 index 0000000..94b7da2 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-internet @@ -0,0 +1,26 @@ +#!/bin/sh + +# Show wifi πΆ and percent strength or π‘ if none. +# Show π if connected to ethernet or β if none. +# Show π if a vpn connection is active + +case $BLOCK_BUTTON in +	1) "$TERMINAL" -e nmtui; pkill -RTMIN+4 dwmblocks ;; +	3) notify-send "π Internet module" "\- Click to connect +β: wifi disabled +π‘: no wifi connection +πΆ: wifi connection with quality +β: no ethernet +π: ethernet working +π: vpn is active +" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +if grep -xq 'up' /sys/class/net/w*/operstate 2>/dev/null ; then +	wifiicon="$(awk '/^\s*w/ { print "πΆ", int($3 * 100 / 70) "% " }' /proc/net/wireless)" +elif grep -xq 'down' /sys/class/net/w*/operstate 2>/dev/null ; then +	grep -xq '0x1003' /sys/class/net/w*/flags && wifiicon="π‘ " || wifiicon="β " +fi + +printf "%s%s%s\n" "$wifiicon" "$(sed "s/down/β/;s/up/π/" /sys/class/net/e*/operstate 2>/dev/null)" "$(sed "s/.*/π/" /sys/class/net/tun*/operstate 2>/dev/null)" diff --git a/dotfiles/system/.local/bin/statusbar/sb-iplocate b/dotfiles/system/.local/bin/statusbar/sb-iplocate new file mode 100755 index 0000000..02adab8 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-iplocate @@ -0,0 +1,10 @@ +#!/bin/sh + +# Gets your public ip address checks which country you are in and +# displays that information in the statusbar +# +# https://www.maketecheasier.com/ip-address-geolocation-lookups-linux/ + +ifinstalled "geoip" || exit +addr="$(curl ifconfig.me 2>/dev/null)" || exit +grep "flag: " "${XDG_DATA_HOME:-$HOME/.local/share}/larbs/emoji" | grep "$(geoiplookup "$addr" | sed 's/.*, //')" | sed "s/flag: //;s/;.*//" diff --git a/dotfiles/system/.local/bin/statusbar/sb-kbselect b/dotfiles/system/.local/bin/statusbar/sb-kbselect new file mode 100755 index 0000000..f0c923f --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-kbselect @@ -0,0 +1,16 @@ +#!/bin/sh +# works on any init system +# requirements: dmenu, xorg-setxkbmap +kb="$(setxkbmap -query | grep -oP 'layout:\s*\K\w+')" || exit 1 + +case $BLOCK_BUTTON in +	1) kb_choice="$(awk '/! layout/{flag=1; next} /! variant/{flag=0} flag {print $2, "- " $1}' /usr/share/X11/xkb/rules/base.lst | dmenu -l 15)" +	kb="$(echo "$kb_choice" | awk '{print $3}')" +	setxkbmap "$kb" +	pkill -RTMIN+30 "${STATUSBAR:-dwmblocks}";; +	3) notify-send "β¨  Keyboard/language module" "$(printf "%s" "\- Current layout: $(setxkbmap -query | grep -oP 'layout:\s*\K\w+')") +- Left click to change keyboard.";; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +echo "$kb" diff --git a/dotfiles/system/.local/bin/statusbar/sb-mailbox b/dotfiles/system/.local/bin/statusbar/sb-mailbox new file mode 100755 index 0000000..2132184 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-mailbox @@ -0,0 +1,20 @@ +#!/bin/sh + +# Displays number of unread mail and an loading icon if updating. +# When clicked, brings up `neomutt`. + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e neomutt ;; +	2) setsid -f mw -Y >/dev/null ;; +	3) notify-send "π¬ Mail module" "\- Shows unread mail +- Shows π if syncing mail +- Left click opens neomutt +- Middle click syncs mail" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)" + +pidof mbsync >/dev/null 2>&1 && icon="π" + +[ "$unread" = "0" ] && [ "$icon" = "" ] || echo "π¬$unread$icon" diff --git a/dotfiles/system/.local/bin/statusbar/sb-memory b/dotfiles/system/.local/bin/statusbar/sb-memory new file mode 100755 index 0000000..01d3daf --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-memory @@ -0,0 +1,12 @@ +#!/bin/sh + +case $BLOCK_BUTTON in +	1) notify-send "π§  Memory hogs" "$(ps axch -o cmd:15,%mem --sort=-%mem | head)" ;; +	2) setsid -f "$TERMINAL" -e htop ;; +	3) notify-send "π§  Memory module" "\- Shows Memory Used/Total. +- Click to show memory hogs. +- Middle click to open htop." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +free --mebi | sed -n '2{p;q}' | awk '{printf ("π§ %2.2fGiB/%2.2fGiB\n", ( $3 / 1024), ($2 / 1024))}' diff --git a/dotfiles/system/.local/bin/statusbar/sb-moonphase b/dotfiles/system/.local/bin/statusbar/sb-moonphase new file mode 100755 index 0000000..fab8b4d --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-moonphase @@ -0,0 +1,37 @@ +#!/bin/sh + +# Shows the current moon phase. + +moonfile="${XDG_DATA_HOME:-$HOME/.local/share}/moonphase" + +[ "$(stat -c %y "$moonfile" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] || +	{ curl -sf "wttr.in/?format=%m" > "$moonfile" || exit 1 ;} + +icon="$(cat "$moonfile")" + +case "$icon" in +	π) name="New" ;; +	π) name="Waxing Crescent" ;; +	π) name="First Quarter" ;; +	π) name="Waxing Gibbous" ;; +	π) name="Full" ;; +	π) name="Waning Gibbous" ;; +	π) name="Last Quarter" ;; +	π) name="Waning Crescent" ;; +	*) exit 1 ;; +esac + +echo "${icon-?}" + +case $BLOCK_BUTTON in +	3) notify-send "π Moon phase module" "Displays current moon phase. +- π: New +- π: Waxing Crescent +- π: First Quarter +- π: Waxing Gibbous +- π: Full +- π: Waning Gibbous +- π: Last Quarter +- π: Waning Crescent" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac diff --git a/dotfiles/system/.local/bin/statusbar/sb-mpdup b/dotfiles/system/.local/bin/statusbar/sb-mpdup new file mode 100755 index 0000000..af81a7d --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-mpdup @@ -0,0 +1,8 @@ +#!/bin/sh + +# This loop will update the mpd statusbar module whenever a command changes the +# music player's status. mpd must be running on X's start for this to work. + +while : ; do +	mpc idle >/dev/null && kill -45 "$(pidof "${STATUSBAR:-dwmblocks}")" || break +done diff --git a/dotfiles/system/.local/bin/statusbar/sb-music b/dotfiles/system/.local/bin/statusbar/sb-music new file mode 100755 index 0000000..7ea7032 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-music @@ -0,0 +1,19 @@ +#!/bin/sh + +filter() { mpc | sed "/^volume:/d;s/\\&/&/g;s/\\[paused\\].*/βΈ/g;/\\[playing\\].*/d;/^ERROR/Q" | paste -sd ' ' -;} + +pidof -x sb-mpdup >/dev/null 2>&1 || sb-mpdup >/dev/null 2>&1 & + +case $BLOCK_BUTTON in +	1) mpc status | filter ; setsid -f "$TERMINAL" -e ncmpcpp ;;  # right click, pause/unpause +	2) mpc toggle | filter ;;  # right click, pause/unpause +	3) mpc status | filter ; notify-send "π΅ Music module" "\- Shows mpd song playing. +- βΈ when paused. +- Left click opens ncmpcpp. +- Middle click pauses. +- Scroll changes track.";;  # right click, pause/unpause +	4) mpc prev   | filter ;;  # scroll up, previous +	5) mpc next   | filter ;;  # scroll down, next +	6) mpc status | filter ; "$TERMINAL" -e "$EDITOR" "$0" ;; +	*) mpc status | filter ;; +esac diff --git a/dotfiles/system/.local/bin/statusbar/sb-nettraf b/dotfiles/system/.local/bin/statusbar/sb-nettraf new file mode 100755 index 0000000..178f677 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-nettraf @@ -0,0 +1,29 @@ +#!/bin/sh + +# Module showing network traffic. Shows how much data has been received (RX) or +# transmitted (TX) since the previous time this script ran. So if run every +# second, gives network traffic per second. + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e bmon ;; +	3) notify-send "π Network traffic module" "π»: Traffic received +πΊ: Traffic transmitted" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +update() { +    sum=0 +    for arg; do +        read -r i < "$arg" +        sum=$(( sum + i )) +    done +    cache=/tmp/${1##*/} +    [ -f "$cache" ] && read -r old < "$cache" || old=0 +    printf %d\\n "$sum" > "$cache" +    printf %d\\n $(( sum - old )) +} + +rx=$(update /sys/class/net/[ew]*/statistics/rx_bytes) +tx=$(update /sys/class/net/[ew]*/statistics/tx_bytes) + +printf "π»%4sB πΊ%4sB\\n" $(numfmt --to=iec $rx $tx) diff --git a/dotfiles/system/.local/bin/statusbar/sb-news b/dotfiles/system/.local/bin/statusbar/sb-news new file mode 100755 index 0000000..fe701db --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-news @@ -0,0 +1,17 @@ +#!/bin/sh + +# Displays number of unread news items and an loading icon if updating. +# When clicked, brings up `newsboat`. + +case $BLOCK_BUTTON in +        1) setsid "$TERMINAL" -e newsboat ;; +	2) setsid -f newsup >/dev/null exit ;; +        3) notify-send "π° News module" "\- Shows unread news items +- Shows π if updating with \`newsup\` +- Left click opens newsboat +- Middle click syncs RSS feeds +<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + + cat /tmp/newsupdate 2>/dev/null || echo "$(newsboat -x print-unread | awk '{ if($1>0) print "π°" $1}')$(cat "${XDG_CONFIG_HOME:-$HOME/.config}"/newsboat/.update 2>/dev/null)" diff --git a/dotfiles/system/.local/bin/statusbar/sb-pacpackages b/dotfiles/system/.local/bin/statusbar/sb-pacpackages new file mode 100755 index 0000000..37ebed3 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-pacpackages @@ -0,0 +1,29 @@ +#!/bin/sh + +# Displays number of upgradeable packages. +# For this to work, have a `pacman -Sy` command run in the background as a +# cronjob every so often as root. This script will then read those packages. +# When clicked, it will run an upgrade via pacman. +# +# Add the following text as a file in /usr/share/libalpm/hooks/statusbar.hook: +# +# [Trigger] +# Operation = Upgrade +# Type = Package +# Target = * +# +# [Action] +# Description = Updating statusbar... +# When = PostTransaction +# Exec = /usr/bin/pkill -RTMIN+8 dwmblocks # Or i3blocks if using i3. + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e sb-popupgrade ;; +	2) notify-send "$(/usr/bin/pacman -Qu)" ;; +	3) notify-send "π Upgrade module" "π¦: number of upgradable packages +- Left click to upgrade packages +- Middle click to show upgradable packages" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +pacman -Qu | grep -Fcv "[ignored]" | sed "s/^/π¦/;s/^π¦0$//g" diff --git a/dotfiles/system/.local/bin/statusbar/sb-popupgrade b/dotfiles/system/.local/bin/statusbar/sb-popupgrade new file mode 100755 index 0000000..29d6230 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-popupgrade @@ -0,0 +1,9 @@ +#!/bin/sh + +printf "Beginning upgrade.\\n" + +yay -Syu +pkill -RTMIN+8 "${STATUSBAR:-dwmblocks}" + +printf "\\nUpgrade complete.\\nPress <Enter> to exit window.\\n\\n" +read -r _ diff --git a/dotfiles/system/.local/bin/statusbar/sb-price b/dotfiles/system/.local/bin/statusbar/sb-price new file mode 100755 index 0000000..42c84c1 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-price @@ -0,0 +1,50 @@ +#!/bin/sh + +# Usage: +#	price <url> <Name of currency> <icon> <Price to show in> +#	price bat "Basic Attention Token" π¦ +# When the name of the currency is multi-word, put it in quotes. + +[ -z "$3" ] && exit 1 + +# use $4 as currency, if not passed in use "usd" as default +currency="${4:-usd}" +interval="@14d"	# History contained in chart preceded by '@' (7d = 7 days) +dir="${XDG_DATA_HOME:-$HOME/.local/share}/crypto-prices" +pricefile="$dir/$1-$currency" +chartfile="$dir/$1-$currency-chart" + +updateprice() { temp="$(mktemp)" +	curl -s "$currency.rate.sx/1$1" > "$temp" && +		mv -f "$temp" "$pricefile" && +		curl -s "$currency.rate.sx/$1$interval" > "$temp" && +		mv -f "$temp" "$chartfile" ;} + +[ -d "$dir" ] || mkdir -p "$dir" + +[ "$(stat -c %x "$pricefile" 2>/dev/null | cut -d' ' -f1)" != "$(date '+%Y-%m-%d')" ] && +	updateprice "$1" + +case $BLOCK_BUTTON in +	1) setsid "$TERMINAL" -e less -Srf "$chartfile" ;; +	2) notify-send -u low "$3 Updating..." "Updating $2 price..." +		updateprice "$1" && notify-send "$3 Update complete." "$2 price is now +\$$(cat "$pricefile")" ;; +	3) uptime="$(date -d "$(stat -c %x "$pricefile")" '+%D at %T' | sed "s|$(date '+%D')|Today|")" +		notify-send "$3 $2 module" "\- <b>Exact price: \$$(cat "$pricefile")</b> +- Left click for chart of changes. +- Middle click to update. +- Shows π if updating prices. +- <b>Last updated: +	$uptime</b>" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +case "$currency" in +	usd) symb="$" ;; +	gbp) symb="Β£" ;; +	eur) symb="β¬" ;; +	btc) symb="βΏ" ;; +esac + +printf "$3$symb%0.2f$after" "$(cat "$pricefile")" diff --git a/dotfiles/system/.local/bin/statusbar/sb-tasks b/dotfiles/system/.local/bin/statusbar/sb-tasks new file mode 100755 index 0000000..586300e --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-tasks @@ -0,0 +1,20 @@ +#!/bin/sh + +# Originally by Andr3as07 <https://github.com/Andr3as07> +# Some changes by Luke +# Rebuild by Tenyun + +# This block displays the number running background tasks.  Requires tsp. + +num=$(tsp -l | awk -v numr=0 -v numq=0 '{if (/running/)numr++; if (/queued/)numq++} END{print numr+numq"("numq")"}') + +# Handle mouse clicks +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e tsp -l ;; +	3) notify-send "Tasks module" "π€: number of running/queued background tasks +- Left click opens tsp" ;; # Right click +	2) $EDITOR "$0" ;; # Middle click +esac + +[ "$num" != "0(0)" ] && +	echo "π€$num" diff --git a/dotfiles/system/.local/bin/statusbar/sb-torrent b/dotfiles/system/.local/bin/statusbar/sb-torrent new file mode 100755 index 0000000..6527005 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-torrent @@ -0,0 +1,27 @@ +#!/bin/sh + +transmission-remote -l | grep % | +	sed " # The letters are for sorting and will not appear. +	s/.*Stopped.*/A π/; +	s/.*Seeding.*/Z π±/; +	s/.*100%.*/N β
/; +	s/.*Idle.*/B π°οΈ/; +	s/.*Uploading.*/L β¬οΈ/; +	s/.*%.*/M β¬οΈ/" | +		sort -h | uniq -c | awk '{print $3 $1}' | paste -sd ' ' - + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e tremc ;; +	2) td-toggle ;; +	3) notify-send "π± Torrent module" "\- Left click to open tremc. +- Middle click to toggle transmission. +- Shift click to edit script. +Module shows number of torrents: +π: paused +π°: idle (seeds needed) +πΌ: uploading (unfinished) +π½: downloading +β
: done +π±: done and seeding" ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac diff --git a/dotfiles/system/.local/bin/statusbar/sb-volume b/dotfiles/system/.local/bin/statusbar/sb-volume new file mode 100755 index 0000000..3cfdc45 --- /dev/null +++ b/dotfiles/system/.local/bin/statusbar/sb-volume @@ -0,0 +1,30 @@ +#!/bin/sh + +# Prints the current volume or π if muted. + +case $BLOCK_BUTTON in +	1) setsid -f "$TERMINAL" -e pulsemixer ;; +	2) pamixer -t ;; +	4) pamixer --allow-boost -i 1 ;; +	5) pamixer --allow-boost -d 1 ;; +	3) notify-send "π’ Volume module" "\- Shows volume π, π if muted. +- Middle click to mute. +- Scroll to change." ;; +	6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +[ $(pamixer --get-mute) = true ] && echo π && exit + +vol="$(pamixer --get-volume)" + +if [ "$vol" -gt "70" ]; then +	icon="π" +elif [ "$vol" -gt "30" ]; then +	icon="π" +elif [ "$vol" -gt "0" ]; then +	icon="π" +else +        echo π && exit +fi + +echo "$icon$vol%" diff --git a/dotfiles/system/.local/bin/steam b/dotfiles/system/.local/bin/steam new file mode 100755 index 0000000..3d30238 --- /dev/null +++ b/dotfiles/system/.local/bin/steam @@ -0,0 +1,2 @@ +#!/bin/sh +flatpak run com.valvesoftware.Steam >> "$HOME/.local/var/logs/steam.log" 2>&1 diff --git a/dotfiles/system/.local/bin/sudo-update-grub b/dotfiles/system/.local/bin/sudo-update-grub new file mode 100755 index 0000000..5d67823 --- /dev/null +++ b/dotfiles/system/.local/bin/sudo-update-grub @@ -0,0 +1 @@ +sudo grub-mkconfig -o /boot/grub/grub.cfg diff --git a/dotfiles/system/.local/bin/sysupdate b/dotfiles/system/.local/bin/sysupdate new file mode 100755 index 0000000..357348d --- /dev/null +++ b/dotfiles/system/.local/bin/sysupdate @@ -0,0 +1,5 @@ +#!/bin/sh +# Craig Jennings <c@cjennings.net> + + +yay -Syu --noconfirm
\ No newline at end of file diff --git a/dotfiles/system/.local/bin/td-toggle b/dotfiles/system/.local/bin/td-toggle new file mode 100755 index 0000000..de1a0e6 --- /dev/null +++ b/dotfiles/system/.local/bin/td-toggle @@ -0,0 +1,12 @@ +#!/bin/sh + +# If transmission-daemon is running, will ask to kill, else will ask to start. + +if pidof transmission-daemon >/dev/null ; +then +	[ "$(printf "No\\nYes" | dmenu -i -p "Turn off transmission-daemon?")" = "Yes" ] && killall transmission-daemon && notify-send "transmission-daemon disabled." +else +	ifinstalled transmission-cli || exit +	[ "$(printf "No\\nYes" | dmenu -i -p "Turn on transmission daemon?")" = "Yes" ] && transmission-daemon && notify-send "transmission-daemon enabled." +fi +sleep 3 && pkill -RTMIN+7 "${STATUSBAR:-dwmblocks}" diff --git a/dotfiles/system/.local/bin/timezone-change b/dotfiles/system/.local/bin/timezone-change new file mode 100755 index 0000000..c5a4e5a --- /dev/null +++ b/dotfiles/system/.local/bin/timezone-change @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Craig Jennings <c@cjennings.net> + +# Convenience script since I can't ever remember the two part TZ identifier to +# change timezones. + +case $1 in +    "eastern"|"ny"|"nyc"|"boston"|"dc") +        sudo timedatectl set-timezone "US/Eastern" +        ;; +    "central"|"nola"|"home"|"chicago") +        sudo timedatectl set-timezone "US/Central" +        ;; +    "mountain") +        sudo timedatectl set-timezone "US/Mountain" +        ;; +    "pacific"|"sf"|"oakland"|"berkeley"|"hb"|"california") +        sudo timedatectl set-timezone "US/Pacific" +        ;; +    "london"|"england"|"britain"|"gb") +        sudo timedatectl set-timezone "Europe/London" +        ;; +    "hawaii") +        sudo timedatectl set-timezone "US/Hawaii" +        ;; +    "armenia"|"yerevan") +        sudo timedatectl set-timezone "Asia/Yerevan" +        ;; +    "greece"|"athens") +        sudo timedatectl set-timezone "Europe/Athens" +        ;; +    "germany"|"berlin") +        sudo timedatectl set-timezone "Europe/Berlin" +        ;; +    "turkey"|"istanbul") +        sudo timedatectl set-timezone "Europe/Istanbul" +        ;; +    "portugal"|"lisbon") +        sudo timedatectl set-timezone "Europe/Portugal" +        ;; +    "spain"|"madrid") +        sudo timedatectl set-timezone "Europe/Madrid" +        ;; +    "france"|"paris") +        sudo timedatectl set-timezone "Europe/Paris" +        ;; +    "italy"|"rome"|"naples"|"ischia") +        sudo timedatectl set-timezone "Europe/Rome" +        ;; +    "austria"|"vienna") +        sudo timedatectl set-timezone "Europe/Vienna" +        ;; +    "japan"|"tokyo") +        sudo timedatectl set-timezone "Asia/Tokyo" +        ;; +    "jamaica") +        sudo timedatectl set-timezone "America/Jamaica" +        ;; +    "st_lucia"|"grenadines"|"st_vincent"|"nevis"|"st_kitts"|"puerto_rico") +        sudo timedatectl set-timezone "America/Puerto_Rico" +        ;; +    *) +        echo +        "Invalid option chosen." +        echo +        "Some valid options are: eastern, central, pacific, rome, london, st_lucia, italy, france, spain ." +        ;; +esac diff --git a/dotfiles/system/.local/bin/timezone-set b/dotfiles/system/.local/bin/timezone-set new file mode 100755 index 0000000..8f48729 --- /dev/null +++ b/dotfiles/system/.local/bin/timezone-set @@ -0,0 +1,17 @@ +#!/bin/sh +# Craig Jennings <c@cjennings.net> + +# sets timezone based on the ip-address when there's network connectivity + +# Check network status +if ping -q -c 1 -W 1 google.com >/dev/null; then +    NEW_TIMEZONE="$(curl --fail --silent https://ipapi.co/timezone)" +    if sudo timedatectl set-timezone "$NEW_TIMEZONE"; then +        notify-send "Setting timezone to $NEW_TIMEZONE successul." +    else +        notify-send "Attempt to set timezone failed." +    fi +else +    notify-send "No network connection detected. Cannot set timezone automatically." +fi +dwmstatus diff --git a/dotfiles/system/.local/bin/toggle-touchpad b/dotfiles/system/.local/bin/toggle-touchpad new file mode 100755 index 0000000..9dde99b --- /dev/null +++ b/dotfiles/system/.local/bin/toggle-touchpad @@ -0,0 +1,16 @@ +#!/bin/sh +# Toggle touchpad status +# Using libinput and xinput + +# Use xinput list and do a search for touchpads. Then get the first one and get its name. +device="$(xinput list | grep -P '(?<= )[\w\s:]*(?i)(touchpad|synaptics)(?-i).*?(?=\s*id)' -o | head -n1)" + +# If it was activated disable it and if it wasn't disable it +if [[ "$(xinput list-props "$device" | grep -P ".*Device Enabled.*\K.(?=$)" -o)" == "1" ]] +then +    xinput disable "$device" +    notify-send "Touchpad" "Touchpad disabled" +else +    xinput enable "$device" +    notify-send "Touchpad" "Touchpad enabled" +fi diff --git a/dotfiles/system/.local/bin/torwrap b/dotfiles/system/.local/bin/torwrap new file mode 100755 index 0000000..8b20ad4 --- /dev/null +++ b/dotfiles/system/.local/bin/torwrap @@ -0,0 +1,7 @@ +#!/bin/sh + +ifinstalled tremc transmission-cli || exit + +! pidof transmission-daemon >/dev/null && transmission-daemon && notify-send "Starting torrent daemon..." + +$TERMINAL -e tremc; pkill -RTMIN+7 "${STATUSBAR:-dwmblocks}" diff --git a/dotfiles/system/.local/bin/touchpad-app b/dotfiles/system/.local/bin/touchpad-app new file mode 100755 index 0000000..6310c6d --- /dev/null +++ b/dotfiles/system/.local/bin/touchpad-app @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/bin/touchpad-indicator & 
\ No newline at end of file diff --git a/dotfiles/system/.local/bin/transadd b/dotfiles/system/.local/bin/transadd new file mode 100755 index 0000000..a598fad --- /dev/null +++ b/dotfiles/system/.local/bin/transadd @@ -0,0 +1,9 @@ +#!/bin/sh + +# Mimeapp script for adding torrent to transmission-daemon, but will also start the daemon first if not running. + +# transmission-daemon sometimes fails to take remote requests in its first moments, hence the sleep. + +pidof transmission-daemon >/dev/null || (transmission-daemon && notify-send "Starting transmission daemon..." && sleep 3 && pkill -RTMIN+7 "${STATUSBAR:-dwmblocks}") + +transmission-remote -a "$@" && notify-send "π½ Torrent added." diff --git a/dotfiles/system/.local/bin/update-backup-repositories b/dotfiles/system/.local/bin/update-backup-repositories new file mode 100755 index 0000000..a91b438 --- /dev/null +++ b/dotfiles/system/.local/bin/update-backup-repositories @@ -0,0 +1,56 @@ +#!/bin/sh +# Craig Jennings <c@cjennings.net> +# updates or clones repositories for backup + +REPOS_HOME="/media/backup/repositories/" + +# Create REPOS_HOME dir if not exists +if [ ! -d "$REPOS_HOME" ]; then +    mkdir -p "$REPOS_HOME" +fi + +# Make sure REPOS_HOME dir is writable +if [ ! -w "$REPOS_HOME" ]; then +    echo "Directory $REPOS_HOME is not writable." +    exit 1 +fi + +# Make sure git is available +if ! command -v git >/dev/null 2>&1; then +    echo "Git command does not exist. Please install git." +    exit 1 +fi + +cd "$REPOS_HOME" + +repos=" +git@cjennings.net:archsetup.git +git@cjennings.net:dmenu.git +git@cjennings.net:dotemacs.git +git@cjennings.net:dotfiles.git +git@cjennings.net:dwm.git +git@cjennings.net:pinentry-dmenu.git +git@cjennings.net:rsyncshot.git +git@cjennings.net:st.git +git@github.com:cjennings/emacs-wttrin.git +https://github.com/d12frosted/elpa-mirror.git +https://github.com/mirrors/emacs.git +" + +for repo in $repos; do +    dir=$(echo "$repo" | awk -F'[/:]' '{gsub(/.git/, "", $NF); print $NF}') +    # Note on the above awk command: +    # awk -F'[/:]':                        uses awk with a regex field delimiter that matches both : and / +    #                                      which will work with both ssh and http style URLs. +    # '{gsub(/.git/, "", $NF); print $NF}: This removes the ".git" postfixes from the last field ($NF) +    #                                      which leaves only the repo name. + +    fullpath="$REPOS_HOME$dir" +    if [ -d "$fullpath" ]; then +        echo "Repository $dir exists at $fullpath, pulling..." +        (cd "$fullpath" && git pull) +    else +        echo "Repository $dir doesn't exist at $fullpath, cloning..." +        cd "$REPOS_HOME" && git clone --depth 1 "$repo" "$fullpath" +    fi +done diff --git a/dotfiles/system/.local/bin/updatemirrors b/dotfiles/system/.local/bin/updatemirrors new file mode 100755 index 0000000..3ba4f7f --- /dev/null +++ b/dotfiles/system/.local/bin/updatemirrors @@ -0,0 +1,20 @@ +#!/bin/sh +# cjennings generates arch linux mirror list +echo "Updating mirrorlist. Please be patient. This may take a few minutes...." +echo " " + +sudo reflector \ +          --connection-timeout 3 \ +          --download-timeout 3 \ +          --protocol https \ +		  --country US \ +		  --age 18 \ +          --latest 20 \ +          --score 10 \ +          --fastest 5 \ +		  --sort score \ +          --save /etc/pacman.d/mirrorlist > /dev/null 2>&1  + +cat /etc/pacman.d/mirrorlist +echo " " +echo "Done." diff --git a/dotfiles/system/.local/bin/virtstart b/dotfiles/system/.local/bin/virtstart new file mode 100755 index 0000000..f9a326f --- /dev/null +++ b/dotfiles/system/.local/bin/virtstart @@ -0,0 +1,8 @@ +#!/bin/bash +# launch a virtual machine in fullscreen. +# the argument is the machine name   +  +export LIBVIRT_DEFAULT_URI="qemu:///system" + +/usr/bin/virsh start $1 +/usr/bin/virt-viewer -f -w -a $1 diff --git a/dotfiles/system/.local/bin/wallsearch b/dotfiles/system/.local/bin/wallsearch new file mode 100755 index 0000000..f71d150 --- /dev/null +++ b/dotfiles/system/.local/bin/wallsearch @@ -0,0 +1,43 @@ +#!/bin/bash + +walldir="$HOME/Pictures/wallpaper/incoming" +tmpdir="$HOME/.wallsearch" +maxpage=5 +tagoptions="CANCEL\n#minimalism\n#digital art\n#4K\n#nature\n#abstract\n#landscape\n#cityscape\n#surf\n#technology" +sortoptions="CANCEL\ndate_added\nrelevance\nrandom\nfavorites\ntoplist" + +if [ -d $tmpdir ]; then +    rm -rf $tmpdir +fi +mkdir -p $tmpdir  + +if [ -z $1 ]; then +   query=$(echo -e $tagoptions | dmenu -p "Search Wallhaven: " -i) +else +   query=$1 +fi + +[ $query == "CANCEL" ] && notify-send "Cancelled wallpaper search." && exit 0; + +sorting=$(echo -e $sortoptions | dmenu -p "Sort Order: " -i) + +[ $sorting == "CANCEL" ] && notify-send "Cancelled wallpaper search." && exit 0; + +query="$(sed 's/#//g' <<<$query)" +query="$(sed 's/ /+/g' <<<$query)" +echo #query + +notify-send "wallsearch" "Searching wallpapers..." + +for i in $(seq 1 10); +do +    curl -s https://wallhaven.cc/api/v1/search\?atleast\=1920x1080\&sorting\=$sorting\&q\=$query\&purity=111\&page\=$i > tmp.txt +    page=$(cat tmp.txt | jq '.' | grep -Eoh "https:\/\/w\.wallhaven.cc\/full\/.*(jpg|png)\b") +    wget -nc -P $tmpdir $page +    notify-send "wallsearch" "Searching wallpapers..." +done + +rm tmp.txt +notify-send "wallsearch" "Done searching wallpaper." +sxiv -to $tmpdir/* | xargs -I % mv % $walldir  +#rm -rf $tmpdir diff --git a/dotfiles/system/.local/bin/ytp b/dotfiles/system/.local/bin/ytp new file mode 100755 index 0000000..93ca9fc --- /dev/null +++ b/dotfiles/system/.local/bin/ytp @@ -0,0 +1 @@ +yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s' $1 $2 $3 $4 | 
