mirror of
https://github.com/acedanger/shell.git
synced 2025-12-05 21:40:12 -08:00
Merge branch 'main' of github.com:acedanger/shell
This commit is contained in:
@@ -457,7 +457,8 @@ backup_service() {
|
|||||||
|
|
||||||
# Handle Jellyseerr services with specialized backup function
|
# Handle Jellyseerr services with specialized backup function
|
||||||
if [[ "$service" == jellyseerr_* ]]; then
|
if [[ "$service" == jellyseerr_* ]]; then
|
||||||
return $(backup_jellyseerr_service "$service")
|
backup_jellyseerr_service "$service"
|
||||||
|
return $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Handle special cases for container names
|
# Handle special cases for container names
|
||||||
@@ -867,6 +868,57 @@ generate_summary_report() {
|
|||||||
} >> "$MARKDOWN_LOG"
|
} >> "$MARKDOWN_LOG"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Wrapper function for parallel backup execution
|
||||||
|
backup_service_wrapper() {
|
||||||
|
local service="$1"
|
||||||
|
local results_file="$2"
|
||||||
|
|
||||||
|
# Call the main backup function and capture result
|
||||||
|
if backup_service "$service"; then
|
||||||
|
# Use a lock file to safely write to results file
|
||||||
|
local lock_file="${results_file}.lock"
|
||||||
|
local max_wait=30
|
||||||
|
local wait_count=0
|
||||||
|
|
||||||
|
while [ $wait_count -lt $max_wait ]; do
|
||||||
|
if (set -C; echo $$ > "$lock_file") 2>/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
((wait_count++))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $wait_count -lt $max_wait ]; then
|
||||||
|
echo "SUCCESS:$service" >> "$results_file"
|
||||||
|
rm -f "$lock_file"
|
||||||
|
else
|
||||||
|
# Fallback if lock acquisition fails
|
||||||
|
echo "SUCCESS:$service" >> "$results_file"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Use a lock file to safely write to results file
|
||||||
|
local lock_file="${results_file}.lock"
|
||||||
|
local max_wait=30
|
||||||
|
local wait_count=0
|
||||||
|
|
||||||
|
while [ $wait_count -lt $max_wait ]; do
|
||||||
|
if (set -C; echo $$ > "$lock_file") 2>/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
((wait_count++))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $wait_count -lt $max_wait ]; then
|
||||||
|
echo "FAILED:$service" >> "$results_file"
|
||||||
|
rm -f "$lock_file"
|
||||||
|
else
|
||||||
|
# Fallback if lock acquisition fails
|
||||||
|
echo "FAILED:$service" >> "$results_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Main backup execution function
|
# Main backup execution function
|
||||||
main() {
|
main() {
|
||||||
local script_start_time
|
local script_start_time
|
||||||
@@ -970,10 +1022,10 @@ main() {
|
|||||||
# Collect results
|
# Collect results
|
||||||
while IFS= read -r result; do
|
while IFS= read -r result; do
|
||||||
if [[ "$result" == SUCCESS:* ]]; then
|
if [[ "$result" == SUCCESS:* ]]; then
|
||||||
((success_count++))
|
success_count=$((success_count + 1))
|
||||||
backup_results+=("✓ ${result#SUCCESS:}")
|
backup_results+=("✓ ${result#SUCCESS:}")
|
||||||
elif [[ "$result" == FAILED:* ]]; then
|
elif [[ "$result" == FAILED:* ]]; then
|
||||||
((failed_count++))
|
failed_count=$((failed_count + 1))
|
||||||
backup_results+=("✗ ${result#FAILED:}")
|
backup_results+=("✗ ${result#FAILED:}")
|
||||||
fi
|
fi
|
||||||
done < "$temp_results"
|
done < "$temp_results"
|
||||||
@@ -991,10 +1043,10 @@ main() {
|
|||||||
# Run backups sequentially
|
# Run backups sequentially
|
||||||
for service in "${!MEDIA_SERVICES[@]}"; do
|
for service in "${!MEDIA_SERVICES[@]}"; do
|
||||||
if backup_service "$service"; then
|
if backup_service "$service"; then
|
||||||
((success_count++))
|
success_count=$((success_count + 1))
|
||||||
backup_results+=("✓ $service")
|
backup_results+=("✓ $service")
|
||||||
else
|
else
|
||||||
((failed_count++))
|
failed_count=$((failed_count + 1))
|
||||||
backup_results+=("✗ $service")
|
backup_results+=("✗ $service")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -8,11 +8,42 @@ The completion system provides intelligent tab completion for command-line flags
|
|||||||
|
|
||||||
## Supported Scripts
|
## Supported Scripts
|
||||||
|
|
||||||
### backup-immich.sh
|
### Plex Management Scripts ⭐ **NEW**
|
||||||
|
|
||||||
|
#### plex.sh
|
||||||
|
- `start` - Start Plex Media Server
|
||||||
|
- `stop` - Stop Plex Media Server
|
||||||
|
- `restart` - Restart Plex Media Server
|
||||||
|
- `status` - Show detailed service status
|
||||||
|
- `scan` - Launch library scanner (with sub-commands)
|
||||||
|
- `repair` - Repair database corruption issues
|
||||||
|
- `nuclear` - Nuclear database recovery (last resort)
|
||||||
|
- `help` - Show help message
|
||||||
|
|
||||||
|
#### scan-plex-libraries.sh
|
||||||
|
- `list` - List all library sections
|
||||||
|
- `scan` - Scan for new media (with optional section ID)
|
||||||
|
- `refresh` - Refresh metadata (with optional section ID and force flag)
|
||||||
|
- `analyze` - Analyze media (with optional section ID and deep flag)
|
||||||
|
- `generate` - Generate thumbnails (with optional section ID)
|
||||||
|
- `tree` - Show library tree structure (requires section ID)
|
||||||
|
- `interactive` - Interactive mode
|
||||||
|
- `-v`, `--verbose` - Enable verbose output
|
||||||
|
- `-h`, `--help` - Show help message
|
||||||
|
|
||||||
|
#### Plex Aliases
|
||||||
|
- `plex`, `px` - Main plex.sh script with command completion
|
||||||
|
- `plex-start`, `plex-stop`, `plex-restart`, `plex-status` - Direct service commands
|
||||||
|
- `plex-scan` - Library scanner via plex.sh (supports scanner sub-commands)
|
||||||
|
- `plex-scanner` - Direct access to scan-plex-libraries.sh
|
||||||
|
|
||||||
|
### Backup Scripts
|
||||||
|
|
||||||
|
#### backup-immich.sh
|
||||||
|
|
||||||
- `--help`, `-h` - Show help message
|
- `--help`, `-h` - Show help message
|
||||||
- `--dry-run` - Preview backup without executing
|
- `--dry-run` - Preview backup without executing
|
||||||
- `--no-upload` - Skip B2 upload (local backup only)
|
- `--no-upload` - Skip B2 upload (local backup only)
|
||||||
- `--verbose` - Enable verbose logging
|
- `--verbose` - Enable verbose logging
|
||||||
|
|
||||||
### backup-plex.sh
|
### backup-plex.sh
|
||||||
@@ -73,6 +104,16 @@ autoload -U compinit && compinit -u
|
|||||||
if [ -f "$HOME/shell/completions/backup-scripts-completion.bash" ]; then
|
if [ -f "$HOME/shell/completions/backup-scripts-completion.bash" ]; then
|
||||||
source "$HOME/shell/completions/backup-scripts-completion.bash"
|
source "$HOME/shell/completions/backup-scripts-completion.bash"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Load Plex scripts completion
|
||||||
|
if [ -f "$HOME/shell/completions/plex-scripts-completion.bash" ]; then
|
||||||
|
source "$HOME/shell/completions/plex-scripts-completion.bash"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load environment backup completion
|
||||||
|
if [ -f "$HOME/shell/completions/env-backup-completion.bash" ]; then
|
||||||
|
source "$HOME/shell/completions/env-backup-completion.bash"
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
@@ -89,6 +130,40 @@ $ backup-immich.sh --d<TAB>
|
|||||||
$ backup-immich.sh --dry-run
|
$ backup-immich.sh --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Plex Script Completion ⭐ **NEW**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Main plex.sh command completion
|
||||||
|
$ plex.sh <TAB>
|
||||||
|
start stop restart status scan repair nuclear help
|
||||||
|
|
||||||
|
# Scanner sub-command completion when using scan
|
||||||
|
$ plex.sh scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive -v --verbose -h --help
|
||||||
|
|
||||||
|
# Direct scanner script completion
|
||||||
|
$ scan-plex-libraries.sh <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
# Scanner options completion
|
||||||
|
$ scan-plex-libraries.sh -<TAB>
|
||||||
|
-v --verbose -h --help
|
||||||
|
|
||||||
|
# Boolean flag completion for refresh and analyze
|
||||||
|
$ scan-plex-libraries.sh refresh 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
$ scan-plex-libraries.sh analyze 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
# Alias completion works too
|
||||||
|
$ plex-scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
$ plex-scanner <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
```
|
||||||
|
|
||||||
### Webhook URL Completion
|
### Webhook URL Completion
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -112,7 +187,7 @@ backup-immich.sh --<TAB>
|
|||||||
# Relative path
|
# Relative path
|
||||||
./backup-immich.sh --<TAB>
|
./backup-immich.sh --<TAB>
|
||||||
|
|
||||||
# Absolute path
|
# Absolute path
|
||||||
/home/acedanger/shell/immich/backup-immich.sh --<TAB>
|
/home/acedanger/shell/immich/backup-immich.sh --<TAB>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -201,9 +276,9 @@ _script_name_completion() {
|
|||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
opts="--option1 --option2 --option3"
|
opts="--option1 --option2 --option3"
|
||||||
|
|
||||||
# Handle special cases
|
# Handle special cases
|
||||||
case "${prev}" in
|
case "${prev}" in
|
||||||
--special-option)
|
--special-option)
|
||||||
@@ -211,7 +286,7 @@ _script_name_completion() {
|
|||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Standard flag completion
|
# Standard flag completion
|
||||||
if [[ ${cur} == -* ]]; then
|
if [[ ${cur} == -* ]]; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
|||||||
273
completions/plex-completion-implementation.md
Normal file
273
completions/plex-completion-implementation.md
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
# Plex Scripts Bash Completion Implementation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Implemented comprehensive bash completion for all Plex-related scripts to provide intelligent tab completion functionality. The completion system supports both the main `plex.sh` script and the new `scan-plex-libraries.sh` scanner script, along with all related aliases.
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### New Completion Script
|
||||||
|
|
||||||
|
**File**: `/home/acedanger/shell/completions/plex-scripts-completion.bash`
|
||||||
|
|
||||||
|
**Features Implemented**:
|
||||||
|
- ✅ **plex.sh Command Completion**: Complete main commands (start, stop, restart, status, scan, repair, nuclear, help)
|
||||||
|
- ✅ **Scanner Sub-command Completion**: When using `plex.sh scan`, completes with scanner commands
|
||||||
|
- ✅ **Direct Scanner Completion**: Complete commands for `scan-plex-libraries.sh`
|
||||||
|
- ✅ **Option Completion**: Complete flags like `-v`, `--verbose`, `-h`, `--help`
|
||||||
|
- ✅ **Boolean Flag Completion**: For `refresh` and `analyze` commands, completes with `true`/`false`
|
||||||
|
- ✅ **Alias Support**: Completion for all plex-related aliases
|
||||||
|
- ✅ **Path-based Completion**: Works with relative and absolute paths
|
||||||
|
- ✅ **Advanced Features**: Optional library section ID completion (with timeout protection)
|
||||||
|
|
||||||
|
### Supported Scripts and Aliases
|
||||||
|
|
||||||
|
#### Main Scripts
|
||||||
|
- `plex.sh` - Main Plex management script
|
||||||
|
- `scan-plex-libraries.sh` - Library scanner script
|
||||||
|
- `./plex.sh` - Relative path execution
|
||||||
|
- `/home/acedanger/shell/plex/plex.sh` - Absolute path execution
|
||||||
|
|
||||||
|
#### Aliases
|
||||||
|
- `plex` - Main plex script
|
||||||
|
- `px` - Quick shortcut for plex script
|
||||||
|
- `plex-start` - Direct start command
|
||||||
|
- `plex-stop` - Direct stop command
|
||||||
|
- `plex-restart` - Direct restart command
|
||||||
|
- `plex-status` - Direct status command
|
||||||
|
- `plex-scan` - Scanner via plex.sh
|
||||||
|
- `plex-scanner` - Direct scanner access
|
||||||
|
|
||||||
|
### Completion Examples
|
||||||
|
|
||||||
|
#### plex.sh Commands
|
||||||
|
```bash
|
||||||
|
$ plex.sh <TAB>
|
||||||
|
start stop restart status scan repair nuclear help
|
||||||
|
|
||||||
|
$ plex.sh s<TAB>
|
||||||
|
start status scan
|
||||||
|
|
||||||
|
$ plex.sh scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive -v --verbose -h --help
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scanner Commands
|
||||||
|
```bash
|
||||||
|
$ scan-plex-libraries.sh <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
$ scan-plex-libraries.sh -<TAB>
|
||||||
|
-v --verbose -h --help
|
||||||
|
|
||||||
|
$ scan-plex-libraries.sh refresh 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
$ scan-plex-libraries.sh analyze 29 <TAB>
|
||||||
|
true false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Alias Completion
|
||||||
|
```bash
|
||||||
|
$ plex-scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
$ plex-scanner <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation Integration
|
||||||
|
|
||||||
|
### Setup Scripts Updated
|
||||||
|
|
||||||
|
#### bootstrap.sh
|
||||||
|
```bash
|
||||||
|
# Added plex completion to executable permissions
|
||||||
|
chmod +x "$DOTFILES_DIR/completions/plex-scripts-completion.bash" 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### setup.sh
|
||||||
|
```bash
|
||||||
|
# Enhanced completion installation section
|
||||||
|
# - Installs backup, plex, and environment backup completions
|
||||||
|
# - Creates ~/.local/share/bash-completion/completions/ directory
|
||||||
|
# - Copies and makes completion scripts executable
|
||||||
|
```
|
||||||
|
|
||||||
|
#### .zshrc
|
||||||
|
```bash
|
||||||
|
# Added plex completion loading
|
||||||
|
if [ -f "$HOME/shell/completions/plex-scripts-completion.bash" ]; then
|
||||||
|
source "$HOME/shell/completions/plex-scripts-completion.bash"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Installation
|
||||||
|
|
||||||
|
For immediate testing or manual setup:
|
||||||
|
```bash
|
||||||
|
# Enable bash completion in zsh
|
||||||
|
autoload -U +X bashcompinit && bashcompinit
|
||||||
|
autoload -U compinit && compinit -u
|
||||||
|
|
||||||
|
# Source the completion script
|
||||||
|
source /home/acedanger/shell/completions/plex-scripts-completion.bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
### Context-Aware Completion
|
||||||
|
|
||||||
|
The completion system is context-aware and provides different suggestions based on:
|
||||||
|
- **Command Position**: Different completions for first argument vs subsequent arguments
|
||||||
|
- **Previous Word**: When previous word is specific command, shows relevant options
|
||||||
|
- **Script Type**: Different behavior for main plex.sh vs scanner script
|
||||||
|
|
||||||
|
### Intelligent Boolean Completion
|
||||||
|
|
||||||
|
For commands that accept boolean flags:
|
||||||
|
```bash
|
||||||
|
# Refresh with force flag
|
||||||
|
$ scan-plex-libraries.sh refresh 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
# Analyze with deep flag
|
||||||
|
$ scan-plex-libraries.sh analyze 29 <TAB>
|
||||||
|
true false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional Section ID Completion
|
||||||
|
|
||||||
|
The completion script includes an advanced feature (commented out by default) that can:
|
||||||
|
- Connect to the running Plex server
|
||||||
|
- Retrieve actual library section IDs
|
||||||
|
- Provide them as completion options
|
||||||
|
- Include timeout protection to prevent hanging
|
||||||
|
|
||||||
|
To enable:
|
||||||
|
```bash
|
||||||
|
# Uncomment this line in the completion script:
|
||||||
|
# complete -F _plex_scanner_with_sections plex-scanner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Helper Functions
|
||||||
|
|
||||||
|
#### _plex_list_sections
|
||||||
|
Manual function to list available library sections:
|
||||||
|
```bash
|
||||||
|
$ _plex_list_sections
|
||||||
|
Available Plex library sections:
|
||||||
|
29: Movies
|
||||||
|
30: TV Shows
|
||||||
|
31: Music
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling and Safety
|
||||||
|
|
||||||
|
### Timeout Protection
|
||||||
|
- Advanced section ID completion includes 3-second timeout
|
||||||
|
- Prevents hanging if Plex server is unresponsive
|
||||||
|
- Gracefully falls back to basic completion
|
||||||
|
|
||||||
|
### Service Validation
|
||||||
|
- Checks if completion functions exist before calling
|
||||||
|
- Handles missing scripts gracefully
|
||||||
|
- Provides meaningful fallbacks
|
||||||
|
|
||||||
|
### Cross-Shell Compatibility
|
||||||
|
- Works in both bash and zsh
|
||||||
|
- Uses bash completion compatibility mode in zsh
|
||||||
|
- Handles shell-specific export differences
|
||||||
|
|
||||||
|
## Testing and Validation
|
||||||
|
|
||||||
|
### Manual Testing
|
||||||
|
```bash
|
||||||
|
# Test basic completion
|
||||||
|
source /home/acedanger/shell/completions/plex-scripts-completion.bash
|
||||||
|
|
||||||
|
# Test with specific commands
|
||||||
|
complete -p plex.sh
|
||||||
|
complete -p scan-plex-libraries.sh
|
||||||
|
complete -p plex-scan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Testing
|
||||||
|
- Completion works after setup script execution
|
||||||
|
- Aliases receive appropriate completion
|
||||||
|
- Path-based execution maintains completion
|
||||||
|
- No conflicts with existing backup script completion
|
||||||
|
|
||||||
|
## Documentation Updates
|
||||||
|
|
||||||
|
### Updated Files
|
||||||
|
1. **completions/README.md**: Added comprehensive Plex completion documentation
|
||||||
|
2. **completions/plex-scripts-completion.bash**: New completion script
|
||||||
|
3. **setup/setup.sh**: Enhanced completion installation
|
||||||
|
4. **setup/bootstrap.sh**: Added plex completion to executable permissions
|
||||||
|
5. **dotfiles/.zshrc**: Added plex completion loading
|
||||||
|
|
||||||
|
### Documentation Sections Added
|
||||||
|
- Plex Management Scripts section in completions README
|
||||||
|
- Usage examples with all plex commands
|
||||||
|
- Installation instructions including manual setup
|
||||||
|
- Troubleshooting information
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
### User Experience
|
||||||
|
- **Faster Command Entry**: Tab completion reduces typing
|
||||||
|
- **Discovery**: Users can discover available commands via tab
|
||||||
|
- **Error Prevention**: Reduces command typos and invalid options
|
||||||
|
- **Consistency**: Same completion experience across all scripts
|
||||||
|
|
||||||
|
### Developer Experience
|
||||||
|
- **Maintainable**: Completion logic is well-documented and modular
|
||||||
|
- **Extensible**: Easy to add new commands or options
|
||||||
|
- **Robust**: Handles edge cases and errors gracefully
|
||||||
|
- **Standard**: Follows bash completion best practices
|
||||||
|
|
||||||
|
### Integration Benefits
|
||||||
|
- **Seamless**: Works with existing alias system
|
||||||
|
- **Automatic**: Installed by setup scripts
|
||||||
|
- **Compatible**: Works with both direct script calls and aliases
|
||||||
|
- **Flexible**: Supports multiple invocation methods
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Potential Improvements
|
||||||
|
1. **Dynamic Section IDs**: Enable real-time library section completion
|
||||||
|
2. **File Path Completion**: For commands that accept file paths
|
||||||
|
3. **History-based Completion**: Remember frequently used section IDs
|
||||||
|
4. **Custom Completions**: User-configurable completion preferences
|
||||||
|
5. **Performance Optimization**: Cache completion data for faster response
|
||||||
|
|
||||||
|
### Extensibility Points
|
||||||
|
- Easy to add new commands to existing completion functions
|
||||||
|
- Modular design allows for script-specific completion logic
|
||||||
|
- Helper functions can be extended for more intelligence
|
||||||
|
- Integration points for future plex-related scripts
|
||||||
|
|
||||||
|
## Best Practices Followed
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- **Modular Functions**: Separate completion functions for each script
|
||||||
|
- **Error Handling**: Graceful fallbacks and error prevention
|
||||||
|
- **Documentation**: Comprehensive inline comments
|
||||||
|
- **Consistency**: Follows existing completion script patterns
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- **Minimal Overhead**: Fast completion without unnecessary processing
|
||||||
|
- **Timeout Protection**: Prevents blocking on slow operations
|
||||||
|
- **Efficient Logic**: Optimized completion tree traversal
|
||||||
|
- **Resource Conscious**: Minimal memory and CPU usage
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
- **Shell Agnostic**: Works in bash and zsh
|
||||||
|
- **Path Independent**: Handles various script invocation methods
|
||||||
|
- **Version Safe**: Compatible with different bash completion versions
|
||||||
|
- **System Independent**: Works across different Linux distributions
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Plex scripts bash completion implementation provides a comprehensive, user-friendly tab completion system that enhances the command-line experience for all Plex-related operations. The system is well-integrated with the existing shell setup, follows best practices, and provides room for future enhancements while maintaining backward compatibility.
|
||||||
220
completions/plex-scripts-completion.bash
Executable file
220
completions/plex-scripts-completion.bash
Executable file
@@ -0,0 +1,220 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Bash completion for Plex-related scripts
|
||||||
|
# Author: Peter Wood <peter@peterwood.dev>
|
||||||
|
#
|
||||||
|
# This file provides intelligent tab completion for:
|
||||||
|
# - plex.sh (main Plex management script)
|
||||||
|
# - scan-plex-libraries.sh (library scanner script)
|
||||||
|
# - plex-* aliases
|
||||||
|
#
|
||||||
|
# Installation:
|
||||||
|
# Source this file in your bash/zsh configuration or place it in:
|
||||||
|
# ~/.local/share/bash-completion/completions/
|
||||||
|
|
||||||
|
# Completion function for plex.sh
|
||||||
|
_plex_sh_completion() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
# Available commands for plex.sh
|
||||||
|
local commands="start stop restart status scan repair nuclear help"
|
||||||
|
|
||||||
|
# If we're completing the first argument (command)
|
||||||
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If previous word was 'scan', complete with scanner options
|
||||||
|
if [[ "$prev" == "scan" ]]; then
|
||||||
|
local scanner_commands="list scan refresh analyze generate tree interactive"
|
||||||
|
local scanner_options="-v --verbose -h --help"
|
||||||
|
COMPREPLY=($(compgen -W "$scanner_commands $scanner_options" -- "$cur"))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For other commands, no additional completion needed
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Completion function for scan-plex-libraries.sh
|
||||||
|
_scan_plex_libraries_completion() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
# Available commands for scan-plex-libraries.sh
|
||||||
|
local commands="list scan refresh analyze generate tree interactive"
|
||||||
|
local options="-v --verbose -h --help"
|
||||||
|
|
||||||
|
# If we're completing the first argument
|
||||||
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
||||||
|
# If it starts with -, show options
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$options" -- "$cur"))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle specific command completions
|
||||||
|
case "${COMP_WORDS[1]}" in
|
||||||
|
refresh)
|
||||||
|
# For refresh command: refresh [section_id] [force]
|
||||||
|
if [[ ${COMP_CWORD} -eq 3 ]]; then
|
||||||
|
# Third argument can be 'true' or 'false' for force flag
|
||||||
|
COMPREPLY=($(compgen -W "true false" -- "$cur"))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
analyze|analyse)
|
||||||
|
# For analyze command: analyze [section_id] [deep]
|
||||||
|
if [[ ${COMP_CWORD} -eq 3 ]]; then
|
||||||
|
# Third argument can be 'true' or 'false' for deep flag
|
||||||
|
COMPREPLY=($(compgen -W "true false" -- "$cur"))
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
tree)
|
||||||
|
# tree command requires section_id but we can't predict them
|
||||||
|
# Could potentially call the script to get section IDs but that's expensive
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Completion function for generic plex aliases that might pass through to plex.sh
|
||||||
|
_plex_alias_completion() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
|
# For plex-scan alias, complete with scanner commands
|
||||||
|
if [[ "${COMP_WORDS[0]}" == *"plex-scan"* ]]; then
|
||||||
|
local scanner_commands="list scan refresh analyze generate tree interactive"
|
||||||
|
local scanner_options="-v --verbose -h --help"
|
||||||
|
|
||||||
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
||||||
|
if [[ "$cur" == -* ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "$scanner_options" -- "$cur"))
|
||||||
|
else
|
||||||
|
COMPREPLY=($(compgen -W "$scanner_commands" -- "$cur"))
|
||||||
|
fi
|
||||||
|
elif [[ ${COMP_CWORD} -eq 2 && "${COMP_WORDS[1]}" == "refresh" ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "true false" -- "$cur"))
|
||||||
|
elif [[ ${COMP_CWORD} -eq 2 && ("${COMP_WORDS[1]}" == "analyze" || "${COMP_WORDS[1]}" == "analyse") ]]; then
|
||||||
|
COMPREPLY=($(compgen -W "true false" -- "$cur"))
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For plex-scanner alias, use the full scanner completion
|
||||||
|
if [[ "${COMP_WORDS[0]}" == *"plex-scanner"* ]]; then
|
||||||
|
_scan_plex_libraries_completion
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For other plex aliases, just complete with main plex.sh commands
|
||||||
|
local commands="start stop restart status scan repair nuclear help"
|
||||||
|
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Advanced completion function that attempts to get library section IDs
|
||||||
|
# This is more expensive but provides better completion
|
||||||
|
_plex_scanner_with_sections() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
# First try the basic completion
|
||||||
|
_scan_plex_libraries_completion
|
||||||
|
|
||||||
|
# If we didn't get any completions and we're looking for a section ID
|
||||||
|
if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
|
||||||
|
case "${COMP_WORDS[1]}" in
|
||||||
|
scan|refresh|analyze|generate|tree)
|
||||||
|
# Try to get section IDs if Plex is running
|
||||||
|
if [[ ${COMP_CWORD} -eq 2 ]]; then
|
||||||
|
# Attempt to get section IDs from the scanner
|
||||||
|
local script_dir="$(dirname "${COMP_WORDS[0]}")"
|
||||||
|
local scanner_script=""
|
||||||
|
|
||||||
|
# Try to find the scanner script
|
||||||
|
if [[ -f "$script_dir/scan-plex-libraries.sh" ]]; then
|
||||||
|
scanner_script="$script_dir/scan-plex-libraries.sh"
|
||||||
|
elif [[ -f "/home/acedanger/shell/plex/scan-plex-libraries.sh" ]]; then
|
||||||
|
scanner_script="/home/acedanger/shell/plex/scan-plex-libraries.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$scanner_script" ]]; then
|
||||||
|
# Attempt to get section IDs (with timeout to avoid hanging)
|
||||||
|
local section_ids
|
||||||
|
if section_ids=$(timeout 3s "$scanner_script" list 2>/dev/null | grep -oE '^\s*[0-9]+:' | grep -oE '[0-9]+' 2>/dev/null); then
|
||||||
|
COMPREPLY=($(compgen -W "$section_ids" -- "$cur"))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register completion functions for the main scripts
|
||||||
|
complete -F _plex_sh_completion plex.sh
|
||||||
|
complete -F _plex_sh_completion ./plex.sh
|
||||||
|
complete -F _plex_sh_completion /home/acedanger/shell/plex/plex.sh
|
||||||
|
|
||||||
|
complete -F _scan_plex_libraries_completion scan-plex-libraries.sh
|
||||||
|
complete -F _scan_plex_libraries_completion ./scan-plex-libraries.sh
|
||||||
|
complete -F _scan_plex_libraries_completion /home/acedanger/shell/plex/scan-plex-libraries.sh
|
||||||
|
|
||||||
|
# Register completion functions for aliases
|
||||||
|
# These will be available when the aliases are defined in the shell
|
||||||
|
complete -F _plex_alias_completion plex
|
||||||
|
complete -F _plex_alias_completion px
|
||||||
|
complete -F _plex_alias_completion plex-start
|
||||||
|
complete -F _plex_alias_completion plex-stop
|
||||||
|
complete -F _plex_alias_completion plex-restart
|
||||||
|
complete -F _plex_alias_completion plex-status
|
||||||
|
complete -F _plex_alias_completion plex-scan
|
||||||
|
complete -F _scan_plex_libraries_completion plex-scanner
|
||||||
|
|
||||||
|
# Optional: Enable advanced completion with section ID lookup for plex-scanner
|
||||||
|
# Uncomment the following line if you want more intelligent section ID completion
|
||||||
|
# (Note: This may cause slight delays if Plex is not running)
|
||||||
|
# complete -F _plex_scanner_with_sections plex-scanner
|
||||||
|
|
||||||
|
# Helper function to list available Plex library sections
|
||||||
|
# This can be called manually: _plex_list_sections
|
||||||
|
_plex_list_sections() {
|
||||||
|
echo "Available Plex library sections:"
|
||||||
|
if command -v /home/acedanger/shell/plex/scan-plex-libraries.sh >/dev/null 2>&1; then
|
||||||
|
/home/acedanger/shell/plex/scan-plex-libraries.sh list 2>/dev/null || echo " (Plex Media Server not running)"
|
||||||
|
else
|
||||||
|
echo " (Scanner script not found)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Completion for special plex commands that might be added in the future
|
||||||
|
_plex_extended_completion() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
local cmd="${COMP_WORDS[0]}"
|
||||||
|
|
||||||
|
# This function can be extended for future plex-related commands
|
||||||
|
case "$cmd" in
|
||||||
|
*plex-backup*)
|
||||||
|
# If backup scripts get their own completion, handle here
|
||||||
|
;;
|
||||||
|
*plex-restore*)
|
||||||
|
# If restore scripts get their own completion, handle here
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export functions for manual use (zsh compatible)
|
||||||
|
if declare -f >/dev/null 2>&1; then
|
||||||
|
# In bash, export functions
|
||||||
|
export -f _plex_list_sections 2>/dev/null || true
|
||||||
|
fi
|
||||||
@@ -2,3 +2,4 @@
|
|||||||
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
|
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
|
||||||
0 3 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
|
0 3 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
|
||||||
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
|
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
|
||||||
|
30 2 * * * { echo "Starting media backup"; /home/acedanger/shell/backup-media.sh; echo "Media backup completed with exit code: $?"; } 2>&1 | logger -t media-backup -p user.info
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
# Validates the integrity of .env backup repository
|
# Validates the integrity of .env backup repository
|
||||||
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
|
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
|
||||||
|
|
||||||
|
# Daily media backup at 0230 with enhanced logging
|
||||||
|
# Backs up media server configurations and metadata
|
||||||
|
30 2 * * * { echo "Starting media backup"; /home/acedanger/shell/backup-media.sh; echo "Media backup completed with exit code: $?"; } 2>&1 | logger -t media-backup -p user.info
|
||||||
|
|
||||||
# Optional: Monitor Docker container health (every 6 hours)
|
# Optional: Monitor Docker container health (every 6 hours)
|
||||||
# This can help detect if any download services are failing
|
# This can help detect if any download services are failing
|
||||||
# 0 */6 * * * { echo "Docker health check"; docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v "Up"; } 2>&1 | logger -t docker-health -p user.info
|
# 0 */6 * * * { echo "Docker health check"; docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v "Up"; } 2>&1 | logger -t docker-health -p user.info
|
||||||
|
|||||||
@@ -128,6 +128,16 @@ if [ -f "$HOME/shell/completions/backup-scripts-completion.bash" ]; then
|
|||||||
source "$HOME/shell/completions/backup-scripts-completion.bash"
|
source "$HOME/shell/completions/backup-scripts-completion.bash"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Load Plex scripts completion
|
||||||
|
if [ -f "$HOME/shell/completions/plex-scripts-completion.bash" ]; then
|
||||||
|
source "$HOME/shell/completions/plex-scripts-completion.bash"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load environment backup completion
|
||||||
|
if [ -f "$HOME/shell/completions/env-backup-completion.bash" ]; then
|
||||||
|
source "$HOME/shell/completions/env-backup-completion.bash"
|
||||||
|
fi
|
||||||
|
|
||||||
# Go environment variables (required for Fabric and other Go tools)
|
# Go environment variables (required for Fabric and other Go tools)
|
||||||
# GOROOT is auto-detected by Go when installed via package manager
|
# GOROOT is auto-detected by Go when installed via package manager
|
||||||
export GOPATH=$HOME/go
|
export GOPATH=$HOME/go
|
||||||
|
|||||||
118
plex/README.md
118
plex/README.md
@@ -229,6 +229,7 @@ ls -lah "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/
|
|||||||
- Interactive menu system for easy management
|
- Interactive menu system for easy management
|
||||||
- Process monitoring and health checks
|
- Process monitoring and health checks
|
||||||
- Integration with other backup/repair tools
|
- Integration with other backup/repair tools
|
||||||
|
- **NEW:** Library scanner integration
|
||||||
|
|
||||||
**Usage:**
|
**Usage:**
|
||||||
```bash
|
```bash
|
||||||
@@ -236,6 +237,7 @@ ls -lah "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/
|
|||||||
./plex.sh stop # Stop Plex service
|
./plex.sh stop # Stop Plex service
|
||||||
./plex.sh restart # Restart Plex service
|
./plex.sh restart # Restart Plex service
|
||||||
./plex.sh status # Show service status
|
./plex.sh status # Show service status
|
||||||
|
./plex.sh scan # Launch library scanner
|
||||||
./plex.sh repair # Launch repair menu (calls other tools)
|
./plex.sh repair # Launch repair menu (calls other tools)
|
||||||
./plex.sh nuclear # Nuclear recovery option
|
./plex.sh nuclear # Nuclear recovery option
|
||||||
./plex.sh # Interactive menu
|
./plex.sh # Interactive menu
|
||||||
@@ -248,11 +250,16 @@ $ ./plex.sh status
|
|||||||
Plex Media Server Status: RUNNING
|
Plex Media Server Status: RUNNING
|
||||||
Uptime: 2 days, 14 hours, 23 minutes
|
Uptime: 2 days, 14 hours, 23 minutes
|
||||||
|
|
||||||
|
# Launch library scanner
|
||||||
|
$ ./plex.sh scan
|
||||||
|
[*] Launching Plex Library Scanner...
|
||||||
|
[i] Launching scanner in interactive mode...
|
||||||
|
|
||||||
# Interactive menu
|
# Interactive menu
|
||||||
$ ./plex.sh
|
$ ./plex.sh
|
||||||
=== Plex Management Menu ===
|
=== Plex Management Menu ===
|
||||||
1) Start Service
|
1) Start Service
|
||||||
2) Stop Service
|
2) Stop Service
|
||||||
3) Restart Service
|
3) Restart Service
|
||||||
4) Service Status
|
4) Service Status
|
||||||
5) Database Check
|
5) Database Check
|
||||||
@@ -260,6 +267,93 @@ $ ./plex.sh
|
|||||||
Enter your choice [1-6]:
|
Enter your choice [1-6]:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `scan-plex-libraries.sh` ⭐ **NEW**
|
||||||
|
**Comprehensive Plex library scanning and management**
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- List all Plex library sections with IDs
|
||||||
|
- Scan libraries for new media files
|
||||||
|
- Refresh metadata with force options
|
||||||
|
- Deep media analysis capabilities
|
||||||
|
- Generate thumbnails and fanart
|
||||||
|
- Show library tree structures
|
||||||
|
- Interactive and command-line modes
|
||||||
|
- Comprehensive error handling and logging
|
||||||
|
|
||||||
|
**Status:** ✅ **ACTIVE** (June 26, 2025)
|
||||||
|
|
||||||
|
**Integration:**
|
||||||
|
- Integrated into `plex.sh` as the `scan` command
|
||||||
|
- Available via aliases: `plex-scan`, `plex-scanner`
|
||||||
|
- Direct command-line access available
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
# Via plex.sh (recommended)
|
||||||
|
./plex.sh scan # Interactive scanner
|
||||||
|
./plex.sh scan list # List all libraries
|
||||||
|
./plex.sh scan scan 29 # Scan Movies library
|
||||||
|
./plex.sh scan refresh "" true # Force refresh all libraries
|
||||||
|
|
||||||
|
# Direct usage
|
||||||
|
./scan-plex-libraries.sh list # List all library sections
|
||||||
|
./scan-plex-libraries.sh scan # Scan all libraries for new media
|
||||||
|
./scan-plex-libraries.sh scan 29 # Scan specific library (ID 29)
|
||||||
|
./scan-plex-libraries.sh refresh 29 true # Force refresh specific library
|
||||||
|
./scan-plex-libraries.sh analyze 29 true # Deep analyze library
|
||||||
|
./scan-plex-libraries.sh generate 29 # Generate thumbnails
|
||||||
|
./scan-plex-libraries.sh tree 29 # Show library structure
|
||||||
|
./scan-plex-libraries.sh -v scan 29 # Verbose scanning
|
||||||
|
|
||||||
|
# Via aliases
|
||||||
|
plex-scan # Launch via plex.sh
|
||||||
|
plex-scanner list # Direct scanner access
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Operations:**
|
||||||
|
```bash
|
||||||
|
# List all available libraries
|
||||||
|
$ ./scan-plex-libraries.sh list
|
||||||
|
Available Library Sections:
|
||||||
|
=========================
|
||||||
|
29: Movies
|
||||||
|
30: TV Shows
|
||||||
|
31: Music
|
||||||
|
|
||||||
|
# Scan Movies library for new content
|
||||||
|
$ ./scan-plex-libraries.sh scan 29
|
||||||
|
[>] Scanning library section 29 for new media...
|
||||||
|
[✓] Library section 29 scan completed
|
||||||
|
|
||||||
|
# Force refresh all libraries
|
||||||
|
$ ./scan-plex-libraries.sh refresh "" true
|
||||||
|
[~] Refreshing metadata for all libraries...
|
||||||
|
[i] Refreshing section 29...
|
||||||
|
[✓] Section 29 refreshed successfully
|
||||||
|
[i] Refreshing section 30...
|
||||||
|
[✓] Section 30 refreshed successfully
|
||||||
|
[✓] All libraries refreshed successfully
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interactive Mode:**
|
||||||
|
```bash
|
||||||
|
$ ./scan-plex-libraries.sh
|
||||||
|
🎬 Plex Library Scanner - Interactive Mode
|
||||||
|
Select an operation to perform:
|
||||||
|
|
||||||
|
Available Operations:
|
||||||
|
1) List all libraries
|
||||||
|
2) Scan libraries for new media
|
||||||
|
3) Refresh library metadata
|
||||||
|
4) Analyze library media
|
||||||
|
5) Generate thumbnails
|
||||||
|
6) Show library tree
|
||||||
|
q) Quit
|
||||||
|
|
||||||
|
Choose an option [1-6,q]:
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
### 📊 Utility Scripts
|
### 📊 Utility Scripts
|
||||||
|
|
||||||
#### `check-plex-builtin-backups.sh` ⭐ **NEW**
|
#### `check-plex-builtin-backups.sh` ⭐ **NEW**
|
||||||
@@ -325,7 +419,7 @@ sudo ./cleanup-plex-databases.sh # Perform actual cleanup
|
|||||||
|
|
||||||
**What it removes:**
|
**What it removes:**
|
||||||
- Temporary files (`*-tmp`, `*-2025-*-tmp`)
|
- Temporary files (`*-tmp`, `*-2025-*-tmp`)
|
||||||
- MD5 checksum files (`*.md5`)
|
- MD5 checksum files (`*.md5`)
|
||||||
- Recovery files (`*recovery*`)
|
- Recovery files (`*recovery*`)
|
||||||
- Repaired files (`*repaired*`)
|
- Repaired files (`*repaired*`)
|
||||||
- Empty backup files (`*empty-backup*`)
|
- Empty backup files (`*empty-backup*`)
|
||||||
@@ -363,7 +457,7 @@ sudo ./cleanup-plex-databases.sh # Perform actual cleanup
|
|||||||
$ ./test-plex-backup.sh --quick
|
$ ./test-plex-backup.sh --quick
|
||||||
[TEST] Quick smoke test suite
|
[TEST] Quick smoke test suite
|
||||||
[PASS] Database access test
|
[PASS] Database access test
|
||||||
[PASS] Service management test
|
[PASS] Service management test
|
||||||
[PASS] Backup directory access test
|
[PASS] Backup directory access test
|
||||||
[PASS] All 3 tests passed in 15 seconds
|
[PASS] All 3 tests passed in 15 seconds
|
||||||
```
|
```
|
||||||
@@ -402,7 +496,7 @@ $ ./test-plex-backup.sh --quick
|
|||||||
Scripts moved to `deprecated/` folder (June 21, 2025):
|
Scripts moved to `deprecated/` folder (June 21, 2025):
|
||||||
|
|
||||||
- **`plex-database-repair.sh`** → Use `plex-db-manager.sh`
|
- **`plex-database-repair.sh`** → Use `plex-db-manager.sh`
|
||||||
- **`recover-plex-database.sh`** → Use `nuclear-plex-recovery.sh`
|
- **`recover-plex-database.sh`** → Use `nuclear-plex-recovery.sh`
|
||||||
- **`restore-plex.sh`** → Use `nuclear-plex-recovery.sh`
|
- **`restore-plex.sh`** → Use `nuclear-plex-recovery.sh`
|
||||||
- **`icu-aware-recovery.sh`** → Functionality built into other scripts
|
- **`icu-aware-recovery.sh`** → Functionality built into other scripts
|
||||||
|
|
||||||
@@ -425,7 +519,7 @@ graph TD
|
|||||||
I --> A
|
I --> A
|
||||||
J[integration-test-plex.sh] --> C
|
J[integration-test-plex.sh] --> C
|
||||||
K[plex-recent-additions.sh]
|
K[plex-recent-additions.sh]
|
||||||
|
|
||||||
style A fill:#e1f5fe
|
style A fill:#e1f5fe
|
||||||
style C fill:#f3e5f5
|
style C fill:#f3e5f5
|
||||||
style F fill:#ffebee
|
style F fill:#ffebee
|
||||||
@@ -538,7 +632,7 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
|
|
||||||
- Backup success/failure rates
|
- Backup success/failure rates
|
||||||
- Database integrity status
|
- Database integrity status
|
||||||
- Service uptime and performance
|
- Service uptime and performance
|
||||||
- Disk space utilization
|
- Disk space utilization
|
||||||
- Recovery operation success
|
- Recovery operation success
|
||||||
|
|
||||||
@@ -553,7 +647,7 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
./plex-db-manager.sh check
|
./plex-db-manager.sh check
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Review Service Status**
|
2. **Review Service Status**
|
||||||
```bash
|
```bash
|
||||||
./plex.sh status
|
./plex.sh status
|
||||||
systemctl status plexmediaserver
|
systemctl status plexmediaserver
|
||||||
@@ -575,10 +669,10 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
```bash
|
```bash
|
||||||
# Always dry-run first
|
# Always dry-run first
|
||||||
sudo ./nuclear-plex-recovery.sh --dry-run
|
sudo ./nuclear-plex-recovery.sh --dry-run
|
||||||
|
|
||||||
# If satisfied with plan:
|
# If satisfied with plan:
|
||||||
sudo ./nuclear-plex-recovery.sh --auto
|
sudo ./nuclear-plex-recovery.sh --auto
|
||||||
|
|
||||||
# Validate recovery
|
# Validate recovery
|
||||||
./validate-plex-recovery.sh
|
./validate-plex-recovery.sh
|
||||||
```
|
```
|
||||||
@@ -640,7 +734,7 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
|
|
||||||
**Recent Major Changes (June 21, 2025):**
|
**Recent Major Changes (June 21, 2025):**
|
||||||
- ✅ Fixed database corruption root cause (30-minute auto-repair loop)
|
- ✅ Fixed database corruption root cause (30-minute auto-repair loop)
|
||||||
- ✅ **DISABLED custom backup system** - Replaced with Plex built-in backups
|
- ✅ **DISABLED custom backup system** - Replaced with Plex built-in backups
|
||||||
- ✅ Retired redundant repair scripts to `deprecated/` folder
|
- ✅ Retired redundant repair scripts to `deprecated/` folder
|
||||||
- ✅ Implemented Plex native scheduled backups (every 3 days)
|
- ✅ Implemented Plex native scheduled backups (every 3 days)
|
||||||
- ✅ Created consolidated database manager (`plex-db-manager.sh`)
|
- ✅ Created consolidated database manager (`plex-db-manager.sh`)
|
||||||
@@ -669,7 +763,7 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
```bash
|
```bash
|
||||||
# Check Plex built-in backup status
|
# Check Plex built-in backup status
|
||||||
ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/"*.backup.*
|
ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/"*.backup.*
|
||||||
|
|
||||||
# Verify backup location on NAS
|
# Verify backup location on NAS
|
||||||
ls -lah /mnt/share/media/backups/plex/
|
ls -lah /mnt/share/media/backups/plex/
|
||||||
```
|
```
|
||||||
@@ -678,7 +772,7 @@ ls -lath "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server
|
|||||||
|
|
||||||
❌ **Don't re-enable daily custom backup scripts** (conflicts with Plex built-in)
|
❌ **Don't re-enable daily custom backup scripts** (conflicts with Plex built-in)
|
||||||
❌ **Don't enable 30-minute auto-repair schedules** (causes corruption)
|
❌ **Don't enable 30-minute auto-repair schedules** (causes corruption)
|
||||||
❌ **Don't use force-kill on Plex processes** (corrupts databases)
|
❌ **Don't use force-kill on Plex processes** (corrupts databases)
|
||||||
❌ **Don't bypass safety checks** in scripts
|
❌ **Don't bypass safety checks** in scripts
|
||||||
❌ **Don't run multiple repair operations simultaneously**
|
❌ **Don't run multiple repair operations simultaneously**
|
||||||
❌ **Don't ignore failed service shutdowns** (wait for graceful exit)
|
❌ **Don't ignore failed service shutdowns** (wait for graceful exit)
|
||||||
|
|||||||
342
plex/docs/plex-library-scanner.md
Normal file
342
plex/docs/plex-library-scanner.md
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
# Plex Library Scanner Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Plex Library Scanner provides a comprehensive command-line interface for managing Plex Media Server libraries. It wraps the native Plex Media Scanner binary with enhanced functionality, error handling, and user-friendly output.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Library Management**: List, scan, refresh, and analyze Plex libraries
|
||||||
|
- **Interactive Mode**: User-friendly menu system for easy operation
|
||||||
|
- **Batch Operations**: Perform operations on all libraries or specific ones
|
||||||
|
- **Enhanced Output**: Colored, styled terminal output with progress indicators
|
||||||
|
- **Error Handling**: Comprehensive validation and error reporting
|
||||||
|
- **Logging**: Detailed operation logs for troubleshooting
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
### scan-plex-libraries.sh
|
||||||
|
Primary script for library scanning operations.
|
||||||
|
|
||||||
|
**Location**: `/home/acedanger/shell/plex/scan-plex-libraries.sh`
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Automatic detection of Plex Media Scanner binary
|
||||||
|
- Service status validation
|
||||||
|
- Section ID validation
|
||||||
|
- Comprehensive error handling
|
||||||
|
- Interactive and command-line modes
|
||||||
|
|
||||||
|
### Integration with plex.sh
|
||||||
|
The scanner is integrated into the main `plex.sh` script as the `scan` command.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Via plex.sh (Recommended)
|
||||||
|
```bash
|
||||||
|
# Launch interactive scanner
|
||||||
|
plex scan
|
||||||
|
|
||||||
|
# Pass arguments directly to scanner
|
||||||
|
plex scan list
|
||||||
|
plex scan scan 29
|
||||||
|
plex scan refresh "" true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Usage
|
||||||
|
```bash
|
||||||
|
# Interactive mode
|
||||||
|
./scan-plex-libraries.sh
|
||||||
|
|
||||||
|
# List all libraries
|
||||||
|
./scan-plex-libraries.sh list
|
||||||
|
|
||||||
|
# Scan specific library
|
||||||
|
./scan-plex-libraries.sh scan 29
|
||||||
|
|
||||||
|
# Scan all libraries
|
||||||
|
./scan-plex-libraries.sh scan
|
||||||
|
|
||||||
|
# Refresh with force flag
|
||||||
|
./scan-plex-libraries.sh refresh 29 true
|
||||||
|
|
||||||
|
# Deep analysis
|
||||||
|
./scan-plex-libraries.sh analyze 29 true
|
||||||
|
|
||||||
|
# Generate thumbnails
|
||||||
|
./scan-plex-libraries.sh generate 29
|
||||||
|
|
||||||
|
# Show library tree
|
||||||
|
./scan-plex-libraries.sh tree 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via Aliases
|
||||||
|
```bash
|
||||||
|
# Using new aliases
|
||||||
|
plex-scan # Launch scanner via plex.sh
|
||||||
|
plex-scanner # Direct access to scanner script
|
||||||
|
plex-scanner list # List libraries directly
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### list
|
||||||
|
Lists all available library sections with their IDs and names.
|
||||||
|
|
||||||
|
**Usage**: `scan-plex-libraries.sh list`
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```
|
||||||
|
Available Library Sections:
|
||||||
|
=========================
|
||||||
|
29: Movies
|
||||||
|
30: TV Shows
|
||||||
|
31: Music
|
||||||
|
```
|
||||||
|
|
||||||
|
### scan
|
||||||
|
Scans libraries for new media files.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
- `scan-plex-libraries.sh scan` (all libraries)
|
||||||
|
- `scan-plex-libraries.sh scan <section_id>` (specific library)
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Scan all libraries
|
||||||
|
scan-plex-libraries.sh scan
|
||||||
|
|
||||||
|
# Scan Movies library (ID 29)
|
||||||
|
scan-plex-libraries.sh scan 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### refresh
|
||||||
|
Refreshes metadata for library items.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
- `scan-plex-libraries.sh refresh [section_id] [force]`
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `section_id`: Library section ID (optional, scans all if omitted)
|
||||||
|
- `force`: Set to "true" for forced refresh (optional)
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Refresh all libraries
|
||||||
|
scan-plex-libraries.sh refresh
|
||||||
|
|
||||||
|
# Refresh specific library
|
||||||
|
scan-plex-libraries.sh refresh 29
|
||||||
|
|
||||||
|
# Force refresh all libraries
|
||||||
|
scan-plex-libraries.sh refresh "" true
|
||||||
|
|
||||||
|
# Force refresh specific library
|
||||||
|
scan-plex-libraries.sh refresh 29 true
|
||||||
|
```
|
||||||
|
|
||||||
|
### analyze
|
||||||
|
Analyzes media files for metadata and codec information.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
- `scan-plex-libraries.sh analyze [section_id] [deep]`
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `section_id`: Library section ID (optional)
|
||||||
|
- `deep`: Set to "true" for deep analysis (optional)
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Analyze all libraries
|
||||||
|
scan-plex-libraries.sh analyze
|
||||||
|
|
||||||
|
# Analyze specific library
|
||||||
|
scan-plex-libraries.sh analyze 29
|
||||||
|
|
||||||
|
# Deep analyze all libraries
|
||||||
|
scan-plex-libraries.sh analyze "" true
|
||||||
|
|
||||||
|
# Deep analyze specific library
|
||||||
|
scan-plex-libraries.sh analyze 29 true
|
||||||
|
```
|
||||||
|
|
||||||
|
### generate
|
||||||
|
Generates thumbnails and fanart for media items.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
- `scan-plex-libraries.sh generate [section_id]`
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Generate for all libraries
|
||||||
|
scan-plex-libraries.sh generate
|
||||||
|
|
||||||
|
# Generate for specific library
|
||||||
|
scan-plex-libraries.sh generate 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### tree
|
||||||
|
Shows the hierarchical structure of a library.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
- `scan-plex-libraries.sh tree <section_id>`
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```bash
|
||||||
|
# Show Movies library structure
|
||||||
|
scan-plex-libraries.sh tree 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### interactive
|
||||||
|
Launches the interactive menu system.
|
||||||
|
|
||||||
|
**Usage**: `scan-plex-libraries.sh interactive` (or just `scan-plex-libraries.sh`)
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### -v, --verbose
|
||||||
|
Enables verbose output showing detailed progress information.
|
||||||
|
|
||||||
|
**Usage**: `scan-plex-libraries.sh -v <command>`
|
||||||
|
|
||||||
|
### -h, --help
|
||||||
|
Shows help information and usage examples.
|
||||||
|
|
||||||
|
**Usage**: `scan-plex-libraries.sh --help`
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **Plex Media Server**: Must be installed and running
|
||||||
|
2. **Plex Media Scanner**: Binary must be available (automatically detected)
|
||||||
|
3. **Permissions**: Script must be run with appropriate user permissions
|
||||||
|
4. **Service Access**: Access to systemctl for service status checking
|
||||||
|
|
||||||
|
## Automatic Detection
|
||||||
|
|
||||||
|
The scanner automatically detects the Plex Media Scanner binary from these locations:
|
||||||
|
- `/usr/lib/plexmediaserver/Plex Media Scanner` (Linux - most common)
|
||||||
|
- `/opt/plex/Plex Media Scanner` (Alternative Linux)
|
||||||
|
- `/usr/local/plex/Plex Media Scanner` (Custom installations)
|
||||||
|
- `/Applications/Plex Media Server.app/Contents/MacOS/Plex Media Scanner` (macOS)
|
||||||
|
- System PATH
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
The script provides comprehensive error handling for:
|
||||||
|
- Missing Plex Media Scanner binary
|
||||||
|
- Plex service not running
|
||||||
|
- Invalid section IDs
|
||||||
|
- Scanner operation failures
|
||||||
|
- Permission issues
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Operations are logged to: `/home/acedanger/shell/logs/plex-scanner.log`
|
||||||
|
|
||||||
|
Log entries include:
|
||||||
|
- Timestamp
|
||||||
|
- Operation type
|
||||||
|
- Success/failure status
|
||||||
|
- Error details (if applicable)
|
||||||
|
- Debug information (in verbose mode)
|
||||||
|
|
||||||
|
## Integration Examples
|
||||||
|
|
||||||
|
### Automated Scanning
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Scan all libraries after adding new media
|
||||||
|
plex-scanner scan
|
||||||
|
|
||||||
|
# Force refresh if metadata seems outdated
|
||||||
|
plex-scanner refresh "" true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Maintenance Script
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Daily maintenance routine
|
||||||
|
echo "Starting Plex maintenance..."
|
||||||
|
|
||||||
|
# Scan for new media
|
||||||
|
plex-scanner scan
|
||||||
|
|
||||||
|
# Analyze any new files
|
||||||
|
plex-scanner analyze
|
||||||
|
|
||||||
|
# Generate missing thumbnails
|
||||||
|
plex-scanner generate
|
||||||
|
|
||||||
|
echo "Maintenance complete!"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specific Library Management
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Update specific library after batch media addition
|
||||||
|
MOVIES_ID=29
|
||||||
|
TV_SHOWS_ID=30
|
||||||
|
|
||||||
|
# Scan and refresh movies
|
||||||
|
plex-scanner scan $MOVIES_ID
|
||||||
|
plex-scanner refresh $MOVIES_ID true
|
||||||
|
|
||||||
|
# Deep analyze TV shows
|
||||||
|
plex-scanner analyze $TV_SHOWS_ID true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **"Plex Media Server is not running"**
|
||||||
|
- Start Plex: `sudo systemctl start plexmediaserver`
|
||||||
|
- Check status: `systemctl status plexmediaserver`
|
||||||
|
|
||||||
|
2. **"Plex Media Scanner binary not found"**
|
||||||
|
- Verify Plex installation
|
||||||
|
- Check if binary exists in expected locations
|
||||||
|
- Ensure proper PATH configuration
|
||||||
|
|
||||||
|
3. **"Section ID not found"**
|
||||||
|
- Use `plex-scanner list` to see valid section IDs
|
||||||
|
- Ensure the library hasn't been deleted
|
||||||
|
|
||||||
|
4. **Permission denied errors**
|
||||||
|
- Don't run as root
|
||||||
|
- Ensure user has access to Plex directories
|
||||||
|
- Check file permissions on scanner binary
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
Enable verbose logging for troubleshooting:
|
||||||
|
```bash
|
||||||
|
plex-scanner -v list
|
||||||
|
plex-scanner -v scan 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Analysis
|
||||||
|
Check logs for detailed error information:
|
||||||
|
```bash
|
||||||
|
tail -f /home/acedanger/shell/logs/plex-scanner.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
- **Large Libraries**: Operations on large libraries may take considerable time
|
||||||
|
- **Deep Analysis**: Deep analysis is resource-intensive, use sparingly
|
||||||
|
- **Concurrent Operations**: Avoid running multiple scanner operations simultaneously
|
||||||
|
- **System Resources**: Scanner operations can be CPU and I/O intensive
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Regular Scanning**: Set up regular scans for libraries with frequent additions
|
||||||
|
2. **Selective Operations**: Use specific section IDs for targeted operations
|
||||||
|
3. **Monitor Logs**: Regular log review helps identify issues early
|
||||||
|
4. **Test First**: Use verbose mode to understand operation impact
|
||||||
|
5. **Service Health**: Ensure Plex service is healthy before operations
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Plex Media Scanner CLI Documentation](https://support.plex.tv/articles/201242707-plex-media-scanner-via-command-line/)
|
||||||
|
- [Plex Management Documentation](plex-management.md)
|
||||||
|
- [Backup System Documentation](plex-backup.md)
|
||||||
248
plex/docs/scanner-implementation-summary.md
Normal file
248
plex/docs/scanner-implementation-summary.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# Plex Library Scanner Implementation Summary
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Successfully implemented a comprehensive Plex library scanning solution based on the Plex Media Scanner CLI documentation. The implementation provides both standalone functionality and seamless integration with the existing plex.sh management script.
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### 1. New Script: `scan-plex-libraries.sh`
|
||||||
|
|
||||||
|
**Location**: `/home/acedanger/shell/plex/scan-plex-libraries.sh`
|
||||||
|
|
||||||
|
**Features Implemented**:
|
||||||
|
- ✅ **Library Listing**: Lists all Plex libraries with section IDs and names
|
||||||
|
- ✅ **Library Scanning**: Scan for new media (individual or all libraries)
|
||||||
|
- ✅ **Metadata Refresh**: Refresh library metadata with force option
|
||||||
|
- ✅ **Media Analysis**: Analyze media files (standard and deep analysis)
|
||||||
|
- ✅ **Thumbnail Generation**: Generate thumbnails and fanart
|
||||||
|
- ✅ **Library Tree**: Display hierarchical library structure
|
||||||
|
- ✅ **Interactive Mode**: User-friendly menu system
|
||||||
|
- ✅ **Verbose Logging**: Detailed operation logging
|
||||||
|
- ✅ **Error Handling**: Comprehensive validation and error reporting
|
||||||
|
- ✅ **Service Validation**: Checks Plex service status before operations
|
||||||
|
- ✅ **Scanner Detection**: Automatically finds Plex Media Scanner binary
|
||||||
|
|
||||||
|
**Command Examples**:
|
||||||
|
```bash
|
||||||
|
# List all libraries
|
||||||
|
./scan-plex-libraries.sh list
|
||||||
|
|
||||||
|
# Scan specific library
|
||||||
|
./scan-plex-libraries.sh scan 29
|
||||||
|
|
||||||
|
# Force refresh all libraries
|
||||||
|
./scan-plex-libraries.sh refresh "" true
|
||||||
|
|
||||||
|
# Deep analyze specific library
|
||||||
|
./scan-plex-libraries.sh analyze 29 true
|
||||||
|
|
||||||
|
# Generate thumbnails for library
|
||||||
|
./scan-plex-libraries.sh generate 29
|
||||||
|
|
||||||
|
# Interactive mode
|
||||||
|
./scan-plex-libraries.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Integration with `plex.sh`
|
||||||
|
|
||||||
|
**Changes Made**:
|
||||||
|
- ✅ Added `scan` command to main case statement
|
||||||
|
- ✅ Added `launch_scanner()` function for integration
|
||||||
|
- ✅ Updated help documentation to include scan command
|
||||||
|
- ✅ Updated script header documentation
|
||||||
|
- ✅ Supports argument passthrough to scanner script
|
||||||
|
|
||||||
|
**Integration Examples**:
|
||||||
|
```bash
|
||||||
|
# Launch interactive scanner
|
||||||
|
./plex.sh scan
|
||||||
|
|
||||||
|
# Pass commands directly to scanner
|
||||||
|
./plex.sh scan list
|
||||||
|
./plex.sh scan scan 29
|
||||||
|
./plex.sh scan refresh "" true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. New Aliases
|
||||||
|
|
||||||
|
**Added to `/home/acedanger/shell/dotfiles/my-aliases.zsh`**:
|
||||||
|
```bash
|
||||||
|
alias plex-scan="/home/acedanger/shell/plex/plex.sh scan" # Library scanner via plex.sh
|
||||||
|
alias plex-scanner="/home/acedanger/shell/plex/scan-plex-libraries.sh" # Direct scanner access
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage Examples**:
|
||||||
|
```bash
|
||||||
|
plex-scan # Launch scanner via plex.sh
|
||||||
|
plex-scanner list # Direct scanner access to list libraries
|
||||||
|
plex-scanner scan 29 # Direct scan of specific library
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Documentation
|
||||||
|
|
||||||
|
**Created**:
|
||||||
|
- ✅ **Comprehensive Documentation**: `/home/acedanger/shell/plex/docs/plex-library-scanner.md`
|
||||||
|
- ✅ **Updated Main README**: Added scanner section to `/home/acedanger/shell/plex/README.md`
|
||||||
|
|
||||||
|
**Documentation Includes**:
|
||||||
|
- Complete usage guide with examples
|
||||||
|
- Command reference with parameters
|
||||||
|
- Integration examples
|
||||||
|
- Troubleshooting guide
|
||||||
|
- Best practices
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
### Automatic Scanner Detection
|
||||||
|
The script automatically detects the Plex Media Scanner binary from common locations:
|
||||||
|
- `/usr/lib/plexmediaserver/Plex Media Scanner` (Linux - most common)
|
||||||
|
- `/opt/plex/Plex Media Scanner`
|
||||||
|
- `/usr/local/plex/Plex Media Scanner`
|
||||||
|
- `/Applications/Plex Media Server.app/Contents/MacOS/Plex Media Scanner` (macOS)
|
||||||
|
- System PATH
|
||||||
|
|
||||||
|
### Comprehensive Error Handling
|
||||||
|
- Service status validation (Plex must be running)
|
||||||
|
- Scanner binary detection and validation
|
||||||
|
- Section ID validation against actual libraries
|
||||||
|
- Operation failure detection and reporting
|
||||||
|
- Graceful error recovery
|
||||||
|
|
||||||
|
### Interactive Mode
|
||||||
|
Full-featured interactive menu system with:
|
||||||
|
- Library listing and selection
|
||||||
|
- Operation choice with sub-menus
|
||||||
|
- Force/deep options for advanced operations
|
||||||
|
- User-friendly prompts and confirmations
|
||||||
|
|
||||||
|
### Logging and Debugging
|
||||||
|
- Detailed logging to `/home/acedanger/shell/logs/plex-scanner.log`
|
||||||
|
- Verbose mode for debugging (`-v` flag)
|
||||||
|
- Timestamp-based log entries
|
||||||
|
- Operation success/failure tracking
|
||||||
|
|
||||||
|
## Based on Plex CLI Documentation
|
||||||
|
|
||||||
|
Implementation follows the official Plex Media Scanner CLI documentation:
|
||||||
|
- Uses standard Plex scanner commands (`--list`, `--scan`, `--refresh`, etc.)
|
||||||
|
- Proper section ID handling
|
||||||
|
- Force flags for metadata refresh
|
||||||
|
- Deep analysis options
|
||||||
|
- Thumbnail generation capabilities
|
||||||
|
|
||||||
|
## Testing Results
|
||||||
|
|
||||||
|
### ✅ Syntax Validation
|
||||||
|
```bash
|
||||||
|
$ bash -n ./scan-plex-libraries.sh
|
||||||
|
$ bash -n ./plex.sh
|
||||||
|
# Both scripts pass syntax validation
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ Help System
|
||||||
|
```bash
|
||||||
|
$ ./plex.sh help
|
||||||
|
# Shows new scan command in help output
|
||||||
|
|
||||||
|
$ ./scan-plex-libraries.sh --help
|
||||||
|
# Shows comprehensive usage information
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ Error Handling
|
||||||
|
```bash
|
||||||
|
$ ./scan-plex-libraries.sh list
|
||||||
|
# Correctly detects Plex service not running
|
||||||
|
# Provides helpful error message with resolution
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ Integration
|
||||||
|
```bash
|
||||||
|
$ ./plex.sh scan --help
|
||||||
|
# Successfully launches scanner with arguments
|
||||||
|
# Proper argument passthrough working
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Modifications Summary
|
||||||
|
|
||||||
|
1. **New Files Created**:
|
||||||
|
- `/home/acedanger/shell/plex/scan-plex-libraries.sh` (775 lines)
|
||||||
|
- `/home/acedanger/shell/plex/docs/plex-library-scanner.md`
|
||||||
|
|
||||||
|
2. **Modified Files**:
|
||||||
|
- `/home/acedanger/shell/plex/plex.sh`:
|
||||||
|
- Added `launch_scanner()` function
|
||||||
|
- Added `scan` case to main command handler
|
||||||
|
- Updated help text and documentation
|
||||||
|
- `/home/acedanger/shell/dotfiles/my-aliases.zsh`:
|
||||||
|
- Added `plex-scan` alias
|
||||||
|
- Added `plex-scanner` alias
|
||||||
|
- `/home/acedanger/shell/plex/README.md`:
|
||||||
|
- Added comprehensive scanner documentation section
|
||||||
|
|
||||||
|
3. **File Permissions**:
|
||||||
|
- Made `/home/acedanger/shell/plex/scan-plex-libraries.sh` executable
|
||||||
|
|
||||||
|
## Usage Workflow
|
||||||
|
|
||||||
|
### Basic Operations
|
||||||
|
```bash
|
||||||
|
# List all libraries
|
||||||
|
plex-scanner list
|
||||||
|
|
||||||
|
# Scan all libraries for new media
|
||||||
|
plex-scanner scan
|
||||||
|
|
||||||
|
# Scan specific library
|
||||||
|
plex-scanner scan 29
|
||||||
|
|
||||||
|
# Force refresh specific library
|
||||||
|
plex-scanner refresh 29 true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via plex.sh Integration
|
||||||
|
```bash
|
||||||
|
# Interactive scanner
|
||||||
|
plex scan
|
||||||
|
|
||||||
|
# Quick library list
|
||||||
|
plex scan list
|
||||||
|
|
||||||
|
# Quick scan of all libraries
|
||||||
|
plex scan scan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Via Aliases
|
||||||
|
```bash
|
||||||
|
# Launch scanner interface
|
||||||
|
plex-scan
|
||||||
|
|
||||||
|
# Direct scanner commands
|
||||||
|
plex-scanner list
|
||||||
|
plex-scanner scan 29
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
1. **User-Friendly**: Interactive mode makes library management accessible
|
||||||
|
2. **Comprehensive**: Covers all major Plex scanner operations
|
||||||
|
3. **Integrated**: Seamlessly works with existing plex.sh workflow
|
||||||
|
4. **Robust**: Extensive error handling and validation
|
||||||
|
5. **Documented**: Comprehensive documentation and examples
|
||||||
|
6. **Flexible**: Both command-line and interactive interfaces
|
||||||
|
7. **Safe**: Validates operations before execution
|
||||||
|
8. **Logged**: All operations logged for troubleshooting
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Potential future improvements could include:
|
||||||
|
- Scheduled scanning capabilities
|
||||||
|
- Library-specific configuration files
|
||||||
|
- Integration with backup operations
|
||||||
|
- Performance monitoring and metrics
|
||||||
|
- Batch operation support
|
||||||
|
- Custom scan triggers
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Plex library scanner implementation successfully provides a modern, user-friendly interface to Plex's native scanning capabilities while maintaining compatibility with the existing script ecosystem. The solution is production-ready and follows established coding standards and error handling practices.
|
||||||
@@ -11,15 +11,16 @@ This document provides comprehensive documentation for the modern `plex.sh` scri
|
|||||||
The enhanced `plex.sh` script is a modern Plex service management tool that performs the following advanced tasks:
|
The enhanced `plex.sh` script is a modern Plex service management tool that performs the following advanced tasks:
|
||||||
|
|
||||||
1. **Smart Service Management**: Intelligent start/stop/restart operations with dependency checking
|
1. **Smart Service Management**: Intelligent start/stop/restart operations with dependency checking
|
||||||
2. **Enhanced Status Display**: Detailed service status with health indicators and port monitoring
|
2. **Library Scanner Integration**: Integrated access to comprehensive library scanning operations
|
||||||
3. **Safety Validation**: Pre-operation checks and post-operation verification
|
3. **Enhanced Status Display**: Detailed service status with health indicators and port monitoring
|
||||||
4. **Progress Indicators**: Visual feedback for all operations with timing information
|
4. **Safety Validation**: Pre-operation checks and post-operation verification
|
||||||
5. **Comprehensive Logging**: Detailed logging with color-coded output and timestamps
|
5. **Progress Indicators**: Visual feedback for all operations with timing information
|
||||||
6. **Configuration Validation**: Checks for common configuration issues
|
6. **Comprehensive Logging**: Detailed logging with color-coded output and timestamps
|
||||||
7. **Network Monitoring**: Port availability and network configuration validation
|
7. **Configuration Validation**: Checks for common configuration issues
|
||||||
8. **Process Management**: Advanced process monitoring and cleanup capabilities
|
8. **Network Monitoring**: Port availability and network configuration validation
|
||||||
9. **Recovery Operations**: Automatic recovery from common service issues
|
9. **Process Management**: Advanced process monitoring and cleanup capabilities
|
||||||
10. **Performance Monitoring**: Service health and resource usage tracking
|
10. **Recovery Operations**: Automatic recovery from common service issues
|
||||||
|
11. **Performance Monitoring**: Service health and resource usage tracking
|
||||||
|
|
||||||
## Related Scripts in the Plex Ecosystem
|
## Related Scripts in the Plex Ecosystem
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ This script is part of a comprehensive Plex management suite:
|
|||||||
### Core Management Scripts
|
### Core Management Scripts
|
||||||
|
|
||||||
- **`plex.sh`** (this script) - Service management and control
|
- **`plex.sh`** (this script) - Service management and control
|
||||||
|
- **`scan-plex-libraries.sh`** ⭐ **NEW** - Comprehensive library scanning and metadata management
|
||||||
- **`backup-plex.sh`** - Database backup with integrity checking and auto-repair
|
- **`backup-plex.sh`** - Database backup with integrity checking and auto-repair
|
||||||
- **`restore-plex.sh`** - Safe database restoration with validation
|
- **`restore-plex.sh`** - Safe database restoration with validation
|
||||||
|
|
||||||
@@ -49,6 +51,266 @@ This script is part of a comprehensive Plex management suite:
|
|||||||
|
|
||||||
- **`plex-recent-additions.sh`** - Recent media additions reporting and statistics
|
- **`plex-recent-additions.sh`** - Recent media additions reporting and statistics
|
||||||
|
|
||||||
|
## Library Scanner Integration ⭐ **NEW**
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
The `plex.sh` script now includes integrated access to comprehensive library scanning functionality through the new `scan-plex-libraries.sh` script. This integration provides seamless access to all Plex Media Scanner operations directly from the main management interface.
|
||||||
|
|
||||||
|
### Library Scanner Features
|
||||||
|
|
||||||
|
The integrated library scanner provides:
|
||||||
|
|
||||||
|
- **Library Management**: List all Plex library sections with IDs and names
|
||||||
|
- **Media Scanning**: Scan for new media files (individual libraries or all at once)
|
||||||
|
- **Metadata Refresh**: Refresh library metadata with force options for complete rebuilds
|
||||||
|
- **Deep Analysis**: Comprehensive media analysis for codec information and metadata
|
||||||
|
- **Thumbnail Generation**: Generate preview thumbnails and fanart images
|
||||||
|
- **Library Structure**: Display hierarchical library structure and organization
|
||||||
|
- **Interactive Mode**: User-friendly menu system for easy library management
|
||||||
|
- **Batch Operations**: Perform operations on all libraries or specific selections
|
||||||
|
- **Error Handling**: Comprehensive validation and error reporting
|
||||||
|
- **Service Integration**: Automatic Plex service status validation before operations
|
||||||
|
|
||||||
|
### Scanner Integration Commands
|
||||||
|
|
||||||
|
The library scanner is accessible through the main `plex.sh` script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Launch interactive scanner interface
|
||||||
|
./plex.sh scan
|
||||||
|
|
||||||
|
# Pass commands directly to scanner
|
||||||
|
./plex.sh scan list # List all libraries
|
||||||
|
./plex.sh scan scan # Scan all libraries for new media
|
||||||
|
./plex.sh scan scan 29 # Scan specific library (ID 29)
|
||||||
|
./plex.sh scan refresh 29 true # Force refresh specific library
|
||||||
|
./plex.sh scan analyze 29 true # Deep analyze specific library
|
||||||
|
./plex.sh scan generate 29 # Generate thumbnails for library
|
||||||
|
./plex.sh scan tree 29 # Show library structure
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Scanner Access
|
||||||
|
|
||||||
|
The scanner can also be accessed directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Direct script execution
|
||||||
|
./scan-plex-libraries.sh interactive # Interactive mode
|
||||||
|
./scan-plex-libraries.sh list # List libraries
|
||||||
|
./scan-plex-libraries.sh scan # Scan all libraries
|
||||||
|
./scan-plex-libraries.sh -v scan 29 # Verbose scan of specific library
|
||||||
|
|
||||||
|
# Via aliases (if shell configuration is loaded)
|
||||||
|
plex-scan # Launch via plex.sh integration
|
||||||
|
plex-scanner list # Direct scanner access
|
||||||
|
plex-scanner scan 29 # Direct scan of specific library
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scanner Command Reference
|
||||||
|
|
||||||
|
#### Core Scanner Commands
|
||||||
|
|
||||||
|
- **`list`** - Display all available library sections with IDs and names
|
||||||
|
- **`scan [section_id]`** - Scan for new media files (all libraries if no ID specified)
|
||||||
|
- **`refresh [section_id] [force]`** - Refresh metadata (set force=true for complete refresh)
|
||||||
|
- **`analyze [section_id] [deep]`** - Analyze media files (set deep=true for comprehensive analysis)
|
||||||
|
- **`generate [section_id]`** - Generate thumbnails and fanart images
|
||||||
|
- **`tree <section_id>`** - Display hierarchical library structure (requires section ID)
|
||||||
|
- **`interactive`** - Launch interactive menu system
|
||||||
|
|
||||||
|
#### Scanner Options
|
||||||
|
|
||||||
|
- **`-v, --verbose`** - Enable verbose output for detailed operation information
|
||||||
|
- **`-h, --help`** - Display comprehensive help and usage information
|
||||||
|
|
||||||
|
#### Example Scanner Operations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all available libraries with their section IDs
|
||||||
|
$ ./plex.sh scan list
|
||||||
|
Available Library Sections:
|
||||||
|
=========================
|
||||||
|
29: Movies
|
||||||
|
30: TV Shows
|
||||||
|
31: Music
|
||||||
|
|
||||||
|
# Scan Movies library for new content
|
||||||
|
$ ./plex.sh scan scan 29
|
||||||
|
[>] Scanning library section 29 for new media...
|
||||||
|
[✓] Library section 29 scan completed
|
||||||
|
|
||||||
|
# Force refresh all libraries (complete metadata rebuild)
|
||||||
|
$ ./plex.sh scan refresh "" true
|
||||||
|
[~] Refreshing metadata for all libraries...
|
||||||
|
[i] Refreshing section 29...
|
||||||
|
[✓] Section 29 refreshed successfully
|
||||||
|
[i] Refreshing section 30...
|
||||||
|
[✓] Section 30 refreshed successfully
|
||||||
|
[✓] All libraries refreshed successfully
|
||||||
|
|
||||||
|
# Deep analyze TV Shows library
|
||||||
|
$ ./plex.sh scan analyze 30 true
|
||||||
|
[?] Analyzing media in library section 30...
|
||||||
|
[✓] Library section 30 analysis completed
|
||||||
|
|
||||||
|
# Generate thumbnails for Movies library
|
||||||
|
$ ./plex.sh scan generate 29
|
||||||
|
[*] Generating thumbnails for library section 29...
|
||||||
|
[✓] Thumbnails generated for library section 29
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interactive Scanner Mode
|
||||||
|
|
||||||
|
The interactive mode provides a user-friendly menu system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ./plex.sh scan
|
||||||
|
🎬 Plex Library Scanner - Interactive Mode
|
||||||
|
Select an operation to perform:
|
||||||
|
|
||||||
|
Available Operations:
|
||||||
|
1) List all libraries
|
||||||
|
2) Scan libraries for new media
|
||||||
|
3) Refresh library metadata
|
||||||
|
4) Analyze library media
|
||||||
|
5) Generate thumbnails
|
||||||
|
6) Show library tree
|
||||||
|
q) Quit
|
||||||
|
|
||||||
|
Choose an option [1-6,q]: 2
|
||||||
|
|
||||||
|
Scan Options:
|
||||||
|
1) Scan all libraries
|
||||||
|
2) Scan specific library
|
||||||
|
Choose [1-2]: 1
|
||||||
|
|
||||||
|
[>] Scanning all libraries for new media...
|
||||||
|
[✓] All libraries scanned successfully
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scanner Error Handling and Validation
|
||||||
|
|
||||||
|
The library scanner includes comprehensive error handling:
|
||||||
|
|
||||||
|
- **Service Validation**: Automatically checks if Plex Media Server is running
|
||||||
|
- **Scanner Detection**: Locates Plex Media Scanner binary across different installation paths
|
||||||
|
- **Section ID Validation**: Verifies library section IDs exist before operations
|
||||||
|
- **Operation Verification**: Confirms successful completion of all scanner operations
|
||||||
|
- **Graceful Failure**: Provides helpful error messages and recovery suggestions
|
||||||
|
|
||||||
|
### Scanner Logging and Monitoring
|
||||||
|
|
||||||
|
All scanner operations are logged:
|
||||||
|
|
||||||
|
- **Operation Logs**: Detailed logs written to `/home/acedanger/shell/logs/plex-scanner.log`
|
||||||
|
- **Timestamp Tracking**: All operations include precise timestamps
|
||||||
|
- **Verbose Mode**: Enhanced debugging information available with `-v` flag
|
||||||
|
- **Status Reporting**: Real-time status updates during long-running operations
|
||||||
|
|
||||||
|
### Scanner Performance Considerations
|
||||||
|
|
||||||
|
For optimal performance:
|
||||||
|
|
||||||
|
- **Large Libraries**: Scanner operations may take considerable time for large media collections
|
||||||
|
- **Deep Analysis**: Deep analysis is resource-intensive, use selectively
|
||||||
|
- **Concurrent Operations**: Avoid running multiple scanner operations simultaneously
|
||||||
|
- **System Resources**: Scanner operations can be CPU and I/O intensive during execution
|
||||||
|
|
||||||
|
### Scanner Best Practices
|
||||||
|
|
||||||
|
Recommended usage patterns:
|
||||||
|
|
||||||
|
1. **Regular Scanning**: Set up regular scans for libraries with frequent media additions
|
||||||
|
2. **Selective Operations**: Use specific section IDs for targeted operations when possible
|
||||||
|
3. **Monitor Progress**: Use verbose mode to understand operation progress and impact
|
||||||
|
4. **Service Health**: Ensure Plex service is healthy before performing scanner operations
|
||||||
|
5. **Batch Operations**: For multiple libraries, consider using "scan all" for efficiency
|
||||||
|
|
||||||
|
### Scanner Troubleshooting
|
||||||
|
|
||||||
|
Common issues and solutions:
|
||||||
|
|
||||||
|
1. **"Plex Media Server is not running"**
|
||||||
|
- Start Plex: `./plex.sh start`
|
||||||
|
- Verify status: `./plex.sh status`
|
||||||
|
|
||||||
|
2. **"Plex Media Scanner binary not found"**
|
||||||
|
- Verify Plex installation is complete
|
||||||
|
- Check if binary exists in standard locations
|
||||||
|
- Ensure proper system PATH configuration
|
||||||
|
|
||||||
|
3. **"Section ID not found"**
|
||||||
|
- Use `./plex.sh scan list` to see valid section IDs
|
||||||
|
- Verify the library hasn't been deleted or renamed
|
||||||
|
|
||||||
|
4. **Scanner operations timeout or fail**
|
||||||
|
- Check system resources (CPU, memory, disk I/O)
|
||||||
|
- Verify media files are accessible and not corrupted
|
||||||
|
- Review scanner logs for detailed error information
|
||||||
|
|
||||||
|
### Scanner Bash Completion ⭐ **NEW**
|
||||||
|
|
||||||
|
The library scanner includes comprehensive bash completion support:
|
||||||
|
|
||||||
|
#### Tab Completion Features
|
||||||
|
|
||||||
|
- **Command Completion**: Tab completion for all scanner commands and options
|
||||||
|
- **Context-Aware**: Different completions based on command context
|
||||||
|
- **Boolean Flags**: Smart completion for `true`/`false` values where applicable
|
||||||
|
- **Alias Support**: Completion works with all plex-related aliases
|
||||||
|
|
||||||
|
#### Example Tab Completion Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Main plex.sh command completion
|
||||||
|
$ ./plex.sh <TAB>
|
||||||
|
start stop restart status scan repair nuclear help
|
||||||
|
|
||||||
|
# Scanner sub-command completion
|
||||||
|
$ ./plex.sh scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive -v --verbose -h --help
|
||||||
|
|
||||||
|
# Boolean flag completion for refresh and analyze
|
||||||
|
$ ./plex.sh scan refresh 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
$ ./plex.sh scan analyze 29 <TAB>
|
||||||
|
true false
|
||||||
|
|
||||||
|
# Direct scanner completion
|
||||||
|
$ ./scan-plex-libraries.sh <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
# Alias completion
|
||||||
|
$ plex-scan <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
|
||||||
|
$ plex-scanner <TAB>
|
||||||
|
list scan refresh analyze generate tree interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Completion Installation
|
||||||
|
|
||||||
|
Bash completion is automatically installed by the setup scripts:
|
||||||
|
|
||||||
|
- **Automatic Setup**: Installed by `bootstrap.sh` and `setup.sh`
|
||||||
|
- **Shell Integration**: Sourced in `.zshrc` for immediate availability
|
||||||
|
- **Alias Support**: Works with all plex-related aliases (`plex`, `plex-scan`, `plex-scanner`, etc.)
|
||||||
|
|
||||||
|
#### Manual Completion Setup
|
||||||
|
|
||||||
|
If needed, completion can be enabled manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable bash completion in zsh
|
||||||
|
autoload -U +X bashcompinit && bashcompinit
|
||||||
|
autoload -U compinit && compinit -u
|
||||||
|
|
||||||
|
# Source the completion script
|
||||||
|
source /home/acedanger/shell/completions/plex-scripts-completion.bash
|
||||||
|
```
|
||||||
|
|
||||||
## Enhanced Features
|
## Enhanced Features
|
||||||
|
|
||||||
### Smart Terminal Detection and Color Output
|
### Smart Terminal Detection and Color Output
|
||||||
@@ -139,6 +401,9 @@ Shows:
|
|||||||
|
|
||||||
# Display comprehensive status
|
# Display comprehensive status
|
||||||
./plex.sh status
|
./plex.sh status
|
||||||
|
|
||||||
|
# Launch library scanner
|
||||||
|
./plex.sh scan
|
||||||
```
|
```
|
||||||
|
|
||||||
### Available Commands
|
### Available Commands
|
||||||
@@ -148,12 +413,23 @@ The script supports the following commands:
|
|||||||
```bash
|
```bash
|
||||||
# Basic service operations
|
# Basic service operations
|
||||||
./plex.sh start # Start Plex Media Server
|
./plex.sh start # Start Plex Media Server
|
||||||
./plex.sh stop # Stop Plex Media Server
|
./plex.sh stop # Stop Plex Media Server
|
||||||
./plex.sh restart # Restart Plex Media Server (also accepts 'reload')
|
./plex.sh restart # Restart Plex Media Server (also accepts 'reload')
|
||||||
./plex.sh status # Show detailed service status (also accepts 'info')
|
./plex.sh status # Show detailed service status (also accepts 'info')
|
||||||
|
./plex.sh scan # Launch library scanner (NEW)
|
||||||
./plex.sh logs # Display recent Plex Media Server logs
|
./plex.sh logs # Display recent Plex Media Server logs
|
||||||
./plex.sh help # Show help message (also accepts '--help' or '-h')
|
./plex.sh help # Show help message (also accepts '--help' or '-h')
|
||||||
|
|
||||||
|
# Library scanner operations (NEW)
|
||||||
|
./plex.sh scan # Interactive scanner mode
|
||||||
|
./plex.sh scan list # List all library sections
|
||||||
|
./plex.sh scan scan # Scan all libraries for new media
|
||||||
|
./plex.sh scan scan 29 # Scan specific library (ID 29)
|
||||||
|
./plex.sh scan refresh 29 true # Force refresh specific library
|
||||||
|
./plex.sh scan analyze 29 true # Deep analyze specific library
|
||||||
|
./plex.sh scan generate 29 # Generate thumbnails for library
|
||||||
|
./plex.sh scan tree 29 # Show library structure
|
||||||
|
|
||||||
# Logs command with options
|
# Logs command with options
|
||||||
./plex.sh logs # Display last 20 lines of Plex logs
|
./plex.sh logs # Display last 20 lines of Plex logs
|
||||||
./plex.sh logs -n 50 # Display last 50 lines of Plex logs
|
./plex.sh logs -n 50 # Display last 50 lines of Plex logs
|
||||||
@@ -185,8 +461,17 @@ The `plex.sh` script is designed to work seamlessly with other Plex management s
|
|||||||
# Used by recovery scripts for service control
|
# Used by recovery scripts for service control
|
||||||
./recover-plex-database.sh # Uses plex.sh for service management
|
./recover-plex-database.sh # Uses plex.sh for service management
|
||||||
|
|
||||||
|
# Library scanner integration with service management
|
||||||
|
./plex.sh scan scan # Scan all libraries
|
||||||
|
./plex.sh scan refresh "" true # Force refresh all libraries after service restart
|
||||||
|
|
||||||
# Manual integration with porcelain output
|
# Manual integration with porcelain output
|
||||||
./plex.sh --porcelain start # For use in scripts/automation
|
./plex.sh --porcelain start # For use in scripts/automation
|
||||||
|
|
||||||
|
# Scanner integration in maintenance scripts
|
||||||
|
./plex.sh scan scan # Regular media scanning
|
||||||
|
./plex.sh scan analyze # Weekly deep analysis
|
||||||
|
./plex.sh scan generate # Thumbnail regeneration
|
||||||
```
|
```
|
||||||
|
|
||||||
### Security and Safety Features
|
### Security and Safety Features
|
||||||
@@ -434,6 +719,11 @@ For scheduled operations:
|
|||||||
|
|
||||||
# Daily health check
|
# Daily health check
|
||||||
0 6 * * * /home/acedanger/shell/plex/plex.sh status --validate
|
0 6 * * * /home/acedanger/shell/plex/plex.sh status --validate
|
||||||
|
|
||||||
|
# Automated library scanning (NEW)
|
||||||
|
0 4 * * * /home/acedanger/shell/plex/plex.sh scan scan # Daily scan for new media
|
||||||
|
0 2 * * 0 /home/acedanger/shell/plex/plex.sh scan refresh "" true # Weekly metadata refresh
|
||||||
|
0 1 * * 1 /home/acedanger/shell/plex/plex.sh scan analyze # Weekly media analysis
|
||||||
```
|
```
|
||||||
|
|
||||||
## Exit Codes and Return Values
|
## Exit Codes and Return Values
|
||||||
|
|||||||
151
plex/plex.sh
151
plex/plex.sh
@@ -19,6 +19,7 @@
|
|||||||
# Related Scripts:
|
# Related Scripts:
|
||||||
# - backup-plex.sh: Comprehensive backup solution
|
# - backup-plex.sh: Comprehensive backup solution
|
||||||
# - restore-plex.sh: Backup restoration utilities
|
# - restore-plex.sh: Backup restoration utilities
|
||||||
|
# - scan-plex-libraries.sh: Library scanning and metadata management
|
||||||
# - monitor-plex-backup.sh: Backup system monitoring
|
# - monitor-plex-backup.sh: Backup system monitoring
|
||||||
# - validate-plex-backups.sh: Backup validation tools
|
# - validate-plex-backups.sh: Backup validation tools
|
||||||
# - test-plex-backup.sh: Testing framework
|
# - test-plex-backup.sh: Testing framework
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
# ./plex.sh stop # Stop Plex service
|
# ./plex.sh stop # Stop Plex service
|
||||||
# ./plex.sh restart # Restart Plex service
|
# ./plex.sh restart # Restart Plex service
|
||||||
# ./plex.sh status # Show service status
|
# ./plex.sh status # Show service status
|
||||||
|
# ./plex.sh scan # Launch library scanner
|
||||||
# ./plex.sh # Interactive menu
|
# ./plex.sh # Interactive menu
|
||||||
#
|
#
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
@@ -111,40 +113,40 @@ show_loading() {
|
|||||||
# 🔧 Enhanced function to repair database issues
|
# 🔧 Enhanced function to repair database issues
|
||||||
repair_database() {
|
repair_database() {
|
||||||
print_status "${INFO}" "Attempting to repair Plex database..." "${BLUE}"
|
print_status "${INFO}" "Attempting to repair Plex database..." "${BLUE}"
|
||||||
|
|
||||||
local db_dir="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
|
local db_dir="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
|
||||||
local main_db="$db_dir/com.plexapp.plugins.library.db"
|
local main_db="$db_dir/com.plexapp.plugins.library.db"
|
||||||
local backup_db="$db_dir/com.plexapp.plugins.library.db.backup.$(date +%Y%m%d_%H%M%S)"
|
local backup_db="$db_dir/com.plexapp.plugins.library.db.backup.$(date +%Y%m%d_%H%M%S)"
|
||||||
local corrupted_dir="$db_dir/corrupted-$(date +%Y%m%d_%H%M%S)"
|
local corrupted_dir="$db_dir/corrupted-$(date +%Y%m%d_%H%M%S)"
|
||||||
|
|
||||||
if [[ ! -f "$main_db" ]]; then
|
if [[ ! -f "$main_db" ]]; then
|
||||||
print_status "${CROSS}" "Main database not found at: $main_db" "${RED}"
|
print_status "${CROSS}" "Main database not found at: $main_db" "${RED}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Stop Plex service first
|
# Stop Plex service first
|
||||||
print_status "${INFO}" "Stopping Plex service..." "${BLUE}"
|
print_status "${INFO}" "Stopping Plex service..." "${BLUE}"
|
||||||
sudo systemctl stop "$PLEX_SERVICE" 2>/dev/null || true
|
sudo systemctl stop "$PLEX_SERVICE" 2>/dev/null || true
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
# Check if critical tables exist
|
# Check if critical tables exist
|
||||||
print_status "${INFO}" "Checking database structure..." "${BLUE}"
|
print_status "${INFO}" "Checking database structure..." "${BLUE}"
|
||||||
local has_metadata_table=false
|
local has_metadata_table=false
|
||||||
if sudo -u plex sqlite3 "$main_db" "SELECT name FROM sqlite_master WHERE type='table' AND name='metadata_items';" 2>/dev/null | grep -q metadata_items; then
|
if sudo -u plex sqlite3 "$main_db" "SELECT name FROM sqlite_master WHERE type='table' AND name='metadata_items';" 2>/dev/null | grep -q metadata_items; then
|
||||||
has_metadata_table=true
|
has_metadata_table=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$has_metadata_table" == "false" ]]; then
|
if [[ "$has_metadata_table" == "false" ]]; then
|
||||||
print_status "${CROSS}" "Critical table 'metadata_items' is missing! Database is severely corrupted." "${RED}"
|
print_status "${CROSS}" "Critical table 'metadata_items' is missing! Database is severely corrupted." "${RED}"
|
||||||
print_status "${INFO}" "Attempting recovery from available backups..." "${YELLOW}"
|
print_status "${INFO}" "Attempting recovery from available backups..." "${YELLOW}"
|
||||||
|
|
||||||
# Find the best recovery candidate
|
# Find the best recovery candidate
|
||||||
local recovery_db=""
|
local recovery_db=""
|
||||||
local recovery_candidates=(
|
local recovery_candidates=(
|
||||||
"$db_dir/com.plexapp.plugins.library.db.recovery-"*
|
"$db_dir/com.plexapp.plugins.library.db.recovery-"*
|
||||||
"$db_dir/com.plexapp.plugins.library.db.20"*
|
"$db_dir/com.plexapp.plugins.library.db.20"*
|
||||||
)
|
)
|
||||||
|
|
||||||
for candidate in "${recovery_candidates[@]}"; do
|
for candidate in "${recovery_candidates[@]}"; do
|
||||||
if [[ -f "$candidate" && "$candidate" != *"tmp"* && "$candidate" != *"empty"* ]]; then
|
if [[ -f "$candidate" && "$candidate" != *"tmp"* && "$candidate" != *"empty"* ]]; then
|
||||||
# Test if this candidate has the metadata_items table
|
# Test if this candidate has the metadata_items table
|
||||||
@@ -154,36 +156,36 @@ repair_database() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ -n "$recovery_db" ]]; then
|
if [[ -n "$recovery_db" ]]; then
|
||||||
print_status "${CHECKMARK}" "Found recovery database: $(basename "$recovery_db")" "${GREEN}"
|
print_status "${CHECKMARK}" "Found recovery database: $(basename "$recovery_db")" "${GREEN}"
|
||||||
|
|
||||||
# Move corrupted database to backup location
|
# Move corrupted database to backup location
|
||||||
print_status "${INFO}" "Moving corrupted database to backup location..." "${BLUE}"
|
print_status "${INFO}" "Moving corrupted database to backup location..." "${BLUE}"
|
||||||
sudo mkdir -p "$corrupted_dir"
|
sudo mkdir -p "$corrupted_dir"
|
||||||
sudo mv "$main_db" "$corrupted_dir/"
|
sudo mv "$main_db" "$corrupted_dir/"
|
||||||
sudo mv "$main_db-shm" "$corrupted_dir/" 2>/dev/null || true
|
sudo mv "$main_db-shm" "$corrupted_dir/" 2>/dev/null || true
|
||||||
sudo mv "$main_db-wal" "$corrupted_dir/" 2>/dev/null || true
|
sudo mv "$main_db-wal" "$corrupted_dir/" 2>/dev/null || true
|
||||||
|
|
||||||
# Copy recovery database as new main database
|
# Copy recovery database as new main database
|
||||||
print_status "${INFO}" "Restoring database from recovery file..." "${BLUE}"
|
print_status "${INFO}" "Restoring database from recovery file..." "${BLUE}"
|
||||||
if sudo cp "$recovery_db" "$main_db"; then
|
if sudo cp "$recovery_db" "$main_db"; then
|
||||||
sudo chown plex:plex "$main_db"
|
sudo chown plex:plex "$main_db"
|
||||||
sudo chmod 644 "$main_db"
|
sudo chmod 644 "$main_db"
|
||||||
print_status "${CHECKMARK}" "Database restored successfully!" "${GREEN}"
|
print_status "${CHECKMARK}" "Database restored successfully!" "${GREEN}"
|
||||||
|
|
||||||
# Verify the restored database
|
# Verify the restored database
|
||||||
print_status "${INFO}" "Verifying restored database..." "${BLUE}"
|
print_status "${INFO}" "Verifying restored database..." "${BLUE}"
|
||||||
local integrity_result
|
local integrity_result
|
||||||
integrity_result=$(sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>&1)
|
integrity_result=$(sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>&1)
|
||||||
|
|
||||||
if echo "$integrity_result" | grep -q "ok"; then
|
if echo "$integrity_result" | grep -q "ok"; then
|
||||||
print_status "${CHECKMARK}" "Restored database integrity verified!" "${GREEN}"
|
print_status "${CHECKMARK}" "Restored database integrity verified!" "${GREEN}"
|
||||||
return 0
|
return 0
|
||||||
elif echo "$integrity_result" | grep -q "no such collation sequence: icu"; then
|
elif echo "$integrity_result" | grep -q "no such collation sequence: icu"; then
|
||||||
print_status "${CROSS}" "ICU collation sequence issue detected!" "${YELLOW}"
|
print_status "${CROSS}" "ICU collation sequence issue detected!" "${YELLOW}"
|
||||||
print_status "${INFO}" "Attempting ICU-aware recovery..." "${BLUE}"
|
print_status "${INFO}" "Attempting ICU-aware recovery..." "${BLUE}"
|
||||||
|
|
||||||
# Try ICU-aware recovery script
|
# Try ICU-aware recovery script
|
||||||
local icu_script="${SCRIPT_DIR}/icu-aware-recovery.sh"
|
local icu_script="${SCRIPT_DIR}/icu-aware-recovery.sh"
|
||||||
if [[ -f "$icu_script" ]]; then
|
if [[ -f "$icu_script" ]]; then
|
||||||
@@ -195,28 +197,28 @@ repair_database() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
print_status "${INFO}" "ICU recovery script not found, trying manual fix..." "${YELLOW}"
|
print_status "${INFO}" "ICU recovery script not found, trying manual fix..." "${YELLOW}"
|
||||||
|
|
||||||
# Try to recreate database without ICU dependencies
|
# Try to recreate database without ICU dependencies
|
||||||
local temp_db="/tmp/plex_temp_$(date +%Y%m%d_%H%M%S).db"
|
local temp_db="/tmp/plex_temp_$(date +%Y%m%d_%H%M%S).db"
|
||||||
print_status "${INFO}" "Attempting to dump and recreate database..." "${BLUE}"
|
print_status "${INFO}" "Attempting to dump and recreate database..." "${BLUE}"
|
||||||
|
|
||||||
if sudo -u plex sqlite3 "$recovery_db" ".dump" | grep -v "COLLATE icu_" | sudo -u plex sqlite3 "$temp_db"; then
|
if sudo -u plex sqlite3 "$recovery_db" ".dump" | grep -v "COLLATE icu_" | sudo -u plex sqlite3 "$temp_db"; then
|
||||||
print_status "${INFO}" "Database dump successful, replacing main database..." "${BLUE}"
|
print_status "${INFO}" "Database dump successful, replacing main database..." "${BLUE}"
|
||||||
sudo mv "$temp_db" "$main_db"
|
sudo mv "$temp_db" "$main_db"
|
||||||
sudo chown plex:plex "$main_db"
|
sudo chown plex:plex "$main_db"
|
||||||
sudo chmod 644 "$main_db"
|
sudo chmod 644 "$main_db"
|
||||||
|
|
||||||
# Verify the recreated database
|
# Verify the recreated database
|
||||||
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
||||||
print_status "${CHECKMARK}" "Database recreated successfully without ICU!" "${GREEN}"
|
print_status "${CHECKMARK}" "Database recreated successfully without ICU!" "${GREEN}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean up temp file if it exists
|
# Clean up temp file if it exists
|
||||||
sudo rm -f "$temp_db" 2>/dev/null || true
|
sudo rm -f "$temp_db" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${CROSS}" "Failed to resolve ICU collation issues!" "${RED}"
|
print_status "${CROSS}" "Failed to resolve ICU collation issues!" "${RED}"
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
@@ -238,21 +240,21 @@ repair_database() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create backup of current database
|
# Create backup of current database
|
||||||
print_status "${INFO}" "Creating backup of current database..." "${BLUE}"
|
print_status "${INFO}" "Creating backup of current database..." "${BLUE}"
|
||||||
if ! sudo cp "$main_db" "$backup_db"; then
|
if ! sudo cp "$main_db" "$backup_db"; then
|
||||||
print_status "${CROSS}" "Failed to create database backup!" "${RED}"
|
print_status "${CROSS}" "Failed to create database backup!" "${RED}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${CHECKMARK}" "Database backed up to: $backup_db" "${GREEN}"
|
print_status "${CHECKMARK}" "Database backed up to: $backup_db" "${GREEN}"
|
||||||
|
|
||||||
# Try to vacuum the database
|
# Try to vacuum the database
|
||||||
print_status "${INFO}" "Running VACUUM on database..." "${BLUE}"
|
print_status "${INFO}" "Running VACUUM on database..." "${BLUE}"
|
||||||
if sudo -u plex sqlite3 "$main_db" "VACUUM;" 2>/dev/null; then
|
if sudo -u plex sqlite3 "$main_db" "VACUUM;" 2>/dev/null; then
|
||||||
print_status "${CHECKMARK}" "Database VACUUM completed successfully" "${GREEN}"
|
print_status "${CHECKMARK}" "Database VACUUM completed successfully" "${GREEN}"
|
||||||
|
|
||||||
# Test integrity again
|
# Test integrity again
|
||||||
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
||||||
print_status "${CHECKMARK}" "Database integrity restored!" "${GREEN}"
|
print_status "${CHECKMARK}" "Database integrity restored!" "${GREEN}"
|
||||||
@@ -264,41 +266,41 @@ repair_database() {
|
|||||||
else
|
else
|
||||||
print_status "${CROSS}" "VACUUM operation failed" "${RED}"
|
print_status "${CROSS}" "VACUUM operation failed" "${RED}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Try reindex as last resort
|
# Try reindex as last resort
|
||||||
print_status "${INFO}" "Attempting REINDEX operation..." "${BLUE}"
|
print_status "${INFO}" "Attempting REINDEX operation..." "${BLUE}"
|
||||||
if sudo -u plex sqlite3 "$main_db" "REINDEX;" 2>/dev/null; then
|
if sudo -u plex sqlite3 "$main_db" "REINDEX;" 2>/dev/null; then
|
||||||
print_status "${CHECKMARK}" "Database REINDEX completed" "${GREEN}"
|
print_status "${CHECKMARK}" "Database REINDEX completed" "${GREEN}"
|
||||||
|
|
||||||
# Test integrity one more time
|
# Test integrity one more time
|
||||||
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
if sudo -u plex sqlite3 "$main_db" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
|
||||||
print_status "${CHECKMARK}" "Database integrity restored after REINDEX!" "${GREEN}"
|
print_status "${CHECKMARK}" "Database integrity restored after REINDEX!" "${GREEN}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${CROSS}" "All repair attempts failed" "${RED}"
|
print_status "${CROSS}" "All repair attempts failed" "${RED}"
|
||||||
print_status "${INFO}" "Manual intervention required. Consider:" "${YELLOW}"
|
print_status "${INFO}" "Manual intervention required. Consider:" "${YELLOW}"
|
||||||
echo -e "${DIM}${YELLOW} 1. Restore from external backup using restore-plex.sh${RESET}"
|
echo -e "${DIM}${YELLOW} 1. Restore from external backup using restore-plex.sh${RESET}"
|
||||||
echo -e "${DIM}${YELLOW} 2. Use nuclear recovery: ./nuclear-plex-recovery.sh${RESET}"
|
echo -e "${DIM}${YELLOW} 2. Use nuclear recovery: ./nuclear-plex-recovery.sh${RESET}"
|
||||||
echo -e "${DIM}${YELLOW} 3. Check corrupted database moved to: $corrupted_dir${RESET}"
|
echo -e "${DIM}${YELLOW} 3. Check corrupted database moved to: $corrupted_dir${RESET}"
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# 🔍 Function to check database integrity
|
# 🔍 Function to check database integrity
|
||||||
check_database_integrity() {
|
check_database_integrity() {
|
||||||
print_status "${INFO}" "Checking database integrity..." "${BLUE}"
|
print_status "${INFO}" "Checking database integrity..." "${BLUE}"
|
||||||
|
|
||||||
local db_dir="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
|
local db_dir="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
|
||||||
local main_db="$db_dir/com.plexapp.plugins.library.db"
|
local main_db="$db_dir/com.plexapp.plugins.library.db"
|
||||||
local repair_script="${SCRIPT_DIR}/plex-database-repair.sh"
|
local repair_script="${SCRIPT_DIR}/plex-database-repair.sh"
|
||||||
|
|
||||||
if [[ ! -f "$main_db" ]]; then
|
if [[ ! -f "$main_db" ]]; then
|
||||||
print_status "${CROSS}" "Main database not found at: $main_db" "${RED}"
|
print_status "${CROSS}" "Main database not found at: $main_db" "${RED}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use shared repair script for integrity checking if available
|
# Use shared repair script for integrity checking if available
|
||||||
if [[ -f "$repair_script" ]]; then
|
if [[ -f "$repair_script" ]]; then
|
||||||
if "$repair_script" check "$main_db" >/dev/null 2>&1; then
|
if "$repair_script" check "$main_db" >/dev/null 2>&1; then
|
||||||
@@ -316,7 +318,7 @@ check_database_integrity() {
|
|||||||
print_status "${INFO}" "Consider running database repair or restore from backup" "${YELLOW}"
|
print_status "${INFO}" "Consider running database repair or restore from backup" "${YELLOW}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${CHECKMARK}" "Database integrity check passed" "${GREEN}"
|
print_status "${CHECKMARK}" "Database integrity check passed" "${GREEN}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -334,7 +336,7 @@ start_plex() {
|
|||||||
|
|
||||||
# Reset any failed state first
|
# Reset any failed state first
|
||||||
sudo systemctl reset-failed "$PLEX_SERVICE" 2>/dev/null || true
|
sudo systemctl reset-failed "$PLEX_SERVICE" 2>/dev/null || true
|
||||||
|
|
||||||
# Check database integrity before starting
|
# Check database integrity before starting
|
||||||
if ! check_database_integrity; then
|
if ! check_database_integrity; then
|
||||||
print_status "${CROSS}" "Database integrity issues detected. Service may fail to start." "${RED}"
|
print_status "${CROSS}" "Database integrity issues detected. Service may fail to start." "${RED}"
|
||||||
@@ -343,15 +345,15 @@ start_plex() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${INFO}" "Attempting to start service..." "${BLUE}"
|
print_status "${INFO}" "Attempting to start service..." "${BLUE}"
|
||||||
|
|
||||||
if ! sudo systemctl start "$PLEX_SERVICE"; then
|
if ! sudo systemctl start "$PLEX_SERVICE"; then
|
||||||
print_status "${CROSS}" "Failed to start Plex Media Server!" "${RED}"
|
print_status "${CROSS}" "Failed to start Plex Media Server!" "${RED}"
|
||||||
print_status "${INFO}" "Checking service logs..." "${BLUE}"
|
print_status "${INFO}" "Checking service logs..." "${BLUE}"
|
||||||
|
|
||||||
# Show recent error logs
|
# Show recent error logs
|
||||||
echo -e "\n${DIM}${RED}Recent error logs:${RESET}"
|
echo -e "\n${DIM}${RED}Recent error logs:${RESET}"
|
||||||
sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 5 --since "1 minute ago" | tail -5
|
sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 5 --since "1 minute ago" | tail -5
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -359,34 +361,34 @@ start_plex() {
|
|||||||
sleep 3
|
sleep 3
|
||||||
local timeout=30
|
local timeout=30
|
||||||
local elapsed=0
|
local elapsed=0
|
||||||
|
|
||||||
print_status "${HOURGLASS}" "Waiting for service to initialize..." "${CYAN}"
|
print_status "${HOURGLASS}" "Waiting for service to initialize..." "${CYAN}"
|
||||||
|
|
||||||
while [[ $elapsed -lt $timeout ]]; do
|
while [[ $elapsed -lt $timeout ]]; do
|
||||||
if systemctl is-active --quiet "$PLEX_SERVICE"; then
|
if systemctl is-active --quiet "$PLEX_SERVICE"; then
|
||||||
print_status "${CHECKMARK}" "Plex Media Server started successfully!" "${GREEN}"
|
print_status "${CHECKMARK}" "Plex Media Server started successfully!" "${GREEN}"
|
||||||
print_footer
|
print_footer
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
elapsed=$((elapsed + 2))
|
elapsed=$((elapsed + 2))
|
||||||
echo -ne "${DIM}${CYAN} Waiting... ${elapsed}s/${timeout}s${RESET}\r"
|
echo -ne "${DIM}${CYAN} Waiting... ${elapsed}s/${timeout}s${RESET}\r"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
print_status "${CROSS}" "Service startup timeout or failed!" "${RED}"
|
print_status "${CROSS}" "Service startup timeout or failed!" "${RED}"
|
||||||
|
|
||||||
# Show current status
|
# Show current status
|
||||||
local status
|
local status
|
||||||
status=$(systemctl is-active "$PLEX_SERVICE" 2>/dev/null || echo "unknown")
|
status=$(systemctl is-active "$PLEX_SERVICE" 2>/dev/null || echo "unknown")
|
||||||
print_status "${INFO}" "Current status: $status" "${YELLOW}"
|
print_status "${INFO}" "Current status: $status" "${YELLOW}"
|
||||||
|
|
||||||
if [[ "$status" == "failed" ]]; then
|
if [[ "$status" == "failed" ]]; then
|
||||||
echo -e "\n${DIM}${RED}Recent error logs:${RESET}"
|
echo -e "\n${DIM}${RED}Recent error logs:${RESET}"
|
||||||
sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 10 --since "2 minutes ago"
|
sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 10 --since "2 minutes ago"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +471,7 @@ show_detailed_status() {
|
|||||||
|
|
||||||
# Show recent logs
|
# Show recent logs
|
||||||
echo -e "\n${DIM}${CYAN}+--- Recent Service Logs (24h) ---+${RESET}"
|
echo -e "\n${DIM}${CYAN}+--- Recent Service Logs (24h) ---+${RESET}"
|
||||||
|
|
||||||
# Try to get logs with sudo, fall back to user permissions
|
# Try to get logs with sudo, fall back to user permissions
|
||||||
local logs
|
local logs
|
||||||
if logs=$(sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 5 --since "24 hours ago" --output=short 2>/dev/null); then
|
if logs=$(sudo journalctl -u "$PLEX_SERVICE" --no-pager -n 5 --since "24 hours ago" --output=short 2>/dev/null); then
|
||||||
@@ -487,7 +489,7 @@ show_detailed_status() {
|
|||||||
echo -e "${DIM}${logs}${RESET}"
|
echo -e "${DIM}${logs}${RESET}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${DIM}${CYAN}+----------------------------------+${RESET}"
|
echo -e "${DIM}${CYAN}+----------------------------------+${RESET}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,6 +502,7 @@ show_help() {
|
|||||||
echo -e " ${YELLOW}${BOLD}stop${RESET} ${STOP_SIGN} Stop Plex Media Server"
|
echo -e " ${YELLOW}${BOLD}stop${RESET} ${STOP_SIGN} Stop Plex Media Server"
|
||||||
echo -e " ${BLUE}${BOLD}restart${RESET} ${RECYCLE} Restart Plex Media Server"
|
echo -e " ${BLUE}${BOLD}restart${RESET} ${RECYCLE} Restart Plex Media Server"
|
||||||
echo -e " ${CYAN}${BOLD}status${RESET} ${INFO} Show detailed service status"
|
echo -e " ${CYAN}${BOLD}status${RESET} ${INFO} Show detailed service status"
|
||||||
|
echo -e " ${PURPLE}${BOLD}scan${RESET} ${SPARKLES} Library scanner operations"
|
||||||
echo -e " ${RED}${BOLD}repair${RESET} [!] Repair database corruption issues"
|
echo -e " ${RED}${BOLD}repair${RESET} [!] Repair database corruption issues"
|
||||||
echo -e " ${RED}${BOLD}nuclear${RESET} [!!] Nuclear database recovery (last resort)"
|
echo -e " ${RED}${BOLD}nuclear${RESET} [!!] Nuclear database recovery (last resort)"
|
||||||
echo -e " ${PURPLE}${BOLD}help${RESET} [*] Show this help message"
|
echo -e " ${PURPLE}${BOLD}help${RESET} [*] Show this help message"
|
||||||
@@ -507,41 +510,72 @@ show_help() {
|
|||||||
echo -e "${DIM}${WHITE}Examples:${RESET}"
|
echo -e "${DIM}${WHITE}Examples:${RESET}"
|
||||||
echo -e " ${DIM}${SCRIPT_NAME} start # Start the Plex service${RESET}"
|
echo -e " ${DIM}${SCRIPT_NAME} start # Start the Plex service${RESET}"
|
||||||
echo -e " ${DIM}${SCRIPT_NAME} status # Show current status${RESET}"
|
echo -e " ${DIM}${SCRIPT_NAME} status # Show current status${RESET}"
|
||||||
|
echo -e " ${DIM}${SCRIPT_NAME} scan # Launch library scanner interface${RESET}"
|
||||||
echo -e " ${DIM}${SCRIPT_NAME} repair # Fix database issues${RESET}"
|
echo -e " ${DIM}${SCRIPT_NAME} repair # Fix database issues${RESET}"
|
||||||
echo -e " ${DIM}${SCRIPT_NAME} nuclear # Complete database replacement${RESET}"
|
echo -e " ${DIM}${SCRIPT_NAME} nuclear # Complete database replacement${RESET}"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 📚 Function to launch library scanner
|
||||||
|
launch_scanner() {
|
||||||
|
print_status "${SPARKLES}" "Launching Plex Library Scanner..." "${PURPLE}"
|
||||||
|
|
||||||
|
local scanner_script="${SCRIPT_DIR}/scan-plex-libraries.sh"
|
||||||
|
|
||||||
|
if [[ ! -f "$scanner_script" ]]; then
|
||||||
|
print_status "${CROSS}" "Library scanner script not found: $scanner_script" "${RED}"
|
||||||
|
print_status "${INFO}" "Expected location: ${SCRIPT_DIR}/scan-plex-libraries.sh" "${YELLOW}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -x "$scanner_script" ]]; then
|
||||||
|
print_status "${INFO}" "Making scanner script executable..." "${BLUE}"
|
||||||
|
chmod +x "$scanner_script"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if additional arguments were passed
|
||||||
|
if [[ $# -gt 1 ]]; then
|
||||||
|
# Pass remaining arguments to the scanner script
|
||||||
|
shift # Remove 'scan' argument
|
||||||
|
print_status "${INFO}" "Executing scanner with arguments: $*" "${BLUE}"
|
||||||
|
exec "$scanner_script" "$@"
|
||||||
|
else
|
||||||
|
# No additional arguments, launch interactive mode
|
||||||
|
print_status "${INFO}" "Launching scanner in interactive mode..." "${BLUE}"
|
||||||
|
exec "$scanner_script" interactive
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Nuclear database recovery function
|
# Nuclear database recovery function
|
||||||
nuclear_recovery() {
|
nuclear_recovery() {
|
||||||
print_status "${INFO}" "Starting nuclear database recovery..." "${RED}"
|
print_status "${INFO}" "Starting nuclear database recovery..." "${RED}"
|
||||||
|
|
||||||
local nuclear_script="${SCRIPT_DIR}/nuclear-plex-recovery.sh"
|
local nuclear_script="${SCRIPT_DIR}/nuclear-plex-recovery.sh"
|
||||||
|
|
||||||
if [[ ! -f "$nuclear_script" ]]; then
|
if [[ ! -f "$nuclear_script" ]]; then
|
||||||
print_status "${CROSS}" "Nuclear recovery script not found: $nuclear_script" "${RED}"
|
print_status "${CROSS}" "Nuclear recovery script not found: $nuclear_script" "${RED}"
|
||||||
print_status "${INFO}" "This script should be in the same directory as plex.sh" "${YELLOW}"
|
print_status "${INFO}" "This script should be in the same directory as plex.sh" "${YELLOW}"
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Warning message
|
# Warning message
|
||||||
echo -e "\n${RED}${BOLD}⚠️ WARNING: NUCLEAR RECOVERY ⚠️${RESET}"
|
echo -e "\n${RED}${BOLD}⚠️ WARNING: NUCLEAR RECOVERY ⚠️${RESET}"
|
||||||
echo -e "${RED}This will completely replace your Plex database with a backup!${RESET}"
|
echo -e "${RED}This will completely replace your Plex database with a backup!${RESET}"
|
||||||
echo -e "${RED}All changes since the backup was created will be lost!${RESET}"
|
echo -e "${RED}All changes since the backup was created will be lost!${RESET}"
|
||||||
echo -e "${YELLOW}This should only be used when standard repair methods have failed.${RESET}\n"
|
echo -e "${YELLOW}This should only be used when standard repair methods have failed.${RESET}\n"
|
||||||
|
|
||||||
# Get user confirmation
|
# Get user confirmation
|
||||||
echo -e "${CYAN}Do you want to proceed with nuclear recovery? ${RESET}"
|
echo -e "${CYAN}Do you want to proceed with nuclear recovery? ${RESET}"
|
||||||
echo -e "${DIM}Type 'YES' (uppercase) to confirm: ${RESET}"
|
echo -e "${DIM}Type 'YES' (uppercase) to confirm: ${RESET}"
|
||||||
read -r confirmation
|
read -r confirmation
|
||||||
|
|
||||||
if [[ "$confirmation" != "YES" ]]; then
|
if [[ "$confirmation" != "YES" ]]; then
|
||||||
print_status "${INFO}" "Nuclear recovery cancelled by user" "${YELLOW}"
|
print_status "${INFO}" "Nuclear recovery cancelled by user" "${YELLOW}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "${INFO}" "Executing nuclear recovery script..." "${BLUE}"
|
print_status "${INFO}" "Executing nuclear recovery script..." "${BLUE}"
|
||||||
|
|
||||||
# Execute the nuclear recovery script
|
# Execute the nuclear recovery script
|
||||||
if sudo "$nuclear_script" --auto; then
|
if sudo "$nuclear_script" --auto; then
|
||||||
print_status "${CHECKMARK}" "Nuclear recovery completed successfully!" "${GREEN}"
|
print_status "${CHECKMARK}" "Nuclear recovery completed successfully!" "${GREEN}"
|
||||||
@@ -551,7 +585,7 @@ nuclear_recovery() {
|
|||||||
else
|
else
|
||||||
local exit_code=$?
|
local exit_code=$?
|
||||||
print_status "${CROSS}" "Nuclear recovery failed!" "${RED}"
|
print_status "${CROSS}" "Nuclear recovery failed!" "${RED}"
|
||||||
|
|
||||||
case $exit_code in
|
case $exit_code in
|
||||||
2) print_status "${INFO}" "Backup file issues - check backup integrity" "${YELLOW}" ;;
|
2) print_status "${INFO}" "Backup file issues - check backup integrity" "${YELLOW}" ;;
|
||||||
3) print_status "${INFO}" "Database replacement failure - check permissions" "${YELLOW}" ;;
|
3) print_status "${INFO}" "Database replacement failure - check permissions" "${YELLOW}" ;;
|
||||||
@@ -559,7 +593,7 @@ nuclear_recovery() {
|
|||||||
5) print_status "${INFO}" "Rollback performed due to failure" "${YELLOW}" ;;
|
5) print_status "${INFO}" "Rollback performed due to failure" "${YELLOW}" ;;
|
||||||
*) print_status "${INFO}" "Unknown error occurred during recovery" "${YELLOW}" ;;
|
*) print_status "${INFO}" "Unknown error occurred during recovery" "${YELLOW}" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
print_footer
|
print_footer
|
||||||
return $exit_code
|
return $exit_code
|
||||||
fi
|
fi
|
||||||
@@ -568,11 +602,11 @@ nuclear_recovery() {
|
|||||||
# Database repair function
|
# Database repair function
|
||||||
repair_plex() {
|
repair_plex() {
|
||||||
print_status "${INFO}" "Starting Plex database repair..." "${YELLOW}"
|
print_status "${INFO}" "Starting Plex database repair..." "${YELLOW}"
|
||||||
|
|
||||||
# Run the enhanced repair function
|
# Run the enhanced repair function
|
||||||
if repair_database; then
|
if repair_database; then
|
||||||
print_status "${CHECKMARK}" "Database repair completed successfully!" "${GREEN}"
|
print_status "${CHECKMARK}" "Database repair completed successfully!" "${GREEN}"
|
||||||
|
|
||||||
# Try to start the service
|
# Try to start the service
|
||||||
print_status "${INFO}" "Starting Plex service..." "${BLUE}"
|
print_status "${INFO}" "Starting Plex service..." "${BLUE}"
|
||||||
if sudo systemctl start "$PLEX_SERVICE"; then
|
if sudo systemctl start "$PLEX_SERVICE"; then
|
||||||
@@ -594,17 +628,17 @@ repair_plex() {
|
|||||||
else
|
else
|
||||||
local repair_exit_code=$?
|
local repair_exit_code=$?
|
||||||
print_status "${CROSS}" "Database repair failed!" "${RED}"
|
print_status "${CROSS}" "Database repair failed!" "${RED}"
|
||||||
|
|
||||||
# Try to start the service anyway in case partial repair helped
|
# Try to start the service anyway in case partial repair helped
|
||||||
print_status "${INFO}" "Attempting to start Plex service anyway..." "${BLUE}"
|
print_status "${INFO}" "Attempting to start Plex service anyway..." "${BLUE}"
|
||||||
sudo systemctl start "$PLEX_SERVICE" 2>/dev/null || true
|
sudo systemctl start "$PLEX_SERVICE" 2>/dev/null || true
|
||||||
|
|
||||||
if [[ $repair_exit_code -eq 2 ]]; then
|
if [[ $repair_exit_code -eq 2 ]]; then
|
||||||
print_status "${INFO}" "Critical error - manual intervention required" "${YELLOW}"
|
print_status "${INFO}" "Critical error - manual intervention required" "${YELLOW}"
|
||||||
else
|
else
|
||||||
print_status "${INFO}" "Repair failed but service may still work with corrupted database" "${YELLOW}"
|
print_status "${INFO}" "Repair failed but service may still work with corrupted database" "${YELLOW}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_footer
|
print_footer
|
||||||
return $repair_exit_code
|
return $repair_exit_code
|
||||||
fi
|
fi
|
||||||
@@ -637,6 +671,9 @@ main() {
|
|||||||
"status"|"info")
|
"status"|"info")
|
||||||
show_detailed_status
|
show_detailed_status
|
||||||
;;
|
;;
|
||||||
|
"scan"|"scanner"|"library")
|
||||||
|
launch_scanner "$@"
|
||||||
|
;;
|
||||||
"repair"|"fix")
|
"repair"|"fix")
|
||||||
repair_plex
|
repair_plex
|
||||||
;;
|
;;
|
||||||
|
|||||||
770
plex/scan-plex-libraries.sh
Executable file
770
plex/scan-plex-libraries.sh
Executable file
@@ -0,0 +1,770 @@
|
|||||||
|
#!/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 "$@"
|
||||||
@@ -78,6 +78,8 @@ fi
|
|||||||
# Make scripts executable
|
# Make scripts executable
|
||||||
chmod +x "$DOTFILES_DIR/setup/setup.sh"
|
chmod +x "$DOTFILES_DIR/setup/setup.sh"
|
||||||
chmod +x "$DOTFILES_DIR/completions/backup-scripts-completion.bash" 2>/dev/null || true
|
chmod +x "$DOTFILES_DIR/completions/backup-scripts-completion.bash" 2>/dev/null || true
|
||||||
|
chmod +x "$DOTFILES_DIR/completions/plex-scripts-completion.bash" 2>/dev/null || true
|
||||||
|
chmod +x "$DOTFILES_DIR/completions/env-backup-completion.bash" 2>/dev/null || true
|
||||||
|
|
||||||
# Run setup script
|
# Run setup script
|
||||||
"$DOTFILES_DIR/setup/setup.sh"
|
"$DOTFILES_DIR/setup/setup.sh"
|
||||||
|
|||||||
@@ -430,21 +430,39 @@ clone_zsh_plugin "https://github.com/zsh-users/zsh-syntax-highlighting" "$PLUGIN
|
|||||||
clone_zsh_plugin "https://github.com/MichaelAquilina/zsh-you-should-use" "$PLUGINS_DIR/zsh-you-should-use"
|
clone_zsh_plugin "https://github.com/MichaelAquilina/zsh-you-should-use" "$PLUGINS_DIR/zsh-you-should-use"
|
||||||
|
|
||||||
# Set up bash completion for backup scripts
|
# Set up bash completion for backup scripts
|
||||||
echo -e "${YELLOW}Setting up bash completion for backup scripts...${NC}"
|
echo -e "${YELLOW}Setting up bash completion for scripts...${NC}"
|
||||||
COMPLETION_SCRIPT="$DOTFILES_DIR/completions/backup-scripts-completion.bash"
|
|
||||||
if [ -f "$COMPLETION_SCRIPT" ]; then
|
|
||||||
# Create completions directory in home
|
|
||||||
mkdir -p "$HOME/.local/share/bash-completion/completions"
|
|
||||||
|
|
||||||
# Copy completion script to user's completion directory
|
# Create completions directory in home
|
||||||
cp "$COMPLETION_SCRIPT" "$HOME/.local/share/bash-completion/completions/"
|
mkdir -p "$HOME/.local/share/bash-completion/completions"
|
||||||
|
|
||||||
# Make sure it's executable
|
# Install backup scripts completion
|
||||||
|
BACKUP_COMPLETION_SCRIPT="$DOTFILES_DIR/completions/backup-scripts-completion.bash"
|
||||||
|
if [ -f "$BACKUP_COMPLETION_SCRIPT" ]; then
|
||||||
|
cp "$BACKUP_COMPLETION_SCRIPT" "$HOME/.local/share/bash-completion/completions/"
|
||||||
chmod +x "$HOME/.local/share/bash-completion/completions/backup-scripts-completion.bash"
|
chmod +x "$HOME/.local/share/bash-completion/completions/backup-scripts-completion.bash"
|
||||||
|
echo -e "${GREEN}Backup scripts completion installed!${NC}"
|
||||||
echo -e "${GREEN}Bash completion script installed successfully!${NC}"
|
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}Warning: Bash completion script not found at $COMPLETION_SCRIPT${NC}"
|
echo -e "${YELLOW}Warning: Backup completion script not found at $BACKUP_COMPLETION_SCRIPT${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install plex scripts completion
|
||||||
|
PLEX_COMPLETION_SCRIPT="$DOTFILES_DIR/completions/plex-scripts-completion.bash"
|
||||||
|
if [ -f "$PLEX_COMPLETION_SCRIPT" ]; then
|
||||||
|
cp "$PLEX_COMPLETION_SCRIPT" "$HOME/.local/share/bash-completion/completions/"
|
||||||
|
chmod +x "$HOME/.local/share/bash-completion/completions/plex-scripts-completion.bash"
|
||||||
|
echo -e "${GREEN}Plex scripts completion installed!${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Plex completion script not found at $PLEX_COMPLETION_SCRIPT${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install environment backup completion
|
||||||
|
ENV_COMPLETION_SCRIPT="$DOTFILES_DIR/completions/env-backup-completion.bash"
|
||||||
|
if [ -f "$ENV_COMPLETION_SCRIPT" ]; then
|
||||||
|
cp "$ENV_COMPLETION_SCRIPT" "$HOME/.local/share/bash-completion/completions/"
|
||||||
|
chmod +x "$HOME/.local/share/bash-completion/completions/env-backup-completion.bash"
|
||||||
|
echo -e "${GREEN}Environment backup completion installed!${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Environment backup completion script not found at $ENV_COMPLETION_SCRIPT${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set up dotfiles
|
# Set up dotfiles
|
||||||
|
|||||||
Reference in New Issue
Block a user