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:
Krzysztof kuhy Rudnicki 2025-12-07 14:27:19 +01:00
parent 7c15b2d3aa
commit 774c28b7a7
2 changed files with 343 additions and 0 deletions

View 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

View 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