Files
shell/plex/scan-plex-libraries.sh

771 lines
27 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Plex Library Scanner Script
################################################################################
#
# Author: Peter Wood <peter@peterwood.dev>
# Description: Command-line interface for Plex Media Scanner operations.
# Provides library scanning, metadata refresh, and analysis
# capabilities with styled output and error handling.
#
# Features:
# - List all Plex libraries with section IDs
# - Scan specific libraries or all libraries for new media
# - Force refresh metadata for libraries
# - Deep analysis of media files
# - Generate thumbnails and preview images
# - Interactive mode for easy library management
# - Comprehensive error handling and validation
#
# Usage:
# ./scan-plex-libraries.sh list # List all libraries
# ./scan-plex-libraries.sh scan [section_id] # Scan library (all if no ID)
# ./scan-plex-libraries.sh refresh [section_id] # Refresh library metadata
# ./scan-plex-libraries.sh analyze [section_id] # Analyze library media
# ./scan-plex-libraries.sh generate [section_id] # Generate thumbnails
# ./scan-plex-libraries.sh # Interactive mode
#
# Dependencies:
# - Plex Media Server
# - Plex Media Scanner binary
# - systemctl (for service management)
#
# Exit Codes:
# 0 - Success
# 1 - General error
# 2 - Plex service not running
# 3 - Scanner binary not found
# 4 - Invalid section ID
# 5 - Scanner operation failed
#
################################################################################
set -euo pipefail
# 🎨 Color definitions for styled output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly WHITE='\033[1;37m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly RESET='\033[0m'
# 🔧 Configuration
readonly PLEX_SERVICE="plexmediaserver"
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
readonly LOG_DIR="${SCRIPT_DIR}/../logs"
readonly LOG_FILE="${LOG_DIR}/plex-scanner.log"
# Plex Media Scanner paths (common locations)
readonly SCANNER_PATHS=(
"/usr/lib/plexmediaserver/Plex Media Scanner"
"/opt/plex/Plex Media Scanner"
"/usr/local/plex/Plex Media Scanner"
"/Applications/Plex Media Server.app/Contents/MacOS/Plex Media Scanner"
)
# 🎭 ASCII symbols for compatible output
readonly CHECKMARK="[✓]"
readonly CROSS="[✗]"
readonly ROCKET="[>]"
readonly STOP_SIGN="[■]"
readonly RECYCLE="[~]"
readonly INFO="[i]"
readonly HOURGLASS="[*]"
readonly SPARKLES="[*]"
readonly SEARCH="[?]"
readonly LIBRARY="[📚]"
# Global variables
SCANNER_PATH=""
VERBOSE=false
# 🎉 Function to print completion footer
print_footer() {
echo -e "\n${DIM}${CYAN}--- Operation completed ${SPARKLES} ---${RESET}\n"
}
# 🎯 Function to print status with style
print_status() {
local status="$1"
local message="$2"
local color="$3"
echo -e "${color}${BOLD}[${status}]${RESET} ${message}"
# Log to file if log directory exists
if [[ -d "$LOG_DIR" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - [${status}] ${message}" >> "$LOG_FILE"
fi
}
# 📝 Function to log verbose output
log_verbose() {
local message="$1"
if [[ "$VERBOSE" == true ]]; then
echo -e "${DIM}${CYAN}[DEBUG]${RESET} ${message}"
fi
# Always log to file if available
if [[ -d "$LOG_DIR" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - [DEBUG] ${message}" >> "$LOG_FILE"
fi
}
# 🔍 Function to find Plex Media Scanner binary
find_scanner() {
log_verbose "Searching for Plex Media Scanner binary..."
for path in "${SCANNER_PATHS[@]}"; do
if [[ -f "$path" && -x "$path" ]]; then
SCANNER_PATH="$path"
log_verbose "Found scanner at: $path"
return 0
fi
done
# Try to find it in PATH
if command -v "Plex Media Scanner" >/dev/null 2>&1; then
SCANNER_PATH="Plex Media Scanner"
log_verbose "Found scanner in PATH"
return 0
fi
print_status "${CROSS}" "Plex Media Scanner binary not found!" "${RED}"
print_status "${INFO}" "Checked locations:" "${YELLOW}"
for path in "${SCANNER_PATHS[@]}"; do
echo -e " ${DIM}${YELLOW}$path${RESET}"
done
return 1
}
# 🏥 Function to check Plex service status
check_plex_service() {
log_verbose "Checking Plex service status..."
if ! systemctl is-active --quiet "$PLEX_SERVICE"; then
print_status "${CROSS}" "Plex Media Server is not running!" "${RED}"
print_status "${INFO}" "Start Plex with: ${BOLD}sudo systemctl start $PLEX_SERVICE${RESET}" "${YELLOW}"
return 1
fi
log_verbose "Plex service is running"
return 0
}
# 📚 Function to list all library sections
list_libraries() {
print_status "${LIBRARY}" "Listing all Plex library sections..." "${BLUE}"
if ! check_plex_service; then
return 2
fi
if ! find_scanner; then
return 3
fi
# Set library path for Linux
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local output
if output=$("$SCANNER_PATH" --list 2>&1); then
echo ""
echo -e "${BOLD}${CYAN}Available Library Sections:${RESET}"
echo -e "${DIM}${CYAN}=========================${RESET}"
# Parse and format the output
echo "$output" | while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*([0-9]+):[[:space:]]*(.+)$ ]]; then
local section_id="${BASH_REMATCH[1]}"
local section_name="${BASH_REMATCH[2]}"
echo -e "${GREEN}${BOLD} ${section_id}:${RESET} ${WHITE}${section_name}${RESET}"
elif [[ -n "$line" ]]; then
echo -e "${DIM} $line${RESET}"
fi
done
echo ""
print_status "${CHECKMARK}" "Library listing completed" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to list libraries" "${RED}"
echo -e "${DIM}${RED}Error output: $output${RESET}"
return 5
fi
}
# 🔍 Function to validate section ID
validate_section_id() {
local section_id="$1"
if [[ ! "$section_id" =~ ^[0-9]+$ ]]; then
print_status "${CROSS}" "Invalid section ID: $section_id (must be a number)" "${RED}"
return 1
fi
# Get list of valid section IDs
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local valid_ids
if valid_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then
if echo "$valid_ids" | grep -q "^${section_id}$"; then
return 0
else
print_status "${CROSS}" "Section ID $section_id not found" "${RED}"
print_status "${INFO}" "Valid section IDs: $(echo "$valid_ids" | tr '\n' ' ')" "${YELLOW}"
return 1
fi
else
print_status "${CROSS}" "Unable to validate section ID" "${RED}"
return 1
fi
}
# 🔄 Function to scan library for new media
scan_library() {
local section_id="$1"
if [[ -n "$section_id" ]]; then
print_status "${ROCKET}" "Scanning library section $section_id for new media..." "${BLUE}"
if ! validate_section_id "$section_id"; then
return 4
fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
if "$SCANNER_PATH" --scan --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id scan completed" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to scan library section $section_id" "${RED}"
return 5
fi
else
print_status "${ROCKET}" "Scanning all libraries for new media..." "${BLUE}"
# Get all section IDs and scan each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then
local failed_sections=()
while IFS= read -r id; do
[[ -n "$id" ]] || continue
print_status "${INFO}" "Scanning section $id..." "${YELLOW}"
if "$SCANNER_PATH" --scan --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id scanned successfully" "${GREEN}"
else
print_status "${CROSS}" "Failed to scan section $id" "${RED}"
failed_sections+=("$id")
fi
done <<< "$section_ids"
if [[ ${#failed_sections[@]} -eq 0 ]]; then
print_status "${CHECKMARK}" "All libraries scanned successfully" "${GREEN}"
return 0
else
print_status "${CROSS}" "Some libraries failed to scan: ${failed_sections[*]}" "${RED}"
return 5
fi
else
print_status "${CROSS}" "Unable to retrieve library list" "${RED}"
return 5
fi
fi
}
# 🔄 Function to refresh library metadata
refresh_library() {
local section_id="$1"
local force="${2:-false}"
local force_flag=""
if [[ "$force" == "true" ]]; then
force_flag="--force"
fi
if [[ -n "$section_id" ]]; then
print_status "${RECYCLE}" "Refreshing metadata for library section $section_id..." "${BLUE}"
if ! validate_section_id "$section_id"; then
return 4
fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
if "$SCANNER_PATH" --refresh $force_flag --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id metadata refreshed" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to refresh library section $section_id" "${RED}"
return 5
fi
else
print_status "${RECYCLE}" "Refreshing metadata for all libraries..." "${BLUE}"
# Get all section IDs and refresh each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then
local failed_sections=()
while IFS= read -r id; do
[[ -n "$id" ]] || continue
print_status "${INFO}" "Refreshing section $id..." "${YELLOW}"
if "$SCANNER_PATH" --refresh $force_flag --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id refreshed successfully" "${GREEN}"
else
print_status "${CROSS}" "Failed to refresh section $id" "${RED}"
failed_sections+=("$id")
fi
done <<< "$section_ids"
if [[ ${#failed_sections[@]} -eq 0 ]]; then
print_status "${CHECKMARK}" "All libraries refreshed successfully" "${GREEN}"
return 0
else
print_status "${CROSS}" "Some libraries failed to refresh: ${failed_sections[*]}" "${RED}"
return 5
fi
else
print_status "${CROSS}" "Unable to retrieve library list" "${RED}"
return 5
fi
fi
}
# 🔬 Function to analyze library media
analyze_library() {
local section_id="$1"
local deep="${2:-false}"
local analyze_flag="--analyze"
if [[ "$deep" == "true" ]]; then
analyze_flag="--analyze-deeply"
fi
if [[ -n "$section_id" ]]; then
print_status "${SEARCH}" "Analyzing media in library section $section_id..." "${BLUE}"
if ! validate_section_id "$section_id"; then
return 4
fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
if "$SCANNER_PATH" $analyze_flag --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id analysis completed" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to analyze library section $section_id" "${RED}"
return 5
fi
else
print_status "${SEARCH}" "Analyzing media in all libraries..." "${BLUE}"
# Get all section IDs and analyze each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then
local failed_sections=()
while IFS= read -r id; do
[[ -n "$id" ]] || continue
print_status "${INFO}" "Analyzing section $id..." "${YELLOW}"
if "$SCANNER_PATH" $analyze_flag --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id analyzed successfully" "${GREEN}"
else
print_status "${CROSS}" "Failed to analyze section $id" "${RED}"
failed_sections+=("$id")
fi
done <<< "$section_ids"
if [[ ${#failed_sections[@]} -eq 0 ]]; then
print_status "${CHECKMARK}" "All libraries analyzed successfully" "${GREEN}"
return 0
else
print_status "${CROSS}" "Some libraries failed to analyze: ${failed_sections[*]}" "${RED}"
return 5
fi
else
print_status "${CROSS}" "Unable to retrieve library list" "${RED}"
return 5
fi
fi
}
# 🖼️ Function to generate thumbnails and fanart
generate_thumbnails() {
local section_id="$1"
if [[ -n "$section_id" ]]; then
print_status "${SPARKLES}" "Generating thumbnails for library section $section_id..." "${BLUE}"
if ! validate_section_id "$section_id"; then
return 4
fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
if "$SCANNER_PATH" --generate --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Thumbnails generated for library section $section_id" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to generate thumbnails for library section $section_id" "${RED}"
return 5
fi
else
print_status "${SPARKLES}" "Generating thumbnails for all libraries..." "${BLUE}"
# Get all section IDs and generate thumbnails for each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then
local failed_sections=()
while IFS= read -r id; do
[[ -n "$id" ]] || continue
print_status "${INFO}" "Generating thumbnails for section $id..." "${YELLOW}"
if "$SCANNER_PATH" --generate --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id thumbnails generated successfully" "${GREEN}"
else
print_status "${CROSS}" "Failed to generate thumbnails for section $id" "${RED}"
failed_sections+=("$id")
fi
done <<< "$section_ids"
if [[ ${#failed_sections[@]} -eq 0 ]]; then
print_status "${CHECKMARK}" "Thumbnails generated for all libraries" "${GREEN}"
return 0
else
print_status "${CROSS}" "Some libraries failed thumbnail generation: ${failed_sections[*]}" "${RED}"
return 5
fi
else
print_status "${CROSS}" "Unable to retrieve library list" "${RED}"
return 5
fi
fi
}
# 🌳 Function to show library tree structure
show_library_tree() {
local section_id="$1"
if [[ -z "$section_id" ]]; then
print_status "${CROSS}" "Section ID required for tree display" "${RED}"
return 1
fi
print_status "${LIBRARY}" "Showing tree structure for library section $section_id..." "${BLUE}"
if ! validate_section_id "$section_id"; then
return 4
fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
if "$SCANNER_PATH" --tree --section "$section_id"; then
print_status "${CHECKMARK}" "Tree display completed for library section $section_id" "${GREEN}"
return 0
else
print_status "${CROSS}" "Failed to display tree for library section $section_id" "${RED}"
return 5
fi
}
# ❓ Function to show help
show_help() {
echo -e "${BOLD}${CYAN}Plex Library Scanner${RESET}"
echo -e "${DIM}Modern command-line interface for Plex Media Scanner${RESET}"
echo ""
echo -e "${BOLD}Usage:${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}list${RESET} ${DIM}# List all library sections${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}scan${RESET} [section_id] ${DIM}# Scan for new media (all if no ID)${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}refresh${RESET} [section_id] [force] ${DIM}# Refresh metadata (force=true for forced refresh)${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}analyze${RESET} [section_id] [deep] ${DIM}# Analyze media (deep=true for deep analysis)${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}generate${RESET} [section_id] ${DIM}# Generate thumbnails and fanart${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}tree${RESET} <section_id> ${DIM}# Show library tree structure${RESET}"
echo -e " ${SCRIPT_NAME} ${GREEN}interactive${RESET} ${DIM}# Interactive mode${RESET}"
echo ""
echo -e "${BOLD}Options:${RESET}"
echo -e " ${GREEN}-v, --verbose${RESET} ${DIM}# Enable verbose output${RESET}"
echo -e " ${GREEN}-h, --help${RESET} ${DIM}# Show this help message${RESET}"
echo ""
echo -e "${BOLD}Examples:${RESET}"
echo -e " ${DIM}# List all libraries${RESET}"
echo -e " ${SCRIPT_NAME} list"
echo ""
echo -e " ${DIM}# Scan all libraries for new media${RESET}"
echo -e " ${SCRIPT_NAME} scan"
echo ""
echo -e " ${DIM}# Scan specific library (ID 29)${RESET}"
echo -e " ${SCRIPT_NAME} scan 29"
echo ""
echo -e " ${DIM}# Force refresh all libraries${RESET}"
echo -e " ${SCRIPT_NAME} refresh \"\" true"
echo ""
echo -e " ${DIM}# Deep analyze specific library${RESET}"
echo -e " ${SCRIPT_NAME} analyze 29 true"
echo ""
echo -e "${BOLD}Notes:${RESET}"
echo -e " ${DIM}• Requires Plex Media Server to be running${RESET}"
echo -e " ${DIM}• Operations may take considerable time for large libraries${RESET}"
echo -e " ${DIM}• Use verbose mode (-v) for detailed progress information${RESET}"
echo -e " ${DIM}• Log files are written to: ${LOG_FILE}${RESET}"
}
# 🎯 Interactive mode function
interactive_mode() {
echo -e "${BOLD}${CYAN}🎬 Plex Library Scanner - Interactive Mode${RESET}"
echo -e "${DIM}Select an operation to perform:${RESET}"
echo ""
# First, check if Plex is running and scanner is available
if ! check_plex_service; then
return 2
fi
if ! find_scanner; then
return 3
fi
while true; do
echo -e "${BOLD}Available Operations:${RESET}"
echo -e "${GREEN}1)${RESET} List all libraries"
echo -e "${GREEN}2)${RESET} Scan libraries for new media"
echo -e "${GREEN}3)${RESET} Refresh library metadata"
echo -e "${GREEN}4)${RESET} Analyze library media"
echo -e "${GREEN}5)${RESET} Generate thumbnails"
echo -e "${GREEN}6)${RESET} Show library tree"
echo -e "${GREEN}q)${RESET} Quit"
echo ""
read -p "$(echo -e "${BOLD}Choose an option [1-6,q]:${RESET} ")" choice
case "$choice" in
1)
echo ""
list_libraries
;;
2)
echo ""
echo -e "${BOLD}Scan Options:${RESET}"
echo -e "${GREEN}1)${RESET} Scan all libraries"
echo -e "${GREEN}2)${RESET} Scan specific library"
read -p "$(echo -e "${BOLD}Choose [1-2]:${RESET} ")" scan_choice
case "$scan_choice" in
1)
scan_library ""
;;
2)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
scan_library "$section_id"
;;
*)
print_status "${CROSS}" "Invalid choice" "${RED}"
;;
esac
;;
3)
echo ""
echo -e "${BOLD}Refresh Options:${RESET}"
echo -e "${GREEN}1)${RESET} Refresh all libraries"
echo -e "${GREEN}2)${RESET} Refresh specific library"
echo -e "${GREEN}3)${RESET} Force refresh all libraries"
echo -e "${GREEN}4)${RESET} Force refresh specific library"
read -p "$(echo -e "${BOLD}Choose [1-4]:${RESET} ")" refresh_choice
case "$refresh_choice" in
1)
refresh_library "" false
;;
2)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
refresh_library "$section_id" false
;;
3)
refresh_library "" true
;;
4)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
refresh_library "$section_id" true
;;
*)
print_status "${CROSS}" "Invalid choice" "${RED}"
;;
esac
;;
4)
echo ""
echo -e "${BOLD}Analysis Options:${RESET}"
echo -e "${GREEN}1)${RESET} Analyze all libraries"
echo -e "${GREEN}2)${RESET} Analyze specific library"
echo -e "${GREEN}3)${RESET} Deep analyze all libraries"
echo -e "${GREEN}4)${RESET} Deep analyze specific library"
read -p "$(echo -e "${BOLD}Choose [1-4]:${RESET} ")" analyze_choice
case "$analyze_choice" in
1)
analyze_library "" false
;;
2)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
analyze_library "$section_id" false
;;
3)
analyze_library "" true
;;
4)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
analyze_library "$section_id" true
;;
*)
print_status "${CROSS}" "Invalid choice" "${RED}"
;;
esac
;;
5)
echo ""
echo -e "${BOLD}Thumbnail Generation Options:${RESET}"
echo -e "${GREEN}1)${RESET} Generate for all libraries"
echo -e "${GREEN}2)${RESET} Generate for specific library"
read -p "$(echo -e "${BOLD}Choose [1-2]:${RESET} ")" gen_choice
case "$gen_choice" in
1)
generate_thumbnails ""
;;
2)
read -p "$(echo -e "${BOLD}Enter section ID:${RESET} ")" section_id
generate_thumbnails "$section_id"
;;
*)
print_status "${CROSS}" "Invalid choice" "${RED}"
;;
esac
;;
6)
echo ""
read -p "$(echo -e "${BOLD}Enter section ID to show tree:${RESET} ")" section_id
show_library_tree "$section_id"
;;
q|Q)
print_status "${INFO}" "Goodbye!" "${CYAN}"
break
;;
*)
print_status "${CROSS}" "Invalid choice: $choice" "${RED}"
;;
esac
echo ""
echo -e "${DIM}Press Enter to continue...${RESET}"
read -r
echo ""
done
}
# 🚀 Main script logic
main() {
# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"
# Check if running as root
if [[ $EUID -eq 0 ]]; then
print_status "${CROSS}" "Don't run this script as root! Use your regular user account." "${RED}"
exit 1
fi
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
break
;;
esac
done
# Check if no arguments provided or interactive requested
if [[ $# -eq 0 ]] || [[ "${1,,}" == "interactive" ]]; then
interactive_mode
exit $?
fi
# Pre-flight checks
if ! check_plex_service; then
exit 2
fi
if ! find_scanner; then
exit 3
fi
# Handle commands
case "${1,,}" in
"list")
list_libraries
;;
"scan")
local section_id="${2:-}"
scan_library "$section_id"
;;
"refresh")
local section_id="${2:-}"
local force="${3:-false}"
refresh_library "$section_id" "$force"
;;
"analyze"|"analyse")
local section_id="${2:-}"
local deep="${3:-false}"
analyze_library "$section_id" "$deep"
;;
"generate"|"thumbnails")
local section_id="${2:-}"
generate_thumbnails "$section_id"
;;
"tree")
local section_id="$2"
if [[ -z "$section_id" ]]; then
print_status "${CROSS}" "Section ID required for tree command" "${RED}"
exit 1
fi
show_library_tree "$section_id"
;;
"help"|"--help"|"-h")
show_help
;;
*)
print_status "${CROSS}" "Unknown command: ${RED}${BOLD}$1${RESET}" "${RED}"
echo ""
show_help
exit 1
;;
esac
# Print footer for successful operations
print_footer
}
# 🎬 Execute main function with all arguments
main "$@"