feat: Enhance Plex library management with API integration and improved scanning functions

This commit is contained in:
Peter Wood
2026-03-07 11:25:20 -05:00
parent c3af84b3e6
commit dc8d35f593

View File

@@ -57,6 +57,8 @@ readonly RESET='\033[0m'
# 🔧 Configuration # 🔧 Configuration
readonly PLEX_SERVICE="plexmediaserver" readonly PLEX_SERVICE="plexmediaserver"
readonly PLEX_PREFS="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Preferences.xml"
readonly PLEX_API_BASE="http://localhost:32400"
readonly SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" readonly SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
readonly LOG_DIR="${SCRIPT_DIR}/../logs" readonly LOG_DIR="${SCRIPT_DIR}/../logs"
@@ -144,6 +146,55 @@ find_scanner() {
return 1 return 1
} }
# 🚀 Run Plex Media Scanner as the plex user with correct environment
# Usage: run_scanner [args...] — runs and returns exit code
run_scanner() {
sudo -u plex \
env LD_LIBRARY_PATH=/usr/lib/plexmediaserver \
PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR="/var/lib/plexmediaserver/Library/Application Support" \
"$SCANNER_PATH" "$@"
}
# 🔑 Get Plex authentication token from Preferences.xml
get_plex_token() {
local token
token=$(sudo grep -oP 'PlexOnlineToken="\K[^"]+' "$PLEX_PREFS" 2>/dev/null)
if [[ -z "$token" ]]; then
log_verbose "Could not read Plex token from Preferences.xml"
return 1
fi
echo "$token"
}
# 📡 List library sections via the Plex API
# Output format: "key|title|type" per line (e.g. "1|Movies|movie")
api_list_sections() {
local token
if ! token=$(get_plex_token); then
return 1
fi
local xml
if ! xml=$(curl -fsS "${PLEX_API_BASE}/library/sections?X-Plex-Token=${token}" 2>/dev/null); then
log_verbose "Plex API request failed"
return 1
fi
# Parse XML: extract key, title, and type from <Directory> elements
echo "$xml" | grep -oP '<Directory[^>]*>' | while IFS= read -r tag; do
local key title type
key=$(echo "$tag" | grep -oP 'key="\K[^"]+')
title=$(echo "$tag" | grep -oP 'title="\K[^"]+')
type=$(echo "$tag" | grep -oP 'type="\K[^"]+')
echo "${key}|${title}|${type}"
done
}
# 📋 Get just the section IDs from the API (one per line)
api_list_section_ids() {
api_list_sections | cut -d'|' -f1
}
# 🏥 Function to check Plex service status # 🏥 Function to check Plex service status
check_plex_service() { check_plex_service() {
log_verbose "Checking Plex service status..." log_verbose "Checking Plex service status..."
@@ -166,38 +217,24 @@ list_libraries() {
return 2 return 2
fi fi
if ! find_scanner; then local sections
return 3 if ! sections=$(api_list_sections) || [[ -z "$sections" ]]; then
print_status "${CROSS}" "Failed to retrieve library sections from Plex API" "${RED}"
return 5
fi 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 ""
echo -e "${BOLD}${CYAN}Available Library Sections:${RESET}" echo -e "${BOLD}${CYAN}Available Library Sections:${RESET}"
echo -e "${DIM}${CYAN}=========================${RESET}" echo -e "${DIM}${CYAN}=========================${RESET}"
# Parse and format the output while IFS='|' read -r key title type; do
echo "$output" | while IFS= read -r line; do [[ -n "$key" ]] || continue
if [[ "$line" =~ ^[[:space:]]*([0-9]+):[[:space:]]*(.+)$ ]]; then printf " ${GREEN}${BOLD}%-4s${RESET} ${WHITE}%-30s${RESET} ${DIM}(%s)${RESET}\n" "${key}:" "$title" "$type"
local section_id="${BASH_REMATCH[1]}" done <<< "$sections"
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 "" echo ""
print_status "${CHECKMARK}" "Library listing completed" "${GREEN}" print_status "${CHECKMARK}" "Library listing completed" "${GREEN}"
return 0 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 # 🔍 Function to validate section ID
@@ -209,10 +246,9 @@ validate_section_id() {
return 1 return 1
fi fi
# Get list of valid section IDs # Get list of valid section IDs via API
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local valid_ids local valid_ids
if valid_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then if valid_ids=$(api_list_section_ids) && [[ -n "$valid_ids" ]]; then
if echo "$valid_ids" | grep -q "^${section_id}$"; then if echo "$valid_ids" | grep -q "^${section_id}$"; then
return 0 return 0
else else
@@ -237,9 +273,7 @@ scan_library() {
return 4 return 4
fi fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-} if run_scanner --scan --section "$section_id" ${VERBOSE:+--verbose}; then
if "$SCANNER_PATH" --scan --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id scan completed" "${GREEN}" print_status "${CHECKMARK}" "Library section $section_id scan completed" "${GREEN}"
return 0 return 0
else else
@@ -250,16 +284,15 @@ scan_library() {
print_status "${ROCKET}" "Scanning all libraries for new media..." "${BLUE}" print_status "${ROCKET}" "Scanning all libraries for new media..." "${BLUE}"
# Get all section IDs and scan each one # Get all section IDs and scan each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then if section_ids=$(api_list_section_ids) && [[ -n "$section_ids" ]]; then
local failed_sections=() local failed_sections=()
while IFS= read -r id; do while IFS= read -r id; do
[[ -n "$id" ]] || continue [[ -n "$id" ]] || continue
print_status "${INFO}" "Scanning section $id..." "${YELLOW}" print_status "${INFO}" "Scanning section $id..." "${YELLOW}"
if "$SCANNER_PATH" --scan --section "$id" ${VERBOSE:+--verbose}; then if run_scanner --scan --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id scanned successfully" "${GREEN}" print_status "${CHECKMARK}" "Section $id scanned successfully" "${GREEN}"
else else
print_status "${CROSS}" "Failed to scan section $id" "${RED}" print_status "${CROSS}" "Failed to scan section $id" "${RED}"
@@ -298,9 +331,7 @@ refresh_library() {
return 4 return 4
fi fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-} if run_scanner --refresh $force_flag --section "$section_id" ${VERBOSE:+--verbose}; then
if "$SCANNER_PATH" --refresh $force_flag --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id metadata refreshed" "${GREEN}" print_status "${CHECKMARK}" "Library section $section_id metadata refreshed" "${GREEN}"
return 0 return 0
else else
@@ -311,16 +342,15 @@ refresh_library() {
print_status "${RECYCLE}" "Refreshing metadata for all libraries..." "${BLUE}" print_status "${RECYCLE}" "Refreshing metadata for all libraries..." "${BLUE}"
# Get all section IDs and refresh each one # Get all section IDs and refresh each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then if section_ids=$(api_list_section_ids) && [[ -n "$section_ids" ]]; then
local failed_sections=() local failed_sections=()
while IFS= read -r id; do while IFS= read -r id; do
[[ -n "$id" ]] || continue [[ -n "$id" ]] || continue
print_status "${INFO}" "Refreshing section $id..." "${YELLOW}" print_status "${INFO}" "Refreshing section $id..." "${YELLOW}"
if "$SCANNER_PATH" --refresh $force_flag --section "$id" ${VERBOSE:+--verbose}; then if run_scanner --refresh $force_flag --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id refreshed successfully" "${GREEN}" print_status "${CHECKMARK}" "Section $id refreshed successfully" "${GREEN}"
else else
print_status "${CROSS}" "Failed to refresh section $id" "${RED}" print_status "${CROSS}" "Failed to refresh section $id" "${RED}"
@@ -359,9 +389,7 @@ analyze_library() {
return 4 return 4
fi fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-} if run_scanner $analyze_flag --section "$section_id" ${VERBOSE:+--verbose}; then
if "$SCANNER_PATH" $analyze_flag --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Library section $section_id analysis completed" "${GREEN}" print_status "${CHECKMARK}" "Library section $section_id analysis completed" "${GREEN}"
return 0 return 0
else else
@@ -372,16 +400,15 @@ analyze_library() {
print_status "${SEARCH}" "Analyzing media in all libraries..." "${BLUE}" print_status "${SEARCH}" "Analyzing media in all libraries..." "${BLUE}"
# Get all section IDs and analyze each one # Get all section IDs and analyze each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then if section_ids=$(api_list_section_ids) && [[ -n "$section_ids" ]]; then
local failed_sections=() local failed_sections=()
while IFS= read -r id; do while IFS= read -r id; do
[[ -n "$id" ]] || continue [[ -n "$id" ]] || continue
print_status "${INFO}" "Analyzing section $id..." "${YELLOW}" print_status "${INFO}" "Analyzing section $id..." "${YELLOW}"
if "$SCANNER_PATH" $analyze_flag --section "$id" ${VERBOSE:+--verbose}; then if run_scanner $analyze_flag --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id analyzed successfully" "${GREEN}" print_status "${CHECKMARK}" "Section $id analyzed successfully" "${GREEN}"
else else
print_status "${CROSS}" "Failed to analyze section $id" "${RED}" print_status "${CROSS}" "Failed to analyze section $id" "${RED}"
@@ -414,9 +441,7 @@ generate_thumbnails() {
return 4 return 4
fi fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-} if run_scanner --generate --section "$section_id" ${VERBOSE:+--verbose}; then
if "$SCANNER_PATH" --generate --section "$section_id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Thumbnails generated for library section $section_id" "${GREEN}" print_status "${CHECKMARK}" "Thumbnails generated for library section $section_id" "${GREEN}"
return 0 return 0
else else
@@ -427,16 +452,15 @@ generate_thumbnails() {
print_status "${SPARKLES}" "Generating thumbnails for all libraries..." "${BLUE}" print_status "${SPARKLES}" "Generating thumbnails for all libraries..." "${BLUE}"
# Get all section IDs and generate thumbnails for each one # Get all section IDs and generate thumbnails for each one
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-}
local section_ids local section_ids
if section_ids=$("$SCANNER_PATH" --list 2>/dev/null | grep -oE '^[[:space:]]*[0-9]+:' | grep -oE '[0-9]+'); then if section_ids=$(api_list_section_ids) && [[ -n "$section_ids" ]]; then
local failed_sections=() local failed_sections=()
while IFS= read -r id; do while IFS= read -r id; do
[[ -n "$id" ]] || continue [[ -n "$id" ]] || continue
print_status "${INFO}" "Generating thumbnails for section $id..." "${YELLOW}" print_status "${INFO}" "Generating thumbnails for section $id..." "${YELLOW}"
if "$SCANNER_PATH" --generate --section "$id" ${VERBOSE:+--verbose}; then if run_scanner --generate --section "$id" ${VERBOSE:+--verbose}; then
print_status "${CHECKMARK}" "Section $id thumbnails generated successfully" "${GREEN}" print_status "${CHECKMARK}" "Section $id thumbnails generated successfully" "${GREEN}"
else else
print_status "${CROSS}" "Failed to generate thumbnails for section $id" "${RED}" print_status "${CROSS}" "Failed to generate thumbnails for section $id" "${RED}"
@@ -473,9 +497,7 @@ show_library_tree() {
return 4 return 4
fi fi
export LD_LIBRARY_PATH=/usr/lib/plexmediaserver:${LD_LIBRARY_PATH:-} if run_scanner --tree --section "$section_id"; then
if "$SCANNER_PATH" --tree --section "$section_id"; then
print_status "${CHECKMARK}" "Tree display completed for library section $section_id" "${GREEN}" print_status "${CHECKMARK}" "Tree display completed for library section $section_id" "${GREEN}"
return 0 return 0
else else