diff --git a/util/bigdirs b/util/bigdirs new file mode 100755 index 0000000..00f0b48 --- /dev/null +++ b/util/bigdirs @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Show the largest directories within a target path (default: $HOME) +TARGET="${1:-$HOME}" + +if [[ ! -d "$TARGET" ]]; then + echo "โŒ '$TARGET' is not a directory." + echo "Usage: $0 [path]" + exit 1 +fi + +echo "๐Ÿ”Ž Scanning: $TARGET" +echo "๐Ÿ“ Top 10 largest directories:" +echo + +du -h --max-depth=1 "$TARGET" 2>/dev/null \ + | sort -hr \ + | head -n 10 diff --git a/util/changed b/util/changed new file mode 100755 index 0000000..768d423 --- /dev/null +++ b/util/changed @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Show the N most recently modified files in your home directory +# Default: 20 files, or pass a number like: ./changed.sh 50 + +LIMIT=${1:-20} + +echo -e "\n๐Ÿ” Showing the $LIMIT most recently modified files in $HOME\n" + +find "$HOME" \ + -type f \ + -not -path "*/.cache/*" \ + -not -path "*/.config/google-chrome/*" \ + -printf "%T@ %TY-%Tm-%Td %TH:%TM:%TS %p\n" \ + 2>/dev/null | \ + sort -nr | \ + head -n "$LIMIT" | \ + awk '{ timestamp=$2" "$3; $1=""; $2=""; $3=""; sub(/^ +/, ""); print timestamp " -> " $0 }' + +echo -e "\nDone.\n" diff --git a/util/cleandownloads b/util/cleandownloads new file mode 100755 index 0000000..fa87754 --- /dev/null +++ b/util/cleandownloads @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Automatically organize your ~/Downloads folder by file type and age. +# Default age: 7 days (change DAYS= value) + +DOWNLOADS="$HOME/Downloads" +DAYS=7 + +declare -A FOLDERS=( + ["Images"]="jpg jpeg png gif svg bmp tif tiff webp" + ["Videos"]="mp4 mkv mov avi webm m4v" + ["Docs"]="pdf txt md doc docx xls xlsx ppt pptx odt" + ["Archives"]="zip tar gz bz2 xz rar 7z" +) + +mkdir -p "$DOWNLOADS"/{Images,Videos,Docs,Archives,Other} + +find "$DOWNLOADS" -maxdepth 1 -type f -mtime +"$DAYS" | while read -r file; do + ext="${file##*.}" + moved=false + + for folder in "${!FOLDERS[@]}"; do + for pattern in ${FOLDERS[$folder]}; do + if [[ "$ext" == "$pattern" ]]; then + mv "$file" "$DOWNLOADS/$folder/" + echo "Moved: $(basename "$file") โ†’ $folder" + moved=true + break 2 + fi + done + done + + if ! $moved; then + mv "$file" "$DOWNLOADS/Other/" + echo "Moved: $(basename "$file") โ†’ Other" + fi +done + +echo "Cleanup done!" + diff --git a/util/datepath b/util/datepath old mode 100644 new mode 100755 diff --git a/util/dockerclean b/util/dockerclean old mode 100644 new mode 100755 diff --git a/util/dquote b/util/dquote old mode 100644 new mode 100755 diff --git a/util/findrecent b/util/findrecent new file mode 100755 index 0000000..f1216f5 --- /dev/null +++ b/util/findrecent @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Show most recently modified files under a directory +# Default: $HOME + +DIR="${1:-$HOME}" +COUNT="${2:-20}" + +if [[ ! -d "$DIR" ]]; then + echo "Usage: $0 [directory] [count]" + exit 1 +fi + +echo "๐Ÿ“ Directory: $DIR" +echo "๐Ÿ”ข Showing $COUNT most recent files" +echo + +find "$DIR" -type f -printf "%T@ %TY-%Tm-%Td %TH:%TM:%TS %p\n" 2>/dev/null \ + | sort -nr \ + | head -n "$COUNT" \ + | awk '{ timestamp=$2" "$3; $1=""; $2=""; $3=""; sub(/^ +/, ""); print timestamp " -> " $0 }' + diff --git a/util/gandi_dyndns b/util/gandi_dyndns old mode 100644 new mode 100755 diff --git a/util/imgview b/util/imgview new file mode 100755 index 0000000..0499150 --- /dev/null +++ b/util/imgview @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Preview an image in the terminal using viu or catimg + +if [[ -z "$1" ]]; then + echo "Usage: $0 " + exit 1 +fi + +IMG="$1" + +if [[ ! -f "$IMG" ]]; then + echo "โŒ File not found: $IMG" + exit 1 +fi + +# Preferred: viu (rust-based, supports many formats) +if command -v viu >/dev/null 2>&1; then + viu "$IMG" + exit 0 +fi + +# Fallback: catimg +if command -v catimg >/dev/null 2>&1; then + catimg "$IMG" + exit 0 +fi + +echo "โŒ No compatible terminal image viewer installed." +echo "Install one with:" +echo " sudo apt install catimg" +echo "or" +echo " cargo install viu" +exit 1 + diff --git a/util/killmenu b/util/killmenu new file mode 100755 index 0000000..11f9dba --- /dev/null +++ b/util/killmenu @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Interactive process killer using fzf +# Requires: fzf + +if ! command -v fzf >/dev/null 2>&1; then + echo "โŒ Error: fzf is required (sudo apt install fzf)" + exit 1 +fi + +# List processes with PID, CPU, MEM, command +SELECTED=$(ps -eo pid,%cpu,%mem,cmd --sort=-%cpu | \ + fzf --header="Select a process to kill" --prompt="Search > ") + +# If nothing selected, exit +[[ -z "$SELECTED" ]] && echo "No process selected." && exit 0 + +# Extract PID (first column) +PID=$(echo "$SELECTED" | awk '{print $1}') + +echo "โš ๏ธ Killing PID $PID..." +kill "$PID" 2>/dev/null + +if [[ $? -eq 0 ]]; then + echo "โœ… Successfully killed PID $PID" +else + echo "โŒ Failed to kill PID $PID (try sudo?)" +fi + diff --git a/util/mictoggle b/util/mictoggle new file mode 100755 index 0000000..ceeef1c --- /dev/null +++ b/util/mictoggle @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Toggle microphone mute state using pactl (PulseAudio / PipeWire) +# Requires: pactl, notify-send + +# Get default source (microphone) +SRC=$(pactl info | awk -F': ' '/Default Source/ { print $2 }') + +if [[ -z "$SRC" ]]; then + notify-send "๐ŸŽ™๏ธ Mic Toggle" "No microphone source found." + exit 1 +fi + +# Get current mute state: yes/no +STATE=$(pactl get-source-mute "$SRC" | awk '{print $2}') + +if [[ "$STATE" == "yes" ]]; then + pactl set-source-mute "$SRC" 0 + notify-send "๐ŸŽ™๏ธ Microphone" "๐Ÿ”Š Unmuted" +else + pactl set-source-mute "$SRC" 1 + notify-send "๐ŸŽ™๏ธ Microphone" "๐Ÿ”‡ Muted" +fi + diff --git a/util/myip b/util/myip old mode 100644 new mode 100755 diff --git a/util/newnote b/util/newnote new file mode 100755 index 0000000..5fc5376 --- /dev/null +++ b/util/newnote @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Temporary scratchpad that saves only if you choose to keep the note + +SCRATCHDIR="$HOME/Documents/notes" +mkdir -p "$SCRATCHDIR" + +TMPFILE="$(mktemp)" +vim "$TMPFILE" + +# If file is empty, discard automatically +if [[ ! -s "$TMPFILE" ]]; then + echo "๐Ÿงน Scratchpad empty โ€” discarded." + rm -f "$TMPFILE" + exit 0 +fi + +echo "๐Ÿ“ Scratchpad contents:" +echo "----------------------------------" +cat "$TMPFILE" +echo "----------------------------------" + +read -p "Save this note? (y/N): " answer + +if [[ $answer =~ ^[Yy]$ ]]; then + OUTFILE="$SCRATCHDIR/note-$(date '+%Y-%m-%d_%H-%M-%S').txt" + mv "$TMPFILE" "$OUTFILE" + echo "โœ… Saved to: $OUTFILE" +else + rm -f "$TMPFILE" + echo "โŒ Discarded." +fi + diff --git a/util/ramwork b/util/ramwork new file mode 100755 index 0000000..fd044dc --- /dev/null +++ b/util/ramwork @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Create a temporary RAM-backed workspace in /dev/shm +WORKDIR="/dev/shm/work_$$" +mkdir -p "$WORKDIR" + +echo "๐Ÿš€ RAM workspace created at: $WORKDIR" +echo "Type 'exit' when you're done to clean it up." +echo + +# Start an interactive subshell **inside** the RAM dir +( + cd "$WORKDIR" || exit 1 + bash --noprofile --norc -i +) + +# Cleanup happens AFTER the subshell exits +echo "๐Ÿงน Cleaning up RAM workspace: $WORKDIR" +rm -rf "$WORKDIR" +echo "Done." + diff --git a/util/saveclip b/util/saveclip new file mode 100755 index 0000000..dedc955 --- /dev/null +++ b/util/saveclip @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Save current clipboard text to a timestamped file + +# Supports both X11 (xclip) and Wayland (wl-paste) + +OUTDIR="$HOME/Documents/clips" +mkdir -p "$OUTDIR" + +# Detect paste command +if command -v wl-paste >/dev/null 2>&1; then + CLIP=$(wl-paste) +elif command -v xclip >/dev/null 2>&1; then + CLIP=$(xclip -selection clipboard -o) +else + echo "โŒ No clipboard tool found (need wl-paste or xclip)" + exit 1 +fi + +# If clipboard is empty, ignore +if [[ -z "$CLIP" ]]; then + echo "๐Ÿ“ญ Clipboard is empty โ€” nothing saved." + exit 0 +fi + +FILENAME="clip-$(date '+%Y-%m-%d_%H-%M-%S').txt" +FILEPATH="$OUTDIR/$FILENAME" + +echo "$CLIP" > "$FILEPATH" + +echo "๐Ÿ“Ž Clipboard saved to:" +echo " $FILEPATH" + diff --git a/util/urld b/util/urld new file mode 100755 index 0000000..f3e5a28 --- /dev/null +++ b/util/urld @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +urldecode_single_printf () { + local string=$1 + + string=${string//+/ } + + printf '%b\n' "${string//%/\\x}" +} + +urldecode_single_printf "$1" diff --git a/util/voicenote b/util/voicenote new file mode 100755 index 0000000..3413172 --- /dev/null +++ b/util/voicenote @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# Requires: arecord (ALSA utils) + +OUTDIR="$HOME/Documents/voice-notes" +mkdir -p "$OUTDIR" + +FILENAME="note-$(date '+%Y-%m-%d_%H-%M-%S').wav" +FILEPATH="$OUTDIR/$FILENAME" + +echo "๐ŸŽค Recording... (Ctrl+C to stop)" +echo "Saving to: $FILEPATH" +echo + +# Start recording: 16-bit PCM, 44.1 kHz, mono +arecord -f cd -t wav -r 44100 -c 1 "$FILEPATH" + diff --git a/util/watchdir b/util/watchdir new file mode 100755 index 0000000..b2272af --- /dev/null +++ b/util/watchdir @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Directory change notifier +# Requires: inotify-tools (inotifywait) and notify-send (libnotify) + +# If the user supplied a directory, use it. Otherwise default to ~/Downloads. +DIR="${1:-$HOME/Downloads}" + +# Resolve to an absolute path +DIR="$(realpath "$DIR" 2>/dev/null)" + +# Validate directory exists +if [[ ! -d "$DIR" ]]; then + echo "โŒ Error: '$DIR' is not a valid directory." + echo "Usage: $0 /path/to/directory" + exit 1 +fi + +# Check dependency +if ! command -v inotifywait >/dev/null 2>&1; then + echo "โŒ Error: inotifywait (from inotify-tools) is required." + exit 1 +fi + +echo "๐Ÿ‘€ Watching directory: $DIR" +echo "Press Ctrl+C to stop." +echo + +# Watch for changes: created, deleted, modified +inotifywait -m -e create -e delete -e modify "$DIR" --format '%e %f' | +while read -r event file; do + notify-send "๐Ÿ“‚ Directory Change" "$event: $file" + echo "$(date '+%F %T') $event $file" +done +