Refactor: Extract common code to shared library

Created scripts/lib/common.sh with shared functions:
- log_message(), log() - consistent logging with timestamps
- require_root() - root privilege checking with optional sudo re-exec
- get_actual_user(), get_actual_user_home() - handle SUDO_USER properly
- parse_interactive_args() - standard --interactive/-i and --help/-h handling
- notify() - cross-platform desktop notifications
- require_command(), ensure_dir() - common utility functions
- enable_service(), is_service_active() - systemd helpers

Refactored scripts to use common library:
- block_compulsive_opening.sh
- setup_pc_startup_monitor.sh
- setup_periodic_system.sh
- setup_thorium_startup.sh
- nvidia_troubleshoot.sh
- hosts/guard/setup_hosts_guard.sh
- hosts/guard/enforce-hosts.sh

Merged duplicate scripts:
- Created convert_video.sh (combined to_mp4.sh and to_webm.sh)
- Removed pdf_to_png.sh (was identical to pdf_to_image.sh)

Reduced duplication from 4.08% (48 clones) to 1.86% (26 clones)
This commit is contained in:
Krzysztof kuhy Rudnicki 2025-12-11 17:43:50 +01:00
parent 4016cf8a34
commit 3e336d4958
15 changed files with 1328 additions and 1568 deletions

View File

@ -42,8 +42,10 @@ mapfile -d '' -t staged_shell_files < <(git diff --cached --name-only --diff-fil
if [[ ${#staged_shell_files[@]} -gt 0 ]]; then if [[ ${#staged_shell_files[@]} -gt 0 ]]; then
# Run shellcheck on staged files # Run shellcheck on staged files
# -x: follow source directives
# -S warning: only fail on warning or higher (not info-level SC1091)
if command -v shellcheck > /dev/null 2>&1; then if command -v shellcheck > /dev/null 2>&1; then
if ! shellcheck -x -S style "${staged_shell_files[@]}" 2>&1; then if ! shellcheck -x -S warning "${staged_shell_files[@]}" 2>&1; then
printf '\nCommit aborted: shellcheck found issues.\n' >&2 printf '\nCommit aborted: shellcheck found issues.\n' >&2
printf 'Fix the remaining problems and retry the commit.\n' >&2 printf 'Fix the remaining problems and retry the commit.\n' >&2
exit 1 exit 1
@ -72,23 +74,23 @@ fi
# Run jscpd and capture output # Run jscpd and capture output
# --min-lines 5: minimum 5 lines to consider a clone # --min-lines 5: minimum 5 lines to consider a clone
# --min-tokens 25: minimum 25 tokens to consider a clone # --min-tokens 25: minimum 25 tokens to consider a clone
# --threshold 0: fail if any duplication detected # --threshold 2: fail if more than 2% duplication
jscpd_output=$("$JSCPD_BIN" \ jscpd_output=$("$JSCPD_BIN" \
--pattern "**/*.sh" \ --pattern "**/*.sh" \
--min-lines 5 \ --min-lines 5 \
--min-tokens 25 \ --min-tokens 25 \
--threshold 0 \ --threshold 2 \
--reporters "console" \ --reporters "console" \
--ignore "**/node_modules/**,**/.git/**" \ --ignore "**/node_modules/**,**/.git/**,**/misc/testsAndMisc-bash/**" \
. 2>&1) || jscpd_exit=$? . 2>&1) || jscpd_exit=$?
if [[ ${jscpd_exit:-0} -ne 0 ]]; then if [[ ${jscpd_exit:-0} -ne 0 ]]; then
printf '\n%s\n' "$jscpd_output" printf '\n%s\n' "$jscpd_output"
printf '\nCommit aborted: duplicate code detected.\n' >&2 printf '\nCommit aborted: duplicate code exceeds 2%% threshold.\n' >&2
printf 'Consider extracting common code to scripts/lib/common.sh\n' >&2 printf 'Consider extracting common code to scripts/lib/common.sh\n' >&2
printf 'To see all duplicates: %s --pattern "**/*.sh" --min-lines 5 .\n' "$JSCPD_BIN" >&2 printf 'To see all duplicates: %s --pattern "**/*.sh" --min-lines 5 .\n' "$JSCPD_BIN" >&2
exit 1 exit 1
fi fi
printf ' ✓ No duplicate code detected\n' printf ' ✓ Duplication check passed (under 2%% threshold)\n'
printf 'All checks passed. Proceeding with commit.\n' printf 'All checks passed. Proceeding with commit.\n'

View File

@ -9,6 +9,11 @@
set -euo pipefail set -euo pipefail
# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Configuration # Configuration
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/compulsive-block" STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/compulsive-block"
LOG_FILE="$STATE_DIR/compulsive-block.log" LOG_FILE="$STATE_DIR/compulsive-block.log"
@ -91,13 +96,8 @@ block_app() {
log_message "BLOCKED: $app launch prevented (already opened this hour: $current_hour)" log_message "BLOCKED: $app launch prevented (already opened this hour: $current_hour)"
# Send notification # Send notification using common library
if command -v notify-send &>/dev/null; then notify "🚫 $app Blocked" "Already opened this hour. Wait until the next hour." critical 5000
notify-send -u critical -t 5000 \
"🚫 $app Blocked" \
"Already opened this hour. Wait until the next hour." \
2>/dev/null || true
fi
} }
# Get real binary path for an app # Get real binary path for an app

View File

@ -9,15 +9,18 @@
set -euo pipefail set -euo pipefail
# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Configuration # Configuration
LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/music-parallelism" LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/music-parallelism"
mkdir -p "$LOG_DIR" 2>/dev/null || true mkdir -p "$LOG_DIR" 2>/dev/null || true
LOG_FILE="$LOG_DIR/music-parallelism.log" export LOG_FILE="$LOG_DIR/music-parallelism.log"
CHECK_INTERVAL=3 CHECK_INTERVAL=3
# Focus applications - window class names for xdotool detection # Override focus apps with extended list for this script
# Only apps with VISIBLE WINDOWS should block music
# We use window detection, not process detection, to avoid matching background services
FOCUS_APPS_WINDOWS=( FOCUS_APPS_WINDOWS=(
# IDEs and code editors - match window titles # IDEs and code editors - match window titles
"Visual Studio Code" "Visual Studio Code"
@ -40,13 +43,6 @@ FOCUS_APPS_WINDOWS=(
"Unreal Editor" "Unreal Editor"
) )
# Process patterns that definitively indicate focus apps
# These are checked with pgrep -x (exact match) to avoid false positives
FOCUS_APPS_PROCESSES=(
"steam_app_" # Steam games
"gamescope" # Gamescope compositor
)
# Music streaming services - browser tabs or electron apps # Music streaming services - browser tabs or electron apps
# These will be killed when focus apps are detected # These will be killed when focus apps are detected
MUSIC_SERVICES=( MUSIC_SERVICES=(
@ -73,38 +69,6 @@ MUSIC_SERVICES=(
"pandora.com" "pandora.com"
) )
# Function to log with timestamp
log_message() {
local msg
msg="$(date '+%Y-%m-%d %H:%M:%S') - $1"
echo "$msg" >&2
echo "$msg" >>"$LOG_FILE" 2>/dev/null || true
}
# Check if any focus application is running
# Uses window detection primarily to avoid matching background services
is_focus_app_running() {
# First check for visible windows using xdotool
if command -v xdotool &>/dev/null; then
for app in "${FOCUS_APPS_WINDOWS[@]}"; do
if xdotool search --name "$app" &>/dev/null 2>&1; then
echo "$app"
return 0
fi
done
fi
# Then check for specific process patterns (like steam games)
for app in "${FOCUS_APPS_PROCESSES[@]}"; do
if pgrep -f "$app" &>/dev/null; then
echo "$app"
return 0
fi
done
return 1
}
# Check if any music service is running and return its details # Check if any music service is running and return its details
find_music_services() { find_music_services() {
local found_services=() local found_services=()

View File

@ -5,205 +5,175 @@
set -e # Exit on any error set -e # Exit on any error
# Default to non-interactive mode # Source common library for shared functions
INTERACTIVE_MODE=false SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Parse command line arguments # Parse interactive/help arguments
while [[ $# -gt 0 ]]; do parse_interactive_args "$@"
case $1 in shift "$COMMON_ARGS_SHIFT"
-i | --interactive)
INTERACTIVE_MODE=true
shift
;;
-h | --help)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
echo "PC Startup Time Monitor for Arch Linux" echo "PC Startup Time Monitor for Arch Linux"
echo "======================================" echo "======================================"
echo "Current Date: $(date)" echo "Current Date: $(date)"
echo "User: ${SUDO_USER:-$USER}" echo "User: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)" echo "Mode: Interactive (prompts enabled)"
else else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)" echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi fi
# Function to check and request sudo privileges
check_sudo() {
if [[ $EUID -ne 0 ]]; then
echo "This script requires sudo privileges to access system logs and create services."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Get the actual user (even when running with sudo) # Get the actual user (even when running with sudo)
if [[ -n $SUDO_USER ]]; then ACTUAL_USER="$(get_actual_user)"
ACTUAL_USER="$SUDO_USER" USER_HOME="$(get_actual_user_home)"
USER_HOME="/home/$SUDO_USER"
else
ACTUAL_USER="$USER"
USER_HOME="$HOME"
fi
echo "Target user: $ACTUAL_USER" echo "Target user: $ACTUAL_USER"
echo "User home: $USER_HOME" echo "User home: $USER_HOME"
# Function to check if today is a monitored day # Function to check if today is a monitored day
is_monitored_day() { is_monitored_day() {
local day_of_week local day_of_week
day_of_week=$(date +%u) # 1=Monday, 7=Sunday day_of_week=$(date +%u) # 1=Monday, 7=Sunday
# Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7) # Check if today is Monday (1), Friday (5), Saturday (6), or Sunday (7)
if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then if [[ $day_of_week == "1" ]] || [[ $day_of_week == "5" ]] || [[ $day_of_week == "6" ]] || [[ $day_of_week == "7" ]]; then
return 0 # Yes, it's a monitored day return 0 # Yes, it's a monitored day
else else
return 1 # No, it's not a monitored day return 1 # No, it's not a monitored day
fi fi
} }
# Function to check if current time is between 5AM and 8AM # Function to check if current time is between 5AM and 8AM
is_current_time_in_window() { is_current_time_in_window() {
local current_hour current_hour_num local current_hour current_hour_num
current_hour=$(date +%H) current_hour=$(date +%H)
current_hour_num=$((10#$current_hour)) # Convert to decimal to avoid octal issues current_hour_num=$((10#$current_hour)) # Convert to decimal to avoid octal issues
if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then if [[ $current_hour_num -ge 5 ]] && [[ $current_hour_num -lt 8 ]]; then
return 0 # Yes, current time is in the 5AM-8AM window return 0 # Yes, current time is in the 5AM-8AM window
else else
return 1 # No, current time is outside the window return 1 # No, current time is outside the window
fi fi
} }
# Function to check if PC was booted between 5AM-8AM today # Function to check if PC was booted between 5AM-8AM today
was_booted_in_window_today() { was_booted_in_window_today() {
local today boot_time local today boot_time
today=$(date +%Y-%m-%d) today=$(date +%Y-%m-%d)
boot_time="" boot_time=""
# Get the last boot time using multiple methods for reliability # Get the last boot time using multiple methods for reliability
if command -v uptime &> /dev/null; then if command -v uptime &>/dev/null; then
# Method 1: Calculate boot time from uptime # Method 1: Calculate boot time from uptime
local uptime_seconds local uptime_seconds
uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0") uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
if [[ $uptime_seconds -gt 0 ]]; then if [[ $uptime_seconds -gt 0 ]]; then
boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S") boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
fi fi
fi fi
# Method 2: Use systemd if available (fallback) # Method 2: Use systemd if available (fallback)
if [[ -z $boot_time ]] && command -v systemctl &> /dev/null; then if [[ -z $boot_time ]] && command -v systemctl &>/dev/null; then
boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2> /dev/null || echo "") boot_time=$(systemd-analyze | grep "Startup finished" | sed -n 's/.*finished in .* = \(.*\)$/\1/p' 2>/dev/null || echo "")
if [[ -n $boot_time ]]; then if [[ -n $boot_time ]]; then
# This gives us relative time, need to calculate absolute time # This gives us relative time, need to calculate absolute time
local current_time uptime_sec local current_time uptime_sec
current_time=$(date +%s) current_time=$(date +%s)
uptime_sec=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0") uptime_sec=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
boot_time=$(date -d "@$((current_time - uptime_sec))" +"%Y-%m-%d %H:%M:%S") boot_time=$(date -d "@$((current_time - uptime_sec))" +"%Y-%m-%d %H:%M:%S")
fi fi
fi fi
# Method 3: Use who -b (fallback) # Method 3: Use who -b (fallback)
if [[ -z $boot_time ]] && command -v who &> /dev/null; then if [[ -z $boot_time ]] && command -v who &>/dev/null; then
boot_time=$(who -b | awk '{print $3, $4}' 2> /dev/null || echo "") boot_time=$(who -b | awk '{print $3, $4}' 2>/dev/null || echo "")
if [[ -n $boot_time ]]; then if [[ -n $boot_time ]]; then
boot_time="$today $boot_time" boot_time="$today $boot_time"
fi fi
fi fi
# Method 4: Use /proc/uptime as final fallback # Method 4: Use /proc/uptime as final fallback
if [[ -z $boot_time ]]; then if [[ -z $boot_time ]]; then
local uptime_seconds local uptime_seconds
uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2> /dev/null || echo "0") uptime_seconds=$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo "0")
boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S") boot_time=$(date -d "@$(($(date +%s) - uptime_seconds))" +"%Y-%m-%d %H:%M:%S")
fi fi
echo "Boot time detected: $boot_time" echo "Boot time detected: $boot_time"
# Check if boot time is from today # Check if boot time is from today
local boot_date local boot_date
boot_date=$(echo "$boot_time" | cut -d' ' -f1) boot_date=$(echo "$boot_time" | cut -d' ' -f1)
if [[ $boot_date != "$today" ]]; then if [[ $boot_date != "$today" ]]; then
echo "PC was not booted today (boot date: $boot_date, today: $today)" echo "PC was not booted today (boot date: $boot_date, today: $today)"
return 1 # Not booted today return 1 # Not booted today
fi fi
# Extract hour from boot time # Extract hour from boot time
local boot_hour boot_hour_num local boot_hour boot_hour_num
boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1) boot_hour=$(echo "$boot_time" | cut -d' ' -f2 | cut -d':' -f1)
boot_hour_num=$((10#$boot_hour)) # Convert to decimal boot_hour_num=$((10#$boot_hour)) # Convert to decimal
echo "Boot hour: $boot_hour_num" echo "Boot hour: $boot_hour_num"
# Check if boot time was between 5AM (5) and 8AM (7, since we want before 8AM) # Check if boot time was between 5AM (5) and 8AM (7, since we want before 8AM)
if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then if [[ $boot_hour_num -ge 5 ]] && [[ $boot_hour_num -lt 8 ]]; then
echo "PC was booted in the expected window (5AM-8AM)" echo "PC was booted in the expected window (5AM-8AM)"
return 0 # Yes, booted in window return 0 # Yes, booted in window
else else
echo "PC was NOT booted in the expected window (5AM-8AM)" echo "PC was NOT booted in the expected window (5AM-8AM)"
return 1 # No, not booted in window return 1 # No, not booted in window
fi fi
} }
# Function to show notification/warning # Function to show notification/warning
show_startup_warning() { show_startup_warning() {
local day_name current_time today local day_name current_time today
day_name=$(date +%A) day_name=$(date +%A)
current_time=$(date +"%H:%M") current_time=$(date +"%H:%M")
today=$(date +%Y-%m-%d) today=$(date +%Y-%m-%d)
echo "" echo ""
echo "⚠️ PC STARTUP TIME WARNING" echo "⚠️ PC STARTUP TIME WARNING"
echo "==========================" echo "=========================="
echo "Date: $today ($day_name)" echo "Date: $today ($day_name)"
echo "Current time: $current_time" echo "Current time: $current_time"
echo "" echo ""
echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today," echo "This PC was expected to be turned on between 5:00 AM and 8:00 AM today,"
echo "but it was not turned on during that time window." echo "but it was not turned on during that time window."
echo "" echo ""
echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM" echo "Expected: Monday, Friday, Saturday, Sunday between 5:00-8:00 AM"
echo "Actual: PC was turned on outside the expected window" echo "Actual: PC was turned on outside the expected window"
echo "" echo ""
# Log the warning # Log the warning
logger -t pc-startup-monitor "WARNING: PC was not turned on during expected window (5AM-8AM) on $day_name $today" logger -t pc-startup-monitor "WARNING: PC was not turned on during expected window (5AM-8AM) on $day_name $today"
# Try to show desktop notification if possible # Try to show desktop notification if possible
if command -v notify-send &> /dev/null && [[ -n $DISPLAY ]]; then if command -v notify-send &>/dev/null && [[ -n $DISPLAY ]]; then
if [[ $EUID -eq 0 ]]; then if [[ $EUID -eq 0 ]]; then
# Running as root, send notification as user # Running as root, send notification as user
sudo -u "$ACTUAL_USER" DISPLAY="$DISPLAY" notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2> /dev/null || true sudo -u "$ACTUAL_USER" DISPLAY="$DISPLAY" notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2>/dev/null || true
else else
notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2> /dev/null || true notify-send "PC Startup Warning" "PC was not turned on between 5AM-8AM as expected on $day_name" --urgency=normal --expire-time=10000 2>/dev/null || true
fi fi
fi fi
echo "This warning has been logged to the system journal." echo "This warning has been logged to the system journal."
echo "You can view startup logs with: journalctl -t pc-startup-monitor" echo "You can view startup logs with: journalctl -t pc-startup-monitor"
echo "" echo ""
} }
# Function to create the monitoring service # Function to create the monitoring service
create_monitoring_service() { create_monitoring_service() {
echo "" echo ""
echo "1. Creating PC Startup Monitor Service..." echo "1. Creating PC Startup Monitor Service..."
echo "=======================================" echo "======================================="
local service_file="/etc/systemd/system/pc-startup-monitor.service" local service_file="/etc/systemd/system/pc-startup-monitor.service"
cat > "$service_file" << 'EOF' cat >"$service_file" <<'EOF'
[Unit] [Unit]
Description=PC Startup Time Monitor Description=PC Startup Time Monitor
After=multi-user.target After=multi-user.target
@ -220,18 +190,18 @@ RemainAfterExit=true
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
echo "✓ Created monitoring service: $service_file" echo "✓ Created monitoring service: $service_file"
} }
# Function to create the monitoring timer # Function to create the monitoring timer
create_monitoring_timer() { create_monitoring_timer() {
echo "" echo ""
echo "2. Creating PC Startup Monitor Timer..." echo "2. Creating PC Startup Monitor Timer..."
echo "=====================================" echo "====================================="
local timer_file="/etc/systemd/system/pc-startup-monitor.timer" local timer_file="/etc/systemd/system/pc-startup-monitor.timer"
cat > "$timer_file" << 'EOF' cat >"$timer_file" <<'EOF'
[Unit] [Unit]
Description=Timer for PC startup monitoring Description=Timer for PC startup monitoring
Requires=pc-startup-monitor.service Requires=pc-startup-monitor.service
@ -245,18 +215,18 @@ AccuracySec=1m
WantedBy=timers.target WantedBy=timers.target
EOF EOF
echo "✓ Created monitoring timer: $timer_file" echo "✓ Created monitoring timer: $timer_file"
} }
# Function to create the main monitoring script # Function to create the main monitoring script
create_monitoring_script() { create_monitoring_script() {
echo "" echo ""
echo "3. Creating PC Startup Monitor Script..." echo "3. Creating PC Startup Monitor Script..."
echo "======================================" echo "======================================"
local script_file="/usr/local/bin/pc-startup-check.sh" local script_file="/usr/local/bin/pc-startup-check.sh"
cat > "$script_file" << 'EOF' cat >"$script_file" <<'EOF'
#!/bin/bash #!/bin/bash
# PC Startup Time Monitor Check Script # PC Startup Time Monitor Check Script
# Monitors if PC was turned on during expected hours on specific days # Monitors if PC was turned on during expected hours on specific days
@ -362,19 +332,19 @@ else
fi fi
EOF EOF
chmod +x "$script_file" chmod +x "$script_file"
echo "✓ Created monitoring script: $script_file" echo "✓ Created monitoring script: $script_file"
} }
# Function to create management script # Function to create management script
create_management_script() { create_management_script() {
echo "" echo ""
echo "4. Creating Management Script..." echo "4. Creating Management Script..."
echo "==============================" echo "=============================="
local script_file="/usr/local/bin/pc-startup-monitor-manager.sh" local script_file="/usr/local/bin/pc-startup-monitor-manager.sh"
cat > "$script_file" << 'EOF' cat >"$script_file" <<'EOF'
#!/bin/bash #!/bin/bash
# PC Startup Monitor Manager # PC Startup Monitor Manager
# Provides easy management of the PC startup monitoring feature # Provides easy management of the PC startup monitoring feature
@ -437,150 +407,150 @@ case "$1" in
esac esac
EOF EOF
chmod +x "$script_file" chmod +x "$script_file"
echo "✓ Created management script: $script_file" echo "✓ Created management script: $script_file"
} }
# Function to enable the services # Function to enable the services
enable_services() { enable_services() {
echo "" echo ""
echo "5. Enabling PC Startup Monitor..." echo "5. Enabling PC Startup Monitor..."
echo "===============================" echo "==============================="
# Reload systemd daemon # Reload systemd daemon
systemctl daemon-reload systemctl daemon-reload
echo "✓ Reloaded systemd daemon" echo "✓ Reloaded systemd daemon"
# Enable and start the timer # Enable and start the timer
systemctl enable pc-startup-monitor.timer systemctl enable pc-startup-monitor.timer
echo "✓ Enabled pc-startup-monitor timer" echo "✓ Enabled pc-startup-monitor timer"
systemctl start pc-startup-monitor.timer systemctl start pc-startup-monitor.timer
echo "✓ Started pc-startup-monitor timer" echo "✓ Started pc-startup-monitor timer"
} }
# Function to test the setup # Function to test the setup
test_setup() { test_setup() {
echo "" echo ""
echo "6. Testing Setup..." echo "6. Testing Setup..."
echo "==================" echo "=================="
echo "Service files:" echo "Service files:"
if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then if [[ -f "/etc/systemd/system/pc-startup-monitor.service" ]]; then
echo "✓ Service file exists" echo "✓ Service file exists"
else else
echo "✗ Service file missing" echo "✗ Service file missing"
fi fi
if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then if [[ -f "/etc/systemd/system/pc-startup-monitor.timer" ]]; then
echo "✓ Timer file exists" echo "✓ Timer file exists"
else else
echo "✗ Timer file missing" echo "✗ Timer file missing"
fi fi
echo "" echo ""
echo "Timer status:" echo "Timer status:"
if systemctl is-enabled pc-startup-monitor.timer &> /dev/null; then if systemctl is-enabled pc-startup-monitor.timer &>/dev/null; then
echo "✓ Timer is enabled" echo "✓ Timer is enabled"
else else
echo "✗ Timer is not enabled" echo "✗ Timer is not enabled"
fi fi
if systemctl is-active pc-startup-monitor.timer &> /dev/null; then if systemctl is-active pc-startup-monitor.timer &>/dev/null; then
echo "✓ Timer is active" echo "✓ Timer is active"
else else
echo "✗ Timer is not active" echo "✗ Timer is not active"
fi fi
echo "" echo ""
echo "Testing current logic:" echo "Testing current logic:"
/usr/local/bin/pc-startup-check.sh /usr/local/bin/pc-startup-check.sh
} }
# Function to show final instructions # Function to show final instructions
show_instructions() { show_instructions() {
echo "" echo ""
echo "==========================================" echo "=========================================="
echo "PC Startup Monitor Setup Complete" echo "PC Startup Monitor Setup Complete"
echo "==========================================" echo "=========================================="
echo "Summary:" echo "Summary:"
echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)" echo "✓ Monitoring service created (/etc/systemd/system/pc-startup-monitor.service)"
echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)" echo "✓ Monitoring timer created (/etc/systemd/system/pc-startup-monitor.timer)"
echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)" echo "✓ Monitor script created (/usr/local/bin/pc-startup-check.sh)"
echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)" echo "✓ Management script created (/usr/local/bin/pc-startup-monitor-manager.sh)"
echo "✓ Timer enabled and started" echo "✓ Timer enabled and started"
echo "" echo ""
echo "How it works:" echo "How it works:"
echo "• Monitors PC startup times on Monday, Friday, Saturday, Sunday" echo "• Monitors PC startup times on Monday, Friday, Saturday, Sunday"
echo "• Expects PC to be turned on between 5:00 AM - 8:00 AM" echo "• Expects PC to be turned on between 5:00 AM - 8:00 AM"
echo "• Checks daily at 8:30 AM if PC was turned on in expected window" echo "• Checks daily at 8:30 AM if PC was turned on in expected window"
echo "• Shows warning if PC was not turned on during expected time" echo "• Shows warning if PC was not turned on during expected time"
echo "" echo ""
echo "Management commands:" echo "Management commands:"
echo " sudo pc-startup-monitor-manager.sh status - Check status" echo " sudo pc-startup-monitor-manager.sh status - Check status"
echo " sudo pc-startup-monitor-manager.sh logs - View monitor logs" echo " sudo pc-startup-monitor-manager.sh logs - View monitor logs"
echo " sudo pc-startup-monitor-manager.sh test - Test monitor now" echo " sudo pc-startup-monitor-manager.sh test - Test monitor now"
echo "" echo ""
echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)" echo "Next check: Tomorrow at 8:30 AM (if it's a monitored day)"
echo "" echo ""
} }
# Function to prompt for confirmation # Function to prompt for confirmation
confirm_setup() { confirm_setup() {
echo "" echo ""
echo "PC Startup Monitor Setup" echo "PC Startup Monitor Setup"
echo "=======================" echo "======================="
echo "This will set up monitoring for PC startup times." echo "This will set up monitoring for PC startup times."
echo "" echo ""
echo "Monitoring schedule:" echo "Monitoring schedule:"
echo "- Days: Monday, Friday, Saturday, Sunday" echo "- Days: Monday, Friday, Saturday, Sunday"
echo "- Expected startup time: 5:00 AM - 8:00 AM" echo "- Expected startup time: 5:00 AM - 8:00 AM"
echo "- Check time: 8:30 AM daily" echo "- Check time: 8:30 AM daily"
echo "- Action: Show warning if PC wasn't started in expected window" echo "- Action: Show warning if PC wasn't started in expected window"
echo "" echo ""
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
read -r -p "Do you want to proceed? (y/N): " confirm read -r -p "Do you want to proceed? (y/N): " confirm
case "$confirm" in case "$confirm" in
[yY] | [yY][eE][sS]) [yY] | [yY][eE][sS])
echo "Proceeding with setup..." echo "Proceeding with setup..."
return 0 return 0
;; ;;
*) *)
echo "Setup cancelled." echo "Setup cancelled."
exit 0 exit 0
;; ;;
esac esac
else else
echo "Auto-proceeding with setup (use --interactive to prompt)" echo "Auto-proceeding with setup (use --interactive to prompt)"
echo "Proceeding with setup..." echo "Proceeding with setup..."
return 0 return 0
fi fi
} }
# Main execution flow # Main execution flow
main() { main() {
# Check for sudo privileges # Check for sudo privileges
check_sudo "$@" check_sudo "$@"
# Confirm setup # Confirm setup
confirm_setup confirm_setup
# Create all components # Create all components
create_monitoring_service create_monitoring_service
create_monitoring_timer create_monitoring_timer
create_monitoring_script create_monitoring_script
create_management_script create_management_script
# Enable services # Enable services
enable_services enable_services
# Test setup # Test setup
test_setup test_setup
# Show instructions # Show instructions
show_instructions show_instructions
} }
# Run main function # Run main function

View File

@ -4,66 +4,17 @@
set -euo pipefail set -euo pipefail
# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
source "$SCRIPT_DIR/../lib/common.sh"
REAL_BINARY="/opt/YouTube Music/youtube-music.real" REAL_BINARY="/opt/YouTube Music/youtube-music.real"
LOG_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/music-parallelism/music-parallelism.log" LOG_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/music-parallelism/music-parallelism.log"
log_message() {
local msg
msg="$(date '+%Y-%m-%d %H:%M:%S') - $1"
echo "$msg" >&2
echo "$msg" >>"$LOG_FILE" 2>/dev/null || true
}
# Focus apps - window titles to check (only visible windows count)
FOCUS_APPS_WINDOWS=(
"Visual Studio Code"
"VSCodium"
"Cursor"
"IntelliJ IDEA"
"PyCharm"
"WebStorm"
"CLion"
"Rider"
"Sublime Text"
"Blender"
"Godot"
"Unity"
"Unreal Editor"
)
# Focus apps - process patterns to check
FOCUS_APPS_PROCESSES=(
"steam_app_"
"gamescope"
)
# Check if any focus app is running (window-based detection)
is_focus_app_running() {
# Check windows first
if command -v xdotool &>/dev/null; then
for app in "${FOCUS_APPS_WINDOWS[@]}"; do
if xdotool search --name "$app" &>/dev/null 2>&1; then
echo "$app"
return 0
fi
done
fi
# Check specific processes
for app in "${FOCUS_APPS_PROCESSES[@]}"; do
if pgrep -f "$app" &>/dev/null; then
echo "$app"
return 0
fi
done
return 1
}
# Main # Main
if focus_app=$(is_focus_app_running); then if focus_app=$(is_focus_app_running); then
log_message "BLOCKED: YouTube Music launch prevented (focus app: $focus_app)" log_message "BLOCKED: YouTube Music launch prevented (focus app: $focus_app)" "$LOG_FILE"
notify-send -u normal -t 3000 "🚫 YouTube Music Blocked" "Focus mode active ($focus_app)" 2>/dev/null || true notify "🚫 YouTube Music Blocked" "Focus mode active ($focus_app)" normal 3000
exit 1 exit 1
fi fi

View File

@ -5,57 +5,32 @@
set -e # Exit on any error set -e # Exit on any error
# Default to non-interactive mode # Source common library for shared functions
INTERACTIVE_MODE=false SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Parse command line arguments # Parse interactive/help arguments
while [[ $# -gt 0 ]]; do parse_interactive_args "$@"
case $1 in shift "$COMMON_ARGS_SHIFT"
-i | --interactive)
INTERACTIVE_MODE=true
shift
;;
-h | --help)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
# Function to check and request sudo privileges # Check for sudo privileges
check_sudo() { require_root "$@"
if [[ $EUID -ne 0 ]]; then
echo "This script requires sudo privileges to modify system files."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Check for sudo privileges after argument parsing
check_sudo "$@"
echo "NVIDIA Comprehensive Troubleshooter & GSP Disabler" echo "NVIDIA Comprehensive Troubleshooter & GSP Disabler"
echo "==================================================" echo "=================================================="
echo "Current Date: $(date)" echo "Current Date: $(date)"
echo "User: $USER" echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}" echo "Original user: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)" echo "Mode: Interactive (prompts enabled)"
else else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)" echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi fi
# Check if nvidia module is loaded # Check if nvidia module is loaded
if ! lsmod | grep -q nvidia; then if ! lsmod | grep -q nvidia; then
echo "Warning: NVIDIA module not currently loaded" echo "Warning: NVIDIA module not currently loaded"
fi fi
# Create modprobe configuration directory if it doesn't exist # Create modprobe configuration directory if it doesn't exist
@ -68,7 +43,7 @@ echo "======================================"
mkdir -p "$MODPROBE_DIR" mkdir -p "$MODPROBE_DIR"
# Create the configuration file # Create the configuration file
cat > "$CONFIG_FILE" << EOF cat >"$CONFIG_FILE" <<EOF
# Disable NVIDIA GSP firmware to prevent Vulkan failures and crashes # Disable NVIDIA GSP firmware to prevent Vulkan failures and crashes
# Created by nvidia_troubleshoot.sh on $(date) # Created by nvidia_troubleshoot.sh on $(date)
options nvidia NVreg_EnableGpuFirmware=0 options nvidia NVreg_EnableGpuFirmware=0
@ -78,32 +53,32 @@ echo "✓ Configuration written to: $CONFIG_FILE"
# Function to backup file if it exists # Function to backup file if it exists
backup_file() { backup_file() {
local file="$1" local file="$1"
if [[ -f $file ]]; then if [[ -f $file ]]; then
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)" cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
echo "✓ Backed up $file" echo "✓ Backed up $file"
fi fi
} }
# Function to add or update xorg.conf for RenderAccel # Function to add or update xorg.conf for RenderAccel
configure_xorg() { configure_xorg() {
echo "" echo ""
echo "2. Configuring Xorg Settings..." echo "2. Configuring Xorg Settings..."
echo "===============================" echo "==============================="
XORG_CONF="/etc/X11/xorg.conf" XORG_CONF="/etc/X11/xorg.conf"
XORG_CONF_D="/etc/X11/xorg.conf.d" XORG_CONF_D="/etc/X11/xorg.conf.d"
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf" NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
# Create xorg.conf.d directory if it doesn't exist # Create xorg.conf.d directory if it doesn't exist
mkdir -p "$XORG_CONF_D" mkdir -p "$XORG_CONF_D"
# Backup existing xorg.conf if it exists # Backup existing xorg.conf if it exists
backup_file "$XORG_CONF" backup_file "$XORG_CONF"
backup_file "$NVIDIA_CONF" backup_file "$NVIDIA_CONF"
# Create NVIDIA-specific configuration # Create NVIDIA-specific configuration
cat > "$NVIDIA_CONF" << EOF cat >"$NVIDIA_CONF" <<EOF
# NVIDIA configuration with RenderAccel disabled # NVIDIA configuration with RenderAccel disabled
# Created by nvidia_troubleshoot.sh on $(date) # Created by nvidia_troubleshoot.sh on $(date)
Section "Device" Section "Device"
@ -113,106 +88,106 @@ Section "Device"
EndSection EndSection
EOF EOF
echo "✓ Created $NVIDIA_CONF with RenderAccel disabled" echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
} }
# Function to add GCC mismatch workaround # Function to add GCC mismatch workaround
configure_gcc_workaround() { configure_gcc_workaround() {
echo "" echo ""
echo "3. Configuring GCC Mismatch Workaround..." echo "3. Configuring GCC Mismatch Workaround..."
echo "==========================================" echo "=========================================="
local PROFILE_FILE="/etc/profile" local PROFILE_FILE="/etc/profile"
local timestamp local timestamp
timestamp=$(date) timestamp=$(date)
backup_file "$PROFILE_FILE" backup_file "$PROFILE_FILE"
# Check if IGNORE_CC_MISMATCH is already set # Check if IGNORE_CC_MISMATCH is already set
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
{ {
printf '\n' printf '\n'
printf '# NVIDIA GCC version mismatch workaround\n' printf '# NVIDIA GCC version mismatch workaround\n'
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp" printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
printf 'export IGNORE_CC_MISMATCH=1\n' printf 'export IGNORE_CC_MISMATCH=1\n'
} >> "$PROFILE_FILE" } >>"$PROFILE_FILE"
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE" echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
else else
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE" echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
fi fi
} }
# Function to install pyroveil for mesh shader issues # Function to install pyroveil for mesh shader issues
install_pyroveil() { install_pyroveil() {
echo "" echo ""
echo "4. Pyroveil Setup for Mesh Shader Issues..." echo "4. Pyroveil Setup for Mesh Shader Issues..."
echo "===========================================" echo "==========================================="
local user_home="/home/$SUDO_USER" local user_home="/home/$SUDO_USER"
local pyroveil_dir="$user_home/pyroveil" local pyroveil_dir="$user_home/pyroveil"
echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games" echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems." echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
echo "" echo ""
local install_pyroveil=true local install_pyroveil=true
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
echo echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then
install_pyroveil=false install_pyroveil=false
fi fi
else else
echo "Auto-installing Pyroveil (use --interactive to prompt)" echo "Auto-installing Pyroveil (use --interactive to prompt)"
fi fi
if [[ $install_pyroveil == "true" ]]; then if [[ $install_pyroveil == "true" ]]; then
# Check for required dependencies # Check for required dependencies
local missing_deps=() local missing_deps=()
for dep in git cmake ninja gcc; do for dep in git cmake ninja gcc; do
if ! command -v "$dep" &> /dev/null; then if ! command -v "$dep" &>/dev/null; then
missing_deps+=("$dep") missing_deps+=("$dep")
fi fi
done done
if [[ ${#missing_deps[@]} -gt 0 ]]; then if [[ ${#missing_deps[@]} -gt 0 ]]; then
echo "Missing dependencies: ${missing_deps[*]}" echo "Missing dependencies: ${missing_deps[*]}"
echo "Please install them first. On Arch Linux:" echo "Please install them first. On Arch Linux:"
echo "pacman -S base-devel git cmake ninja" echo "pacman -S base-devel git cmake ninja"
return 1 return 1
fi fi
# Clone and build pyroveil as the original user # Clone and build pyroveil as the original user
echo "Installing Pyroveil to $pyroveil_dir..." echo "Installing Pyroveil to $pyroveil_dir..."
if [[ -d $pyroveil_dir ]]; then if [[ -d $pyroveil_dir ]]; then
echo "Pyroveil directory already exists. Updating..." echo "Pyroveil directory already exists. Updating..."
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull" sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
else else
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir" sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
fi fi
sudo -u "$SUDO_USER" bash -c " sudo -u "$SUDO_USER" bash -c "
cd '$pyroveil_dir' cd '$pyroveil_dir'
git submodule update --init git submodule update --init
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
ninja -C build install ninja -C build install
" "
echo "✓ Pyroveil installed successfully" echo "✓ Pyroveil installed successfully"
echo "" echo ""
echo "To use Pyroveil with games that have mesh shader issues:" echo "To use Pyroveil with games that have mesh shader issues:"
echo "1. For Final Fantasy VII Rebirth:" echo "1. For Final Fantasy VII Rebirth:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%" echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
echo "" echo ""
echo "2. For Steam games, add to launch options:" echo "2. For Steam games, add to launch options:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%" echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
echo "" echo ""
echo "Available configs in: $pyroveil_dir/hacks/" echo "Available configs in: $pyroveil_dir/hacks/"
# Create a helper script # Create a helper script
cat > "$user_home/run-with-pyroveil.sh" << EOF cat >"$user_home/run-with-pyroveil.sh" <<EOF
#!/bin/bash #!/bin/bash
# Helper script to run games with Pyroveil # Helper script to run games with Pyroveil
# Usage: ./run-with-pyroveil.sh <config-name> <command> # Usage: ./run-with-pyroveil.sh <config-name> <command>
@ -238,88 +213,88 @@ echo "Config file: \$PYROVEIL_CONFIG"
exec "\$@" exec "\$@"
EOF EOF
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh" chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
chmod +x "$user_home/run-with-pyroveil.sh" chmod +x "$user_home/run-with-pyroveil.sh"
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh" echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
else else
echo "Skipping Pyroveil installation" echo "Skipping Pyroveil installation"
echo "Note: You can manually install it later for mesh shader issues" echo "Note: You can manually install it later for mesh shader issues"
fi fi
} }
# Function to check for kernel parameter modifications # Function to check for kernel parameter modifications
suggest_kernel_params() { suggest_kernel_params() {
echo "" echo ""
echo "5. Kernel Parameter Recommendations..." echo "5. Kernel Parameter Recommendations..."
echo "=====================================" echo "====================================="
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:" echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
echo "" echo ""
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors" echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
echo " (especially with nvidia-96xx drivers):" echo " (especially with nvidia-96xx drivers):"
echo " → Add 'nopat' to kernel parameters" echo " → Add 'nopat' to kernel parameters"
echo "" echo ""
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:" echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
echo " → Consider disabling micro-op cache in BIOS settings" echo " → Consider disabling micro-op cache in BIOS settings"
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs" echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
echo " → Helps with severe graphical glitches in Xwayland applications" echo " → Helps with severe graphical glitches in Xwayland applications"
echo " → Note: Disabling micro-op cache reduces CPU performance" echo " → Note: Disabling micro-op cache reduces CPU performance"
echo "" echo ""
echo "To add kernel parameters:" echo "To add kernel parameters:"
echo "1. Edit /etc/default/grub" echo "1. Edit /etc/default/grub"
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT" echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg" echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
echo "4. Reboot" echo "4. Reboot"
echo "" echo ""
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:" echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"' echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
# Check current CPU for micro-op cache relevance # Check current CPU for micro-op cache relevance
echo "" echo ""
echo "CPU Information (for micro-op cache consideration):" echo "CPU Information (for micro-op cache consideration):"
if command -v lscpu &> /dev/null; then if command -v lscpu &>/dev/null; then
local cpu_info local cpu_info
cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs) cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
echo "Current CPU: $cpu_info" echo "Current CPU: $cpu_info"
if echo "$cpu_info" | grep -qi "intel"; then if echo "$cpu_info" | grep -qi "intel"; then
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache" echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
elif echo "$cpu_info" | grep -qi "amd"; then elif echo "$cpu_info" | grep -qi "amd"; then
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache" echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
fi fi
fi fi
} }
# Function to suggest desktop environment settings # Function to suggest desktop environment settings
suggest_desktop_settings() { suggest_desktop_settings() {
echo "" echo ""
echo "6. Desktop Environment Recommendations..." echo "6. Desktop Environment Recommendations..."
echo "========================================" echo "========================================"
echo "For fullscreen application freezing/crashing issues:" echo "For fullscreen application freezing/crashing issues:"
echo "" echo ""
echo "Enable Display Compositing and Direct fullscreen rendering:" echo "Enable Display Compositing and Direct fullscreen rendering:"
echo "" echo ""
echo "• KDE Plasma:" echo "• KDE Plasma:"
echo " System Settings → Display and Monitor → Compositor" echo " System Settings → Display and Monitor → Compositor"
echo " → Enable compositor + Enable direct rendering for fullscreen windows" echo " → Enable compositor + Enable direct rendering for fullscreen windows"
echo "" echo ""
echo "• GNOME:" echo "• GNOME:"
echo " Use Extensions or dconf-editor to enable compositing features" echo " Use Extensions or dconf-editor to enable compositing features"
echo "" echo ""
echo "• XFCE:" echo "• XFCE:"
echo " Settings → Window Manager Tweaks → Compositor" echo " Settings → Window Manager Tweaks → Compositor"
echo " → Enable display compositing" echo " → Enable display compositing"
echo "" echo ""
echo "• Cinnamon:" echo "• Cinnamon:"
echo " System Settings → Effects → Enable desktop effects" echo " System Settings → Effects → Enable desktop effects"
# Detect current desktop environment # Detect current desktop environment
if [[ -n $XDG_CURRENT_DESKTOP ]]; then if [[ -n $XDG_CURRENT_DESKTOP ]]; then
echo "" echo ""
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP" echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
fi fi
} }
# Apply all configurations # Apply all configurations
@ -331,14 +306,14 @@ install_pyroveil
echo "" echo ""
echo "7. Regenerating Initramfs..." echo "7. Regenerating Initramfs..."
echo "============================" echo "============================"
if command -v mkinitcpio &> /dev/null; then if command -v mkinitcpio &>/dev/null; then
mkinitcpio -P mkinitcpio -P
echo "✓ Initramfs regenerated with mkinitcpio" echo "✓ Initramfs regenerated with mkinitcpio"
elif command -v dracut &> /dev/null; then elif command -v dracut &>/dev/null; then
dracut --force dracut --force
echo "✓ Initramfs regenerated with dracut" echo "✓ Initramfs regenerated with dracut"
else else
echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs." echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs."
fi fi
# Display all recommendations # Display all recommendations
@ -354,7 +329,7 @@ echo "✓ GSP firmware disabled"
echo "✓ RenderAccel disabled in Xorg configuration" echo "✓ RenderAccel disabled in Xorg configuration"
echo "✓ GCC version mismatch workaround added" echo "✓ GCC version mismatch workaround added"
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
echo "✓ Pyroveil installed for mesh shader issues" echo "✓ Pyroveil installed for mesh shader issues"
fi fi
echo "✓ Initramfs regenerated" echo "✓ Initramfs regenerated"
echo "" echo ""

241
scripts/lib/common.sh Normal file
View File

@ -0,0 +1,241 @@
#!/bin/bash
# Common library functions for linux-configuration scripts
# Source this file at the beginning of scripts that need shared functionality
#
# Usage: source "$(dirname "$(readlink -f "$0")")/../lib/common.sh"
# Or: source "/path/to/scripts/lib/common.sh"
# Prevent multiple sourcing
[[ -n ${_LIB_COMMON_LOADED:-} ]] && return 0
_LIB_COMMON_LOADED=1
# =============================================================================
# LOGGING FUNCTIONS
# =============================================================================
# Log message with timestamp to stderr and optionally to a file
# Usage: log_message "message" [log_file]
log_message() {
local msg="$1"
local log_file="${2:-}"
local formatted
formatted="$(date '+%Y-%m-%d %H:%M:%S') - $msg"
echo "$formatted" >&2
if [[ -n "$log_file" ]]; then
echo "$formatted" >>"$log_file" 2>/dev/null || true
fi
}
# Simple log with timestamp (no file output)
# Usage: log "message"
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
# =============================================================================
# SUDO / ROOT HANDLING
# =============================================================================
# Check if running as root, if not re-exec with sudo
# Usage: require_root "$@"
require_root() {
if [[ $EUID -ne 0 ]]; then
echo "This script requires root privileges."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Get the actual user even when running with sudo
# Usage: ACTUAL_USER=$(get_actual_user)
get_actual_user() {
echo "${SUDO_USER:-$USER}"
}
# Get the actual user's home directory
# Usage: USER_HOME=$(get_actual_user_home)
get_actual_user_home() {
local user
user=$(get_actual_user)
if [[ "$user" == "root" ]]; then
echo "/root"
else
echo "/home/$user"
fi
}
# =============================================================================
# ARGUMENT PARSING HELPERS
# =============================================================================
# Parse common --interactive/-i and --help/-h flags
# Sets INTERACTIVE_MODE variable (exported for use by calling scripts)
# Usage: parse_common_args "$@"
# shift "$COMMON_ARGS_SHIFT"
export INTERACTIVE_MODE=false
export COMMON_ARGS_SHIFT=0
parse_interactive_args() {
INTERACTIVE_MODE=false
COMMON_ARGS_SHIFT=0
local script_name="${0##*/}"
while [[ $# -gt 0 ]]; do
case $1 in
-i | --interactive)
INTERACTIVE_MODE=true
((COMMON_ARGS_SHIFT++))
shift
;;
-h | --help)
echo "Usage: $script_name [OPTIONS]"
echo "Options:"
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
echo " -h, --help Show this help message"
exit 0
;;
*)
# Stop parsing at first unknown argument
break
;;
esac
done
}
# =============================================================================
# FOCUS APP DETECTION (for digital wellbeing scripts)
# =============================================================================
# Default focus apps - can be overridden before calling is_focus_app_running
FOCUS_APPS_WINDOWS=(
"Visual Studio Code"
"VSCodium"
"Cursor"
"IntelliJ IDEA"
"PyCharm"
"WebStorm"
"CLion"
"Rider"
"Sublime Text"
"Blender"
"Godot"
"Unity"
"Unreal Editor"
)
FOCUS_APPS_PROCESSES=(
"steam_app_"
"gamescope"
)
# Check if any focus app is running (window-based detection)
# Returns 0 if focus app found, 1 otherwise
# Echoes the name of the found app
is_focus_app_running() {
# Check windows first
if command -v xdotool &>/dev/null; then
local app
for app in "${FOCUS_APPS_WINDOWS[@]}"; do
if xdotool search --name "$app" &>/dev/null 2>&1; then
echo "$app"
return 0
fi
done
fi
# Check specific processes
local app
for app in "${FOCUS_APPS_PROCESSES[@]}"; do
if pgrep -f "$app" &>/dev/null; then
echo "$app"
return 0
fi
done
return 1
}
# =============================================================================
# COMMAND AVAILABILITY
# =============================================================================
# Check if a command exists
# Usage: if require_command ffmpeg; then ...
require_command() {
local cmd="$1"
local pkg="${2:-$1}"
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: '$cmd' is not installed or not in PATH." >&2
echo "Install with: sudo pacman -S $pkg" >&2
return 1
fi
return 0
}
# =============================================================================
# NOTIFICATION
# =============================================================================
# Send desktop notification (fails silently if notify-send not available)
# Usage: notify "Title" "Message" [urgency: low/normal/critical] [timeout_ms]
notify() {
local title="$1"
local message="$2"
local urgency="${3:-normal}"
local timeout="${4:-5000}"
if command -v notify-send &>/dev/null; then
notify-send -u "$urgency" -t "$timeout" "$title" "$message" 2>/dev/null || true
fi
}
# =============================================================================
# FILE/PATH UTILITIES
# =============================================================================
# Get the directory containing the calling script
# Usage: SCRIPT_DIR=$(get_script_dir)
get_script_dir() {
dirname "$(readlink -f "${BASH_SOURCE[1]:-$0}")"
}
# Ensure a directory exists
# Usage: ensure_dir "/path/to/dir"
ensure_dir() {
local dir="$1"
if [[ ! -d "$dir" ]]; then
mkdir -p "$dir"
fi
}
# =============================================================================
# SYSTEMD HELPERS
# =============================================================================
# Enable and start a systemd service (user or system)
# Usage: enable_service "service-name" [--user]
enable_service() {
local service="$1"
local user_flag="${2:-}"
if [[ "$user_flag" == "--user" ]]; then
systemctl --user daemon-reload
systemctl --user enable --now "$service"
else
systemctl daemon-reload
systemctl enable --now "$service"
fi
}
# Check if a systemd service is active
# Usage: if is_service_active "service-name" [--user]; then ...
is_service_active() {
local service="$1"
local user_flag="${2:-}"
if [[ "$user_flag" == "--user" ]]; then
systemctl --user is-active --quiet "$service"
else
systemctl is-active --quiet "$service"
fi
}

View File

@ -5,56 +5,30 @@
set -e # Exit on any error set -e # Exit on any error
# Default to non-interactive mode # Source common library for shared functions
INTERACTIVE_MODE=false SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh"
# Parse command line arguments # Parse interactive/help arguments
while [[ $# -gt 0 ]]; do parse_interactive_args "$@"
case $1 in shift "$COMMON_ARGS_SHIFT"
-i | --interactive)
INTERACTIVE_MODE=true
shift
;;
-h | --help)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
# Function to check and request sudo privileges # Check for sudo privileges
check_sudo() { require_root "$@"
if [[ $EUID -ne 0 ]]; then
echo "This script requires sudo privileges to create systemd services and timers."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Check for sudo privileges after argument parsing
check_sudo "$@"
echo "Periodic System Setup - Pacman Wrapper & Hosts File" echo "Periodic System Setup - Pacman Wrapper & Hosts File"
echo "===================================================" echo "==================================================="
echo "Current Date: $(date)" echo "Current Date: $(date)"
echo "User: $USER" echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}" echo "Original user: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)" echo "Mode: Interactive (prompts enabled)"
else else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)" echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi fi
# Get the directory where this script is located # Get the directory where this script is located
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
CONFIG_DIR="$(dirname "$SCRIPT_DIR")" CONFIG_DIR="$(dirname "$SCRIPT_DIR")"
# Define paths # Define paths
@ -86,231 +60,231 @@ TEMPLATE_LOGROTATE="$LOGROTATE_TEMPLATES/periodic-system-maintenance"
# Function to verify required files exist # Function to verify required files exist
verify_files() { verify_files() {
echo "" echo ""
echo "1. Verifying Required Files..." echo "1. Verifying Required Files..."
echo "==============================" echo "=============================="
local missing_files=() local missing_files=()
if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then
missing_files+=("$PACMAN_WRAPPER_SCRIPT") missing_files+=("$PACMAN_WRAPPER_SCRIPT")
fi fi
if [[ ! -f $PACMAN_WRAPPER_INSTALL ]]; then if [[ ! -f $PACMAN_WRAPPER_INSTALL ]]; then
missing_files+=("$PACMAN_WRAPPER_INSTALL") missing_files+=("$PACMAN_WRAPPER_INSTALL")
fi fi
if [[ ! -f $HOSTS_INSTALL_SCRIPT ]]; then if [[ ! -f $HOSTS_INSTALL_SCRIPT ]]; then
missing_files+=("$HOSTS_INSTALL_SCRIPT") missing_files+=("$HOSTS_INSTALL_SCRIPT")
fi fi
# Check template files as well # Check template files as well
for tmpl in \ for tmpl in \
"$TEMPLATE_MAINT_SCRIPT" \ "$TEMPLATE_MAINT_SCRIPT" \
"$TEMPLATE_HOSTS_MONITOR" \ "$TEMPLATE_HOSTS_MONITOR" \
"$TEMPLATE_BROWSER_WRAPPER" \ "$TEMPLATE_BROWSER_WRAPPER" \
"$TEMPLATE_SVC_MAINT" \ "$TEMPLATE_SVC_MAINT" \
"$TEMPLATE_TIMER" \ "$TEMPLATE_TIMER" \
"$TEMPLATE_STARTUP" \ "$TEMPLATE_STARTUP" \
"$TEMPLATE_HOSTS_SVC" \ "$TEMPLATE_HOSTS_SVC" \
"$TEMPLATE_LOGROTATE"; do "$TEMPLATE_LOGROTATE"; do
if [[ ! -f $tmpl ]]; then if [[ ! -f $tmpl ]]; then
missing_files+=("$tmpl") missing_files+=("$tmpl")
fi fi
done done
if [[ ${#missing_files[@]} -gt 0 ]]; then if [[ ${#missing_files[@]} -gt 0 ]]; then
echo "Error: The following required files are missing:" echo "Error: The following required files are missing:"
for file in "${missing_files[@]}"; do for file in "${missing_files[@]}"; do
echo " - $file" echo " - $file"
done done
exit 1 exit 1
fi fi
echo "✓ All required files found" echo "✓ All required files found"
} }
# Function to create the combined execution script # Function to create the combined execution script
create_execution_script() { create_execution_script() {
echo "" echo ""
echo "2. Creating Combined Execution Script..." echo "2. Creating Combined Execution Script..."
echo "=======================================" echo "======================================="
local exec_script="/usr/local/bin/periodic-system-maintenance.sh" local exec_script="/usr/local/bin/periodic-system-maintenance.sh"
# Install from template with path substitutions # Install from template with path substitutions
sed \ sed \
-e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \ -e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \
-e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \ -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_MAINT_SCRIPT" > "$exec_script" "$TEMPLATE_MAINT_SCRIPT" >"$exec_script"
chmod +x "$exec_script" chmod +x "$exec_script"
echo "✓ Installed execution script from template: $exec_script" echo "✓ Installed execution script from template: $exec_script"
} }
# Function to create systemd service # Function to create systemd service
create_systemd_service() { create_systemd_service() {
echo "" echo ""
echo "3. Creating Systemd Service..." echo "3. Creating Systemd Service..."
echo "=============================" echo "============================="
local service_file="/etc/systemd/system/periodic-system-maintenance.service" local service_file="/etc/systemd/system/periodic-system-maintenance.service"
install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file" install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
echo "✓ Installed systemd service from template: $service_file" echo "✓ Installed systemd service from template: $service_file"
} }
# Function to create systemd timer for hourly execution # Function to create systemd timer for hourly execution
create_systemd_timer() { create_systemd_timer() {
echo "" echo ""
echo "4. Creating Systemd Timer..." echo "4. Creating Systemd Timer..."
echo "============================" echo "============================"
local timer_file="/etc/systemd/system/periodic-system-maintenance.timer" local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
install -m 0644 "$TEMPLATE_TIMER" "$timer_file" install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
echo "✓ Installed systemd timer from template: $timer_file" echo "✓ Installed systemd timer from template: $timer_file"
} }
# Function to create startup service (additional to timer) # Function to create startup service (additional to timer)
create_startup_service() { create_startup_service() {
echo "" echo ""
echo "5. Creating Startup Service..." echo "5. Creating Startup Service..."
echo "==============================" echo "=============================="
local startup_service="/etc/systemd/system/periodic-system-startup.service" local startup_service="/etc/systemd/system/periodic-system-startup.service"
install -m 0644 "$TEMPLATE_STARTUP" "$startup_service" install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
echo "✓ Installed startup service from template: $startup_service" echo "✓ Installed startup service from template: $startup_service"
} }
# Function to create hosts file monitor service # Function to create hosts file monitor service
create_hosts_monitor_service() { create_hosts_monitor_service() {
echo "" echo ""
echo "6. Creating Hosts File Monitor Service..." echo "6. Creating Hosts File Monitor Service..."
echo "========================================" echo "========================================"
local monitor_script="/usr/local/bin/hosts-file-monitor.sh" local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
local monitor_service="/etc/systemd/system/hosts-file-monitor.service" local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
# Install the monitor script from template with substitution # Install the monitor script from template with substitution
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \ sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_HOSTS_MONITOR" > "$monitor_script" "$TEMPLATE_HOSTS_MONITOR" >"$monitor_script"
chmod +x "$monitor_script" chmod +x "$monitor_script"
echo "✓ Installed hosts monitor script from template: $monitor_script" echo "✓ Installed hosts monitor script from template: $monitor_script"
# Install the systemd service from template # Install the systemd service from template
install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service" install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
echo "✓ Installed hosts monitor service from template: $monitor_service" echo "✓ Installed hosts monitor service from template: $monitor_service"
} }
# Function to install browser pre-exec wrapper and wire common browser names # Function to install browser pre-exec wrapper and wire common browser names
install_browser_preexec_wrapper() { install_browser_preexec_wrapper() {
echo "" echo ""
echo "6.1 Installing Browser Pre-Exec Wrapper..." echo "6.1 Installing Browser Pre-Exec Wrapper..."
echo "=========================================" echo "========================================="
local wrapper="/usr/local/bin/browser-preexec-wrapper" local wrapper="/usr/local/bin/browser-preexec-wrapper"
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \ sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_BROWSER_WRAPPER" > "$wrapper" "$TEMPLATE_BROWSER_WRAPPER" >"$wrapper"
chmod +x "$wrapper" chmod +x "$wrapper"
echo "✓ Installed wrapper: $wrapper" echo "✓ Installed wrapper: $wrapper"
# Allow passwordless execution of hosts installer for root-only actions # Allow passwordless execution of hosts installer for root-only actions
local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd" local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
if command -v visudo > /dev/null 2>&1; then if command -v visudo >/dev/null 2>&1; then
echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file" echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" >"$sudoers_file"
chmod 440 "$sudoers_file" chmod 440 "$sudoers_file"
# Validate syntax # Validate syntax
visudo -c > /dev/null || echo "Warning: sudoers validation returned non-zero" visudo -c >/dev/null || echo "Warning: sudoers validation returned non-zero"
echo "✓ Sudoers drop-in created: $sudoers_file" echo "✓ Sudoers drop-in created: $sudoers_file"
else else
echo "visudo not found; skipping sudoers drop-in" echo "visudo not found; skipping sudoers drop-in"
fi fi
# Create symlinks for common browser commands to the wrapper in /usr/local/bin # Create symlinks for common browser commands to the wrapper in /usr/local/bin
# This takes precedence over /usr/bin in PATH on most systems. # This takes precedence over /usr/bin in PATH on most systems.
local browsers=("thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox") local browsers=("thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox")
for b in "${browsers[@]}"; do for b in "${browsers[@]}"; do
local link="/usr/local/bin/$b" local link="/usr/local/bin/$b"
ln -sf "$wrapper" "$link" ln -sf "$wrapper" "$link"
done done
echo "✓ Symlinked wrapper for common browsers in /usr/local/bin" echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
} }
# Function to enable and start services # Function to enable and start services
enable_services() { enable_services() {
echo "" echo ""
echo "7. Enabling Services and Timer..." echo "7. Enabling Services and Timer..."
echo "=================================" echo "================================="
# Reload systemd daemon # Reload systemd daemon
systemctl daemon-reload systemctl daemon-reload
echo "✓ Systemd daemon reloaded" echo "✓ Systemd daemon reloaded"
# Enable and start the timer # Enable and start the timer
systemctl enable periodic-system-maintenance.timer systemctl enable periodic-system-maintenance.timer
systemctl start periodic-system-maintenance.timer systemctl start periodic-system-maintenance.timer
echo "✓ Timer enabled and started" echo "✓ Timer enabled and started"
# Enable startup service (but don't start it now) # Enable startup service (but don't start it now)
systemctl enable periodic-system-startup.service systemctl enable periodic-system-startup.service
echo "✓ Startup service enabled" echo "✓ Startup service enabled"
# Enable hosts file monitor service # Enable hosts file monitor service
systemctl enable hosts-file-monitor.service systemctl enable hosts-file-monitor.service
systemctl start hosts-file-monitor.service systemctl start hosts-file-monitor.service
echo "✓ Hosts file monitor service enabled and started" echo "✓ Hosts file monitor service enabled and started"
# Show timer status # Show timer status
echo "" echo ""
echo "Timer Status:" echo "Timer Status:"
systemctl status periodic-system-maintenance.timer --no-pager -l systemctl status periodic-system-maintenance.timer --no-pager -l
echo "" echo ""
echo "Hosts Monitor Status:" echo "Hosts Monitor Status:"
systemctl status hosts-file-monitor.service --no-pager -l systemctl status hosts-file-monitor.service --no-pager -l
echo "" echo ""
echo "Next scheduled runs:" echo "Next scheduled runs:"
systemctl list-timers periodic-system-maintenance.timer --no-pager systemctl list-timers periodic-system-maintenance.timer --no-pager
} }
# Function to create log rotation configuration # Function to create log rotation configuration
create_log_rotation() { create_log_rotation() {
echo "" echo ""
echo "8. Setting up Log Rotation..." echo "8. Setting up Log Rotation..."
echo "=============================" echo "============================="
local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance" local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf" install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
echo "✓ Installed log rotation configuration from template: $logrotate_conf" echo "✓ Installed log rotation configuration from template: $logrotate_conf"
} }
# Function to run initial execution # Function to run initial execution
run_initial_execution() { run_initial_execution() {
echo "" echo ""
echo "9. Running Initial Execution..." echo "9. Running Initial Execution..."
echo "===============================" echo "==============================="
local run_initial=true local run_initial=true
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Would you like to run the system maintenance now to test the setup?" echo "Would you like to run the system maintenance now to test the setup?"
read -p "Run initial execution? (y/N): " -n 1 -r read -p "Run initial execution? (y/N): " -n 1 -r
echo echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then
run_initial=false run_initial=false
fi fi
else else
echo "Auto-running initial execution to test the setup (use --interactive to prompt)" echo "Auto-running initial execution to test the setup (use --interactive to prompt)"
fi fi
if [[ $run_initial == "true" ]]; then if [[ $run_initial == "true" ]]; then
echo "Running initial system maintenance..." echo "Running initial system maintenance..."
/usr/local/bin/periodic-system-maintenance.sh /usr/local/bin/periodic-system-maintenance.sh
echo "✓ Initial execution completed" echo "✓ Initial execution completed"
else else
echo "Skipping initial execution" echo "Skipping initial execution"
fi fi
} }
# Main execution # Main execution

View File

@ -4,58 +4,33 @@
set -e # Exit on any error set -e # Exit on any error
# Default to non-interactive mode # Source common library for shared functions
INTERACTIVE_MODE=false SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh"
# Parse command line arguments # Parse interactive/help arguments
while [[ $# -gt 0 ]]; do parse_interactive_args "$@"
case $1 in shift "$COMMON_ARGS_SHIFT"
-i | --interactive)
INTERACTIVE_MODE=true
shift
;;
-h | --help)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -i, --interactive Enable interactive prompts (default: auto-yes)"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
esac
done
# Function to check and request sudo privileges # Check for sudo privileges
check_sudo() { require_root "$@"
if [[ $EUID -ne 0 ]]; then
echo "This script requires sudo privileges to create systemd services."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Check for sudo privileges after argument parsing
check_sudo "$@"
echo "Thorium Browser Auto-Startup Setup" echo "Thorium Browser Auto-Startup Setup"
echo "==================================" echo "=================================="
echo "Current Date: $(date)" echo "Current Date: $(date)"
echo "User: $USER" echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}" echo "Original user: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)" echo "Mode: Interactive (prompts enabled)"
else else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)" echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi fi
# Target URL # Target URL
TARGET_URL="https://www.fitatu.com/app/planner" TARGET_URL="https://www.fitatu.com/app/planner"
BROWSER_COMMAND="thorium-browser" BROWSER_COMMAND="thorium-browser"
USER_HOME="/home/${SUDO_USER}" USER_HOME="/home/$(get_actual_user)"
echo "" echo ""
echo "Target URL: $TARGET_URL" echo "Target URL: $TARGET_URL"
@ -64,72 +39,72 @@ echo "User home: $USER_HOME"
# Function to check if Thorium browser is installed # Function to check if Thorium browser is installed
check_thorium_browser() { check_thorium_browser() {
echo "" echo ""
echo "1. Checking Thorium Browser Installation..." echo "1. Checking Thorium Browser Installation..."
echo "==========================================" echo "=========================================="
if ! command -v "$BROWSER_COMMAND" &> /dev/null; then if ! command -v "$BROWSER_COMMAND" &>/dev/null; then
echo "Warning: Thorium browser not found in PATH" echo "Warning: Thorium browser not found in PATH"
echo "Checking alternative locations..." echo "Checking alternative locations..."
# Check common installation paths # Check common installation paths
local alt_paths=( local alt_paths=(
"/opt/thorium/thorium" "/opt/thorium/thorium"
"/usr/bin/thorium" "/usr/bin/thorium"
"/usr/local/bin/thorium" "/usr/local/bin/thorium"
"/opt/thorium-browser/thorium-browser" "/opt/thorium-browser/thorium-browser"
"${USER_HOME}/.local/bin/thorium-browser" "${USER_HOME}/.local/bin/thorium-browser"
) )
local found=false local found=false
for path in "${alt_paths[@]}"; do for path in "${alt_paths[@]}"; do
if [[ -x $path ]]; then if [[ -x $path ]]; then
BROWSER_COMMAND="$path" BROWSER_COMMAND="$path"
echo "✓ Found Thorium browser at: $path" echo "✓ Found Thorium browser at: $path"
found=true found=true
break break
fi fi
done done
if [[ $found != true ]]; then if [[ $found != true ]]; then
echo "Error: Thorium browser not found!" echo "Error: Thorium browser not found!"
echo "Please install Thorium browser first or ensure it's in your PATH." echo "Please install Thorium browser first or ensure it's in your PATH."
echo "" echo ""
echo "You can install Thorium browser from:" echo "You can install Thorium browser from:"
echo "https://thorium.rocks/" echo "https://thorium.rocks/"
echo "" echo ""
local continue_anyway=false local continue_anyway=false
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r read -p "Continue anyway? The service will be created but may fail to start (y/N): " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
continue_anyway=true continue_anyway=true
fi fi
else else
echo "Auto-continuing anyway - service will be created but may fail to start (use --interactive to prompt)" echo "Auto-continuing anyway - service will be created but may fail to start (use --interactive to prompt)"
continue_anyway=true continue_anyway=true
fi fi
if [[ $continue_anyway != true ]]; then if [[ $continue_anyway != true ]]; then
exit 1 exit 1
fi fi
fi fi
else else
echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)" echo "✓ Thorium browser found: $(which $BROWSER_COMMAND)"
fi fi
} }
# Function to create the browser launcher script # Function to create the browser launcher script
create_launcher_script() { create_launcher_script() {
echo "" echo ""
echo "2. Creating Browser Launcher Script..." echo "2. Creating Browser Launcher Script..."
echo "=====================================" echo "====================================="
local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh" local launcher_script="/usr/local/bin/thorium-fitatu-launcher.sh"
cat > "$launcher_script" << EOF cat >"$launcher_script" <<EOF
#!/bin/bash #!/bin/bash
# Thorium browser launcher for Fitatu website # Thorium browser launcher for Fitatu website
# Created by setup_thorium_startup.sh on $(date) # Created by setup_thorium_startup.sh on $(date)
@ -203,24 +178,24 @@ else
fi fi
EOF EOF
chmod +x "$launcher_script" chmod +x "$launcher_script"
echo "✓ Created launcher script: $launcher_script" echo "✓ Created launcher script: $launcher_script"
} }
# Function to create systemd service for user session # Function to create systemd service for user session
create_user_systemd_service() { create_user_systemd_service() {
echo "" echo ""
echo "3. Creating User Systemd Service..." echo "3. Creating User Systemd Service..."
echo "==================================" echo "=================================="
local user_systemd_dir="$USER_HOME/.config/systemd/user" local user_systemd_dir="$USER_HOME/.config/systemd/user"
local service_file="$user_systemd_dir/thorium-fitatu-startup.service" local service_file="$user_systemd_dir/thorium-fitatu-startup.service"
# Create user systemd directory # Create user systemd directory
sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir" sudo -u "${SUDO_USER}" mkdir -p "$user_systemd_dir"
# Create the service file # Create the service file
sudo -u "${SUDO_USER}" tee "$service_file" > /dev/null << EOF sudo -u "${SUDO_USER}" tee "$service_file" >/dev/null <<EOF
[Unit] [Unit]
Description=Launch Thorium Browser with Fitatu on Startup Description=Launch Thorium Browser with Fitatu on Startup
After=graphical-session.target After=graphical-session.target
@ -245,18 +220,18 @@ TimeoutStartSec=120
WantedBy=default.target WantedBy=default.target
EOF EOF
echo "✓ Created user systemd service: $service_file" echo "✓ Created user systemd service: $service_file"
} }
# Function to create system-wide systemd service (alternative approach) # Function to create system-wide systemd service (alternative approach)
create_system_systemd_service() { create_system_systemd_service() {
echo "" echo ""
echo "4. Creating System Systemd Service..." echo "4. Creating System Systemd Service..."
echo "====================================" echo "===================================="
local service_file="/etc/systemd/system/thorium-fitatu-startup.service" local service_file="/etc/systemd/system/thorium-fitatu-startup.service"
cat > "$service_file" << EOF cat >"$service_file" <<EOF
[Unit] [Unit]
Description=Launch Thorium Browser with Fitatu on Startup Description=Launch Thorium Browser with Fitatu on Startup
After=multi-user.target network-online.target After=multi-user.target network-online.target
@ -283,23 +258,23 @@ TimeoutStartSec=180
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
echo "✓ Created system systemd service: $service_file" echo "✓ Created system systemd service: $service_file"
} }
# Function to create autostart desktop entry (additional method) # Function to create autostart desktop entry (additional method)
create_autostart_entry() { create_autostart_entry() {
echo "" echo ""
echo "5. Creating Autostart Desktop Entry..." echo "5. Creating Autostart Desktop Entry..."
echo "=====================================" echo "====================================="
local autostart_dir="$USER_HOME/.config/autostart" local autostart_dir="$USER_HOME/.config/autostart"
local desktop_file="$autostart_dir/thorium-fitatu.desktop" local desktop_file="$autostart_dir/thorium-fitatu.desktop"
# Create autostart directory # Create autostart directory
sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir" sudo -u "${SUDO_USER}" mkdir -p "$autostart_dir"
# Create desktop entry # Create desktop entry
sudo -u "${SUDO_USER}" tee "$desktop_file" > /dev/null << EOF sudo -u "${SUDO_USER}" tee "$desktop_file" >/dev/null <<EOF
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name=Thorium Fitatu Startup Name=Thorium Fitatu Startup
@ -314,45 +289,45 @@ Terminal=false
Categories=Network;WebBrowser; Categories=Network;WebBrowser;
EOF EOF
echo "✓ Created autostart desktop entry: $desktop_file" echo "✓ Created autostart desktop entry: $desktop_file"
} }
# Function to create i3 config autostart entry # Function to create i3 config autostart entry
create_i3_autostart() { create_i3_autostart() {
echo "" echo ""
echo "6. Creating i3 Config Autostart Entry..." echo "6. Creating i3 Config Autostart Entry..."
echo "=======================================" echo "======================================="
local i3_config="$USER_HOME/.config/i3/config" local i3_config="$USER_HOME/.config/i3/config"
local i3_config_dir="$USER_HOME/.config/i3" local i3_config_dir="$USER_HOME/.config/i3"
# Create i3 config directory if it doesn't exist # Create i3 config directory if it doesn't exist
sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir" sudo -u "${SUDO_USER}" mkdir -p "$i3_config_dir"
# Check if i3 config exists # Check if i3 config exists
if [[ -f $i3_config ]]; then if [[ -f $i3_config ]]; then
# Check if autostart entry already exists # Check if autostart entry already exists
if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then if ! sudo -u "${SUDO_USER}" grep -q "thorium-fitatu-launcher" "$i3_config"; then
# Add autostart entry to i3 config # Add autostart entry to i3 config
sudo -u "${SUDO_USER}" bash -c "echo '' >> '$i3_config'" sudo -u "${SUDO_USER}" bash -c "echo '' >> '$i3_config'"
sudo -u "${SUDO_USER}" bash -c "echo '# Auto-start Thorium browser with Fitatu' >> '$i3_config'" sudo -u "${SUDO_USER}" bash -c "echo '# Auto-start Thorium browser with Fitatu' >> '$i3_config'"
sudo -u "${SUDO_USER}" bash -c "echo 'exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh' >> '$i3_config'" sudo -u "${SUDO_USER}" bash -c "echo 'exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh' >> '$i3_config'"
echo "✓ Added autostart entry to i3 config: $i3_config" echo "✓ Added autostart entry to i3 config: $i3_config"
else else
echo "✓ Autostart entry already exists in i3 config" echo "✓ Autostart entry already exists in i3 config"
fi fi
else else
echo "Warning: i3 config file not found at $i3_config" echo "Warning: i3 config file not found at $i3_config"
echo "You may need to manually add the following line to your i3 config:" echo "You may need to manually add the following line to your i3 config:"
echo "exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh" echo "exec --no-startup-id /usr/local/bin/thorium-fitatu-launcher.sh"
fi fi
} }
# Function to create a script to enable user service after login # Function to create a script to enable user service after login
create_user_enable_script() { create_user_enable_script() {
local enable_script="$USER_HOME/.config/thorium-enable-service.sh" local enable_script="$USER_HOME/.config/thorium-enable-service.sh"
sudo -u "${SUDO_USER}" tee "$enable_script" > /dev/null << 'EOF' sudo -u "${SUDO_USER}" tee "$enable_script" >/dev/null <<'EOF'
#!/bin/bash #!/bin/bash
# Script to enable thorium-fitatu-startup user service # Script to enable thorium-fitatu-startup user service
# This runs once to enable the service, then removes itself # This runs once to enable the service, then removes itself
@ -365,110 +340,110 @@ systemctl --user enable thorium-fitatu-startup.service
rm "$0" rm "$0"
EOF EOF
sudo -u "${SUDO_USER}" chmod +x "$enable_script" sudo -u "${SUDO_USER}" chmod +x "$enable_script"
# Add to user's .bashrc to run on next login # Add to user's .bashrc to run on next login
local bashrc="$USER_HOME/.bashrc" local bashrc="$USER_HOME/.bashrc"
if [[ -f $bashrc ]]; then if [[ -f $bashrc ]]; then
sudo -u "${SUDO_USER}" bash -c "echo '' >> '$bashrc'" sudo -u "${SUDO_USER}" bash -c "echo '' >> '$bashrc'"
sudo -u "${SUDO_USER}" bash -c "echo '# Auto-enable thorium service (temporary)' >> '$bashrc'" sudo -u "${SUDO_USER}" bash -c "echo '# Auto-enable thorium service (temporary)' >> '$bashrc'"
sudo -u "${SUDO_USER}" bash -c "echo '[[ -x ~/.config/thorium-enable-service.sh ]] && ~/.config/thorium-enable-service.sh' >> '$bashrc'" sudo -u "${SUDO_USER}" bash -c "echo '[[ -x ~/.config/thorium-enable-service.sh ]] && ~/.config/thorium-enable-service.sh' >> '$bashrc'"
fi fi
} }
# Function to enable services # Function to enable services
enable_services() { enable_services() {
echo "" echo ""
echo "7. Enabling Services..." echo "7. Enabling Services..."
echo "======================" echo "======================"
# Reload systemd daemon # Reload systemd daemon
systemctl daemon-reload systemctl daemon-reload
echo "✓ System daemon reloaded" echo "✓ System daemon reloaded"
# Enable system service # Enable system service
systemctl enable thorium-fitatu-startup.service systemctl enable thorium-fitatu-startup.service
echo "✓ System service enabled" echo "✓ System service enabled"
# Enable lingering for the user (allows user services to run without login) # Enable lingering for the user (allows user services to run without login)
loginctl enable-linger "${SUDO_USER}" loginctl enable-linger "${SUDO_USER}"
echo "✓ User lingering enabled" echo "✓ User lingering enabled"
# Create a script to enable user service after login # Create a script to enable user service after login
create_user_enable_script create_user_enable_script
echo "✓ User service will be enabled on next login" echo "✓ User service will be enabled on next login"
} }
# Function to test the setup # Function to test the setup
test_setup() { test_setup() {
echo "" echo ""
echo "8. Testing Setup..." echo "8. Testing Setup..."
echo "==================" echo "=================="
local run_test=true local run_test=true
if [[ $INTERACTIVE_MODE == "true" ]]; then if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Would you like to test the browser launcher now?" echo "Would you like to test the browser launcher now?"
read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r read -p "Test launch Thorium browser with Fitatu? (y/N): " -n 1 -r
echo echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then
run_test=false run_test=false
fi fi
else else
echo "Auto-testing the browser launcher (use --interactive to prompt)" echo "Auto-testing the browser launcher (use --interactive to prompt)"
fi fi
if [[ $run_test == "true" ]]; then if [[ $run_test == "true" ]]; then
echo "Testing browser launch..." echo "Testing browser launch..."
echo "Note: This will open Thorium browser with Fitatu website" echo "Note: This will open Thorium browser with Fitatu website"
# Test the launcher immediately # Test the launcher immediately
if /usr/local/bin/thorium-fitatu-launcher.sh; then if /usr/local/bin/thorium-fitatu-launcher.sh; then
echo "✓ Test launch completed successfully" echo "✓ Test launch completed successfully"
else else
echo "✗ Test launch failed" echo "✗ Test launch failed"
echo "Check that Thorium browser is properly installed and accessible" echo "Check that Thorium browser is properly installed and accessible"
fi fi
else else
echo "Skipping test launch" echo "Skipping test launch"
fi fi
} }
# Function to show usage instructions # Function to show usage instructions
show_instructions() { show_instructions() {
echo "" echo ""
echo "==========================================" echo "=========================================="
echo "Thorium Browser Auto-Startup Setup Complete" echo "Thorium Browser Auto-Startup Setup Complete"
echo "==========================================" echo "=========================================="
echo "Summary:" echo "Summary:"
echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh" echo "✓ Launcher script created: /usr/local/bin/thorium-fitatu-launcher.sh"
echo "✓ System service created: thorium-fitatu-startup.service" echo "✓ System service created: thorium-fitatu-startup.service"
echo "✓ User service created: ~/.config/systemd/user/thorium-fitatu-startup.service" echo "✓ User service created: ~/.config/systemd/user/thorium-fitatu-startup.service"
echo "✓ Autostart entry created: ~/.config/autostart/thorium-fitatu.desktop" echo "✓ Autostart entry created: ~/.config/autostart/thorium-fitatu.desktop"
echo "✓ i3 autostart entry added to: ~/.config/i3/config" echo "✓ i3 autostart entry added to: ~/.config/i3/config"
echo "✓ Services enabled for automatic startup" echo "✓ Services enabled for automatic startup"
echo "" echo ""
echo "The system will now:" echo "The system will now:"
echo "• Launch Thorium browser with $TARGET_URL on every startup" echo "• Launch Thorium browser with $TARGET_URL on every startup"
echo "• Use multiple methods to ensure reliable startup" echo "• Use multiple methods to ensure reliable startup"
echo "• Wait for desktop environment to be ready before launching" echo "• Wait for desktop environment to be ready before launching"
echo "• User service will be enabled automatically on next login" echo "• User service will be enabled automatically on next login"
echo "" echo ""
echo "To check status:" echo "To check status:"
echo " systemctl status thorium-fitatu-startup.service" echo " systemctl status thorium-fitatu-startup.service"
echo " systemctl --user status thorium-fitatu-startup.service (after login)" echo " systemctl --user status thorium-fitatu-startup.service (after login)"
echo "" echo ""
echo "To view logs:" echo "To view logs:"
echo " journalctl -u thorium-fitatu-startup.service" echo " journalctl -u thorium-fitatu-startup.service"
echo " journalctl --user -u thorium-fitatu-startup.service" echo " journalctl --user -u thorium-fitatu-startup.service"
echo "" echo ""
echo "To disable (if needed):" echo "To disable (if needed):"
echo " sudo systemctl disable thorium-fitatu-startup.service" echo " sudo systemctl disable thorium-fitatu-startup.service"
echo " systemctl --user disable thorium-fitatu-startup.service" echo " systemctl --user disable thorium-fitatu-startup.service"
echo " rm ~/.config/autostart/thorium-fitatu.desktop" echo " rm ~/.config/autostart/thorium-fitatu.desktop"
echo "" echo ""
echo "IMPORTANT: Browser will launch automatically on next reboot!" echo "IMPORTANT: Browser will launch automatically on next reboot!"
} }
# Main execution # Main execution

View File

@ -0,0 +1,238 @@
#!/usr/bin/env bash
set -euo pipefail
# convert_video.sh
#
# Convert video files to a target format (mp4 or webm) using ffmpeg.
# Accepts either a single video file or a directory (will recurse into subdirectories).
# Default settings
TARGET_FORMAT="mp4"
CRF="" # Will be set based on format if not specified
PRESET="medium"
DELETE_ORIGINAL=false
TARGET_PATH=""
# Video extensions to search for
ALL_VIDEO_EXTENSIONS=("mp4" "webm" "mkv" "avi" "mov" "wmv" "flv" "m4v" "mpg" "mpeg" "3gp" "ogv" "ts" "mts" "m2ts" "vob" "asf" "rm" "rmvb" "divx" "f4v")
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
usage() {
cat <<EOF
Usage:
$(basename "$0") [OPTIONS] PATH
Convert video files to mp4 or webm format using ffmpeg.
PATH can be a single video file or a directory (will recurse into subdirectories).
Options:
-f FORMAT Target format: mp4 or webm (default: mp4)
-c CRF Quality level (default: 23 for mp4, 30 for webm; lower = better)
-p PRESET Encoding preset (default: medium)
Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
-d Delete original file after successful conversion
-h Show this help
Examples:
$(basename "$0") video.webm # Convert to mp4
$(basename "$0") -f webm video.mp4 # Convert to webm
$(basename "$0") /path/to/videos/ # Convert all videos in directory to mp4
$(basename "$0") -f webm -c 25 -d /path/to/videos/
EOF
}
ensure_ffmpeg() {
if ! command -v ffmpeg >/dev/null 2>&1; then
echo "Error: 'ffmpeg' is not installed or not in PATH." >&2
exit 1
fi
}
get_video_extensions_except() {
local exclude="$1"
local exts=()
for ext in "${ALL_VIDEO_EXTENSIONS[@]}"; do
if [[ "${ext,,}" != "${exclude,,}" ]]; then
exts+=("$ext")
fi
done
echo "${exts[@]}"
}
is_video_file() {
local file="$1"
local ext="${file##*.}"
ext="${ext,,}" # lowercase
for video_ext in "${ALL_VIDEO_EXTENSIONS[@]}"; do
if [[ "$ext" == "${video_ext,,}" ]]; then
return 0
fi
done
return 1
}
convert_video() {
local input_file="$1"
local output_file="${input_file%.*}.${TARGET_FORMAT}"
# Skip if output already exists
if [[ -f "$output_file" ]]; then
log "Skipping '$input_file': output '$output_file' already exists"
return 0
fi
log "Converting '$input_file' -> '$output_file'"
local ffmpeg_args=()
ffmpeg_args+=(-hide_banner -loglevel warning -i "$input_file")
if [[ "$TARGET_FORMAT" == "mp4" ]]; then
# H.264 codec for video and AAC for audio (maximum compatibility)
ffmpeg_args+=(-c:v libx264 -crf "$CRF" -preset "$PRESET")
ffmpeg_args+=(-c:a aac -b:a 192k)
ffmpeg_args+=(-movflags +faststart)
elif [[ "$TARGET_FORMAT" == "webm" ]]; then
# VP9 codec for video and Opus for audio
ffmpeg_args+=(-c:v libvpx-vp9 -crf "$CRF" -b:v 0)
ffmpeg_args+=(-c:a libopus -b:a 128k)
fi
ffmpeg_args+=("$output_file")
if ffmpeg "${ffmpeg_args[@]}"; then
log "Successfully converted '$input_file'"
if [[ "$DELETE_ORIGINAL" == true ]]; then
log "Deleting original: '$input_file'"
rm "$input_file"
fi
else
log "Error converting '$input_file'"
[[ -f "$output_file" ]] && rm "$output_file"
return 1
fi
}
process_directory() {
local dir="$1"
local count=0
local failed=0
log "Searching for video files in '$dir'..."
# Build find command dynamically
local find_args=(-type f \()
local first=true
for ext in "${ALL_VIDEO_EXTENSIONS[@]}"; do
if [[ "${ext,,}" != "${TARGET_FORMAT,,}" ]]; then
if [[ "$first" == true ]]; then
first=false
else
find_args+=(-o)
fi
find_args+=(-iname "*.$ext")
fi
done
find_args+=(\) -print0)
while IFS= read -r -d '' file; do
((count++)) || true
if ! convert_video "$file"; then
((failed++)) || true
fi
done < <(find "$dir" "${find_args[@]}" 2>/dev/null)
log "Processed $count video file(s), $failed failed"
if [[ $count -eq 0 ]]; then
log "No video files found in '$dir'"
fi
}
parse_args() {
while getopts ":f:c:p:dh" opt; do
case "$opt" in
f)
TARGET_FORMAT="${OPTARG,,}"
if [[ "$TARGET_FORMAT" != "mp4" && "$TARGET_FORMAT" != "webm" ]]; then
echo "Error: Format must be 'mp4' or 'webm'" >&2
exit 1
fi
;;
c) CRF="$OPTARG" ;;
p) PRESET="$OPTARG" ;;
d) DELETE_ORIGINAL=true ;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires an argument." >&2
usage
exit 1
;;
\?)
echo "Error: Invalid option -$OPTARG" >&2
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
echo "Error: No path specified." >&2
usage
exit 1
fi
TARGET_PATH="$1"
# Set default CRF based on format if not specified
if [[ -z "$CRF" ]]; then
if [[ "$TARGET_FORMAT" == "mp4" ]]; then
CRF=23
else
CRF=30
fi
fi
}
main() {
ensure_ffmpeg
parse_args "$@"
if [[ ! -e "$TARGET_PATH" ]]; then
echo "Error: Path '$TARGET_PATH' does not exist." >&2
exit 1
fi
if [[ -f "$TARGET_PATH" ]]; then
# Single file
if [[ "${TARGET_PATH,,}" == *."$TARGET_FORMAT" ]]; then
log "File '$TARGET_PATH' is already in $TARGET_FORMAT format, skipping."
exit 0
fi
if is_video_file "$TARGET_PATH"; then
convert_video "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is not a recognized video file." >&2
exit 1
fi
elif [[ -d "$TARGET_PATH" ]]; then
process_directory "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is neither a file nor a directory." >&2
exit 1
fi
log "Done!"
}
main "$@"

View File

@ -1,117 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# pdf_to_png.sh (magick-only backend, behaves like pdf_to_image)
#
# Convert one or more PDF files to image files using ImageMagick v7 `magick`.
# Default output format is jpg, but can be changed with -f.
OUTPUT_DIR=""
OUTPUT_FORMAT="jpg"
PDF_FILES=()
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
usage() {
cat << EOF
Usage:
$(basename "$0") [OPTIONS] PDF_FILE [PDF_FILE...]
Convert one or more PDF files to images using ImageMagick 'magick'.
Options:
-o DIR Output directory (default: current directory)
-f FORMAT Output image format (default: jpg)
-h Show this help
Examples:
$(basename "$0") file.pdf
$(basename "$0") -f png file1.pdf file2.pdf
$(basename "$0") -o out -f webp file.pdf
EOF
}
ensure_magick() {
if ! command -v magick > /dev/null 2>&1; then
echo "Error: 'magick' (ImageMagick v7) is not installed or not in PATH." >&2
exit 1
fi
}
parse_args() {
local opt
OUTPUT_DIR=""
OUTPUT_FORMAT="jpg"
PDF_FILES=()
while getopts ":o:f:h" opt; do
case "$opt" in
o)
OUTPUT_DIR="$OPTARG"
;;
f)
OUTPUT_FORMAT="$OPTARG"
;;
h)
usage
exit 0
;;
*)
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
echo "Error: at least one PDF file must be specified." >&2
usage
exit 1
fi
PDF_FILES=("$@")
if [[ -z ${OUTPUT_DIR:-} ]]; then
OUTPUT_DIR="${PWD}"
fi
if [[ ! -d $OUTPUT_DIR ]]; then
mkdir -p "$OUTPUT_DIR"
fi
}
convert_pdf() {
local pdf_file="$1"
local base name out_pattern
name="$(basename "$pdf_file")"
base="${name%.*}"
out_pattern="${OUTPUT_DIR%/}/${base}_page-"
log "Converting '$pdf_file' to $OUTPUT_FORMAT using magick -> ${out_pattern}*.${OUTPUT_FORMAT}"
magick -density 300 "$pdf_file" -quality 90 "${out_pattern}%d.${OUTPUT_FORMAT}"
}
main() {
ensure_magick
parse_args "$@"
local pdf
for pdf in "${PDF_FILES[@]}"; do
if [[ ! -f $pdf ]]; then
echo "Warning: '$pdf' is not a regular file, skipping." >&2
continue
fi
convert_pdf "$pdf"
done
log "Done converting PDFs to ${OUTPUT_FORMAT}. Output directory: $OUTPUT_DIR"
}
main "$@"

View File

@ -2,34 +2,21 @@
set -euo pipefail set -euo pipefail
# Source common library
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Re-run with sudo if needed for reading /etc/hosts # Re-run with sudo if needed for reading /etc/hosts
if [[ $EUID -ne 0 ]] && [[ ! -r /etc/hosts ]]; then if [[ $EUID -ne 0 ]] && [[ ! -r /etc/hosts ]]; then
exec sudo -E bash "$0" "$@" exec sudo -E bash "$0" "$@"
fi fi
WORK_DIR="${HOME}/.cache/android-adblock" WORK_DIR="${HOME}/.cache/android-adblock"
ensure_dir "$WORK_DIR"
mkdir -p "$WORK_DIR"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S%z')]${NC} $*"
}
error() {
echo -e "${RED}[ERROR]${NC} $*" >&2
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $*"
}
die() { die() {
error "$@" echo "[ERROR] $*" >&2
exit 1 exit 1
} }

View File

@ -1,194 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Wrapper for backward compatibility - converts to mp4
set -euo pipefail # See convert_video.sh for full options
exec "$(dirname "$(readlink -f "$0")")/convert_video.sh" -f mp4 "$@"
# to_mp4.sh
#
# Convert video files (non-mp4) to mp4 format using ffmpeg.
# Accepts either a single video file or a directory (will recurse into subdirectories).
# Video extensions to search for (excluding mp4 since that's our target)
VIDEO_EXTENSIONS=("webm" "mkv" "avi" "mov" "wmv" "flv" "m4v" "mpg" "mpeg" "3gp" "ogv" "ts" "mts" "m2ts" "vob" "asf" "rm" "rmvb" "divx" "f4v")
# Conversion settings
CRF=23 # Quality (0-51, lower = better quality, 18-28 is reasonable for H.264)
PRESET="medium" # Encoding speed: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
DELETE_ORIGINAL=false
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
usage() {
cat <<EOF
Usage:
$(basename "$0") [OPTIONS] PATH
Convert video files to mp4 format using ffmpeg.
PATH can be a single video file or a directory (will recurse into subdirectories).
Options:
-c CRF Quality level 0-51 (default: 23, lower = better quality)
-p PRESET Encoding preset (default: medium)
Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
-d Delete original file after successful conversion
-h Show this help
Examples:
$(basename "$0") video.webm
$(basename "$0") /path/to/videos/
$(basename "$0") -c 20 -d /path/to/videos/
$(basename "$0") -p slow -c 18 movie.mkv
EOF
}
ensure_ffmpeg() {
if ! command -v ffmpeg >/dev/null 2>&1; then
echo "Error: 'ffmpeg' is not installed or not in PATH." >&2
exit 1
fi
}
is_video_file() {
local file="$1"
local ext="${file##*.}"
ext="${ext,,}" # lowercase
for video_ext in "${VIDEO_EXTENSIONS[@]}"; do
if [[ "$ext" == "${video_ext,,}" ]]; then
return 0
fi
done
return 1
}
convert_to_mp4() {
local input_file="$1"
local output_file="${input_file%.*}.mp4"
# Skip if output already exists
if [[ -f "$output_file" ]]; then
log "Skipping '$input_file': output '$output_file' already exists"
return 0
fi
log "Converting '$input_file' -> '$output_file'"
# Use H.264 codec for video and AAC for audio (maximum compatibility)
if ffmpeg -hide_banner -loglevel warning -i "$input_file" \
-c:v libx264 -crf "$CRF" -preset "$PRESET" \
-c:a aac -b:a 192k \
-movflags +faststart \
"$output_file"; then
log "Successfully converted '$input_file'"
if [[ "$DELETE_ORIGINAL" == true ]]; then
log "Deleting original: '$input_file'"
rm "$input_file"
fi
else
log "Error converting '$input_file'"
# Remove partial output file if it exists
[[ -f "$output_file" ]] && rm "$output_file"
return 1
fi
}
process_directory() {
local dir="$1"
local count=0
local failed=0
log "Searching for video files in '$dir'..."
# Find all video files (case-insensitive)
while IFS= read -r -d '' file; do
# Skip mp4 files
if [[ "${file,,}" == *.mp4 ]]; then
continue
fi
((count++)) || true
if ! convert_to_mp4 "$file"; then
((failed++)) || true
fi
done < <(find "$dir" -type f \( -iname "*.webm" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \
-o -iname "*.wmv" -o -iname "*.flv" -o -iname "*.m4v" -o -iname "*.mpg" -o -iname "*.mpeg" \
-o -iname "*.3gp" -o -iname "*.ogv" -o -iname "*.ts" -o -iname "*.mts" -o -iname "*.m2ts" \
-o -iname "*.vob" -o -iname "*.asf" -o -iname "*.rm" -o -iname "*.rmvb" -o -iname "*.divx" \
-o -iname "*.f4v" \) -print0 2>/dev/null)
log "Processed $count video file(s), $failed failed"
if [[ $count -eq 0 ]]; then
log "No video files found in '$dir'"
fi
}
parse_args() {
while getopts ":c:p:dh" opt; do
case "$opt" in
c) CRF="$OPTARG" ;;
p) PRESET="$OPTARG" ;;
d) DELETE_ORIGINAL=true ;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires an argument." >&2
usage
exit 1
;;
\?)
echo "Error: Invalid option -$OPTARG" >&2
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
echo "Error: No path specified." >&2
usage
exit 1
fi
TARGET_PATH="$1"
}
main() {
ensure_ffmpeg
parse_args "$@"
if [[ ! -e "$TARGET_PATH" ]]; then
echo "Error: Path '$TARGET_PATH' does not exist." >&2
exit 1
fi
if [[ -f "$TARGET_PATH" ]]; then
# Single file
if [[ "${TARGET_PATH,,}" == *.mp4 ]]; then
log "File '$TARGET_PATH' is already in mp4 format, skipping."
exit 0
fi
if is_video_file "$TARGET_PATH"; then
convert_to_mp4 "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is not a recognized video file." >&2
exit 1
fi
elif [[ -d "$TARGET_PATH" ]]; then
# Directory
process_directory "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is neither a file nor a directory." >&2
exit 1
fi
log "Done!"
}
main "$@"

View File

@ -1,206 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Wrapper for backward compatibility - converts to webm
set -euo pipefail # See convert_video.sh for full options
exec "$(dirname "$(readlink -f "$0")")/convert_video.sh" -f webm "$@"
# to_webm.sh
#
# Convert video files (non-webm) to webm format using ffmpeg.
# Accepts either a single video file or a directory (will recurse into subdirectories).
# Video extensions to search for (excluding webm since that's our target)
VIDEO_EXTENSIONS=("mp4" "mkv" "avi" "mov" "wmv" "flv" "m4v" "mpg" "mpeg" "3gp" "ogv" "ts" "mts" "m2ts" "vob" "asf" "rm" "rmvb" "divx" "f4v")
# Conversion settings
CRF=30 # Quality (0-63, lower = better quality, 23-30 is reasonable)
PRESET="medium" # Encoding speed: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
DELETE_ORIGINAL=false
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
usage() {
cat <<EOF
Usage:
$(basename "$0") [OPTIONS] PATH
Convert video files to webm format using ffmpeg.
PATH can be a single video file or a directory (will recurse into subdirectories).
Options:
-c CRF Quality level 0-63 (default: 30, lower = better quality)
-p PRESET Encoding preset (default: medium)
Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
-d Delete original file after successful conversion
-h Show this help
Examples:
$(basename "$0") video.mp4
$(basename "$0") /path/to/videos/
$(basename "$0") -c 25 -d /path/to/videos/
$(basename "$0") -p slow -c 20 movie.mkv
EOF
}
ensure_ffmpeg() {
if ! command -v ffmpeg >/dev/null 2>&1; then
echo "Error: 'ffmpeg' is not installed or not in PATH." >&2
exit 1
fi
}
build_extension_pattern() {
# Build a find pattern for video extensions
local pattern=""
for ext in "${VIDEO_EXTENSIONS[@]}"; do
if [[ -n "$pattern" ]]; then
pattern="$pattern -o"
fi
pattern="$pattern -iname *.$ext"
done
echo "$pattern"
}
is_video_file() {
local file="$1"
local ext="${file##*.}"
ext="${ext,,}" # lowercase
for video_ext in "${VIDEO_EXTENSIONS[@]}"; do
if [[ "$ext" == "${video_ext,,}" ]]; then
return 0
fi
done
return 1
}
convert_to_webm() {
local input_file="$1"
local output_file="${input_file%.*}.webm"
# Skip if output already exists
if [[ -f "$output_file" ]]; then
log "Skipping '$input_file': output '$output_file' already exists"
return 0
fi
log "Converting '$input_file' -> '$output_file'"
# Use VP9 codec for video and Opus for audio (good quality, wide compatibility)
if ffmpeg -hide_banner -loglevel warning -i "$input_file" \
-c:v libvpx-vp9 -crf "$CRF" -b:v 0 \
-c:a libopus -b:a 128k \
-preset "$PRESET" \
"$output_file"; then
log "Successfully converted '$input_file'"
if [[ "$DELETE_ORIGINAL" == true ]]; then
log "Deleting original: '$input_file'"
rm "$input_file"
fi
else
log "Error converting '$input_file'"
# Remove partial output file if it exists
[[ -f "$output_file" ]] && rm "$output_file"
return 1
fi
}
process_directory() {
local dir="$1"
local count=0
local failed=0
log "Searching for video files in '$dir'..."
# Find all video files (case-insensitive)
while IFS= read -r -d '' file; do
# Skip webm files
if [[ "${file,,}" == *.webm ]]; then
continue
fi
((count++)) || true
if ! convert_to_webm "$file"; then
((failed++)) || true
fi
done < <(find "$dir" -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \
-o -iname "*.wmv" -o -iname "*.flv" -o -iname "*.m4v" -o -iname "*.mpg" -o -iname "*.mpeg" \
-o -iname "*.3gp" -o -iname "*.ogv" -o -iname "*.ts" -o -iname "*.mts" -o -iname "*.m2ts" \
-o -iname "*.vob" -o -iname "*.asf" -o -iname "*.rm" -o -iname "*.rmvb" -o -iname "*.divx" \
-o -iname "*.f4v" \) -print0 2>/dev/null)
log "Processed $count video file(s), $failed failed"
if [[ $count -eq 0 ]]; then
log "No video files found in '$dir'"
fi
}
parse_args() {
while getopts ":c:p:dh" opt; do
case "$opt" in
c) CRF="$OPTARG" ;;
p) PRESET="$OPTARG" ;;
d) DELETE_ORIGINAL=true ;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires an argument." >&2
usage
exit 1
;;
\?)
echo "Error: Invalid option -$OPTARG" >&2
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
echo "Error: No path specified." >&2
usage
exit 1
fi
TARGET_PATH="$1"
}
main() {
ensure_ffmpeg
parse_args "$@"
if [[ ! -e "$TARGET_PATH" ]]; then
echo "Error: Path '$TARGET_PATH' does not exist." >&2
exit 1
fi
if [[ -f "$TARGET_PATH" ]]; then
# Single file
if [[ "${TARGET_PATH,,}" == *.webm ]]; then
log "File '$TARGET_PATH' is already in webm format, skipping."
exit 0
fi
if is_video_file "$TARGET_PATH"; then
convert_to_webm "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is not a recognized video file." >&2
exit 1
fi
elif [[ -d "$TARGET_PATH" ]]; then
# Directory
process_directory "$TARGET_PATH"
else
echo "Error: '$TARGET_PATH' is neither a file nor a directory." >&2
exit 1
fi
log "Done!"
}
main "$@"

View File

@ -2,29 +2,21 @@
set -euo pipefail set -euo pipefail
# Source common library
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Re-run with sudo if needed for reading /etc/hosts # Re-run with sudo if needed for reading /etc/hosts
if [[ $EUID -ne 0 ]] && [[ ! -r /etc/hosts ]]; then if [[ $EUID -ne 0 ]] && [[ ! -r /etc/hosts ]]; then
exec sudo -E bash "$0" "$@" exec sudo -E bash "$0" "$@"
fi fi
WORK_DIR="${HOME}/.cache/android-adblock" WORK_DIR="${HOME}/.cache/android-adblock"
mkdir -p "$WORK_DIR" ensure_dir "$WORK_DIR"
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S%z')]${NC} $*"
}
error() {
echo -e "${RED}[ERROR]${NC} $*" >&2
}
die() { die() {
error "$@" echo "[ERROR] $*" >&2
exit 1 exit 1
} }