diff --git a/scripts/digital_wellbeing/music_parallelism.sh b/scripts/digital_wellbeing/music_parallelism.sh new file mode 100755 index 0000000..26b20e0 --- /dev/null +++ b/scripts/digital_wellbeing/music_parallelism.sh @@ -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 diff --git a/scripts/digital_wellbeing/systemd/music-parallelism.service b/scripts/digital_wellbeing/systemd/music-parallelism.service new file mode 100644 index 0000000..22077e6 --- /dev/null +++ b/scripts/digital_wellbeing/systemd/music-parallelism.service @@ -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