From fd8e28ed1448a8e1c7092457b74f64a34279288a Mon Sep 17 00:00:00 2001 From: liph Date: Thu, 30 Apr 2026 13:13:42 +0000 Subject: [PATCH] updated scripts --- abook.sh | 35 +++++ aria_1.sh | 91 ++++++++++++ bluelight_1.sh | 87 +++++++++++ borg-backup.sh | 32 ++++ borg-mac.sh | 95 ++++++++++++ borg-server.sh | 95 ++++++++++++ borg.sh | 95 ++++++++++++ br_blue.sh | 74 ++++++++++ br_down.sh | 74 ++++++++++ br_reset.sh | 36 +++++ br_up.sh | 74 ++++++++++ bw-fzf.sh | 109 ++++++++++++++ cleaner.sh | 63 ++++++++ email2vcf.sh | 5 + extraudio_1.sh | 107 ++++++++++++++ fix-bitwarden.sh | 17 +++ fix-bitwarden.sh~ | 17 +++ fzf-address.sh | 25 ++++ fzf-git.sh | 310 +++++++++++++++++++++++++++++++++++++++ fzf_listoldfiles.sh | 29 ++++ kanata.sh | 3 + keyboard-layer.sh | 12 ++ latex.sh | 3 + layer_notify.sh | 9 ++ lxc-update.sh | 131 +++++++++++++++++ lxc-update_1.sh | 92 ++++++++++++ m3u8_1.sh | 17 +++ mail | 0 maildir2vcf.sh | 43 ++++++ move-trash_1.sh | 23 +++ move-trash_1_1.sh | 22 +++ new-sync.sh | 35 +++++ new-sync_1.sh | 15 ++ newfile.vcf | 0 niri_br.sh | 45 ++++++ niri_br_blue.sh | 20 +++ niri_br_down.sh | 15 ++ niri_br_reset.sh | 11 ++ niri_br_up.sh | 19 +++ nonum_1.sh | 37 +++++ nothumb_1.sh | 47 ++++++ podman-update.sh | 95 ++++++++++++ pve_chmod.sh | 6 + pve_chmod_1.sh | 15 ++ retag_1.sh | 76 ++++++++++ sig-picker.sh | 25 ++++ snapraid-ping.sh | 11 ++ split_1.sh | 105 +++++++++++++ sync-all-mail.sh | 19 +++ sync-mail_1.sh | 41 ++++++ sync-mail_1_1.sh | 13 ++ theme-switch | 76 ++++++++++ thumb_1.sh | 76 ++++++++++ tidal-mcp.sh | 11 ++ tidal.log | 2 + tmux-sessionizer.sh | 28 ++++ trash-message.sh | 47 ++++++ yazi-picker.sh | 11 ++ yazi-picker_1.sh | 11 ++ zoxide_openfiles_nvim.sh | 33 +++++ 60 files changed, 2770 insertions(+) create mode 100755 abook.sh create mode 100755 aria_1.sh create mode 100755 bluelight_1.sh create mode 100755 borg-backup.sh create mode 100755 borg-mac.sh create mode 100755 borg-server.sh create mode 100755 borg.sh create mode 100755 br_blue.sh create mode 100755 br_down.sh create mode 100755 br_reset.sh create mode 100755 br_up.sh create mode 100755 bw-fzf.sh create mode 100755 cleaner.sh create mode 100755 email2vcf.sh create mode 100755 extraudio_1.sh create mode 100755 fix-bitwarden.sh create mode 100755 fix-bitwarden.sh~ create mode 100755 fzf-address.sh create mode 100755 fzf-git.sh create mode 100755 fzf_listoldfiles.sh create mode 100755 kanata.sh create mode 100755 keyboard-layer.sh create mode 100755 latex.sh create mode 100755 layer_notify.sh create mode 100755 lxc-update.sh create mode 100755 lxc-update_1.sh create mode 100755 m3u8_1.sh create mode 100755 mail create mode 100755 maildir2vcf.sh create mode 100755 move-trash_1.sh create mode 100755 move-trash_1_1.sh create mode 100755 new-sync.sh create mode 100755 new-sync_1.sh create mode 100755 newfile.vcf create mode 100755 niri_br.sh create mode 100755 niri_br_blue.sh create mode 100755 niri_br_down.sh create mode 100755 niri_br_reset.sh create mode 100755 niri_br_up.sh create mode 100755 nonum_1.sh create mode 100755 nothumb_1.sh create mode 100755 podman-update.sh create mode 100755 pve_chmod_1.sh create mode 100755 retag_1.sh create mode 100755 sig-picker.sh create mode 100755 snapraid-ping.sh create mode 100755 split_1.sh create mode 100755 sync-all-mail.sh create mode 100755 sync-mail_1.sh create mode 100755 sync-mail_1_1.sh create mode 100755 theme-switch create mode 100755 thumb_1.sh create mode 100755 tidal-mcp.sh create mode 100755 tidal.log create mode 100755 tmux-sessionizer.sh create mode 100755 trash-message.sh create mode 100755 yazi-picker.sh create mode 100755 yazi-picker_1.sh create mode 100755 zoxide_openfiles_nvim.sh diff --git a/abook.sh b/abook.sh new file mode 100755 index 0000000..c9f28e7 --- /dev/null +++ b/abook.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Automatically learn email addresses from sent/received mail + +ABOOK_FILE="$HOME/.abook/addressbook" +mkdir -p "$HOME/.abook" +touch "$ABOOK_FILE" + +# Extract all email addresses from notmuch database +notmuch address --output=sender --output=recipients '*' | + sort -u | + while read -r email; do + # Check if email is already in abook + if ! grep -q "$email" "$ABOOK_FILE" 2>/dev/null; then + # Extract name and email + if [[ $email =~ ^(.+)\<(.+)\>$ ]]; then + NAME="${BASH_REMATCH[1]}" + EMAIL="${BASH_REMATCH[2]}" + else + NAME="" + EMAIL="$email" + fi + + # Add to abook (skip if already exists) + echo "[format] +program=abook +version=0.6.1 + +[0] +name=$NAME +email=$EMAIL +" >>"$ABOOK_FILE" + fi + done + +echo "Address book updated: $(grep -c '^\[' "$ABOOK_FILE") entries" diff --git a/aria_1.sh b/aria_1.sh new file mode 100755 index 0000000..ee597fe --- /dev/null +++ b/aria_1.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Fast Aria2 Download Script +# Usage: ./aria2-download.sh [output-filename] + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if aria2c is installed +if ! command -v aria2c &> /dev/null; then + echo -e "${RED}aria2c is not installed!${NC}" + echo "Installing aria2..." + + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if command -v apt-get &> /dev/null; then + sudo apt-get update && sudo apt-get install -y aria2 + elif command -v yum &> /dev/null; then + sudo yum install -y aria2 + elif command -v pacman &> /dev/null; then + sudo pacman -S aria2 + else + echo -e "${RED}Please install aria2 manually${NC}" + exit 1 + fi + elif [[ "$OSTYPE" == "darwin"* ]]; then + if command -v brew &> /dev/null; then + brew install aria2 + else + echo -e "${RED}Please install Homebrew or aria2 manually${NC}" + exit 1 + fi + else + echo -e "${RED}Unsupported OS. Please install aria2 manually${NC}" + exit 1 + fi +fi + +# Check if URL is provided +if [ -z "$1" ]; then + echo -e "${RED}Error: No URL provided${NC}" + echo "Usage: $0 [output-filename]" + exit 1 +fi + +URL="$1" +OUTPUT_FILE="$2" + +# Build aria2c command with optimized settings +ARIA2_CMD="aria2c \ + --max-connection-per-server=16 \ + --split=16 \ + --min-split-size=1M \ + --max-concurrent-downloads=16 \ + --continue=true \ + --max-tries=5 \ + --retry-wait=3 \ + --timeout=60 \ + --connect-timeout=30 \ + --file-allocation=none \ + --summary-interval=0 \ + --console-log-level=notice" + +# Add output filename if provided +if [ -n "$OUTPUT_FILE" ]; then + ARIA2_CMD="$ARIA2_CMD --out=\"$OUTPUT_FILE\"" +fi + +# Add URL +ARIA2_CMD="$ARIA2_CMD \"$URL\"" + +echo -e "${GREEN}Starting download...${NC}" +echo -e "${YELLOW}URL: $URL${NC}" +if [ -n "$OUTPUT_FILE" ]; then + echo -e "${YELLOW}Output: $OUTPUT_FILE${NC}" +fi +echo "" + +# Execute download +eval $ARIA2_CMD + +if [ $? -eq 0 ]; then + echo -e "\n${GREEN}✓ Download completed successfully!${NC}" +else + echo -e "\n${RED}✗ Download failed!${NC}" + exit 1 +fi diff --git a/bluelight_1.sh b/bluelight_1.sh new file mode 100755 index 0000000..6341a3d --- /dev/null +++ b/bluelight_1.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +SHADER_DIR="$HOME/.config/hypr/shaders" +SHADER_FILE="$SHADER_DIR/brightness.frag" + +# Ensure shader directory exists +mkdir -p "$SHADER_DIR" + +# Get screen brightness level +echo "Enter screen brightness level (0-10, where 10 = 100%):" +read -r LEVEL + +# Validate input +if ! [[ "$LEVEL" =~ ^[0-9]+$ ]] || [ "$LEVEL" -lt 0 ] || [ "$LEVEL" -gt 10 ]; then + echo "Invalid input. Please enter a number between 0 and 10." + exit 1 +fi + +# Convert to decimal (0-10 -> 0.0-1.0) +BRIGHTNESS=$(echo "scale=2; $LEVEL / 10" | bc) + +# Ask about keyboard backlight +echo "Adjust keyboard backlight? (y/n):" +read -r KBD_ADJUST + +if [[ "$KBD_ADJUST" == "y" || "$KBD_ADJUST" == "Y" ]]; then + echo "Enter keyboard backlight level (0-10, where 10 = 100%):" + read -r KBD_LEVEL + + # Validate keyboard input + if ! [[ "$KBD_LEVEL" =~ ^[0-9]+$ ]] || [ "$KBD_LEVEL" -lt 0 ] || [ "$KBD_LEVEL" -gt 10 ]; then + echo "Invalid input. Please enter a number between 0 and 10." + exit 1 + fi + + # Convert to percentage (0-10 -> 0-100) + KBD_PERCENT=$((KBD_LEVEL * 10)) + + # Set keyboard backlight + kbdlight set $KBD_PERCENT + echo "Keyboard backlight set to ${KBD_PERCENT}%" +fi + +# Ask about blue light filter +echo "Apply blue light filter? (y/n):" +read -r BLUELIGHT + +# Create shader based on choice +cat > "$SHADER_FILE" << EOF +#version 300 es + +precision mediump float; + +in vec2 v_texcoord; +out vec4 fragColor; +uniform sampler2D tex; + +void main() { + vec4 pixColor = texture(tex, v_texcoord); + +EOF + +if [[ "$BLUELIGHT" == "y" || "$BLUELIGHT" == "Y" ]]; then + cat >> "$SHADER_FILE" << EOF + // Reduce blue light + pixColor.r *= 1.0; + pixColor.g *= 0.85; + pixColor.b *= 0.65; + +EOF +fi + +cat >> "$SHADER_FILE" << EOF + // Adjust brightness + pixColor.rgb *= $BRIGHTNESS; + + fragColor = pixColor; +} +EOF + +# Apply shader +if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ 2>/dev/null | head -n1) +fi +/usr/bin/hyprctl keyword decoration:screen_shader "$SHADER_FILE" + +echo "Screen brightness set to ${LEVEL}0% $([ "$BLUELIGHT" == "y" ] || [ "$BLUELIGHT" == "Y" ] && echo "with blue light filter")" diff --git a/borg-backup.sh b/borg-backup.sh new file mode 100755 index 0000000..5f7f4d5 --- /dev/null +++ b/borg-backup.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +SOURCE_DIRS=("/home/liph/Documents" "/home/liph/Pictures" "/home/liph/Downloads") +REPO_DIR="/home/liph/borg-repo" +BORG_PASSPHRASE="1ChagearC" + +export BORG_PASSPHRASE + +for SOURCE_DIR in "${SOURCE_DIRS[@]}"; do + ARCHIVE_NAME="{hostname}-{user}-{now}" + + echo "Starting backup of ${SOURCE_DIR}..." + borg create --stats --progress \ + "${REPO_DIR}::${ARCHIVE_NAME}" \ + "${SOURCE_DIR}" + + if [ $? -eq 0 ]; then + echo "Backup of ${SOURCE_DIR} completed successfully!" + else + echo "Backup of ${SOURCE_DIR} failed!" + fi +done + +echo "Pruning old backups..." +borg prune --stats --progress \ + --keep-daily=7 \ + --keep-weekly=4 \ + --keep-monthly=6 \ + "${REPO_DIR}" + +echo "Backup and pruning completed!" + diff --git a/borg-mac.sh b/borg-mac.sh new file mode 100755 index 0000000..5175778 --- /dev/null +++ b/borg-mac.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +# Setting this, so the repo does not need to be given on the commandline: +# export BORG_REPO=ssh://username@example.com:2022/~/backup/main +#export BORG_REPO=~/backup/ + +# See the section "Passphrase notes" for more infos. +#export BORG_PASSPHRASE='1ChagearC' + +export BORG_REPO="ssh://100.121.203.110:2222/./repo/borg-repo" +export BORG_PASSPHRASE="1ChagearC" # Encryption key +BACKUP_SOURCES=( + "/home/liph/Downloads/" + "/home/liph/dotfiles/" + "/home/liph/scripts" +) # What to back up +BACKUP_NAME="laptop-$(date +%Y-%m-%d)" # Dynamic backup name + +# some helpers and error handling: +info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } +trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + +info "Starting backup" + +# Initialize Borg repo (if not exists) +borg init --encryption=repokey "$BORG_REPO" 2>/dev/null || true + +# Backup the most important directories into an archive named after +# the machine this script is currently running on: + +borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --progress \ + --compression lz4 \ + --exclude-caches \ + --exclude 'home/*/.cache/*' \ + --exclude 'var/tmp/*' \ + "$BORG_REPO::$BACKUP_NAME" \ + "${BACKUP_SOURCES[@]}" \ +# ::'{hostname}-{user}-{now}' \ +# /home/liph/Documents/ \ +# /home/liph/Pictures/ \ +# /home/liph/Downloads/ +# /etc \ +# /home \ +# /root \ +# /var +backup_exit=$? + +info "Pruning repository" + +# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly +# archives of THIS machine. The '{hostname}-*' matching is very important to +# limit prune's operation to this machine's archives and not apply to +# other machines' archives also: + +borg prune \ + --list \ + --glob-archives '{hostname}-*' \ + --show-rc \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 6 + "$BORG_REPO" + +prune_exit=$? + +# actually free repo disk space by compacting segments + +info "Compacting repository" + +borg compact + +compact_exit=$? + +# use highest exit code as global exit code +global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) +global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) + +if [ ${global_exit} -eq 0 ]; then + info "Backup, Prune, and Compact finished successfully" +elif [ ${global_exit} -eq 1 ]; then + info "Backup, Prune, and/or Compact finished with warnings" +else + info "Backup, Prune, and/or Compact finished with errors" +fi + +exit ${global_exit} + +# Check backup integrity +borg check "$BORG_REPO" diff --git a/borg-server.sh b/borg-server.sh new file mode 100755 index 0000000..7b623ff --- /dev/null +++ b/borg-server.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +# Setting this, so the repo does not need to be given on the commandline: +# export BORG_REPO=ssh://username@example.com:2022/~/backup/main +#export BORG_REPO=~/backup/ + +# See the section "Passphrase notes" for more infos. +#export BORG_PASSPHRASE='1ChagearC' + +export BORG_REPO="ssh://100.121.203.110:2222/./repo/borg-repo" +export BORG_PASSPHRASE="1ChagearC" # Encryption key +BACKUP_SOURCES=( + "/home/liph/Documents" + "/home/liph/Pictures" + "/home/liph/scripts" +) # What to back up +BACKUP_NAME="server-$(date +%Y-%m-%d)" # Dynamic backup name + +# some helpers and error handling: +info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } +trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + +info "Starting backup" + +# Initialize Borg repo (if not exists) +borg init --encryption=repokey "$BORG_REPO" 2>/dev/null || true + +# Backup the most important directories into an archive named after +# the machine this script is currently running on: + +borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --progress \ + --compression lz4 \ + --exclude-caches \ + --exclude 'home/*/.cache/*' \ + --exclude 'var/tmp/*' \ + "$BORG_REPO::$BACKUP_NAME" \ + "${BACKUP_SOURCES[@]}" \ +# ::'{hostname}-{user}-{now}' \ +# /home/liph/Documents/ \ +# /home/liph/Pictures/ \ +# /home/liph/Downloads/ +# /etc \ +# /home \ +# /root \ +# /var +backup_exit=$? + +info "Pruning repository" + +# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly +# archives of THIS machine. The '{hostname}-*' matching is very important to +# limit prune's operation to this machine's archives and not apply to +# other machines' archives also: + +borg prune \ + --list \ + --glob-archives '{hostname}-*' \ + --show-rc \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 6 + "$BORG_REPO" + +prune_exit=$? + +# actually free repo disk space by compacting segments + +info "Compacting repository" + +borg compact + +compact_exit=$? + +# use highest exit code as global exit code +global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) +global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) + +if [ ${global_exit} -eq 0 ]; then + info "Backup, Prune, and Compact finished successfully" +elif [ ${global_exit} -eq 1 ]; then + info "Backup, Prune, and/or Compact finished with warnings" +else + info "Backup, Prune, and/or Compact finished with errors" +fi + +exit ${global_exit} + +# Check backup integrity +borg check "$BORG_REPO" diff --git a/borg.sh b/borg.sh new file mode 100755 index 0000000..b6ea062 --- /dev/null +++ b/borg.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +# Setting this, so the repo does not need to be given on the commandline: +# export BORG_REPO=ssh://username@example.com:2022/~/backup/main +#export BORG_REPO=~/backup/ + +# See the section "Passphrase notes" for more infos. +#export BORG_PASSPHRASE='1ChagearC' + +export BORG_REPO="ssh://100.121.203.110:2222/./repo/borg-repo" +export BORG_PASSPHRASE="1ChagearC" # Encryption key +BACKUP_SOURCES=( + "/home/liph/Documents" + "/home/liph/Pictures" + "/home/liph/scripts" +) # What to back up +BACKUP_NAME="laptop-$(date +%Y-%m-%d)" # Dynamic backup name + +# some helpers and error handling: +info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } +trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + +info "Starting backup" + +# Initialize Borg repo (if not exists) +borg init --encryption=repokey "$BORG_REPO" 2>/dev/null || true + +# Backup the most important directories into an archive named after +# the machine this script is currently running on: + +borg create \ + --verbose \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --progress \ + --compression lz4 \ + --exclude-caches \ + --exclude 'home/*/.cache/*' \ + --exclude 'var/tmp/*' \ + "$BORG_REPO::$BACKUP_NAME" \ + "${BACKUP_SOURCES[@]}" \ +# ::'{hostname}-{user}-{now}' \ +# /home/liph/Documents/ \ +# /home/liph/Pictures/ \ +# /home/liph/Downloads/ +# /etc \ +# /home \ +# /root \ +# /var +backup_exit=$? + +info "Pruning repository" + +# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly +# archives of THIS machine. The '{hostname}-*' matching is very important to +# limit prune's operation to this machine's archives and not apply to +# other machines' archives also: + +borg prune \ + --list \ + --glob-archives '{hostname}-*' \ + --show-rc \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 6 + "$BORG_REPO" + +prune_exit=$? + +# actually free repo disk space by compacting segments + +info "Compacting repository" + +borg compact + +compact_exit=$? + +# use highest exit code as global exit code +global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) +global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) + +if [ ${global_exit} -eq 0 ]; then + info "Backup, Prune, and Compact finished successfully" +elif [ ${global_exit} -eq 1 ]; then + info "Backup, Prune, and/or Compact finished with warnings" +else + info "Backup, Prune, and/or Compact finished with errors" +fi + +exit ${global_exit} + +# Check backup integrity +borg check "$BORG_REPO" diff --git a/br_blue.sh b/br_blue.sh new file mode 100755 index 0000000..984cb24 --- /dev/null +++ b/br_blue.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Path to the brightness shader file +SHADER_DIR="$HOME/.config/hypr/shaders" +SHADER_FILE="$SHADER_DIR/brightness.frag" + +# Ensure shader directory exists +mkdir -p "$SHADER_DIR" + +# Check if shader file exists, if not create with default values +if [ ! -f "$SHADER_FILE" ]; then + CURRENT_BRIGHTNESS=1.0 + BLUELIGHT_ENABLED=false +else + # Get current brightness multiplier + CURRENT_BRIGHTNESS=$(grep -oP 'pixColor.rgb \*= \K[0-9.]+' "$SHADER_FILE" | tail -1) + + # Check current state by looking at the blue channel multiplier + if grep -q "pixColor.b \*= 0.6" "$SHADER_FILE"; then + BLUELIGHT_ENABLED=true + else + BLUELIGHT_ENABLED=false + fi +fi + +# Toggle the blue light filter state +if [ "$BLUELIGHT_ENABLED" = true ]; then + # Filter is ON, turn it OFF + NEW_STATE=false + echo "Blue light filter: OFF" +else + # Filter is OFF, turn it ON + NEW_STATE=true + echo "Blue light filter: ON" +fi + +# Recreate the shader file +cat > "$SHADER_FILE" << EOF +#version 300 es + +precision mediump float; + +in vec2 v_texcoord; +out vec4 fragColor; +uniform sampler2D tex; + +void main() { + vec4 pixColor = texture(tex, v_texcoord); + +EOF + +if [ "$NEW_STATE" = true ]; then + cat >> "$SHADER_FILE" << EOF + // Reduce blue light + pixColor.r *= 1.0; + pixColor.g *= 0.85; + pixColor.b *= 0.6; + +EOF +fi + +cat >> "$SHADER_FILE" << EOF + // Adjust brightness + pixColor.rgb *= $CURRENT_BRIGHTNESS; + + fragColor = pixColor; +} +EOF + +# Apply shader +if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ 2>/dev/null | head -n1) +fi +/usr/bin/hyprctl keyword decoration:screen_shader "$SHADER_FILE" diff --git a/br_down.sh b/br_down.sh new file mode 100755 index 0000000..afe49ed --- /dev/null +++ b/br_down.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Path to the brightness shader file +SHADER_DIR="$HOME/.config/hypr/shaders" +SHADER_FILE="$SHADER_DIR/brightness.frag" + +# Ensure shader directory exists +mkdir -p "$SHADER_DIR" + +# Check if shader file exists, if not create with default brightness +if [ ! -f "$SHADER_FILE" ]; then + CURRENT=1.0 +else + # Get current brightness multiplier + CURRENT=$(grep -oP 'pixColor.rgb \*= \K[0-9.]+' "$SHADER_FILE" | tail -1) +fi + +# Calculate new brightness (decrease by 10%) +NEW=$(echo "$CURRENT * 0.90" | bc -l) + +# Cap at 0.1 to prevent complete darkness +if (( $(echo "$NEW < 0.1" | bc -l) )); then + NEW=0.1 +fi + +# Format to 2 decimal places +NEW=$(printf "%.2f" "$NEW") + +# Check if blue light filter is currently enabled +BLUELIGHT_ENABLED=false +if [ -f "$SHADER_FILE" ] && grep -q "pixColor.b \*= 0.6" "$SHADER_FILE"; then + BLUELIGHT_ENABLED=true +fi + +# Recreate the shader file +cat > "$SHADER_FILE" << EOF +#version 300 es + +precision mediump float; + +in vec2 v_texcoord; +out vec4 fragColor; +uniform sampler2D tex; + +void main() { + vec4 pixColor = texture(tex, v_texcoord); + +EOF + +if [ "$BLUELIGHT_ENABLED" = true ]; then + cat >> "$SHADER_FILE" << EOF + // Reduce blue light + pixColor.r *= 1.0; + pixColor.g *= 0.85; + pixColor.b *= 0.6; + +EOF +fi + +cat >> "$SHADER_FILE" << EOF + // Adjust brightness + pixColor.rgb *= $NEW; + + fragColor = pixColor; +} +EOF + +# Apply shader +if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ 2>/dev/null | head -n1) +fi +/usr/bin/hyprctl keyword decoration:screen_shader "$SHADER_FILE" + +echo "Brightness decreased from $CURRENT to $NEW" diff --git a/br_reset.sh b/br_reset.sh new file mode 100755 index 0000000..f8c4d30 --- /dev/null +++ b/br_reset.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Path to the brightness shader file +SHADER_DIR="$HOME/.config/hypr/shaders" +SHADER_FILE="$SHADER_DIR/brightness.frag" + +# Ensure shader directory exists +mkdir -p "$SHADER_DIR" + +# Create shader file with 100% brightness and no blue light filter +cat > "$SHADER_FILE" << EOF +#version 300 es + +precision mediump float; + +in vec2 v_texcoord; +out vec4 fragColor; +uniform sampler2D tex; + +void main() { + vec4 pixColor = texture(tex, v_texcoord); + + // Adjust brightness + pixColor.rgb *= 1.00; + + fragColor = pixColor; +} +EOF + +# Apply shader +if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ 2>/dev/null | head -n1) +fi +/usr/bin/hyprctl keyword decoration:screen_shader "$SHADER_FILE" + +echo "Screen reset: Brightness 100%, Blue light filter OFF" diff --git a/br_up.sh b/br_up.sh new file mode 100755 index 0000000..88f0800 --- /dev/null +++ b/br_up.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Path to the brightness shader file +SHADER_DIR="$HOME/.config/hypr/shaders" +SHADER_FILE="$SHADER_DIR/brightness.frag" + +# Ensure shader directory exists +mkdir -p "$SHADER_DIR" + +# Check if shader file exists, if not create with default brightness +if [ ! -f "$SHADER_FILE" ]; then + CURRENT=1.0 +else + # Get current brightness multiplier + CURRENT=$(grep -oP 'pixColor.rgb \*= \K[0-9.]+' "$SHADER_FILE" | tail -1) +fi + +# Calculate new brightness (increase by 10%) +NEW=$(echo "$CURRENT * 1.10" | bc -l) + +# Cap at 2.0 to prevent excessive brightness +if (( $(echo "$NEW > 2.0" | bc -l) )); then + NEW=2.0 +fi + +# Format to 2 decimal places +NEW=$(printf "%.2f" "$NEW") + +# Check if blue light filter is currently enabled +BLUELIGHT_ENABLED=false +if [ -f "$SHADER_FILE" ] && grep -q "pixColor.b \*= 0.6" "$SHADER_FILE"; then + BLUELIGHT_ENABLED=true +fi + +# Recreate the shader file +cat > "$SHADER_FILE" << EOF +#version 300 es + +precision mediump float; + +in vec2 v_texcoord; +out vec4 fragColor; +uniform sampler2D tex; + +void main() { + vec4 pixColor = texture(tex, v_texcoord); + +EOF + +if [ "$BLUELIGHT_ENABLED" = true ]; then + cat >> "$SHADER_FILE" << EOF + // Reduce blue light + pixColor.r *= 1.0; + pixColor.g *= 0.85; + pixColor.b *= 0.6; + +EOF +fi + +cat >> "$SHADER_FILE" << EOF + // Adjust brightness + pixColor.rgb *= $NEW; + + fragColor = pixColor; +} +EOF + +# Apply shader +if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then + export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ 2>/dev/null | head -n1) +fi +/usr/bin/hyprctl keyword decoration:screen_shader "$SHADER_FILE" + +echo "Brightness increased from $CURRENT to $NEW" diff --git a/bw-fzf.sh b/bw-fzf.sh new file mode 100755 index 0000000..621fd3b --- /dev/null +++ b/bw-fzf.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +if [ -z "$BW_SESSION" ]; then + export BW_SESSION=$(bw unlock --raw) +fi + +items=$(bw list items) + +# Select item with preview showing full details +selected=$(echo "$items" | jq -r '.[] | "\(.name)\t\(.login.username // "no username")"' | \ + column -t -s $'\t' | \ + fzf --preview 'name=$(echo {} | awk "{print \$1}"); bw get item "$name" 2>/dev/null | jq -r " + \"Name: \" + .name, + \"Type: \" + (.type | tostring), + \"---\", + (if .login then + \"Username: \" + (.login.username // \"none\"), + \"Password: \" + (if .login.password then \"********\" else \"none\" end), + (if .login.uris then \"URIs:\", (.login.uris[] | \" - \" + .uri) else empty end), + (if .login.totp then \"TOTP: configured\" else empty end) + else empty end), + (if .notes then \"---\", \"Notes:\", .notes else empty end), + (if .fields then \"---\", \"Custom Fields:\", (.fields[] | \" \" + .name + \": \" + (if .type == 1 then \"********\" else (.value // \"empty\") end)) else empty end) + " | cat' --preview-window=right:60%:wrap) + +if [ -n "$selected" ]; then + name=$(echo "$selected" | awk '{print $1}') + + # Get full item details + item=$(bw get item "$name" 2>/dev/null) + + if [ -z "$item" ]; then + notify-send "Bitwarden" "Failed to get item for $name" + exit 1 + fi + + # Build a list of available fields + fields=() + + if echo "$item" | jq -e '.login.password' &>/dev/null; then + fields+=("Password") + fi + + if echo "$item" | jq -e '.login.username' &>/dev/null; then + fields+=("Username") + fi + + if echo "$item" | jq -e '.login.uris[]' &>/dev/null; then + fields+=("URI") + fi + + if echo "$item" | jq -e '.login.totp' &>/dev/null; then + fields+=("TOTP Code") + fi + + if echo "$item" | jq -e '.notes' &>/dev/null; then + notes=$(echo "$item" | jq -r '.notes') + if [ -n "$notes" ] && [ "$notes" != "null" ]; then + fields+=("Notes") + fi + fi + + if echo "$item" | jq -e '.fields[]' &>/dev/null; then + while IFS= read -r field_name; do + fields+=("Custom: $field_name") + done < <(echo "$item" | jq -r '.fields[] | .name') + fi + + fields+=("View All (JSON)") + + # Let user choose what to copy + choice=$(printf '%s\n' "${fields[@]}" | fzf --prompt="What to copy? ") + + if [ -n "$choice" ]; then + case "$choice" in + "Password") + echo "$item" | jq -r '.login.password' | wl-copy + notify-send "Bitwarden" "Password copied for $name" + ;; + "Username") + echo "$item" | jq -r '.login.username' | wl-copy + notify-send "Bitwarden" "Username copied for $name" + ;; + "URI") + echo "$item" | jq -r '.login.uris[0].uri' | wl-copy + notify-send "Bitwarden" "URI copied for $name" + ;; + "TOTP Code") + totp=$(bw get totp "$name" 2>/dev/null) + echo -n "$totp" | wl-copy + notify-send "Bitwarden" "TOTP code copied for $name" + ;; + "Notes") + echo "$item" | jq -r '.notes' | wl-copy + notify-send "Bitwarden" "Notes copied for $name" + ;; + Custom:*) + field_name="${choice#Custom: }" + value=$(echo "$item" | jq -r --arg name "$field_name" '.fields[] | select(.name == $name) | .value') + echo -n "$value" | wl-copy + notify-send "Bitwarden" "Custom field '$field_name' copied" + ;; + "View All (JSON)") + echo "$item" | jq '.' | wl-copy + notify-send "Bitwarden" "Full item JSON copied for $name" + ;; + esac + fi +fi diff --git a/cleaner.sh b/cleaner.sh new file mode 100755 index 0000000..3cc8d68 --- /dev/null +++ b/cleaner.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Ensure script is run as root on the Proxmox host +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit +fi + +# Get all LXC IDs +CONTAINERS=$(pct list | awk 'NR>1 {print $1}') + +for VMID in $CONTAINERS; do + # Check if container is running + STATUS=$(pct status $VMID) + + if [[ $STATUS == *"status: running"* ]]; then + echo "========================================================" + echo " CLEANING ARCH LXC: $VMID" + echo "========================================================" + + # 1. Clean Arch Linux Packages + pct exec $VMID -- bash -c ' + echo "--- Removing orphaned packages ---" + ORPHANS=$(pacman -Qtdq) + if [ -n "$ORPHANS" ]; then + pacman -Rns $ORPHANS --noconfirm + else + echo "No orphaned packages found." + fi + + echo "--- Fixing and cleaning pacman cache ---" + # Remove those broken "download-" temporary files manually to stop the FD errors + rm -f /var/cache/pacman/pkg/download-* 2>/dev/null + + # Use paccache if installed (cleans all but latest 3 versions), + # otherwise wipe the cache directory manually. + if command -v paccache >/dev/null 2>&1; then + paccache -r -k 0 --noconfirm + else + echo "paccache not found, performing manual cache wipe..." + # Keeping the directory but removing all files + find /var/cache/pacman/pkg/ -type f -delete + fi + ' + + # 2. Clean Podman + pct exec $VMID -- bash -c ' + if command -v podman >/dev/null 2>&1; then + echo "--- Podman found. Pruning all unused images ---" + podman image prune -af + else + echo "--- Podman not installed. Skipping ---" + fi + ' + + echo "Finished cleaning $VMID" + echo "" + else + echo "Skipping $VMID (Container is powered off)" + fi +done + +echo "All Arch containers processed." diff --git a/email2vcf.sh b/email2vcf.sh new file mode 100755 index 0000000..70cf6a9 --- /dev/null +++ b/email2vcf.sh @@ -0,0 +1,5 @@ +# Convert your aerc/maildir to vCards +em2vcf /mnt/flash1/mail-server/mail/maildir/ \ + --output-folder ~/.contacts_mail/ \ + --no-duplicates \ + --overwrite diff --git a/extraudio_1.sh b/extraudio_1.sh new file mode 100755 index 0000000..6f25661 --- /dev/null +++ b/extraudio_1.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# Ask where to place the MP3 files +read -p "Place MP3s in same folder as MP4s? (y/n) [y]: " same_folder +same_folder=${same_folder:-y} + +if [[ "$same_folder" =~ ^[Nn]$ ]]; then + read -p "Enter output folder path: " output_folder + mkdir -p "$output_folder" +else + output_folder="" +fi + +# Get default values from folder structure +# Current directory is "The Energies of Love" +default_album=$(basename "$PWD") +# Parent directory is "Donna Eden & David Feinstein" +default_album_artist=$(basename "$(dirname "$PWD")") + +# Prompt for metadata with defaults +read -p "Enter Album Artist [$default_album_artist]: " album_artist +album_artist=${album_artist:-$default_album_artist} + +read -p "Enter Release Date (YYYY): " release_date + +echo "" +echo "Processing MP4 files..." +echo "Album Artist: $album_artist" +echo "" + +counter=0 + +# Process all mp4 files in subdirectories +for file in */*.mp4 *.mp4; do + # Skip if file doesn't exist + [ -e "$file" ] || continue + + dir=$(dirname "$file") + filename=$(basename "$file" .mp4) + + # Determine album based on folder structure + if [ "$dir" != "." ]; then + # File is in subfolder (Intro, week1, week2, etc.) + album=$(basename "$dir") + else + # File is in current directory + album="$default_album" + fi + + # Set output location + if [ -n "$output_folder" ]; then + # Create matching folder structure in output folder + if [ "$dir" != "." ]; then + mkdir -p "$output_folder/$dir" + output="$output_folder/$dir/${filename}.mp3" + else + output="$output_folder/${filename}.mp3" + fi + else + output="$dir/${filename}.mp3" + fi + + # Skip if already exists + if [ -f "$output" ]; then + echo "Skip: $filename (exists)" + continue + fi + + # Get track number + track_num=$(echo "$filename" | grep -oP '^\d+') + + echo "Converting: $filename" + echo " Album: $album" + + # Convert using temp file + temp_file="/tmp/convert_$$.mp3" + + if [ -n "$track_num" ]; then + ffmpeg -i "$file" -vn -acodec libmp3lame -q:a 0 \ + -metadata title="$filename" \ + -metadata track="$track_num" \ + -metadata album_artist="$album_artist" \ + -metadata artist="$album_artist" \ + -metadata album="$album" \ + -metadata date="$release_date" \ + "$temp_file" -y >/dev/null 2>&1 + else + ffmpeg -i "$file" -vn -acodec libmp3lame -q:a 0 \ + -metadata title="$filename" \ + -metadata album_artist="$album_artist" \ + -metadata artist="$album_artist" \ + -metadata album="$album" \ + -metadata date="$release_date" \ + "$temp_file" -y >/dev/null 2>&1 + fi + + if [ -f "$temp_file" ]; then + mv "$temp_file" "$output" + echo " ✓ Done" + ((counter++)) + else + echo " ✗ Failed" + fi +done + +echo "" +echo "Converted $counter files" diff --git a/fix-bitwarden.sh b/fix-bitwarden.sh new file mode 100755 index 0000000..59bd363 --- /dev/null +++ b/fix-bitwarden.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Save as ~/.config/niri/scripts/fix-bitwarden.sh +# Make executable: chmod +x ~/.config/niri/scripts/fix-bitwarden.sh + +niri msg -j event-stream | jq --unbuffered -r ' + select(.WindowOpenedOrChanged) | + .WindowOpenedOrChanged.window | + select( + (.title? | match("Extension:.*LibreWolf")) and + .is_floating == false + ) | + .id +' | while read id; do + niri msg action toggle-window-floating --id="$id" + niri msg action set-window-height 50% + niri msg action set-window-width 20% +done diff --git a/fix-bitwarden.sh~ b/fix-bitwarden.sh~ new file mode 100755 index 0000000..59bd363 --- /dev/null +++ b/fix-bitwarden.sh~ @@ -0,0 +1,17 @@ +#!/bin/bash +# Save as ~/.config/niri/scripts/fix-bitwarden.sh +# Make executable: chmod +x ~/.config/niri/scripts/fix-bitwarden.sh + +niri msg -j event-stream | jq --unbuffered -r ' + select(.WindowOpenedOrChanged) | + .WindowOpenedOrChanged.window | + select( + (.title? | match("Extension:.*LibreWolf")) and + .is_floating == false + ) | + .id +' | while read id; do + niri msg action toggle-window-floating --id="$id" + niri msg action set-window-height 50% + niri msg action set-window-width 20% +done diff --git a/fzf-address.sh b/fzf-address.sh new file mode 100755 index 0000000..17a0f9c --- /dev/null +++ b/fzf-address.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Fuzzy find email addresses with fzf + +QUERY="$1" + +# Get all addresses from notmuch +ADDRESSES=$(notmuch address --output=sender --output=recipients \ + --deduplicate=address '*' | sort -u) + +# If query is provided, pre-filter +if [ -n "$QUERY" ]; then + ADDRESSES=$(echo "$ADDRESSES" | grep -i "$QUERY") +fi + +# Use fzf for selection +SELECTED=$(echo "$ADDRESSES" | fzf \ + --height=40% \ + --layout=reverse \ + --border \ + --prompt="Select recipient: " \ + --preview="" \ + --query="$QUERY") + +# Output selected address +echo "$SELECTED" diff --git a/fzf-git.sh b/fzf-git.sh new file mode 100755 index 0000000..d360751 --- /dev/null +++ b/fzf-git.sh @@ -0,0 +1,310 @@ +# Fzf with Git in the shell +# You can find this whole script from Junegunn Github repo below +# https://github.com/junegunn/fzf-git.sh + +# Script is used in .zshrc + +# shellcheck disable=SC2039 +[[ $0 = - ]] && return + +__fzf_git_color() { + if [[ -n $NO_COLOR ]]; then + echo never + elif [[ $# -gt 0 ]] && [[ -n $FZF_GIT_PREVIEW_COLOR ]]; then + echo "$FZF_GIT_PREVIEW_COLOR" + else + echo "${FZF_GIT_COLOR:-always}" + fi +} + +__fzf_git_cat() { + if [[ -n $FZF_GIT_CAT ]]; then + echo "$FZF_GIT_CAT" + return + fi + + # Sometimes bat is installed as batcat + _fzf_git_bat_options="--style='${BAT_STYLE:-full}' --color=$(__fzf_git_color .) --pager=never" + if command -v batcat > /dev/null; then + echo "batcat $_fzf_git_bat_options" + elif command -v bat > /dev/null; then + echo "bat $_fzf_git_bat_options" + else + echo cat + fi +} + +if [[ $# -eq 1 ]]; then + branches() { + git branch "$@" --sort=-committerdate --sort=-HEAD --format=$'%(HEAD) %(color:yellow)%(refname:short) %(color:green)(%(committerdate:relative))\t%(color:blue)%(subject)%(color:reset)' --color=$(__fzf_git_color) | column -ts$'\t' + } + refs() { + git for-each-ref --sort=-creatordate --sort=-HEAD --color=$(__fzf_git_color) --format=$'%(refname) %(color:green)(%(creatordate:relative))\t%(color:blue)%(subject)%(color:reset)' | + eval "$1" | + sed 's#^refs/remotes/#\x1b[95mremote-branch\t\x1b[33m#; s#^refs/heads/#\x1b[92mbranch\t\x1b[33m#; s#^refs/tags/#\x1b[96mtag\t\x1b[33m#; s#refs/stash#\x1b[91mstash\t\x1b[33mrefs/stash#' | + column -ts$'\t' + } + hashes() { + git log --date=short --format="%C(green)%C(bold)%cd %C(auto)%h%d %s (%an)" --graph --color=$(__fzf_git_color) "$@" + } + case "$1" in + branches) + echo $'CTRL-O (open in browser) ╱ ALT-A (show all branches)\n' + branches + ;; + all-branches) + echo $'CTRL-O (open in browser)\n' + branches -a + ;; + hashes) + echo $'CTRL-O (open in browser) ╱ CTRL-D (diff)\nCTRL-S (toggle sort) ╱ ALT-A (show all hashes)\n' + hashes + ;; + all-hashes) + echo $'CTRL-O (open in browser) ╱ CTRL-D (diff)\nCTRL-S (toggle sort)\n' + hashes --all + ;; + refs) + echo $'CTRL-O (open in browser) ╱ ALT-E (examine in editor) ╱ ALT-A (show all refs)\n' + refs 'grep -v ^refs/remotes' + ;; + all-refs) + echo $'CTRL-O (open in browser) ╱ ALT-E (examine in editor)\n' + refs 'cat' + ;; + nobeep) ;; + *) exit 1 ;; + esac +elif [[ $# -gt 1 ]]; then + set -e + + branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null) + if [[ $branch = HEAD ]]; then + branch=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD) + fi + + # Only supports GitHub for now + case "$1" in + commit) + hash=$(grep -o "[a-f0-9]\{7,\}" <<< "$2") + path=/commit/$hash + ;; + branch|remote-branch) + branch=$(sed 's/^[* ]*//' <<< "$2" | cut -d' ' -f1) + remote=$(git config branch."${branch}".remote || echo 'origin') + branch=${branch#$remote/} + path=/tree/$branch + ;; + remote) + remote=$2 + path=/tree/$branch + ;; + file) path=/blob/$branch/$(git rev-parse --show-prefix)$2 ;; + tag) path=/releases/tag/$2 ;; + *) exit 1 ;; + esac + + remote=${remote:-$(git config branch."${branch}".remote || echo 'origin')} + remote_url=$(git remote get-url "$remote" 2> /dev/null || echo "$remote") + + if [[ $remote_url =~ ^git@ ]]; then + url=${remote_url%.git} + url=${url#git@} + url=https://${url/://} + elif [[ $remote_url =~ ^http ]]; then + url=${remote_url%.git} + fi + + case "$(uname -s)" in + Darwin) open "$url$path" ;; + *) xdg-open "$url$path" ;; + esac + exit 0 +fi + +if [[ $- =~ i ]]; then +# ----------------------------------------------------------------------------- + +# Redefine this function to change the options +_fzf_git_fzf() { + fzf-tmux -p80%,60% -- \ + --layout=reverse --multi --height=50% --min-height=20 --border \ + --border-label-pos=2 \ + --color='header:italic:underline,label:blue' \ + --preview-window='right,50%,border-left' \ + --bind='ctrl-/:change-preview-window(down,50%,border-top|hidden|)' "$@" +} + +_fzf_git_check() { + git rev-parse HEAD > /dev/null 2>&1 && return + + [[ -n $TMUX ]] && tmux display-message "Not in a git repository" + return 1 +} + +__fzf_git=${BASH_SOURCE[0]:-${(%):-%x}} +__fzf_git=$(readlink -f "$__fzf_git" 2> /dev/null || /usr/bin/ruby --disable-gems -e 'puts File.expand_path(ARGV.first)' "$__fzf_git" 2> /dev/null) + +_fzf_git_files() { + _fzf_git_check || return + local root query + root=$(git rev-parse --show-toplevel) + [[ $root != "$PWD" ]] && query='!../ ' + + (git -c color.status=$(__fzf_git_color) status --short --no-branch + git ls-files "$root" | grep -vxFf <(git status -s | grep '^[^?]' | cut -c4-; echo :) | sed 's/^/ /') | + _fzf_git_fzf -m --ansi --nth 2..,.. \ + --border-label '📁 Files' \ + --header $'CTRL-O (open in browser) ╱ ALT-E (open in editor)\n\n' \ + --bind "ctrl-o:execute-silent:bash $__fzf_git file {-1}" \ + --bind "alt-e:execute:${EDITOR:-vim} {-1} > /dev/tty" \ + --query "$query" \ + --preview "git diff --no-ext-diff --color=$(__fzf_git_color .) -- {-1} | sed 1,4d; $(__fzf_git_cat) {-1}" "$@" | + cut -c4- | sed 's/.* -> //' +} + +_fzf_git_branches() { + _fzf_git_check || return + bash "$__fzf_git" branches | + _fzf_git_fzf --ansi \ + --border-label '🌲 Branches' \ + --header-lines 2 \ + --tiebreak begin \ + --preview-window down,border-top,40% \ + --color hl:underline,hl+:underline \ + --no-hscroll \ + --bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \ + --bind "ctrl-o:execute-silent:bash $__fzf_git branch {}" \ + --bind "alt-a:change-border-label(🌳 All branches)+reload:bash \"$__fzf_git\" all-branches" \ + --preview "git log --oneline --graph --date=short --color=$(__fzf_git_color .) --pretty='format:%C(auto)%cd %h%d %s' \$(sed s/^..// <<< {} | cut -d' ' -f1) --" "$@" | + sed 's/^..//' | cut -d' ' -f1 +} + +_fzf_git_tags() { + _fzf_git_check || return + git tag --sort -version:refname | + _fzf_git_fzf --preview-window right,70% \ + --border-label '📛 Tags' \ + --header $'CTRL-O (open in browser)\n\n' \ + --bind "ctrl-o:execute-silent:bash $__fzf_git tag {}" \ + --preview "git show --color=$(__fzf_git_color .) {}" "$@" +} + +_fzf_git_hashes() { + _fzf_git_check || return + bash "$__fzf_git" hashes | + _fzf_git_fzf --ansi --no-sort --bind 'ctrl-s:toggle-sort' \ + --border-label '🍡 Hashes' \ + --header-lines 3 \ + --bind "ctrl-o:execute-silent:bash $__fzf_git commit {}" \ + --bind "ctrl-d:execute:grep -o '[a-f0-9]\{7,\}' <<< {} | head -n 1 | xargs git diff --color=$(__fzf_git_color) > /dev/tty" \ + --bind "alt-a:change-border-label(🍇 All hashes)+reload:bash \"$__fzf_git\" all-hashes" \ + --color hl:underline,hl+:underline \ + --preview "grep -o '[a-f0-9]\{7,\}' <<< {} | head -n 1 | xargs git show --color=$(__fzf_git_color .)" "$@" | + awk 'match($0, /[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]*/) { print substr($0, RSTART, RLENGTH) }' +} + +_fzf_git_remotes() { + _fzf_git_check || return + git remote -v | awk '{print $1 "\t" $2}' | uniq | + _fzf_git_fzf --tac \ + --border-label '📡 Remotes' \ + --header $'CTRL-O (open in browser)\n\n' \ + --bind "ctrl-o:execute-silent:bash $__fzf_git remote {1}" \ + --preview-window right,70% \ + --preview "git log --oneline --graph --date=short --color=$(__fzf_git_color .) --pretty='format:%C(auto)%cd %h%d %s' '{1}/$(git rev-parse --abbrev-ref HEAD)' --" "$@" | + cut -d$'\t' -f1 +} + +_fzf_git_stashes() { + _fzf_git_check || return + git stash list | _fzf_git_fzf \ + --border-label '🥡 Stashes' \ + --header $'CTRL-X (drop stash)\n\n' \ + --bind 'ctrl-x:reload(git stash drop -q {1}; git stash list)' \ + -d: --preview "git show --color=$(__fzf_git_color .) {1}" "$@" | + cut -d: -f1 +} + +_fzf_git_lreflogs() { + _fzf_git_check || return + git reflog --color=$(__fzf_git_color) --format="%C(blue)%gD %C(yellow)%h%C(auto)%d %gs" | _fzf_git_fzf --ansi \ + --border-label '📒 Reflogs' \ + --preview "git show --color=$(__fzf_git_color .) {1}" "$@" | + awk '{print $1}' +} + +_fzf_git_each_ref() { + _fzf_git_check || return + bash "$__fzf_git" refs | _fzf_git_fzf --ansi \ + --nth 2,2.. \ + --tiebreak begin \ + --border-label '☘️ Each ref' \ + --header-lines 2 \ + --preview-window down,border-top,40% \ + --color hl:underline,hl+:underline \ + --no-hscroll \ + --bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \ + --bind "ctrl-o:execute-silent:bash $__fzf_git {1} {2}" \ + --bind "alt-e:execute:${EDITOR:-vim} <(git show {2}) > /dev/tty" \ + --bind "alt-a:change-border-label(🍀 Every ref)+reload:bash \"$__fzf_git\" all-refs" \ + --preview "git log --oneline --graph --date=short --color=$(__fzf_git_color .) --pretty='format:%C(auto)%cd %h%d %s' {2} --" "$@" | + awk '{print $2}' +} + +_fzf_git_worktrees() { + _fzf_git_check || return + git worktree list | _fzf_git_fzf \ + --border-label '🌴 Worktrees' \ + --header $'CTRL-X (remove worktree)\n\n' \ + --bind 'ctrl-x:reload(git worktree remove {1} > /dev/null; git worktree list)' \ + --preview " + git -c color.status=$(__fzf_git_color .) -C {1} status --short --branch + echo + git log --oneline --graph --date=short --color=$(__fzf_git_color .) --pretty='format:%C(auto)%cd %h%d %s' {2} -- + " "$@" | + awk '{print $1}' +} + +if [[ -n "${BASH_VERSION:-}" ]]; then + __fzf_git_init() { + bind -m emacs-standard '"\er": redraw-current-line' + bind -m emacs-standard '"\C-z": vi-editing-mode' + bind -m vi-command '"\C-z": emacs-editing-mode' + bind -m vi-insert '"\C-z": emacs-editing-mode' + + local o c + for o in "$@"; do + c=${o:0:1} + bind -m emacs-standard '"\C-g\C-'$c'": " \C-u \C-a\C-k`_fzf_git_'$o'`\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er \C-h"' + bind -m vi-command '"\C-g\C-'$c'": "\C-z\C-g\C-'$c'\C-z"' + bind -m vi-insert '"\C-g\C-'$c'": "\C-z\C-g\C-'$c'\C-z"' + bind -m emacs-standard '"\C-g'$c'": " \C-u \C-a\C-k`_fzf_git_'$o'`\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er \C-h"' + bind -m vi-command '"\C-g'$c'": "\C-z\C-g'$c'\C-z"' + bind -m vi-insert '"\C-g'$c'": "\C-z\C-g'$c'\C-z"' + done + } +elif [[ -n "${ZSH_VERSION:-}" ]]; then + __fzf_git_join() { + local item + while read item; do + echo -n "${(q)item} " + done + } + + __fzf_git_init() { + local m o + for o in "$@"; do + eval "fzf-git-$o-widget() { local result=\$(_fzf_git_$o | __fzf_git_join); zle reset-prompt; LBUFFER+=\$result }" + eval "zle -N fzf-git-$o-widget" + for m in emacs vicmd viins; do + eval "bindkey -M $m '^g^${o[1]}' fzf-git-$o-widget" + eval "bindkey -M $m '^g${o[1]}' fzf-git-$o-widget" + done + done + } +fi +__fzf_git_init files branches tags remotes hashes stashes lreflogs each_ref worktrees + +# ----------------------------------------------------------------------------- +fi diff --git a/fzf_listoldfiles.sh b/fzf_listoldfiles.sh new file mode 100755 index 0000000..0b5ffed --- /dev/null +++ b/fzf_listoldfiles.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Script to list recent files and open nvim using fzf +# set to an alias nlof in .zshrc + +list_oldfiles() { + # Get the oldfiles list from Neovim + local oldfiles=($(nvim -u NONE --headless +'lua io.write(table.concat(vim.v.oldfiles, "\n") .. "\n")' +qa)) + # Filter invalid paths or files not found + local valid_files=() + for file in "${oldfiles[@]}"; do + if [[ -f "$file" ]]; then + valid_files+=("$file") + fi + done + # Use fzf to select from valid files + local files=($(printf "%s\n" "${valid_files[@]}" | \ + grep -v '\[.*' | \ + fzf --multi \ + --preview 'bat -n --color=always --line-range=:500 {} 2>/dev/null || echo "Error previewing file"' \ + --height=70% \ + --layout=default)) + + # Open selected files in Neovim + [[ ${#files[@]} -gt 0 ]] && nvim "${files[@]}" +} + +# Call the function +list_oldfiles "$@" diff --git a/kanata.sh b/kanata.sh new file mode 100755 index 0000000..23221ba --- /dev/null +++ b/kanata.sh @@ -0,0 +1,3 @@ +kanata --cfg ~/.config/kanata/config.kbd & + +sudo mount -a diff --git a/keyboard-layer.sh b/keyboard-layer.sh new file mode 100755 index 0000000..f6874ed --- /dev/null +++ b/keyboard-layer.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# ~/.config/hypr/scripts/keyboard-layer.sh + +# This checks which modifiers are active as a proxy for layers +# Adjust based on your actual layer keys + +while true; do + # You might need to adjust this based on your keyboard + # This is a simplified example + echo '{"text": "Layer ?", "class": "layer"}' + sleep 0.5 +done diff --git a/latex.sh b/latex.sh new file mode 100755 index 0000000..1b677e1 --- /dev/null +++ b/latex.sh @@ -0,0 +1,3 @@ +pdflatex "$1" && zathura "${1%.tex}.pdf" & + + diff --git a/layer_notify.sh b/layer_notify.sh new file mode 100755 index 0000000..5f0a3c0 --- /dev/null +++ b/layer_notify.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +case $1 in + 0) notify-send -t 1500 "Layer 0" "Base/QWERTY" ;; + 1) notify-send -t 1500 "Layer 1" "Navigation/Numbers" ;; + 2) notify-send -t 1500 "Layer 2" "Symbols" ;; + 3) notify-send -t 1500 "Layer 3" "Function Keys" ;; + *) notify-send -t 1500 "Unknown Layer" ;; +esac diff --git a/lxc-update.sh b/lxc-update.sh new file mode 100755 index 0000000..1cb6f04 --- /dev/null +++ b/lxc-update.sh @@ -0,0 +1,131 @@ +#!/bin/bash +set -euo pipefail + +# --- CONFIGURATION --- +# The user that runs paru inside the Arch LXCs +AUR_USER="liph" + +EXCLUDE_LIST=(120 121 150 151 152) + +declare -A CONTAINER_MAP +CONTAINER_MAP[100]="/mnt/flash1/podman/lxc_servarr/" +CONTAINER_MAP[101]="/mnt/flash1/podman/lxc_second/" +CONTAINER_MAP[108]="/mnt/flash1/podman/lxc_dns/" # Multiple paths allowed +CONTAINER_MAP[109]="/mnt/flash1/podman/lxc_immich/" +CONTAINER_MAP[111]="/mnt/flash1/podman/lxc_ollama/" + +# --- UTILS --- +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" +} + +# Wrapper for commands that modify state +run_pct() { + if [[ "${DRY_RUN:-0}" == "1" ]]; then + log "[DRY-RUN] pct $*" + else + pct "$@" + fi +} + +ALL_CTIDS=$(pct list | awk 'NR>1 {print $1}') + +for CTID in $ALL_CTIDS; do + # Check if CTID is in EXCLUDE_LIST + IS_EXCLUDED=false + for EXCLUDE in "${EXCLUDE_LIST[@]}"; do + if [[ "$CTID" == "$EXCLUDE" ]]; then + IS_EXCLUDED=true + break + fi + done + if [ "$IS_EXCLUDED" = true ]; then continue; fi + + if pct config "$CTID" | grep -q "template: 1"; then continue; fi + + NAME=$(pct config "$CTID" | grep "hostname:" | awk '{print $2}') + STATUS=$(pct status "$CTID" | awk '{print $2}') + + echo "=======================================================" + log "PROCESSING LXC: $NAME (ID: $CTID)" + echo "=======================================================" + + WAS_STOPPED=false + if [ "$STATUS" == "stopped" ]; then + run_pct start "$CTID" + [[ "${DRY_RUN:-0}" == "0" ]] && sleep 10 + WAS_STOPPED=true + fi + + # --- 1. SETUP PASSWORDLESS FOR PARU --- + # Paru MUST run as a user, so we keep this sudoers rule for pacman. + log "[+] Configuring temporary sudoers for $AUR_USER..." + run_pct exec "$CTID" -- bash -c "echo '$AUR_USER ALL=(ALL) NOPASSWD: /usr/bin/pacman' > /etc/sudoers.d/99-paru-nopass" + run_pct exec "$CTID" -- chmod 440 /etc/sudoers.d/99-paru-nopass + + # --- 2. OS UPDATER (User Context) --- + log "[+] Updating Arch packages (as $AUR_USER)..." + run_pct exec "$CTID" -- sudo -u "$AUR_USER" paru -Syu --noconfirm --cleanafter + + # --- 3. PODMAN SCAN (Root Context) --- + if [[ -n "${CONTAINER_MAP[$CTID]:-}" ]]; then + # Check Storage Driver (Diagnostic) + DRIVER=$(pct exec "$CTID" -- podman info --format '{{.Store.GraphDriverName}}' 2>/dev/null || echo "unknown") + if [[ "$DRIVER" == "vfs" ]]; then + log "[WARN] Storage driver is 'vfs'. Image operations will be slow." + fi + + # Support multiple paths + ROOT_DIRS=${CONTAINER_MAP[$CTID]} + for ROOT_DIR in $ROOT_DIRS; do + log "[+] Scanning for compose files in: $ROOT_DIR" + + # Determine Command + if pct exec "$CTID" -- podman compose version >/dev/null 2>&1; then + FINAL_CMD="podman compose" + elif pct exec "$CTID" -- podman-compose version >/dev/null 2>&1; then + FINAL_CMD="podman-compose" + elif pct exec "$CTID" -- docker-compose version >/dev/null 2>&1; then + FINAL_CMD="docker-compose" + else FINAL_CMD=""; fi + + if [ -n "$FINAL_CMD" ]; then + # Scan for files + if pct exec "$CTID" -- [ -d "$ROOT_DIR" ]; then + pct exec "$CTID" -- find "$ROOT_DIR" -maxdepth 2 -name "docker-compose.y*ml" | while read -r FILE; do + [ -z "$FILE" ] && continue + DIR=$(dirname "$FILE") + log " -> Updating Project: $DIR" + + log " -> Pulling new images (this may take a while)..." + run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD pull" + + if [ "$WAS_STOPPED" = true ]; then + log " -> Recreating stopped container (up --no-start)..." + run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD up --no-start" || + log " [WARN] Failed to recreate stopped container. Old images may not be pruned." + else + log " -> Applying updates (up -d --force-recreate)..." + run_pct exec "$CTID" -- bash -c "cd \"$DIR\" && $FINAL_CMD up -d --force-recreate" + fi + done + else + log " [WARN] Root directory not found: $ROOT_DIR" + fi + fi + done + log "[+] Pruning all unused images to save space..." + run_pct exec "$CTID" -- podman image prune -a -f + fi + + # --- 4. MAINTENANCE --- + log "[+] Running maintenance tasks (journal, orphans)..." + run_pct exec "$CTID" -- journalctl --rotate + run_pct exec "$CTID" -- journalctl --vacuum-time=7d + run_pct exec "$CTID" -- bash -c "orphans=\$(pacman -Qtdq); if [ -n \"\$orphans\" ]; then echo \" -> Removing orphans: \$orphans\"; pacman -Rns \$orphans --noconfirm; fi" + + if [ "$WAS_STOPPED" = true ]; then + log "[+] Shutting down container (was previously stopped)..." + run_pct shutdown "$CTID" + fi +done diff --git a/lxc-update_1.sh b/lxc-update_1.sh new file mode 100755 index 0000000..b284cd7 --- /dev/null +++ b/lxc-update_1.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# --- CONFIGURATION --- +# The user that runs paru inside the Arch LXCs +AUR_USER="liph" + +# 1. EXCLUDE LIST: Put CTIDs here that you want the script to TOTALLY ignore +# Example: EXCLUDE_LIST=( 105 110 ) +EXCLUDE_LIST=(106 113 114 115 116) + +declare -A CONTAINER_MAP +CONTAINER_MAP[100]="/mnt/flash1/podman/lxc_servarr/" +CONTAINER_MAP[101]="/mnt/flash1/podman/lxc_second/" +CONTAINER_MAP[108]="/mnt/flash1/podman/lxc_dns/" # Multiple paths allowed +CONTAINER_MAP[109]="/mnt/flash1/podman/lxc_immich/" +CONTAINER_MAP[111]="/mnt/flash1/podman/lxc_ollama/" + +ALL_CTIDS=$(pct list | awk 'NR>1 {print $1}') + +for CTID in $ALL_CTIDS; do + if [[ " ${EXCLUDE_LIST[@]} " =~ " ${CTID} " ]]; then continue; fi + if pct config $CTID | grep -q "template: 1"; then continue; fi + + NAME=$(pct config $CTID | grep "hostname:" | awk '{print $2}') + STATUS=$(pct status $CTID | awk '{print $2}') + + echo "=======================================================" + echo " PROCESSING LXC: $NAME (ID: $CTID)" + echo "=======================================================" + + WAS_STOPPED=false + if [ "$STATUS" == "stopped" ]; then + pct start $CTID + sleep 10 + WAS_STOPPED=true + fi + + # 1. OS UPDATER (Handling Paru correctly) + echo "[+] Updating Arch packages..." + # We use -Syu. We run it via 'sudo -u user' but because pct exec is root, + # it won't ask for a password. + pct exec $CTID -- sudo -u $AUR_USER paru -Syu --noconfirm --cleanafter + + # 2. PODMAN SCAN & AUTO-DETECT COMMAND + if [[ -n "${CONTAINER_MAP[$CTID]}" ]]; then + ROOT_DIR=${CONTAINER_MAP[$CTID]} + + # Detect which command works: 'podman compose' (V2) or 'podman-compose' (Python) + # We test this inside the LXC + if pct exec $CTID -- podman compose version >/dev/null 2>&1; then + FINAL_CMD="podman compose" + elif pct exec $CTID -- podman-compose version >/dev/null 2>&1; then + FINAL_CMD="podman-compose" + elif pct exec $CTID -- docker-compose version >/dev/null 2>&1; then + FINAL_CMD="docker-compose" + else + FINAL_CMD="" + fi + + if [ -z "$FINAL_CMD" ]; then + echo "[ERROR] No compose command found in LXC $CTID!" + else + echo "[#] Using command: $FINAL_CMD" + COMPOSE_FILES=$(pct exec $CTID -- find "$ROOT_DIR" -maxdepth 2 -name "docker-compose.y*ml") + + for FILE in $COMPOSE_FILES; do + DIR=$(dirname "$FILE") + echo " -> Updating: $DIR" + + # Run the pull and up + # Note: No 'sudo' here because pct exec is already root full + pct exec $CTID -- bash -c "cd $DIR && $FINAL_CMD pull" + + if [ "$WAS_STOPPED" = false ]; then + pct exec $CTID -- bash -c "cd $DIR && $FINAL_CMD up -d" + fi + done + fi + + echo "[+] Pruning images..." + pct exec $CTID -- podman image prune -f + fi + + # 3. MAINTENANCE + pct exec $CTID -- journalctl --rotate + pct exec $CTID -- journalctl --vacuum-time=7d + pct exec $CTID -- bash -c 'orphans=$(pacman -Qtdq); if [ -n "$orphans" ]; then pacman -Rns $orphans --noconfirm; fi' + + if [ "$WAS_STOPPED" = true ]; then + pct shutdown $CTID + fi +done diff --git a/m3u8_1.sh b/m3u8_1.sh new file mode 100755 index 0000000..ec879c5 --- /dev/null +++ b/m3u8_1.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Ask for the final filename first +echo "=== Video Downloader (yt-dlp + aria2c) ===" +read -p "Enter the final filename (without extension): " filename + +# Then ask for the m3u8 URL +read -p "Enter the m3u8 URL: " url + +# Download using yt-dlp with aria2c +echo "Starting download..." +yt-dlp --external-downloader aria2c \ + --external-downloader-args "aria2c:-x 16 -s 16" \ + -o "${filename}.%(ext)s" \ + "$url" + +echo "Download complete: ${filename}.mp4" diff --git a/mail b/mail new file mode 100755 index 0000000..e69de29 diff --git a/maildir2vcf.sh b/maildir2vcf.sh new file mode 100755 index 0000000..aee5094 --- /dev/null +++ b/maildir2vcf.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# ~/.local/bin/maildir2vcf +import email, os, sys, pathlib, fileinput + +MAILDIR = pathlib.Path.home()/ "Mail" # ← change if your mail sits elsewhere +OUTDIR = pathlib.Path.home()/ ".contacts_mail" +OUTDIR.mkdir(exist_ok=True) + +seen = set() # de-duplicate on lower-case e-mail + +def vcard(name, mail): + safe_name = name.replace(";", " ").strip() or "No Name" + safe_mail = mail.strip().lower() + if safe_mail in seen: # already exported + return + seen.add(safe_mail) + fn = OUTDIR/f"{safe_mail}.vcf" + fn.write_text(f"""\ +BEGIN:VCARD +VERSION:3.0 +FN:{safe_name} +EMAIL:{safe_mail} +END:VCARD +""") + +def addresses_from_msg(path): + with open(path, "rb") as f: + msg = email.message_from_binary_file(f) + for hdr in ("From", "To", "Cc", "Bcc"): + for addr in email.utils.getaddresses(msg.get_all(hdr, [])): + name, mail = addr + if "@" in mail: + vcard(name, mail) + +# walk cur/ + new/ (typical Maildir layout) +for sub in ("cur", "new"): + for root, _, files in os.walk(MAILDIR/sub): + for file in files: + if file.startswith("."): + continue + addresses_from_msg(pathlib.Path(root)/file) + +print("Exported", len(seen), "unique addresses →", OUTDIR) diff --git a/move-trash_1.sh b/move-trash_1.sh new file mode 100755 index 0000000..e8269b3 --- /dev/null +++ b/move-trash_1.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Get the current message's notmuch ID from aerc's environment +# We'll use notmuch to find and move the file + +maildir="$HOME/.local/share/mail" + +# Find files for messages in current thread that aren't already in Trash +notmuch search --output=files thread:{} 2>/dev/null | while read filepath; do + # Skip if already in Trash + if [[ "$filepath" == *"/Trash/"* ]]; then + continue + fi + + if [[ -f "$filepath" ]]; then + filename=$(basename "$filepath") + mkdir -p "$maildir/Trash/cur" + mv "$filepath" "$maildir/Trash/cur/$filename" + fi +done + +# Re-index +notmuch new >/dev/null 2>&1 diff --git a/move-trash_1_1.sh b/move-trash_1_1.sh new file mode 100755 index 0000000..b3c4bde --- /dev/null +++ b/move-trash_1_1.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Read the message file path from aerc +while read -r filepath; do + # Get the base mail directory + maildir="$HOME/.local/share/mail" + + # Extract the filename + filename=$(basename "$filepath") + + # Create Trash/cur if it doesn't exist + mkdir -p "$maildir/Trash/cur" + + # Move the file to Trash + if [[ -f "$filepath" ]]; then + mv "$filepath" "$maildir/Trash/cur/$filename" + echo "Moved to Trash: $filename" >&2 + fi +done + +# Re-index with notmuch +notmuch new >/dev/null 2>&1 diff --git a/new-sync.sh b/new-sync.sh new file mode 100755 index 0000000..9f0d2d8 --- /dev/null +++ b/new-sync.sh @@ -0,0 +1,35 @@ +#!/bin/sh +MBSYNC=$(pgrep mbsync) +NOTMUCH=$(pgrep notmuch) +if [ -n "$MBSYNC" -o -n "$NOTMUCH" ]; then + echo "Already running one instance of mbsync or notmuch. Exiting..." + exit 0 +fi + +maildir="$HOME/.local/share/mail" + +echo "Moving messages tagged as *deleted* to Trash" +notmuch search --format=text0 --output=files tag:deleted | while IFS= read -r -d '' filepath; do + # Skip if already in Trash + case "$filepath" in + */Trash/*) + continue + ;; + esac + + # Move to Trash if file exists + if [ -f "$filepath" ]; then + filename=$(basename "$filepath") + mkdir -p "$maildir/Trash/cur" + mv -v "$filepath" "$maildir/Trash/cur/$filename" + fi +done + +mbsync -Va +notmuch new +PYTHONWARNINGS="ignore::UserWarning" afew --tag --new + +# Auto-tag and cleanup +notmuch tag +sent -- folder:Sent and not tag:sent +notmuch tag +trash -- folder:Trash and not tag:trash +notmuch tag -deleted -- folder:Trash diff --git a/new-sync_1.sh b/new-sync_1.sh new file mode 100755 index 0000000..c8b1ab4 --- /dev/null +++ b/new-sync_1.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +MBSYNC=$(pgrep mbsync) +NOTMUCH=$(pgrep notmuch) + +if [ -n "$MBSYNC" -o -n "$NOTMUCH" ]; then + echo "Already running one instance of mbsync or notmuch. Exiting..." + exit 0 +fi + +echo "Deleting messages tagged as *deleted*" +notmuch search --format=text0 --output=files tag:deleted | xargs -0 --no-run-if-empty rm -v + +mbsync -Va +notmuch new diff --git a/newfile.vcf b/newfile.vcf new file mode 100755 index 0000000..e69de29 diff --git a/niri_br.sh b/niri_br.sh new file mode 100755 index 0000000..715591f --- /dev/null +++ b/niri_br.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +STATE_FILE="$HOME/.cache/brightness_state" +TEMP_FILE="$HOME/.cache/bluelight_state" + +# Get screen brightness level +echo "Enter screen brightness level (0-10, where 10 = 100%):" +read -r LEVEL + +# Validate input +if ! [[ "$LEVEL" =~ ^[0-9]+$ ]] || [ "$LEVEL" -lt 0 ] || [ "$LEVEL" -gt 10 ]; then + echo "Invalid input. Please enter a number between 0 and 10." + exit 1 +fi + +# Convert to decimal (0-10 -> 0.0-1.0) +BRIGHTNESS=$(echo "scale=2; $LEVEL / 10" | bc) + +# Ensure at least 0.1 to avoid complete darkness +if (($(echo "$BRIGHTNESS < 0.1" | bc -l))); then + BRIGHTNESS=0.1 +fi + +# Ask about blue light filter +echo "Apply blue light filter? (y/n):" +read -r BLUELIGHT + +if [[ "$BLUELIGHT" == "y" || "$BLUELIGHT" == "Y" ]]; then + TEMP=3400 + FILTER_MSG="with blue light filter" +else + TEMP=6500 + FILTER_MSG="" +fi + +# Apply settings +pkill -9 gammastep +gammastep -O $TEMP -b "$BRIGHTNESS" &>/dev/null & + +# Save state +echo "$BRIGHTNESS" >"$STATE_FILE" +echo "$TEMP" >"$TEMP_FILE" + +echo "Screen brightness set to ${LEVEL}0% $FILTER_MSG" +notify-send "Brightness" "${LEVEL}0% $FILTER_MSG" -t 2000 diff --git a/niri_br_blue.sh b/niri_br_blue.sh new file mode 100755 index 0000000..f19b164 --- /dev/null +++ b/niri_br_blue.sh @@ -0,0 +1,20 @@ +#!/bin/bash +STATE_FILE="$HOME/.cache/brightness_state" +TEMP_FILE="$HOME/.cache/bluelight_state" + +CURRENT=$(cat "$TEMP_FILE" 2>/dev/null || echo "6500") +BRIGHTNESS=$(cat "$STATE_FILE" 2>/dev/null || echo "1.0") + +if [ "$CURRENT" = "6500" ]; then + NEW=3400 + MSG="ON" +else + NEW=6500 + MSG="OFF" +fi + +pkill -9 gammastep +gammastep -O $NEW -b "$BRIGHTNESS" &>/dev/null & + +echo "$NEW" >"$TEMP_FILE" +notify-send "Blue Light" "$MSG" -t 1000 diff --git a/niri_br_down.sh b/niri_br_down.sh new file mode 100755 index 0000000..ec25e02 --- /dev/null +++ b/niri_br_down.sh @@ -0,0 +1,15 @@ +#!/bin/bash +STATE_FILE="$HOME/.cache/brightness_state" +TEMP_FILE="$HOME/.cache/bluelight_state" + +CURRENT=$(cat "$STATE_FILE" 2>/dev/null || echo "1.0") +NEW=$(echo "$CURRENT - 0.1" | bc -l) +if (($(echo "$NEW < 0.3" | bc -l))); then NEW=0.3; fi + +TEMP=$(cat "$TEMP_FILE" 2>/dev/null || echo "6500") + +pkill -9 gammastep +gammastep -O $TEMP -b "$NEW" &>/dev/null & + +echo "$NEW" >"$STATE_FILE" +notify-send "Brightness" "$(printf '%.0f%%' $(echo "$NEW * 100" | bc))" -t 1000 diff --git a/niri_br_reset.sh b/niri_br_reset.sh new file mode 100755 index 0000000..41e6b4c --- /dev/null +++ b/niri_br_reset.sh @@ -0,0 +1,11 @@ +#!/bin/bash +STATE_FILE="$HOME/.cache/brightness_state" +TEMP_FILE="$HOME/.cache/bluelight_state" + +TEMP=$(cat "$TEMP_FILE" 2>/dev/null || echo "6500") + +pkill -9 gammastep +gammastep -O $TEMP -b 1.0 &>/dev/null & + +echo "1.0" >"$STATE_FILE" +notify-send "Brightness" "100%" -t 1000 diff --git a/niri_br_up.sh b/niri_br_up.sh new file mode 100755 index 0000000..ca455e2 --- /dev/null +++ b/niri_br_up.sh @@ -0,0 +1,19 @@ +#!/bin/bash +STATE_FILE="$HOME/.cache/brightness_state" +TEMP_FILE="$HOME/.cache/bluelight_state" + +CURRENT=$(cat "$STATE_FILE" 2>/dev/null || echo "1.0") +NEW=$(echo "$CURRENT + 0.1" | bc -l) +if (($(echo "$NEW > 1.0" | bc -l))); then + NEW=1.0 + notify-send "Brightness" "Maximum (100%)" -t 1000 + exit 0 +fi + +TEMP=$(cat "$TEMP_FILE" 2>/dev/null || echo "6500") + +pkill -9 gammastep +gammastep -O $TEMP -b "$NEW" &>/dev/null & + +echo "$NEW" >"$STATE_FILE" +notify-send "Brightness" "$(printf '%.0f%%' $(echo "$NEW * 100" | bc))" -t 1000 diff --git a/nonum_1.sh b/nonum_1.sh new file mode 100755 index 0000000..8cdb7df --- /dev/null +++ b/nonum_1.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "Removing numbers from filenames and titles (keeping track numbers)..." +echo "" + +for file in *.mp3; do + filename="${file%.*}" + + # Extract track number before removing it from filename + track_num=$(echo "$filename" | grep -oP '^\d+') + + # Remove leading numbers and dot/space from filename + new_filename=$(echo "$filename" | sed 's/^[0-9]\+[. ]\+//') + new_file="${new_filename}.mp3" + + if [ "$file" != "$new_file" ]; then + echo "Processing: $file" + echo " New filename: $new_file" + echo " New title: $new_filename" + echo " Track number: $track_num (kept)" + + # Retag with new title (without number) but keep track number + ffmpeg -i "$file" -c copy \ + -metadata title="$new_filename" \ + -metadata track="$track_num" \ + "${new_filename}_temp.mp3" 2>/dev/null + + # Remove original and rename temp file + rm "$file" + mv "${new_filename}_temp.mp3" "$new_file" + + echo " ✓ Done" + echo "" + fi +done + +echo "Complete! All files processed." diff --git a/nothumb_1.sh b/nothumb_1.sh new file mode 100755 index 0000000..7d052ee --- /dev/null +++ b/nothumb_1.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +echo "Removing thumbnails and associated CDs from MP3 files..." +echo "" + +counter=0 + +# Process all mp3 files +for file in *.mp3 */*.mp3 */*/*.mp3; do + # Skip if file doesn't exist + [ -e "$file" ] || continue + + filename=$(basename "$file") + + echo "Processing: $file" + + # Create temp file + temp_file="/tmp/strip_${counter}_$$.mp3" + + # Remove all images/artwork and strip video streams + ffmpeg -i "$file" \ + -map 0:a \ + -c copy \ + -map_metadata 0 \ + -id3v2_version 3 \ + -vn \ + "$temp_file" -y >/dev/null 2>&1 + + if [ -f "$temp_file" ] && [ -s "$temp_file" ]; then + # Remove disc number metadata using eyeD3 if available + if command -v eyeD3 &> /dev/null; then + eyeD3 --remove-images \ + --disc-num "" \ + "$temp_file" >/dev/null 2>&1 + fi + + mv "$temp_file" "$file" + echo " ✓ Done" + ((counter++)) + else + echo " ✗ Failed" + [ -f "$temp_file" ] && rm "$temp_file" + fi +done + +echo "" +echo "Processed $counter files" diff --git a/podman-update.sh b/podman-update.sh new file mode 100755 index 0000000..8528041 --- /dev/null +++ b/podman-update.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# 1. CONFIGURATION +# List folders to exclude (separated by spaces, NOT commas) +EXCLUDED_FOLDERS=("ai" "dns-adguard" "dns-pihole" "immich") + +# 2. VALIDATION +# Ensure the script is running as root (Sudo) to avoid permission issues +if [ "$EUID" -ne 0 ]; then + echo "==========================================" + echo "ERROR: Please run this script as root" + echo "Usage: sudo $0" + echo "==========================================" + exit 1 +fi + +# 3. SETUP ABSOLUTE PATHS +# Get the absolute path of the directory where this script is located +# This ensures that even if we 'cd' into folders, we don't lose track of where we are. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "==========================================" +echo "Starting Podman Maintenance Script" +echo "Target Directory: $SCRIPT_DIR" +echo "Excluded Folders: ${EXCLUDED_FOLDERS[*]}" +echo "==========================================" + +# 4. MAIN LOOP (Using Globbing instead of 'find' to prevent Subshell issues) +# "$SCRIPT_DIR"/*/ iterates through all immediate subdirectories +for dir in "$SCRIPT_DIR"/*/; do + # Extract just the folder name (e.g., /podman/essential/ -> "essential") + folder_name=$(basename "$dir") + + # --- EXCLUSION CHECK --- + skip_folder=0 + for excluded in "${EXCLUDED_FOLDERS[@]}"; do + if [[ "$folder_name" == "$excluded" ]]; then + # Debug: echo "Skipping $folder_name (Excluded)" + skip_folder=1 + break + fi + done + if [ "$skip_folder" -eq 1 ]; then + continue + fi + # --------------------- + + # Check for compose file (handles both .yml and .yaml) + COMPOSE_FILE="" + if [ -f "$dir/docker-compose.yml" ]; then + COMPOSE_FILE="$dir/docker-compose.yml" + elif [ -f "$dir/docker-compose.yaml" ]; then + COMPOSE_FILE="$dir/docker-compose.yaml" + fi + + # If file exists, proceed with updates + if [ -n "$COMPOSE_FILE" ]; then + echo "" + echo "==========================================" + echo "[PROCESSING] Folder: $folder_name" + + # Change directory to the stack + # We trap errors in cd to prevent the script from running commands in the wrong place + cd "$dir" || { + echo "[ERROR] Could not enter $dir" + continue + } + + echo " -> Step 1: Stopping containers..." + podman-compose stop + + echo " -> Step 2: Removing containers and networks (Down)..." + podman-compose down + + echo " -> Step 3: Pulling latest images..." + podman-compose pull + + echo " -> Step 4: Starting services (Up -d)..." + podman-compose up -d + + echo " -> [COMPLETED] $folder_name is updated." + + # Return to the base directory (Safety measure) + cd "$SCRIPT_DIR" || exit + else + # Debug: Uncomment the line below to see which folders are being ignored + # echo "[IGNORE] No compose file in: $folder_name" + : + fi +done + +echo "" +echo "==========================================" +echo "All tasks successfully completed." +echo "==========================================" diff --git a/pve_chmod.sh b/pve_chmod.sh index a17b8eb..d015056 100755 --- a/pve_chmod.sh +++ b/pve_chmod.sh @@ -9,3 +9,9 @@ sudo chmod -R 777 /mnt/tank/audio sudo chown -R 101000:101000 /mnt/flash1/downloads/music sudo chmod -R 755 /mnt/flash1/downloads/music + +sudo chown -R 100033:101000 /mnt/ssd2/cloud_phil /mnt/flash1/podman/lxc_servarr/browser/data/.consume +sudo chmod -R 775 /mnt/ssd2/cloud_phil /mnt/flash1/podman/lxc_servarr/browser/data/.consume + +sudo chown -R 101000:101000 /mnt/flash1/podman/lxc_servarr/nextcloud/config/obsidian/vaults +sudo chmod -R 755 /mnt/flash1/podman/lxc_servarr/nextcloud/config/obsidian/vaults diff --git a/pve_chmod_1.sh b/pve_chmod_1.sh new file mode 100755 index 0000000..34d56bc --- /dev/null +++ b/pve_chmod_1.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo chown -R 101000:100033 /mnt/{hdd1,hdd2,hdd3,hdd4,hdd5,ssd1,ssd2} + +sudo chmod -R 775 /mnt/{hdd1,hdd2,hdd3,hdd4,hdd5,ssd1,ssd2} + +sudo chown -R 101000:101000 /mnt/tank/audio +sudo chmod -R 777 /mnt/tank/audio + +sudo chown -R 101000:101000 /mnt/flash1/downloads/music +sudo chmod -R 755 /mnt/flash1/downloads/music + +sudo chown -R 101000:100033 /mnt/ssd2/cloud_phil /mnt/flash1/podman/browser/data/.consume +sudo chmod -R 775 /mnt/ssd2/cloud_phil /mnt/flash1/podman/browser/data/.consume + diff --git a/retag_1.sh b/retag_1.sh new file mode 100755 index 0000000..2033863 --- /dev/null +++ b/retag_1.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Get metadata from first mp3 file if it exists +first_file=$(ls *.mp3 2>/dev/null | head -n 1) + +if [ -n "$first_file" ]; then + default_artist=$(ffprobe -v quiet -show_entries format_tags=artist -of default=noprint_wrappers=1:nokey=1 "$first_file") + default_album=$(ffprobe -v quiet -show_entries format_tags=album -of default=noprint_wrappers=1:nokey=1 "$first_file") + default_date=$(ffprobe -v quiet -show_entries format_tags=date -of default=noprint_wrappers=1:nokey=1 "$first_file") + default_composer=$(ffprobe -v quiet -show_entries format_tags=composer -of default=noprint_wrappers=1:nokey=1 "$first_file") +fi + +# Prompt with defaults +read -p "Enter Album Artist [$default_artist]: " album_artist +album_artist=${album_artist:-$default_artist} + +read -p "Enter Album [$default_album]: " album +album=${album:-$default_album} + +read -p "Enter Release Date (YYYY or YYYY-MM-DD) [$default_date]: " release_date +release_date=${release_date:-$default_date} + +read -p "Enter Composer [$default_composer]: " composer +composer=${composer:-$default_composer} + +echo "" +echo "Retagging files..." +echo "Album Artist: $album_artist" +echo "Album: $album" +echo "Release Date: $release_date" +echo "Composer: $composer" +echo "" + +for file in *.mp3; do + filename="${file%.*}" + track_num=$(echo "$filename" | grep -oP '^\d+') + + echo "Retagging: $file (Track $track_num)" + + if [ -n "$composer" ]; then + ffmpeg -i "$file" -c copy \ + -metadata title="$filename" \ + -metadata track="$track_num" \ + -metadata album_artist="$album_artist" \ + -metadata artist="$album_artist" \ + -metadata album="$album" \ + -metadata date="$release_date" \ + -metadata composer="$composer" \ + -metadata disc="" \ + -metadata discnumber="" \ + -metadata totaldiscs="" \ + -metadata disk="" \ + -metadata disknumber="" \ + "${filename}_temp.mp3" 2>/dev/null + else + ffmpeg -i "$file" -c copy \ + -metadata title="$filename" \ + -metadata track="$track_num" \ + -metadata album_artist="$album_artist" \ + -metadata artist="$album_artist" \ + -metadata album="$album" \ + -metadata date="$release_date" \ + -metadata composer="" \ + -metadata disc="" \ + -metadata discnumber="" \ + -metadata totaldiscs="" \ + -metadata disk="" \ + -metadata disknumber="" \ + "${filename}_temp.mp3" 2>/dev/null + fi + + mv "${filename}_temp.mp3" "$file" +done + +echo "" +echo "Done! All files retagged." diff --git a/sig-picker.sh b/sig-picker.sh new file mode 100755 index 0000000..c7e47e5 --- /dev/null +++ b/sig-picker.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +read input + +case "$input" in + "liiph@proton.me") + cat ~/.config/aerc/sigs/liph.txt + ;; + "liiph@protonmail.com") + cat ~/.config/aerc/sigs/liph.txt + ;; + "liiph@pm.me") + cat ~/.config/aerc/sigs/liph.txt + ;; + "ph.waibel@proton.me") + cat ~/.config/aerc/sigs/formal.txt + ;; + "ph.waibel@pm.me") + cat ~/.config/aerc/sigs/phil.txt + ;; + *) + # Default signature if no match + cat ~/.config/aerc/sigs/default.txt + ;; +esac diff --git a/snapraid-ping.sh b/snapraid-ping.sh new file mode 100755 index 0000000..0574bb6 --- /dev/null +++ b/snapraid-ping.sh @@ -0,0 +1,11 @@ +#!/bin/bash +/usr/bin/python3 /opt/snapraid-runner/snapraid-runner.py -c /opt/snapraid-runner/snapraid-runner.conf +STATE=$? + +if [ $STATE -le 2 ]; then + # Success or Warning (still successful sync) + /usr/bin/curl -fsS "https://uptime.liphlink.xyz/api/push/a73pMB3WnL?status=up&msg=ExitCode_$STATE" +else + # Hard Failure + /usr/bin/curl -fsS "https://uptime.liphlink.xyz/api/push/a73pMB3WnL?status=down&msg=Failed_Code_$STATE" +fi diff --git a/split_1.sh b/split_1.sh new file mode 100755 index 0000000..d539899 --- /dev/null +++ b/split_1.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Handle Ctrl+C gracefully +trap ctrl_c INT + +function ctrl_c() { + echo "" + echo "Cancelled by user." + exit 0 +} + +# Function to read input with tab completion for paths +read_path() { + local prompt="$1" + local input + + # Use read with -e flag for readline editing (enables tab completion) + read -e -p "$prompt" input + echo "$input" +} + +# Get source folder +SOURCE=$(read_path "Enter source folder path: ") + +# Validate source exists +if [ ! -d "$SOURCE" ]; then + echo "Error: Source folder '$SOURCE' does not exist!" + exit 1 +fi + +# Get destination folders +echo "Enter destination folders (one per line, empty line to finish):" +echo "(Tip: Use TAB for folder completion, Ctrl+C to cancel)" +DESTINATIONS=() +while true; do + dest=$(read_path "Destination $((${#DESTINATIONS[@]} + 1)): ") + if [ -z "$dest" ]; then + break + fi + # Create destination if it doesn't exist + if [ ! -d "$dest" ]; then + read -p "Destination '$dest' doesn't exist. Create it? (y/n): " create + if [ "$create" = "y" ]; then + mkdir -p "$dest" + else + continue + fi + fi + DESTINATIONS+=("$dest") +done + +# Validate we have destinations +if [ ${#DESTINATIONS[@]} -eq 0 ]; then + echo "Error: No destination folders provided!" + exit 1 +fi + +# Display summary +echo "" +echo "=== Summary ===" +echo "Source: $SOURCE" +echo "Destinations:" +for i in "${!DESTINATIONS[@]}"; do + echo " $((i + 1)). ${DESTINATIONS[$i]}" +done + +# Get list of items in source +items=("$SOURCE"/*) +total=${#items[@]} +items_per_dest=$((total / ${#DESTINATIONS[@]})) + +echo "" +echo "Total items to split: $total" +echo "Items per destination: ~$items_per_dest" +echo "" + +read -p "Proceed with copy? (y/n): " confirm +if [ "$confirm" != "y" ]; then + echo "Cancelled." + exit 0 +fi + +# Distribute items +echo "" +echo "Starting distribution..." +for i in "${!DESTINATIONS[@]}"; do + start=$((i * items_per_dest)) + if [ $i -eq $((${#DESTINATIONS[@]} - 1)) ]; then + # Last destination gets remainder + end=$total + else + end=$(((i + 1) * items_per_dest)) + fi + + echo "" + echo "Copying to ${DESTINATIONS[$i]} (items $((start + 1)) to $end)..." + for ((j=start; j $item_name" + rsync -a "${items[$j]}" "${DESTINATIONS[$i]}/" + done +done + +echo "" +echo "Distribution complete!" diff --git a/sync-all-mail.sh b/sync-all-mail.sh new file mode 100755 index 0000000..4a236d1 --- /dev/null +++ b/sync-all-mail.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo "=== Syncing all mailboxes ===" + +# 1. Sync emails bidirectionally (includes deletions) +mbsync -a + +# 2. Index each account separately (runs post-new hook) +for account in phil spam; do + echo "Indexing $account..." + NOTMUCH_CONFIG=~/Mail/$account/.notmuch-config notmuch new +done + +# 3. Optional: Sync back any tag changes to IMAP flags +for account in phil spam; do + NOTMUCH_CONFIG=~/Mail/$account/.notmuch-config notmuch tag --batch < /dev/null +done + +echo "=== Sync complete ===" diff --git a/sync-mail_1.sh b/sync-mail_1.sh new file mode 100755 index 0000000..157150a --- /dev/null +++ b/sync-mail_1.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Sync mail from ProtonMail Bridge (suppress CLOSE error) +echo "Syncing mail from ProtonMail..." +mbsync -a 2>&1 | grep -v "CLOSE" + +# Index new mail (this automatically runs post-new hook) +echo "Indexing new messages..." +notmuch new + +# Move all deleted messages to Trash folder +echo "Cleaning up deleted messages..." +DELETED_COUNT=0 +notmuch search --output=files tag:deleted 2>/dev/null | while read filepath; do + if [ -f "$filepath" ]; then + TRASH_DIR="$HOME/.local/share/mail/Trash/cur" + mkdir -p "$TRASH_DIR" + + FILENAME=$(basename "$filepath") + mv "$filepath" "$TRASH_DIR/$FILENAME" + ((DELETED_COUNT++)) + fi +done + +# Update notmuch after moving files +if [ $DELETED_COUNT -gt 0 ]; then + echo "Moved $DELETED_COUNT message(s) to Trash" + notmuch new --no-hooks >/dev/null 2>&1 +fi + +# Sync moved messages back to ProtonMail +echo "Syncing changes to ProtonMail..." +mbsync -a 2>&1 | grep -v "CLOSE" >/dev/null + +# Notify if new mail +NEW=$(notmuch count tag:unread) +if [ $NEW -gt 0 ]; then + notify-send "📬 Mail" "$NEW unread message(s)" +fi + +echo "Sync complete: $(date)" diff --git a/sync-mail_1_1.sh b/sync-mail_1_1.sh new file mode 100755 index 0000000..a9b6565 --- /dev/null +++ b/sync-mail_1_1.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Sync with server (this pushes local changes including deletions) +mbsync -a + +# Re-index +notmuch new + +# Auto-tag folders +notmuch tag +sent -- folder:Sent and not tag:sent +notmuch tag +draft -- folder:Drafts and not tag:draft +notmuch tag +trash -- folder:Trash and not tag:trash +notmuch tag +spam -- folder:Spam and not tag:spam diff --git a/theme-switch b/theme-switch new file mode 100755 index 0000000..cfd6bd3 --- /dev/null +++ b/theme-switch @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +set -e + +THEME="${1:-catppuccin-mocha}" +CONFIG_DIR="$HOME/.config" +# WAYBAR_DIR="$CONFIG_DIR/niri/waybar-niri" + +declare -A THEMES=( + ["cat"]="catppuccin-mocha" + ["rose"]="rose-pine-moon" + ["dracula"]="dracula" +) + +if [[ ! -v "THEMES[$THEME]" ]]; then + echo "Unknown theme: $THEME" + echo "Available themes: ${!THEMES[@]}" + exit 1 +fi + +echo "Switching to theme: $THEME" + +# Neovim +echo "vim.cmd.colorscheme('${THEMES[$THEME]}')" >"$CONFIG_DIR/nvim/lua/current_theme.lua" +echo " ✓ Neovim" + +# Kitty +if command -v kitty &>/dev/null && [[ -f "$CONFIG_DIR/kitty/themes/${THEME}.conf" ]]; then + ln -sf "$CONFIG_DIR/kitty/themes/${THEME}.conf" "$CONFIG_DIR/kitty/current-theme.conf" + kill -SIGUSR1 $(pgrep kitty) 2>/dev/null || true + echo " ✓ Kitty" +fi + +# Btop +if [[ -f "$CONFIG_DIR/btop/themes/${THEME}.theme" ]]; then + sed -i "s|^color_theme = .*|color_theme = \"$CONFIG_DIR/btop/themes/${THEME}.theme\"|" "$CONFIG_DIR/btop/btop.conf" + echo " ✓ Btop" +fi + +# Yazi +if [[ -f "$CONFIG_DIR/yazi/themes/${THEME}.toml" ]]; then + ln -sf "$CONFIG_DIR/yazi/themes/${THEME}.toml" "$CONFIG_DIR/yazi/theme.toml" + echo " ✓ Yazi" +fi + +# # Waybar - simply copy the theme file to theme.css +# if [[ -f "$WAYBAR_DIR/themes/${THEME}.css" ]]; then +# cp "$WAYBAR_DIR/themes/${THEME}.css" "$WAYBAR_DIR/theme.css" +# +# pkill waybar 2>/dev/null || true +# sleep 0.5 +# waybar -c "$WAYBAR_DIR/config.jsonc" -s "$WAYBAR_DIR/style.css" & +# echo " ✓ Waybar" +# else +# echo " ✗ Waybar: theme file not found at $WAYBAR_DIR/themes/${THEME}.css" +# fi + +# Bat +if command -v bat &>/dev/null; then + case $THEME in + catppuccin-mocha) BAT_THEME="Catppuccin Mocha" ;; + rose-pine-moon) BAT_THEME="TwoDark" ;; + *) BAT_THEME="Monokai Extended" ;; + esac + + if [[ -f "$CONFIG_DIR/bat/config" ]]; then + sed -i "s/^--theme=.*/--theme=\"${BAT_THEME}\"/" "$CONFIG_DIR/bat/config" + else + mkdir -p "$CONFIG_DIR/bat" + echo "--theme=\"${BAT_THEME}\"" >"$CONFIG_DIR/bat/config" + fi + echo " ✓ Bat" +fi + +echo "" +echo "✓ Theme switched to: $THEME" diff --git a/thumb_1.sh b/thumb_1.sh new file mode 100755 index 0000000..fc6a56e --- /dev/null +++ b/thumb_1.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Ask for the thumbnail filename +echo "=== Album Art Embedder (Recursive) ===" +echo "Current directory: $(pwd)" +read -p "Enter thumbnail filename in this directory (e.g., cover.jpg): " thumbnail + +# Full path to thumbnail +thumbnail_path="$(pwd)/$thumbnail" + +# Check if thumbnail exists +if [ ! -f "$thumbnail_path" ]; then + echo "Error: Thumbnail not found at $thumbnail_path" + exit 1 +fi + +# Ask for album artist +read -p "Enter album artist name: " album_artist + +# Get album name from current directory +album_name="$(basename "$(pwd)")" + +echo "" +echo "Found thumbnail: $thumbnail_path" +echo "Album: $album_name" +echo "Album Artist: $album_artist" +echo "Processing all mp3/mp4 files in subdirectories..." +echo "" + +# Counter for processed files +count=0 + +# Find all mp3 and mp4 files in subdirectories (not current dir) +find . -mindepth 2 -type f \( -name "*.mp3" -o -name "*.mp4" \) | while read -r file; do + echo "Processing: $file" + + # Get file extension + ext="${file##*.}" + + # Create temporary filename + temp_file="${file}.tmp.${ext}" + + # Add thumbnail and metadata using ffmpeg + if [ "$ext" = "mp3" ]; then + ffmpeg -i "$file" -i "$thumbnail_path" \ + -map 0 -map 1 \ + -c copy \ + -metadata album_artist="$album_artist" \ + -metadata album="$album_name" \ + -disposition:v:0 attached_pic \ + "$temp_file" 2>/dev/null + else + # For mp4 files + ffmpeg -i "$file" -i "$thumbnail_path" \ + -map 0 -map 1 \ + -c copy \ + -metadata artist="$album_artist" \ + -metadata album="$album_name" \ + -disposition:v:0 attached_pic \ + "$temp_file" 2>/dev/null + fi + + if [ $? -eq 0 ]; then + mv "$temp_file" "$file" + ((count++)) + echo "✓ Done" + else + echo "✗ Failed" + [ -f "$temp_file" ] && rm "$temp_file" + fi + echo "" +done + +echo "Finished! Processed $count files." +echo "Album Artist set to: $album_artist" +echo "Album set to: $album_name" diff --git a/tidal-mcp.sh b/tidal-mcp.sh new file mode 100755 index 0000000..e94a51d --- /dev/null +++ b/tidal-mcp.sh @@ -0,0 +1,11 @@ +cd ~/mcp/tidal-mcp/ + +uv venv --clear +source .venv/bin/activate + +uv pip install --editable . + +cd ~/mcp/tidal-mcp/tidal_api/ +uv pip install flask tidalapi + +nohup python app.py > tidal.log 2>&1 & diff --git a/tidal.log b/tidal.log new file mode 100755 index 0000000..9b7c006 --- /dev/null +++ b/tidal.log @@ -0,0 +1,2 @@ +nohup: ignoring input +/usr/bin/python: can't open file '/home/liph/dotfiles/scripts/scripts/app.py': [Errno 2] No such file or directory diff --git a/tmux-sessionizer.sh b/tmux-sessionizer.sh new file mode 100755 index 0000000..e89f2fb --- /dev/null +++ b/tmux-sessionizer.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Save this file as ~/scripts/tmux-sessionizer +# Make it executable with: chmod +x ~/.local/bin/tmux-sessionizer + +if [[ $# -eq 1 ]]; then + selected=$1 +else + selected=$(fd -H . ~/dotfiles ~/Desktop/main-cs ~/Desktop/main-cs/Projects ~/ ~/Desktop/work ~/Desktop/work/youtube -t d -d 1 | fzf) +fi + +if [[ -z $selected ]]; then + exit 0 +fi + +selected_name=$(basename "$selected" | tr . _) +tmux_running=$(pgrep tmux) + +if [[ -z $TMUX ]] && [[ -z $tmux_running ]]; then + tmux new-session -s $selected_name -c $selected + exit 0 +fi + +if ! tmux has-session -t=$selected_name 2> /dev/null; then + tmux new-session -ds $selected_name -c $selected +fi + +tmux switch-client -t $selected_name diff --git a/trash-message.sh b/trash-message.sh new file mode 100755 index 0000000..8970239 --- /dev/null +++ b/trash-message.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Save the piped message to temp file +TEMP_MSG=$(mktemp) +cat > "$TEMP_MSG" + +# Extract Message-ID more robustly +MESSAGE_ID=$(grep -i "^Message-ID:" "$TEMP_MSG" | head -1 | sed 's/^[Mm]essage-[Ii][Dd]: *//; s/^$//' | tr -d '\r\n ') + +# Cleanup temp file +rm "$TEMP_MSG" + +if [ -z "$MESSAGE_ID" ]; then + notify-send "Error" "Could not find Message-ID" + exit 1 +fi + +# Get file path from notmuch +FILEPATH=$(notmuch search --output=files "id:$MESSAGE_ID" 2>/dev/null | head -1) + +if [ -z "$FILEPATH" ] || [ ! -f "$FILEPATH" ]; then + # Try alternative search + FILEPATH=$(notmuch search --output=files --exclude=false "*" 2>/dev/null | xargs grep -l "Message-ID.*$MESSAGE_ID" 2>/dev/null | head -1) +fi + +if [ -z "$FILEPATH" ] || [ ! -f "$FILEPATH" ]; then + notify-send "Error" "File not found for Message-ID: $MESSAGE_ID" + exit 1 +fi + +# Move to Trash folder +TRASH_DIR="$HOME/.local/share/mail/Trash/cur" +mkdir -p "$TRASH_DIR" + +FILENAME=$(basename "$FILEPATH") +mv "$FILEPATH" "$TRASH_DIR/$FILENAME" + +# Update notmuch database +notmuch new --no-hooks >/dev/null 2>&1 + +# Tag as deleted +notmuch tag +deleted +trash -inbox -unread -- "id:$MESSAGE_ID" >/dev/null 2>&1 + +# Trigger sync in background +(sleep 2 && mbsync -a 2>&1 | grep -v CLOSE >/dev/null) & + +notify-send "📧 Aerc" "Moved to Trash" diff --git a/yazi-picker.sh b/yazi-picker.sh new file mode 100755 index 0000000..0a93c7d --- /dev/null +++ b/yazi-picker.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# ~/scripts/yazi-picker.sh + +# Launch Yazi and capture the selected file +selected_file=$(yazi --chooser-file) + +# If a file was selected, print it (or pass it to another application) +if [ -n "$selected_file" ]; then + echo "$selected_file" | xclip -selection clipboard # Copy to clipboard (optional) + echo "Selected file: $selected_file" +fi diff --git a/yazi-picker_1.sh b/yazi-picker_1.sh new file mode 100755 index 0000000..0a93c7d --- /dev/null +++ b/yazi-picker_1.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# ~/scripts/yazi-picker.sh + +# Launch Yazi and capture the selected file +selected_file=$(yazi --chooser-file) + +# If a file was selected, print it (or pass it to another application) +if [ -n "$selected_file" ]; then + echo "$selected_file" | xclip -selection clipboard # Copy to clipboard (optional) + echo "Selected file: $selected_file" +fi diff --git a/zoxide_openfiles_nvim.sh b/zoxide_openfiles_nvim.sh new file mode 100755 index 0000000..3d177ed --- /dev/null +++ b/zoxide_openfiles_nvim.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Script to find every single file and opens in neovim +# alias set as nzo in .zshrc +search_with_zoxdie() { + if [ -z "$1" ]; then + # use fd with fzf to select & open a file when no arg are provided + file="$(fd --type f -I -H -E .git -E .git-crypt -E .cache -E .backup | fzf --height=70% --preview='bat -n --color=always --line-range :500 {}')" + if [ -n "$file" ]; then + nvim "$file" + fi + else + # Handle when an arg is provided + lines=$(zoxide query -l | xargs -I {} fd --type f -I -H -E .git -E .git-crypt -E .cache -E .backup -E .vscode "$1" {} | fzf --no-sort) # Initial filter attempt with fzf + line_count="$(echo "$lines" | wc -l | xargs)" # Trim any leading spaces + + if [ -n "$lines" ] && [ "$line_count" -eq 1 ]; then + # looks for the exact ones and opens it + file="$lines" + nvim "$file" + elif [ -n "$lines" ]; then + # If multiple files are found, allow further selection using fzf and bat for preview + file=$(echo "$lines" | fzf --query="$1" --height=70% --preview='bat -n --color=always --line-range :500 {}') + if [ -n "$file" ]; then + nvim "$file" + fi + else + echo "No matches found." >&2 + fi + fi +} + +search_with_zoxdie "$@"