mirror of
https://github.com/kuhyx/scripts.git
synced 2026-07-04 11:43:03 +02:00
Add music parallelism prevention script
Prevents multitasking between focus work and music streaming. When focus apps (VS Code, Steam, Godot, etc.) are detected running alongside music services (YouTube Music, Spotify, etc.), the music is automatically stopped. Features: - Monitors for focus applications (IDEs, games, creative software) - Detects music streaming via browser tabs and native apps - Closes music windows/processes when conflict detected - Desktop notifications when music is stopped - Status command to check current state - Systemd service for background monitoring
This commit is contained in:
parent
1635469320
commit
addfa1a9ae
318
scripts/digital_wellbeing/music_parallelism.sh
Executable file
318
scripts/digital_wellbeing/music_parallelism.sh
Executable file
@ -0,0 +1,318 @@
|
||||
#!/bin/bash
|
||||
# Music Parallelism Prevention Script
|
||||
# Prevents listening to music while doing focus work (coding, gaming)
|
||||
#
|
||||
# When a focus application (VS Code, Steam games, etc.) is detected alongside
|
||||
# a music streaming service (YouTube Music, Spotify, etc.), the music is stopped.
|
||||
#
|
||||
# Music is fine when running alone - only killed when combined with focus apps.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
LOG_FILE="/var/log/music-parallelism.log"
|
||||
CHECK_INTERVAL=10
|
||||
|
||||
# Focus applications - window class names or process names
|
||||
# These are apps that require focus and shouldn't have music playing
|
||||
FOCUS_APPS=(
|
||||
# IDEs and code editors
|
||||
"code" # VS Code
|
||||
"Code" # VS Code (window class)
|
||||
"vscodium" # VSCodium
|
||||
"cursor" # Cursor IDE
|
||||
"jetbrains" # JetBrains IDEs
|
||||
"idea" # IntelliJ IDEA
|
||||
"pycharm" # PyCharm
|
||||
"webstorm" # WebStorm
|
||||
"clion" # CLion
|
||||
"rider" # Rider
|
||||
"sublime_text" # Sublime Text
|
||||
"atom" # Atom
|
||||
"neovide" # Neovide (Neovim GUI)
|
||||
# Gaming
|
||||
"steam_app" # Steam games (they run as steam_app_XXXXX)
|
||||
"steamwebhelper" # Steam client
|
||||
"gamescope" # Gamescope (Steam Deck compositor)
|
||||
# Other focus apps (add more as needed)
|
||||
"blender" # Blender
|
||||
"godot" # Godot Engine
|
||||
"unity" # Unity Editor
|
||||
"UnrealEditor" # Unreal Engine
|
||||
)
|
||||
|
||||
# Music streaming services - browser tabs or electron apps
|
||||
# These will be killed when focus apps are detected
|
||||
MUSIC_SERVICES=(
|
||||
# YouTube Music specific patterns (NOT regular YouTube)
|
||||
"music.youtube.com"
|
||||
# Spotify
|
||||
"spotify"
|
||||
"Spotify"
|
||||
# Tidal
|
||||
"tidal"
|
||||
"TIDAL"
|
||||
# Deezer
|
||||
"deezer"
|
||||
# Amazon Music
|
||||
"Amazon Music"
|
||||
"amazon music"
|
||||
# Apple Music (web)
|
||||
"music.apple.com"
|
||||
# SoundCloud
|
||||
"soundcloud.com"
|
||||
# Pandora
|
||||
"pandora.com"
|
||||
)
|
||||
|
||||
# Function to log with timestamp
|
||||
log_message() {
|
||||
local msg
|
||||
msg="$(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||
echo "$msg" | tee -a "$LOG_FILE" >&2
|
||||
}
|
||||
|
||||
# Check if any focus application is running
|
||||
is_focus_app_running() {
|
||||
for app in "${FOCUS_APPS[@]}"; do
|
||||
# Check running processes
|
||||
if pgrep -i -f "$app" &>/dev/null; then
|
||||
echo "$app"
|
||||
return 0
|
||||
fi
|
||||
# Check window names using xdotool if available
|
||||
if command -v xdotool &>/dev/null; then
|
||||
if xdotool search --name "$app" &>/dev/null 2>&1; then
|
||||
echo "$app"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if any music service is running and return its details
|
||||
find_music_services() {
|
||||
local found_services=()
|
||||
|
||||
for service in "${MUSIC_SERVICES[@]}"; do
|
||||
# Check for browser tabs with music services
|
||||
# This checks window titles which usually contain the URL or tab title
|
||||
if command -v xdotool &>/dev/null; then
|
||||
if xdotool search --name "$service" &>/dev/null 2>&1; then
|
||||
found_services+=("$service (window)")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for dedicated desktop apps
|
||||
if pgrep -i -f "$service" &>/dev/null; then
|
||||
found_services+=("$service (process)")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#found_services[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${found_services[@]}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Kill music services
|
||||
kill_music_services() {
|
||||
local killed=false
|
||||
|
||||
# Kill YouTube Music browser tabs
|
||||
# YouTube Music runs in browser, so we need to close specific tabs
|
||||
# We use xdotool to find and close windows with "YouTube Music" or "music.youtube.com"
|
||||
if command -v xdotool &>/dev/null; then
|
||||
# Find windows with YouTube Music in title
|
||||
local yt_music_windows
|
||||
yt_music_windows=$(xdotool search --name "YouTube Music" 2>/dev/null || true)
|
||||
for wid in $yt_music_windows; do
|
||||
if [[ -n "$wid" ]]; then
|
||||
# Get window name for logging
|
||||
local wname
|
||||
wname=$(xdotool getwindowname "$wid" 2>/dev/null || echo "unknown")
|
||||
# Only close if it's YouTube Music, not regular YouTube
|
||||
if [[ "$wname" == *"YouTube Music"* ]] || [[ "$wname" == *"music.youtube.com"* ]]; then
|
||||
log_message "Closing YouTube Music window: $wname (ID: $wid)"
|
||||
xdotool windowclose "$wid" 2>/dev/null || true
|
||||
killed=true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Kill Spotify
|
||||
if pgrep -x "spotify" &>/dev/null; then
|
||||
log_message "Killing Spotify"
|
||||
pkill -x "spotify" 2>/dev/null || true
|
||||
killed=true
|
||||
fi
|
||||
|
||||
# Kill other music streaming app processes
|
||||
local music_processes=("tidal" "deezer" "Amazon Music")
|
||||
for proc in "${music_processes[@]}"; do
|
||||
if pgrep -i -f "$proc" &>/dev/null; then
|
||||
log_message "Killing $proc"
|
||||
pkill -i -f "$proc" 2>/dev/null || true
|
||||
killed=true
|
||||
fi
|
||||
done
|
||||
|
||||
# Close browser tabs for web-based music services
|
||||
if command -v xdotool &>/dev/null; then
|
||||
local web_music_patterns=("music.apple.com" "soundcloud.com" "pandora.com" "deezer.com" "tidal.com")
|
||||
for pattern in "${web_music_patterns[@]}"; do
|
||||
local windows
|
||||
windows=$(xdotool search --name "$pattern" 2>/dev/null || true)
|
||||
for wid in $windows; do
|
||||
if [[ -n "$wid" ]]; then
|
||||
local wname
|
||||
wname=$(xdotool getwindowname "$wid" 2>/dev/null || echo "unknown")
|
||||
log_message "Closing music service window: $wname (ID: $wid)"
|
||||
xdotool windowclose "$wid" 2>/dev/null || true
|
||||
killed=true
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if $killed; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Send notification to user
|
||||
notify_user() {
|
||||
local focus_app="$1"
|
||||
local message="Music stopped - focus mode active ($focus_app detected)"
|
||||
|
||||
# Try to send desktop notification
|
||||
if command -v notify-send &>/dev/null; then
|
||||
notify-send -u normal -t 5000 "🎵 Music Parallelism" "$message" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
log_message "$message"
|
||||
}
|
||||
|
||||
# Main monitoring loop
|
||||
monitor_loop() {
|
||||
log_message "=== Music Parallelism Monitor Started ==="
|
||||
log_message "Focus apps monitored: ${FOCUS_APPS[*]}"
|
||||
log_message "Music services monitored: ${MUSIC_SERVICES[*]}"
|
||||
log_message "Check interval: ${CHECK_INTERVAL}s"
|
||||
|
||||
while true; do
|
||||
# Check if a focus app is running
|
||||
local focus_app
|
||||
if focus_app=$(is_focus_app_running); then
|
||||
# Focus app detected, check for music services
|
||||
local music_services
|
||||
if music_services=$(find_music_services); then
|
||||
log_message "Conflict detected: Focus app '$focus_app' running with music services"
|
||||
log_message "Active music services: $music_services"
|
||||
|
||||
# Kill the music services
|
||||
if kill_music_services; then
|
||||
notify_user "$focus_app"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep "$CHECK_INTERVAL"
|
||||
done
|
||||
}
|
||||
|
||||
# Show status
|
||||
show_status() {
|
||||
echo "Music Parallelism Monitor Status"
|
||||
echo "================================="
|
||||
echo ""
|
||||
|
||||
echo "Focus Applications:"
|
||||
local focus_running=false
|
||||
for app in "${FOCUS_APPS[@]}"; do
|
||||
if pgrep -i -f "$app" &>/dev/null; then
|
||||
echo " ✓ $app (RUNNING)"
|
||||
focus_running=true
|
||||
fi
|
||||
done
|
||||
if ! $focus_running; then
|
||||
echo " (none detected)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Music Services:"
|
||||
local music_running=false
|
||||
if music_services=$(find_music_services 2>/dev/null); then
|
||||
echo "$music_services" | while read -r svc; do
|
||||
echo " ♪ $svc (RUNNING)"
|
||||
done
|
||||
music_running=true
|
||||
fi
|
||||
if ! $music_running; then
|
||||
echo " (none detected)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if $focus_running && $music_running; then
|
||||
echo "⚠️ CONFLICT: Focus app and music running together!"
|
||||
echo " Music would be killed in monitoring mode."
|
||||
elif $focus_running; then
|
||||
echo "✓ Focus mode active (no music playing)"
|
||||
elif $music_running; then
|
||||
echo "✓ Music playing (no focus app detected - this is fine)"
|
||||
else
|
||||
echo "✓ Idle (nothing detected)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show usage
|
||||
show_usage() {
|
||||
echo "Music Parallelism Prevention Script"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
echo "Usage: $0 [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " monitor - Start monitoring (default, runs in foreground)"
|
||||
echo " status - Show current status of focus apps and music services"
|
||||
echo " kill - Immediately kill all music services"
|
||||
echo " help - Show this help message"
|
||||
echo ""
|
||||
echo "Description:"
|
||||
echo " This script prevents multitasking between focus work and music."
|
||||
echo " When a focus application (VS Code, Steam, etc.) is detected"
|
||||
echo " alongside a music streaming service, the music is stopped."
|
||||
echo ""
|
||||
echo " Music is allowed when no focus apps are running."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main
|
||||
case "${1:-monitor}" in
|
||||
monitor | start | run)
|
||||
monitor_loop
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
kill)
|
||||
log_message "Manual kill requested"
|
||||
if kill_music_services; then
|
||||
echo "Music services killed"
|
||||
else
|
||||
echo "No music services found to kill"
|
||||
fi
|
||||
;;
|
||||
help | -h | --help)
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
25
scripts/digital_wellbeing/systemd/music-parallelism.service
Normal file
25
scripts/digital_wellbeing/systemd/music-parallelism.service
Normal file
@ -0,0 +1,25 @@
|
||||
[Unit]
|
||||
Description=Music Parallelism Prevention - Stops music when focus apps are running
|
||||
After=graphical-session.target
|
||||
Wants=graphical-session.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/music-parallelism.sh monitor
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# Run as user (needs access to X11/Wayland for window detection)
|
||||
# This will be set during installation based on the actual user
|
||||
|
||||
# Environment for X11/Wayland access
|
||||
Environment=DISPLAY=:0
|
||||
|
||||
# Resource limits
|
||||
MemoryMax=50M
|
||||
CPUQuota=5%
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Loading…
Reference in New Issue
Block a user