.local-bin/scripts/volumizer.sh
2025-05-16 03:33:10 -04:00

168 lines
4.1 KiB
Bash
Executable file

#!/usr/bin/env bash
MAX_VOLUME=50
ONLY_ON_HEADPHONES=false
NOTIFICATION_TITLE="Volume Limiter"
HEADPHONES_CONNECTED=false
DELAY=3
# Function to get the current volume on macOS
get_current_volume_macos() {
osascript -e "output volume of (get volume settings)"
}
# Function to set the volume on macOS
set_volume_macos() {
osascript -e "set volume output volume $1"
}
# Function to display notification on macOS if not already displayed
display_notification_macos() {
local title="$1"
local subtitle="$2"
local notification_file="/tmp/volume_notification"
if [[ -f "$notification_file" ]]; then
local last_notification
last_notification=$(cat "$notification_file")
if [[ "$last_notification" == "$subtitle" ]]; then
return 0 # Notification already displayed
fi
fi
osascript -e "display notification \"$subtitle\" with title \"$title\""
echo "$subtitle" >"$notification_file"
}
# Function to display notification on Linux if not already displayed
display_notification_linux() {
local title="$1"
local subtitle="$2"
local notification_file="/tmp/volume_notification"
if [[ -f "$notification_file" ]]; then
local last_notification
last_notification=$(cat "$notification_file")
if [[ "$last_notification" == "$subtitle" ]]; then
return 0 # Notification already displayed
fi
fi
notify-send "$title" "$subtitle"
echo "$subtitle" >"$notification_file"
}
# Function to check if output is on internal speakers on macOS
on_speaker_macos() {
local output_source
output_source=$(/usr/sbin/system_profiler SPAudioDataType | awk '/Output Source/ {print $NF}')
# Check if 'Default' is found in the output source
if echo "$output_source" | grep -q 'Default'; then
return 1
else
return 0
fi
}
# Function to check if output is on internal speakers on Linux
on_speaker_linux() {
pacmd list-sinks | grep -q 'active port.*analog-output-speaker'
}
# Function to get the current volume on Linux
get_current_volume_linux() {
amixer get Master | grep -oP '\d+%' | head -1 | tr -d '%'
}
# Function to set the volume on Linux
set_volume_linux() {
amixer set Master "$1%"
}
# Function to check if headphones are connected
check_headphones() {
if [ "$ONLY_ON_HEADPHONES" = true ]; then
if $on_speaker; then
if [ "$HEADPHONES_CONNECTED" = true ]; then
HEADPHONES_CONNECTED=false
$display_notification "$NOTIFICATION_TITLE" "Volume limiter is stopped, be careful"
fi
else
if [ "$HEADPHONES_CONNECTED" = false ]; then
HEADPHONES_CONNECTED=true
limit_volume
$display_notification "$NOTIFICATION_TITLE" "Limiting your 🎧 headphones to $MAX_VOLUME% to protect your ears"
fi
fi
fi
}
# Function to limit volume
limit_volume() {
local current_volume
current_volume=$($get_current_volume)
if [ "$ONLY_ON_HEADPHONES" = true ]; then
if [ "$HEADPHONES_CONNECTED" = true ] && [ "$current_volume" -gt "$MAX_VOLUME" ]; then
$set_volume "$MAX_VOLUME"
fi
else
if [ "$current_volume" -gt "$MAX_VOLUME" ]; then
$set_volume "$MAX_VOLUME"
fi
fi
}
# Main logic
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
get_current_volume="get_current_volume_macos"
set_volume="set_volume_macos"
display_notification="display_notification_macos"
on_speaker="on_speaker_macos"
else
# Linux
get_current_volume="get_current_volume_linux"
set_volume="set_volume_linux"
display_notification="display_notification_linux"
on_speaker="on_speaker_linux"
fi
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--headphones-only)
ONLY_ON_HEADPHONES=true
shift
;;
--max-volume)
if [[ "$2" =~ ^[0-9]+$ && "$2" -ge 0 && "$2" -le 100 ]]; then
MAX_VOLUME="$2"
shift 2
else
echo "Error: --max-volume must be followed by a valid percentage (0-100)."
exit 1
fi
;;
--delay)
if [[ "$2" =~ ^[0-9]+$ && "$2" -gt 0 ]]; then
DELAY="$2"
shift 2
else
echo "Error: --delay must be followed by a valid positive integer."
exit 1
fi
;;
*)
echo "Usage: $0 [--headphones-only] [--max-volume <percentage>] [--delay <seconds>] [--macos | --linux]"
exit 1
;;
esac
done
for ((i = 1; i <= $((60 / DELAY)); i++)); do
check_headphones
limit_volume
sleep "$DELAY"
done