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
# 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 ! 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 'Fix the remaining problems and retry the commit.\n' >&2
exit 1
@ -72,23 +74,23 @@ fi
# Run jscpd and capture output
# --min-lines 5: minimum 5 lines 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" \
--pattern "**/*.sh" \
--min-lines 5 \
--min-tokens 25 \
--threshold 0 \
--threshold 2 \
--reporters "console" \
--ignore "**/node_modules/**,**/.git/**" \
--ignore "**/node_modules/**,**/.git/**,**/misc/testsAndMisc-bash/**" \
. 2>&1) || jscpd_exit=$?
if [[ ${jscpd_exit:-0} -ne 0 ]]; then
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 'To see all duplicates: %s --pattern "**/*.sh" --min-lines 5 .\n' "$JSCPD_BIN" >&2
exit 1
fi
printf ' ✓ No duplicate code detected\n'
printf ' ✓ Duplication check passed (under 2%% threshold)\n'
printf 'All checks passed. Proceeding with commit.\n'

View File

@ -9,6 +9,11 @@
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
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/compulsive-block"
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)"
# Send notification
if command -v notify-send &>/dev/null; then
notify-send -u critical -t 5000 \
"🚫 $app Blocked" \
"Already opened this hour. Wait until the next hour." \
2>/dev/null || true
fi
# Send notification using common library
notify "🚫 $app Blocked" "Already opened this hour. Wait until the next hour." critical 5000
}
# Get real binary path for an app

View File

@ -9,15 +9,18 @@
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
LOG_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/music-parallelism"
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
# Focus applications - window class names for xdotool detection
# Only apps with VISIBLE WINDOWS should block music
# We use window detection, not process detection, to avoid matching background services
# Override focus apps with extended list for this script
FOCUS_APPS_WINDOWS=(
# IDEs and code editors - match window titles
"Visual Studio Code"
@ -40,13 +43,6 @@ FOCUS_APPS_WINDOWS=(
"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
# These will be killed when focus apps are detected
MUSIC_SERVICES=(
@ -73,38 +69,6 @@ MUSIC_SERVICES=(
"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
find_music_services() {
local found_services=()

View File

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

View File

@ -4,66 +4,17 @@
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"
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
if focus_app=$(is_focus_app_running); then
log_message "BLOCKED: YouTube Music launch prevented (focus app: $focus_app)"
notify-send -u normal -t 3000 "🚫 YouTube Music Blocked" "Focus mode active ($focus_app)" 2>/dev/null || true
log_message "BLOCKED: YouTube Music launch prevented (focus app: $focus_app)" "$LOG_FILE"
notify "🚫 YouTube Music Blocked" "Focus mode active ($focus_app)" normal 3000
exit 1
fi

View File

@ -5,57 +5,32 @@
set -e # Exit on any error
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-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
# Parse interactive/help arguments
parse_interactive_args "$@"
shift "$COMMON_ARGS_SHIFT"
# Function to check and request sudo privileges
check_sudo() {
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 "$@"
# Check for sudo privileges
require_root "$@"
echo "NVIDIA Comprehensive Troubleshooter & GSP Disabler"
echo "=================================================="
echo "Current Date: $(date)"
echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
echo "Original user: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)"
echo "Mode: Interactive (prompts enabled)"
else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Check if nvidia module is loaded
if ! lsmod | grep -q nvidia; then
echo "Warning: NVIDIA module not currently loaded"
echo "Warning: NVIDIA module not currently loaded"
fi
# Create modprobe configuration directory if it doesn't exist
@ -68,7 +43,7 @@ echo "======================================"
mkdir -p "$MODPROBE_DIR"
# Create the configuration file
cat > "$CONFIG_FILE" << EOF
cat >"$CONFIG_FILE" <<EOF
# Disable NVIDIA GSP firmware to prevent Vulkan failures and crashes
# Created by nvidia_troubleshoot.sh on $(date)
options nvidia NVreg_EnableGpuFirmware=0
@ -78,32 +53,32 @@ echo "✓ Configuration written to: $CONFIG_FILE"
# Function to backup file if it exists
backup_file() {
local file="$1"
if [[ -f $file ]]; then
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
echo "✓ Backed up $file"
fi
local file="$1"
if [[ -f $file ]]; then
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
echo "✓ Backed up $file"
fi
}
# Function to add or update xorg.conf for RenderAccel
configure_xorg() {
echo ""
echo "2. Configuring Xorg Settings..."
echo "==============================="
echo ""
echo "2. Configuring Xorg Settings..."
echo "==============================="
XORG_CONF="/etc/X11/xorg.conf"
XORG_CONF_D="/etc/X11/xorg.conf.d"
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
XORG_CONF="/etc/X11/xorg.conf"
XORG_CONF_D="/etc/X11/xorg.conf.d"
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
# Create xorg.conf.d directory if it doesn't exist
mkdir -p "$XORG_CONF_D"
# Create xorg.conf.d directory if it doesn't exist
mkdir -p "$XORG_CONF_D"
# Backup existing xorg.conf if it exists
backup_file "$XORG_CONF"
backup_file "$NVIDIA_CONF"
# Backup existing xorg.conf if it exists
backup_file "$XORG_CONF"
backup_file "$NVIDIA_CONF"
# Create NVIDIA-specific configuration
cat > "$NVIDIA_CONF" << EOF
# Create NVIDIA-specific configuration
cat >"$NVIDIA_CONF" <<EOF
# NVIDIA configuration with RenderAccel disabled
# Created by nvidia_troubleshoot.sh on $(date)
Section "Device"
@ -113,106 +88,106 @@ Section "Device"
EndSection
EOF
echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
echo "✓ Created $NVIDIA_CONF with RenderAccel disabled"
}
# Function to add GCC mismatch workaround
configure_gcc_workaround() {
echo ""
echo "3. Configuring GCC Mismatch Workaround..."
echo "=========================================="
echo ""
echo "3. Configuring GCC Mismatch Workaround..."
echo "=========================================="
local PROFILE_FILE="/etc/profile"
local timestamp
timestamp=$(date)
backup_file "$PROFILE_FILE"
local PROFILE_FILE="/etc/profile"
local timestamp
timestamp=$(date)
backup_file "$PROFILE_FILE"
# Check if IGNORE_CC_MISMATCH is already set
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
{
printf '\n'
printf '# NVIDIA GCC version mismatch workaround\n'
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
printf 'export IGNORE_CC_MISMATCH=1\n'
} >> "$PROFILE_FILE"
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
else
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
fi
# Check if IGNORE_CC_MISMATCH is already set
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
{
printf '\n'
printf '# NVIDIA GCC version mismatch workaround\n'
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
printf 'export IGNORE_CC_MISMATCH=1\n'
} >>"$PROFILE_FILE"
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
else
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
fi
}
# Function to install pyroveil for mesh shader issues
install_pyroveil() {
echo ""
echo "4. Pyroveil Setup for Mesh Shader Issues..."
echo "==========================================="
echo ""
echo "4. Pyroveil Setup for Mesh Shader Issues..."
echo "==========================================="
local user_home="/home/$SUDO_USER"
local pyroveil_dir="$user_home/pyroveil"
local user_home="/home/$SUDO_USER"
local pyroveil_dir="$user_home/pyroveil"
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 ""
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 ""
local install_pyroveil=true
local install_pyroveil=true
if [[ $INTERACTIVE_MODE == "true" ]]; then
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
install_pyroveil=false
fi
else
echo "Auto-installing Pyroveil (use --interactive to prompt)"
fi
if [[ $INTERACTIVE_MODE == "true" ]]; then
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
install_pyroveil=false
fi
else
echo "Auto-installing Pyroveil (use --interactive to prompt)"
fi
if [[ $install_pyroveil == "true" ]]; then
# Check for required dependencies
local missing_deps=()
if [[ $install_pyroveil == "true" ]]; then
# Check for required dependencies
local missing_deps=()
for dep in git cmake ninja gcc; do
if ! command -v "$dep" &> /dev/null; then
missing_deps+=("$dep")
fi
done
for dep in git cmake ninja gcc; do
if ! command -v "$dep" &>/dev/null; then
missing_deps+=("$dep")
fi
done
if [[ ${#missing_deps[@]} -gt 0 ]]; then
echo "Missing dependencies: ${missing_deps[*]}"
echo "Please install them first. On Arch Linux:"
echo "pacman -S base-devel git cmake ninja"
return 1
fi
if [[ ${#missing_deps[@]} -gt 0 ]]; then
echo "Missing dependencies: ${missing_deps[*]}"
echo "Please install them first. On Arch Linux:"
echo "pacman -S base-devel git cmake ninja"
return 1
fi
# Clone and build pyroveil as the original user
echo "Installing Pyroveil to $pyroveil_dir..."
# Clone and build pyroveil as the original user
echo "Installing Pyroveil to $pyroveil_dir..."
if [[ -d $pyroveil_dir ]]; then
echo "Pyroveil directory already exists. Updating..."
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
else
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
fi
if [[ -d $pyroveil_dir ]]; then
echo "Pyroveil directory already exists. Updating..."
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
else
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
fi
sudo -u "$SUDO_USER" bash -c "
sudo -u "$SUDO_USER" bash -c "
cd '$pyroveil_dir'
git submodule update --init
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
ninja -C build install
"
echo "✓ Pyroveil installed successfully"
echo ""
echo "To use Pyroveil with games that have mesh shader issues:"
echo "1. For Final Fantasy VII Rebirth:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
echo ""
echo "2. For Steam games, add to launch options:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
echo ""
echo "Available configs in: $pyroveil_dir/hacks/"
echo "✓ Pyroveil installed successfully"
echo ""
echo "To use Pyroveil with games that have mesh shader issues:"
echo "1. For Final Fantasy VII Rebirth:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
echo ""
echo "2. For Steam games, add to launch options:"
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
echo ""
echo "Available configs in: $pyroveil_dir/hacks/"
# Create a helper script
cat > "$user_home/run-with-pyroveil.sh" << EOF
# Create a helper script
cat >"$user_home/run-with-pyroveil.sh" <<EOF
#!/bin/bash
# Helper script to run games with Pyroveil
# Usage: ./run-with-pyroveil.sh <config-name> <command>
@ -238,88 +213,88 @@ echo "Config file: \$PYROVEIL_CONFIG"
exec "\$@"
EOF
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
chmod +x "$user_home/run-with-pyroveil.sh"
echo "✓ Created helper script: $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"
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
else
echo "Skipping Pyroveil installation"
echo "Note: You can manually install it later for mesh shader issues"
fi
else
echo "Skipping Pyroveil installation"
echo "Note: You can manually install it later for mesh shader issues"
fi
}
# Function to check for kernel parameter modifications
suggest_kernel_params() {
echo ""
echo "5. Kernel Parameter Recommendations..."
echo "====================================="
echo ""
echo "5. Kernel Parameter Recommendations..."
echo "====================================="
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
echo ""
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
echo " (especially with nvidia-96xx drivers):"
echo " → Add 'nopat' to kernel parameters"
echo ""
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
echo " → Consider disabling micro-op cache in BIOS settings"
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
echo " → Helps with severe graphical glitches in Xwayland applications"
echo " → Note: Disabling micro-op cache reduces CPU performance"
echo ""
echo "To add kernel parameters:"
echo "1. Edit /etc/default/grub"
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
echo "4. Reboot"
echo ""
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
echo ""
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
echo " (especially with nvidia-96xx drivers):"
echo " → Add 'nopat' to kernel parameters"
echo ""
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
echo " → Consider disabling micro-op cache in BIOS settings"
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
echo " → Helps with severe graphical glitches in Xwayland applications"
echo " → Note: Disabling micro-op cache reduces CPU performance"
echo ""
echo "To add kernel parameters:"
echo "1. Edit /etc/default/grub"
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
echo "4. Reboot"
echo ""
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
# Check current CPU for micro-op cache relevance
echo ""
echo "CPU Information (for micro-op cache consideration):"
if command -v lscpu &> /dev/null; then
local cpu_info
cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
echo "Current CPU: $cpu_info"
# Check current CPU for micro-op cache relevance
echo ""
echo "CPU Information (for micro-op cache consideration):"
if command -v lscpu &>/dev/null; then
local cpu_info
cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
echo "Current CPU: $cpu_info"
if echo "$cpu_info" | grep -qi "intel"; then
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
elif echo "$cpu_info" | grep -qi "amd"; then
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
fi
fi
if echo "$cpu_info" | grep -qi "intel"; then
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
elif echo "$cpu_info" | grep -qi "amd"; then
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
fi
fi
}
# Function to suggest desktop environment settings
suggest_desktop_settings() {
echo ""
echo "6. Desktop Environment Recommendations..."
echo "========================================"
echo ""
echo "6. Desktop Environment Recommendations..."
echo "========================================"
echo "For fullscreen application freezing/crashing issues:"
echo ""
echo "Enable Display Compositing and Direct fullscreen rendering:"
echo ""
echo "• KDE Plasma:"
echo " System Settings → Display and Monitor → Compositor"
echo " → Enable compositor + Enable direct rendering for fullscreen windows"
echo ""
echo "• GNOME:"
echo " Use Extensions or dconf-editor to enable compositing features"
echo ""
echo "• XFCE:"
echo " Settings → Window Manager Tweaks → Compositor"
echo " → Enable display compositing"
echo ""
echo "• Cinnamon:"
echo " System Settings → Effects → Enable desktop effects"
echo "For fullscreen application freezing/crashing issues:"
echo ""
echo "Enable Display Compositing and Direct fullscreen rendering:"
echo ""
echo "• KDE Plasma:"
echo " System Settings → Display and Monitor → Compositor"
echo " → Enable compositor + Enable direct rendering for fullscreen windows"
echo ""
echo "• GNOME:"
echo " Use Extensions or dconf-editor to enable compositing features"
echo ""
echo "• XFCE:"
echo " Settings → Window Manager Tweaks → Compositor"
echo " → Enable display compositing"
echo ""
echo "• Cinnamon:"
echo " System Settings → Effects → Enable desktop effects"
# Detect current desktop environment
if [[ -n $XDG_CURRENT_DESKTOP ]]; then
echo ""
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
fi
# Detect current desktop environment
if [[ -n $XDG_CURRENT_DESKTOP ]]; then
echo ""
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
fi
}
# Apply all configurations
@ -331,14 +306,14 @@ install_pyroveil
echo ""
echo "7. Regenerating Initramfs..."
echo "============================"
if command -v mkinitcpio &> /dev/null; then
mkinitcpio -P
echo "✓ Initramfs regenerated with mkinitcpio"
elif command -v dracut &> /dev/null; then
dracut --force
echo "✓ Initramfs regenerated with dracut"
if command -v mkinitcpio &>/dev/null; then
mkinitcpio -P
echo "✓ Initramfs regenerated with mkinitcpio"
elif command -v dracut &>/dev/null; then
dracut --force
echo "✓ Initramfs regenerated with dracut"
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
# Display all recommendations
@ -354,7 +329,7 @@ echo "✓ GSP firmware disabled"
echo "✓ RenderAccel disabled in Xorg configuration"
echo "✓ GCC version mismatch workaround added"
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
echo "✓ Pyroveil installed for mesh shader issues"
echo "✓ Pyroveil installed for mesh shader issues"
fi
echo "✓ Initramfs regenerated"
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
# Default to non-interactive mode
INTERACTIVE_MODE=false
# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-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
# Parse interactive/help arguments
parse_interactive_args "$@"
shift "$COMMON_ARGS_SHIFT"
# Function to check and request sudo privileges
check_sudo() {
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 "$@"
# Check for sudo privileges
require_root "$@"
echo "Periodic System Setup - Pacman Wrapper & Hosts File"
echo "==================================================="
echo "Current Date: $(date)"
echo "User: $USER"
echo "Original user: ${SUDO_USER:-$USER}"
echo "Original user: $(get_actual_user)"
if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Mode: Interactive (prompts enabled)"
echo "Mode: Interactive (prompts enabled)"
else
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
echo "Mode: Automatic (auto-yes, use --interactive for prompts)"
fi
# Get the directory where this script is located
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
CONFIG_DIR="$(dirname "$SCRIPT_DIR")"
# Define paths
@ -86,231 +60,231 @@ TEMPLATE_LOGROTATE="$LOGROTATE_TEMPLATES/periodic-system-maintenance"
# Function to verify required files exist
verify_files() {
echo ""
echo "1. Verifying Required Files..."
echo "=============================="
echo ""
echo "1. Verifying Required Files..."
echo "=============================="
local missing_files=()
local missing_files=()
if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then
missing_files+=("$PACMAN_WRAPPER_SCRIPT")
fi
if [[ ! -f $PACMAN_WRAPPER_SCRIPT ]]; then
missing_files+=("$PACMAN_WRAPPER_SCRIPT")
fi
if [[ ! -f $PACMAN_WRAPPER_INSTALL ]]; then
missing_files+=("$PACMAN_WRAPPER_INSTALL")
fi
if [[ ! -f $PACMAN_WRAPPER_INSTALL ]]; then
missing_files+=("$PACMAN_WRAPPER_INSTALL")
fi
if [[ ! -f $HOSTS_INSTALL_SCRIPT ]]; then
missing_files+=("$HOSTS_INSTALL_SCRIPT")
fi
if [[ ! -f $HOSTS_INSTALL_SCRIPT ]]; then
missing_files+=("$HOSTS_INSTALL_SCRIPT")
fi
# Check template files as well
for tmpl in \
"$TEMPLATE_MAINT_SCRIPT" \
"$TEMPLATE_HOSTS_MONITOR" \
"$TEMPLATE_BROWSER_WRAPPER" \
"$TEMPLATE_SVC_MAINT" \
"$TEMPLATE_TIMER" \
"$TEMPLATE_STARTUP" \
"$TEMPLATE_HOSTS_SVC" \
"$TEMPLATE_LOGROTATE"; do
if [[ ! -f $tmpl ]]; then
missing_files+=("$tmpl")
fi
done
# Check template files as well
for tmpl in \
"$TEMPLATE_MAINT_SCRIPT" \
"$TEMPLATE_HOSTS_MONITOR" \
"$TEMPLATE_BROWSER_WRAPPER" \
"$TEMPLATE_SVC_MAINT" \
"$TEMPLATE_TIMER" \
"$TEMPLATE_STARTUP" \
"$TEMPLATE_HOSTS_SVC" \
"$TEMPLATE_LOGROTATE"; do
if [[ ! -f $tmpl ]]; then
missing_files+=("$tmpl")
fi
done
if [[ ${#missing_files[@]} -gt 0 ]]; then
echo "Error: The following required files are missing:"
for file in "${missing_files[@]}"; do
echo " - $file"
done
exit 1
fi
if [[ ${#missing_files[@]} -gt 0 ]]; then
echo "Error: The following required files are missing:"
for file in "${missing_files[@]}"; do
echo " - $file"
done
exit 1
fi
echo "✓ All required files found"
echo "✓ All required files found"
}
# Function to create the combined execution script
create_execution_script() {
echo ""
echo "2. Creating Combined Execution Script..."
echo "======================================="
echo ""
echo "2. Creating Combined Execution Script..."
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
sed \
-e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \
-e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_MAINT_SCRIPT" > "$exec_script"
# Install from template with path substitutions
sed \
-e "s|__PACMAN_WRAPPER_INSTALL__|$PACMAN_WRAPPER_INSTALL|g" \
-e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_MAINT_SCRIPT" >"$exec_script"
chmod +x "$exec_script"
echo "✓ Installed execution script from template: $exec_script"
chmod +x "$exec_script"
echo "✓ Installed execution script from template: $exec_script"
}
# Function to create systemd service
create_systemd_service() {
echo ""
echo "3. Creating Systemd Service..."
echo "============================="
echo ""
echo "3. Creating Systemd Service..."
echo "============================="
local service_file="/etc/systemd/system/periodic-system-maintenance.service"
install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
echo "✓ Installed systemd service from template: $service_file"
local service_file="/etc/systemd/system/periodic-system-maintenance.service"
install -m 0644 "$TEMPLATE_SVC_MAINT" "$service_file"
echo "✓ Installed systemd service from template: $service_file"
}
# Function to create systemd timer for hourly execution
create_systemd_timer() {
echo ""
echo "4. Creating Systemd Timer..."
echo "============================"
echo ""
echo "4. Creating Systemd Timer..."
echo "============================"
local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
echo "✓ Installed systemd timer from template: $timer_file"
local timer_file="/etc/systemd/system/periodic-system-maintenance.timer"
install -m 0644 "$TEMPLATE_TIMER" "$timer_file"
echo "✓ Installed systemd timer from template: $timer_file"
}
# Function to create startup service (additional to timer)
create_startup_service() {
echo ""
echo "5. Creating Startup Service..."
echo "=============================="
echo ""
echo "5. Creating Startup Service..."
echo "=============================="
local startup_service="/etc/systemd/system/periodic-system-startup.service"
install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
echo "✓ Installed startup service from template: $startup_service"
local startup_service="/etc/systemd/system/periodic-system-startup.service"
install -m 0644 "$TEMPLATE_STARTUP" "$startup_service"
echo "✓ Installed startup service from template: $startup_service"
}
# Function to create hosts file monitor service
create_hosts_monitor_service() {
echo ""
echo "6. Creating Hosts File Monitor Service..."
echo "========================================"
echo ""
echo "6. Creating Hosts File Monitor Service..."
echo "========================================"
local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
local monitor_script="/usr/local/bin/hosts-file-monitor.sh"
local monitor_service="/etc/systemd/system/hosts-file-monitor.service"
# Install the monitor script from template with substitution
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_HOSTS_MONITOR" > "$monitor_script"
chmod +x "$monitor_script"
echo "✓ Installed hosts monitor script from template: $monitor_script"
# Install the monitor script from template with substitution
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_HOSTS_MONITOR" >"$monitor_script"
chmod +x "$monitor_script"
echo "✓ Installed hosts monitor script from template: $monitor_script"
# Install the systemd service from template
install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
echo "✓ Installed hosts monitor service from template: $monitor_service"
# Install the systemd service from template
install -m 0644 "$TEMPLATE_HOSTS_SVC" "$monitor_service"
echo "✓ Installed hosts monitor service from template: $monitor_service"
}
# Function to install browser pre-exec wrapper and wire common browser names
install_browser_preexec_wrapper() {
echo ""
echo "6.1 Installing Browser Pre-Exec Wrapper..."
echo "========================================="
echo ""
echo "6.1 Installing Browser Pre-Exec Wrapper..."
echo "========================================="
local wrapper="/usr/local/bin/browser-preexec-wrapper"
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_BROWSER_WRAPPER" > "$wrapper"
chmod +x "$wrapper"
echo "✓ Installed wrapper: $wrapper"
local wrapper="/usr/local/bin/browser-preexec-wrapper"
sed -e "s|__HOSTS_INSTALL_SCRIPT__|$HOSTS_INSTALL_SCRIPT|g" \
"$TEMPLATE_BROWSER_WRAPPER" >"$wrapper"
chmod +x "$wrapper"
echo "✓ Installed wrapper: $wrapper"
# Allow passwordless execution of hosts installer for root-only actions
local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
if command -v visudo > /dev/null 2>&1; then
echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" > "$sudoers_file"
chmod 440 "$sudoers_file"
# Validate syntax
visudo -c > /dev/null || echo "Warning: sudoers validation returned non-zero"
echo "✓ Sudoers drop-in created: $sudoers_file"
else
echo "visudo not found; skipping sudoers drop-in"
fi
# Allow passwordless execution of hosts installer for root-only actions
local sudoers_file="/etc/sudoers.d/hosts-install-no-passwd"
if command -v visudo >/dev/null 2>&1; then
echo "${SUDO_USER:-$USER} ALL=(ALL) NOPASSWD: $HOSTS_INSTALL_SCRIPT" >"$sudoers_file"
chmod 440 "$sudoers_file"
# Validate syntax
visudo -c >/dev/null || echo "Warning: sudoers validation returned non-zero"
echo "✓ Sudoers drop-in created: $sudoers_file"
else
echo "visudo not found; skipping sudoers drop-in"
fi
# Create symlinks for common browser commands to the wrapper in /usr/local/bin
# 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")
for b in "${browsers[@]}"; do
local link="/usr/local/bin/$b"
ln -sf "$wrapper" "$link"
done
echo "✓ Symlinked wrapper for common browsers 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.
local browsers=("thorium-browser" "google-chrome" "google-chrome-stable" "chromium" "brave" "brave-browser" "vivaldi-stable" "firefox")
for b in "${browsers[@]}"; do
local link="/usr/local/bin/$b"
ln -sf "$wrapper" "$link"
done
echo "✓ Symlinked wrapper for common browsers in /usr/local/bin"
}
# Function to enable and start services
enable_services() {
echo ""
echo "7. Enabling Services and Timer..."
echo "================================="
echo ""
echo "7. Enabling Services and Timer..."
echo "================================="
# Reload systemd daemon
systemctl daemon-reload
echo "✓ Systemd daemon reloaded"
# Reload systemd daemon
systemctl daemon-reload
echo "✓ Systemd daemon reloaded"
# Enable and start the timer
systemctl enable periodic-system-maintenance.timer
systemctl start periodic-system-maintenance.timer
echo "✓ Timer enabled and started"
# Enable and start the timer
systemctl enable periodic-system-maintenance.timer
systemctl start periodic-system-maintenance.timer
echo "✓ Timer enabled and started"
# Enable startup service (but don't start it now)
systemctl enable periodic-system-startup.service
echo "✓ Startup service enabled"
# Enable startup service (but don't start it now)
systemctl enable periodic-system-startup.service
echo "✓ Startup service enabled"
# Enable hosts file monitor service
systemctl enable hosts-file-monitor.service
systemctl start hosts-file-monitor.service
echo "✓ Hosts file monitor service enabled and started"
# Enable hosts file monitor service
systemctl enable hosts-file-monitor.service
systemctl start hosts-file-monitor.service
echo "✓ Hosts file monitor service enabled and started"
# Show timer status
echo ""
echo "Timer Status:"
systemctl status periodic-system-maintenance.timer --no-pager -l
# Show timer status
echo ""
echo "Timer Status:"
systemctl status periodic-system-maintenance.timer --no-pager -l
echo ""
echo "Hosts Monitor Status:"
systemctl status hosts-file-monitor.service --no-pager -l
echo ""
echo "Hosts Monitor Status:"
systemctl status hosts-file-monitor.service --no-pager -l
echo ""
echo "Next scheduled runs:"
systemctl list-timers periodic-system-maintenance.timer --no-pager
echo ""
echo "Next scheduled runs:"
systemctl list-timers periodic-system-maintenance.timer --no-pager
}
# Function to create log rotation configuration
create_log_rotation() {
echo ""
echo "8. Setting up Log Rotation..."
echo "============================="
echo ""
echo "8. Setting up Log Rotation..."
echo "============================="
local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
echo "✓ Installed log rotation configuration from template: $logrotate_conf"
local logrotate_conf="/etc/logrotate.d/periodic-system-maintenance"
install -m 0644 "$TEMPLATE_LOGROTATE" "$logrotate_conf"
echo "✓ Installed log rotation configuration from template: $logrotate_conf"
}
# Function to run initial execution
run_initial_execution() {
echo ""
echo "9. Running Initial Execution..."
echo "==============================="
echo ""
echo "9. Running Initial Execution..."
echo "==============================="
local run_initial=true
local run_initial=true
if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Would you like to run the system maintenance now to test the setup?"
read -p "Run initial execution? (y/N): " -n 1 -r
echo
if [[ $INTERACTIVE_MODE == "true" ]]; then
echo "Would you like to run the system maintenance now to test the setup?"
read -p "Run initial execution? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
run_initial=false
fi
else
echo "Auto-running initial execution to test the setup (use --interactive to prompt)"
fi
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
run_initial=false
fi
else
echo "Auto-running initial execution to test the setup (use --interactive to prompt)"
fi
if [[ $run_initial == "true" ]]; then
echo "Running initial system maintenance..."
/usr/local/bin/periodic-system-maintenance.sh
echo "✓ Initial execution completed"
else
echo "Skipping initial execution"
fi
if [[ $run_initial == "true" ]]; then
echo "Running initial system maintenance..."
/usr/local/bin/periodic-system-maintenance.sh
echo "✓ Initial execution completed"
else
echo "Skipping initial execution"
fi
}
# Main execution

View File

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

View File

@ -1,194 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
# 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 "$@"
# Wrapper for backward compatibility - converts to mp4
# See convert_video.sh for full options
exec "$(dirname "$(readlink -f "$0")")/convert_video.sh" -f mp4 "$@"

View File

@ -1,206 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
# 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 "$@"
# Wrapper for backward compatibility - converts to webm
# See convert_video.sh for full options
exec "$(dirname "$(readlink -f "$0")")/convert_video.sh" -f webm "$@"

View File

@ -2,29 +2,21 @@
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
if [[ $EUID -ne 0 ]] && [[ ! -r /etc/hosts ]]; then
exec sudo -E bash "$0" "$@"
fi
WORK_DIR="${HOME}/.cache/android-adblock"
mkdir -p "$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
}
ensure_dir "$WORK_DIR"
die() {
error "$@"
echo "[ERROR] $*" >&2
exit 1
}