713 lines
24 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# =============================================================================
# Media Manager - First-Time Setup Script
# =============================================================================
# This script guides you through the initial setup of the Media Manager
# application, including environment configuration and database initialization.
# =============================================================================
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Helper functions
print_header() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BOLD}${CYAN} $1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
}
print_step() {
echo -e "${GREEN}${NC} $1"
}
print_info() {
echo -e "${CYAN}${NC} $1"
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1"
}
print_success() {
echo -e "${GREEN}${NC} $1"
}
prompt_with_default() {
local prompt="$1"
local default="$2"
local var_name="$3"
local is_password="$4"
if [ "$is_password" = "true" ]; then
echo -en "${CYAN}?${NC} ${prompt} [${default}]: "
read -s value
echo ""
else
echo -en "${CYAN}?${NC} ${prompt} [${default}]: "
read value
fi
if [ -z "$value" ]; then
eval "$var_name=\"$default\""
else
eval "$var_name=\"$value\""
fi
}
prompt_yes_no() {
local prompt="$1"
local default="$2"
if [ "$default" = "y" ]; then
echo -en "${CYAN}?${NC} ${prompt} [Y/n]: "
else
echo -en "${CYAN}?${NC} ${prompt} [y/N]: "
fi
read answer
answer=${answer:-$default}
case "$answer" in
[Yy]* ) return 0;;
* ) return 1;;
esac
}
wait_for_container() {
local container="$1"
local max_attempts="${2:-30}"
local attempt=1
echo -n " Waiting for $container"
while [ $attempt -le $max_attempts ]; do
if docker compose ps "$container" 2>/dev/null | grep -q "healthy\|running"; then
echo -e " ${GREEN}ready${NC}"
return 0
fi
echo -n "."
sleep 2
attempt=$((attempt + 1))
done
echo -e " ${RED}timeout${NC}"
return 1
}
wait_for_postgres() {
local max_attempts="${1:-30}"
local attempt=1
echo -n " Waiting for PostgreSQL to be healthy"
while [ $attempt -le $max_attempts ]; do
if docker compose exec -T postgres pg_isready -U mediamanager -d library >/dev/null 2>&1; then
echo -e " ${GREEN}ready${NC}"
return 0
fi
echo -n "."
sleep 2
attempt=$((attempt + 1))
done
echo -e " ${RED}timeout${NC}"
return 1
}
wait_for_api() {
local max_attempts="${1:-30}"
local attempt=1
echo -n " Waiting for API to be healthy"
while [ $attempt -le $max_attempts ]; do
if curl -s http://localhost:3001/health >/dev/null 2>&1; then
echo -e " ${GREEN}ready${NC}"
return 0
fi
echo -n "."
sleep 2
attempt=$((attempt + 1))
done
echo -e " ${RED}timeout${NC}"
return 1
}
# =============================================================================
# Main Script
# =============================================================================
clear
echo ""
echo -e "${BOLD}${CYAN}"
echo " ╔══════════════════════════════════════════════════════════════════╗"
echo " ║ ║"
echo " ║ Media Manager - First-Time Setup ║"
echo " ║ ║"
echo " ╚══════════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo ""
echo " This script will guide you through the initial setup process."
echo " Press Ctrl+C at any time to cancel."
echo ""
# =============================================================================
# Prerequisites Check
# =============================================================================
print_header "Checking Prerequisites"
# Check Docker
if command -v docker &> /dev/null; then
DOCKER_VERSION=$(docker --version | cut -d' ' -f3 | tr -d ',')
print_success "Docker installed (v$DOCKER_VERSION)"
else
print_error "Docker is not installed"
echo " Please install Docker: https://docs.docker.com/get-docker/"
exit 1
fi
# Check Docker Compose
if docker compose version &> /dev/null; then
COMPOSE_VERSION=$(docker compose version --short)
print_success "Docker Compose installed (v$COMPOSE_VERSION)"
else
print_error "Docker Compose is not installed"
echo " Please install Docker Compose: https://docs.docker.com/compose/install/"
exit 1
fi
# Check if Docker daemon is running
if docker info &> /dev/null; then
print_success "Docker daemon is running"
else
print_error "Docker daemon is not running"
echo " Please start Docker and try again"
exit 1
fi
# Check for NVIDIA GPU (optional)
if command -v nvidia-smi &> /dev/null; then
if nvidia-smi &> /dev/null; then
GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n1)
print_success "NVIDIA GPU detected: $GPU_NAME"
HAS_GPU=true
else
print_warning "NVIDIA driver not responding"
HAS_GPU=false
fi
else
print_info "No NVIDIA GPU detected (optional - CPU encoding will be used)"
HAS_GPU=false
fi
# =============================================================================
# Check Existing Configuration
# =============================================================================
if [ -f ".env" ]; then
echo ""
print_warning "An existing .env file was found"
if prompt_yes_no "Do you want to overwrite it?" "n"; then
print_info "Backing up existing .env to .env.backup"
cp .env .env.backup
else
print_info "Keeping existing .env file"
SKIP_ENV_CREATION=true
fi
fi
# =============================================================================
# Environment Configuration
# =============================================================================
if [ "$SKIP_ENV_CREATION" != "true" ]; then
print_header "Required Configuration"
# Generate JWT secret
JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 64 | head -n 1)
print_info "Generated secure JWT secret"
# Generate PostgreSQL password
POSTGRES_PASSWORD=$(openssl rand -base64 24 2>/dev/null || cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
print_info "Generated secure PostgreSQL password"
echo ""
echo " Enter the initial admin account credentials."
echo " You will use these to log into the admin dashboard."
echo ""
prompt_with_default "Admin email" "admin@localhost" ADMIN_EMAIL
prompt_with_default "Admin password" "changeme" ADMIN_PASSWORD "true"
# =============================================================================
# Media Paths
# =============================================================================
print_header "Media Library Paths"
echo " Configure where your media files are stored."
echo " Use absolute paths. Leave blank to use defaults."
echo ""
# Try to detect current directory structure
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEFAULT_MEDIA_ROOT="$(dirname "$SCRIPT_DIR")/media"
prompt_with_default "Media root directory" "$DEFAULT_MEDIA_ROOT" MEDIA_ROOT
prompt_with_default "Local media directory" "$MEDIA_ROOT/local" MEDIA_LOCAL
prompt_with_default "Public media directory" "$MEDIA_ROOT/public" MEDIA_PUBLIC
prompt_with_default "Studios directory" "$MEDIA_LOCAL/studios" STUDIOS_PATH
prompt_with_default "GIFs directory" "$MEDIA_LOCAL/gifs" GIFS_PATH
prompt_with_default "Playback output directory" "$MEDIA_PUBLIC/playback" PLAYBACK_PATH
prompt_with_default "Compilations directory" "$MEDIA_PUBLIC/compilations" COMPILATIONS_PATH
prompt_with_default "Curated content directory" "$MEDIA_PUBLIC/curated" PUBLIC_CURATED_PATH
# =============================================================================
# Optional: SMTP Configuration
# =============================================================================
print_header "Email Configuration (Optional)"
echo " Email is used for user verification and password reset."
echo " Skip this section to use admin-approval workflow instead."
echo ""
if prompt_yes_no "Configure SMTP email?" "n"; then
prompt_with_default "SMTP host" "smtp.gmail.com" SMTP_HOST
prompt_with_default "SMTP port" "587" SMTP_PORT
prompt_with_default "SMTP username" "" SMTP_USER
prompt_with_default "SMTP password" "" SMTP_PASS "true"
prompt_with_default "From email address" "noreply@localhost" SMTP_FROM
prompt_with_default "From name" "Media Manager" SMTP_FROM_NAME
SMTP_SECURE="false"
else
SMTP_HOST=""
SMTP_PORT=""
SMTP_USER=""
SMTP_PASS=""
SMTP_FROM=""
SMTP_FROM_NAME=""
SMTP_SECURE=""
print_info "Skipping SMTP configuration"
fi
# =============================================================================
# Optional: AI Features
# =============================================================================
print_header "AI Features Configuration"
if [ "$HAS_GPU" = "true" ]; then
echo " All AI services will start automatically with the application."
echo " These use your NVIDIA GPU for accelerated processing."
echo ""
echo " Services included:"
echo " - Ollama (vision + text models for video analysis)"
echo " - Whisper (speech transcription)"
echo " - JoyCaption (high-quality image captioning)"
echo " - Face Recognition (performer detection)"
echo " - YOLO Detection (person counting)"
echo " - JoyTag (visual tagging)"
echo ""
# Content Safety
echo -e " ${BOLD}Content Safety Moderation${NC}"
echo " AI-powered comment moderation using Llama Guard."
echo " Screens user comments for harmful content."
echo ""
if prompt_yes_no "Enable content safety moderation?" "y"; then
SAFETY_CHECK_ENABLED="true"
print_info "Content safety will be enabled"
else
SAFETY_CHECK_ENABLED="false"
print_info "Content safety disabled (can be enabled later in .env)"
fi
# JoyCaption model check
echo ""
echo -e " ${BOLD}JoyCaption Vision Backend${NC}"
echo " Higher quality image captioning using JoyCaption GGUF model."
echo " Requires model files in ./models directory (~4.6GB download)"
echo ""
if prompt_yes_no "Will you download JoyCaption model files?" "y"; then
DIGEST_VISION_BACKEND="llama-server"
print_info "JoyCaption will be the primary vision backend"
print_warning "Download model files to ./models/ before using"
echo " Download from: https://huggingface.co/Mungert/llama-joycaption-beta-one-hf-llava-GGUF"
else
DIGEST_VISION_BACKEND="ollama"
print_info "Using Ollama for vision (default)"
fi
else
echo " AI features require an NVIDIA GPU."
echo " AI services will start but may not function properly without GPU."
echo ""
SAFETY_CHECK_ENABLED="false"
DIGEST_VISION_BACKEND="ollama"
print_warning "No GPU detected - AI features may be limited"
fi
# =============================================================================
# Application Settings
# =============================================================================
print_header "Application Settings"
prompt_with_default "Public gallery URL" "http://localhost:3080" APP_BASE_URL
prompt_with_default "Timezone" "UTC" TZ
# =============================================================================
# Generate .env File
# =============================================================================
print_header "Generating Configuration"
print_step "Creating .env file..."
cat > .env << EOF
# =============================================================================
# Media Manager Configuration
# Generated by build.sh on $(date)
# =============================================================================
# PostgreSQL Database (REQUIRED)
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
# JWT Authentication
JWT_SECRET=$JWT_SECRET
# Initial Admin Account
INITIAL_ADMIN_EMAIL=$ADMIN_EMAIL
INITIAL_ADMIN_PASSWORD=$ADMIN_PASSWORD
# SMTP Configuration
SMTP_HOST=$SMTP_HOST
SMTP_PORT=$SMTP_PORT
SMTP_SECURE=${SMTP_SECURE:-false}
SMTP_USER=$SMTP_USER
SMTP_PASS=$SMTP_PASS
SMTP_FROM=$SMTP_FROM
SMTP_FROM_NAME=$SMTP_FROM_NAME
# Email Settings
EMAIL_VERIFICATION_EXPIRY_HOURS=24
PASSWORD_RESET_EXPIRY_HOURS=1
# Application Settings
APP_BASE_URL=$APP_BASE_URL
ALLOWED_ORIGINS=http://localhost:3080,http://localhost:8080
TZ=$TZ
VITE_DEFAULT_LOCALE=
# Content Safety
SAFETY_CHECK_ENABLED=$SAFETY_CHECK_ENABLED
OLLAMA_BASE_URL=http://ollama:11434
OLLAMA_MODEL=llama-guard3:1b
# Digest Feature Models (for AI video analysis)
DIGEST_VISION_MODEL=huihui_ai/qwen3-vl-abliterated:2b
DIGEST_TEXT_MODEL=huihui_ai/qwen3-abliterated:4b
DIGEST_OLLAMA_URL=http://ollama:11434
DIGEST_WHISPER_URL=http://whisper:5000
DIGEST_FRAME_INTERVAL=30
# JoyCaption (alternative vision backend)
DIGEST_VISION_BACKEND=$DIGEST_VISION_BACKEND
JOYCAPTION_URL=http://joycaption:8080
JOYCAPTION_MODEL=llama-joycaption-beta-one-hf-llava
JOYCAPTION_FALLBACK_TO_OLLAMA=true
JOYCAPTION_MODEL_PATH=/models/llama-joycaption-beta-one-hf-llava.Q2_K.gguf
JOYCAPTION_MMPROJ_PATH=/models/llama-joycaption-beta-one-llava-mmproj-model-f16.gguf
JOYCAPTION_CTX_SIZE=4096
JOYCAPTION_GPU_LAYERS=999
JOYCAPTION_PARALLEL=1
# Whisper Transcription
WHISPER_MODEL=base
WHISPER_DEVICE=cuda
WHISPER_COMPUTE=float16
# Ollama Auto-Pull Models
OLLAMA_MODELS=llama-guard3:1b,huihui_ai/qwen3-vl-abliterated:2b,huihui_ai/qwen3-abliterated:4b
# Media Library Paths
MEDIA_ROOT=$MEDIA_ROOT
MEDIA_LOCAL=$MEDIA_LOCAL
MEDIA_PUBLIC=$MEDIA_PUBLIC
STUDIOS_PATH=$STUDIOS_PATH
ONLYFANS_PATH=$MEDIA_LOCAL/OnlyFans
GIFS_PATH=$GIFS_PATH
PLAYBACK_PATH=$PLAYBACK_PATH
COMPILATIONS_PATH=$COMPILATIONS_PATH
PUBLIC_CURATED_PATH=$PUBLIC_CURATED_PATH
QUICKIES_PATH=$MEDIA_PUBLIC/quickies
EOF
print_success ".env file created"
fi
# =============================================================================
# Create Docker Network
# =============================================================================
print_header "Setting Up Docker"
print_step "Checking Docker network..."
if docker network inspect media-network &> /dev/null; then
print_success "Docker network 'media-network' already exists"
else
print_step "Creating Docker network 'media-network'..."
docker network create media-network
print_success "Docker network created"
fi
# =============================================================================
# Create Required Directories
# =============================================================================
print_step "Creating data directories..."
mkdir -p data/thumbnails
mkdir -p data/digest_frames
mkdir -p data/ads
mkdir -p data/gallery
mkdir -p models
print_success "Data directories created"
# =============================================================================
# Start Services
# =============================================================================
print_header "Starting Services"
print_step "Pulling Docker images (this may take a while)..."
docker compose pull
# Check for JoyCaption model files
print_step "Checking JoyCaption model files..."
if [ -f "models/llama-joycaption-beta-one-hf-llava-Q3_K_M.gguf" ] && \
[ -f "models/llama-joycaption-beta-one-llava-mmproj-model-f16.gguf" ]; then
print_success "JoyCaption model files found"
else
print_warning "JoyCaption model files not found in ./models/"
echo ""
echo " Download these files to enable JoyCaption vision backend:"
echo " 1. llama-joycaption-beta-one-hf-llava-Q3_K_M.gguf (~4GB)"
echo " 2. llama-joycaption-beta-one-llava-mmproj-model-f16.gguf (~600MB)"
echo ""
echo " From: https://huggingface.co/Mungert/llama-joycaption-beta-one-hf-llava-GGUF"
echo ""
print_info "JoyCaption container will start but won't work until models are downloaded"
fi
print_step "Starting containers..."
# Start core services first
docker compose up -d
# Ask about AI services
echo ""
if [ "$HAS_GPU" = "true" ]; then
if prompt_yes_no "Start AI services now? (can be started later with: docker compose --profile ai up -d)" "y"; then
print_step "Starting AI services..."
docker compose --profile ai up -d
START_AI_SERVICES=true
else
START_AI_SERVICES=false
print_info "AI services not started. Start later with: docker compose --profile ai up -d"
fi
else
START_AI_SERVICES=false
print_info "Skipping AI services (no GPU detected)"
fi
# =============================================================================
# Wait for Services
# =============================================================================
print_header "Waiting for Services"
print_step "Checking service health..."
if ! wait_for_postgres 60; then
print_error "PostgreSQL failed to start"
echo ""
echo " Check the logs with: docker compose logs postgres"
exit 1
fi
# Wait for Ollama to start and pull models (only if AI services were started)
if [ "$START_AI_SERVICES" = "true" ]; then
print_step "Waiting for Ollama to start and pull models..."
echo " (This may take several minutes on first run as models are downloaded)"
echo ""
# Wait for Ollama to be responsive (not necessarily fully ready with models)
MAX_OLLAMA_WAIT=30
OLLAMA_ATTEMPT=1
echo -n " Waiting for Ollama"
while [ $OLLAMA_ATTEMPT -le $MAX_OLLAMA_WAIT ]; do
if curl -s http://localhost:11435/ >/dev/null 2>&1; then
echo -e " ${GREEN}ready${NC}"
break
fi
echo -n "."
sleep 2
OLLAMA_ATTEMPT=$((OLLAMA_ATTEMPT + 1))
done
if [ $OLLAMA_ATTEMPT -gt $MAX_OLLAMA_WAIT ]; then
print_warning "Ollama is still starting (models may still be downloading)"
echo " Check progress with: docker compose --profile ai logs -f ollama"
else
print_info "Ollama is running. Models will be pulled in the background."
echo " Monitor model downloads with: docker compose --profile ai logs -f ollama"
fi
fi
# =============================================================================
# Initialize Database
# =============================================================================
print_header "Initializing Database"
print_step "Pushing database schema..."
if docker compose exec -T api npx drizzle-kit push 2>&1 | tail -5; then
print_success "Database schema initialized"
else
print_error "Failed to initialize database schema"
echo ""
echo " Check the logs with: docker compose logs api"
exit 1
fi
# =============================================================================
# Restart API to Create Admin User
# =============================================================================
print_step "Restarting API to create admin user..."
docker compose restart api
sleep 3
if ! wait_for_api 30; then
print_warning "API health check timed out, but it may still be starting"
fi
# Check if admin was created
print_step "Verifying admin user creation..."
sleep 2
ADMIN_CHECK=$(docker compose logs api 2>&1 | grep -i "initial admin" | tail -1)
if echo "$ADMIN_CHECK" | grep -q "created"; then
print_success "Admin user created successfully"
elif echo "$ADMIN_CHECK" | grep -q "exists"; then
print_info "Admin user already exists"
else
print_warning "Could not verify admin creation (check logs if login fails)"
fi
# =============================================================================
# Seed Tags
# =============================================================================
print_step "Checking tag seeding..."
TAG_CHECK=$(docker compose logs api 2>&1 | grep -i "seed" | tail -1)
if echo "$TAG_CHECK" | grep -q "Seeded"; then
print_success "Default tags seeded"
elif echo "$TAG_CHECK" | grep -q "already"; then
print_info "Tags already exist"
fi
# =============================================================================
# Final Status
# =============================================================================
print_header "Setup Complete!"
echo -e " ${GREEN}${BOLD}Your Media Manager is ready!${NC}"
echo ""
echo " Access the application at:"
echo ""
echo -e " ${CYAN}Admin Dashboard:${NC} http://localhost:8080"
echo -e " ${CYAN}Public Gallery:${NC} http://localhost:3080"
echo -e " ${CYAN}API:${NC} http://localhost:3001"
# Show AI features status
if [ "$HAS_GPU" = "true" ]; then
echo ""
if [ "$START_AI_SERVICES" = "true" ]; then
echo " AI Services (running):"
echo -e " ${GREEN}${NC} Ollama (Vision + Text models)"
echo -e " ${GREEN}${NC} Whisper (Speech transcription)"
echo -e " ${GREEN}${NC} JoyCaption (Image captioning)"
echo -e " ${GREEN}${NC} Face Recognition"
echo -e " ${GREEN}${NC} YOLO Detection"
echo -e " ${GREEN}${NC} JoyTag (Visual tagging)"
if [ "$SAFETY_CHECK_ENABLED" = "true" ]; then
echo -e " ${GREEN}${NC} Content Safety Moderation"
fi
echo ""
echo -e " ${CYAN}Ollama:${NC} http://localhost:11435"
echo -e " ${CYAN}JoyCaption:${NC} http://localhost:11436"
else
echo " AI Services (not started):"
echo -e " ${YELLOW}${NC} Ollama, Whisper, JoyCaption, Face Recognition, YOLO, JoyTag"
echo ""
echo -e " Start with: ${YELLOW}docker compose --profile ai up -d${NC}"
fi
fi
echo ""
echo " Login credentials:"
echo ""
echo -e " ${CYAN}Email:${NC} $ADMIN_EMAIL"
echo -e " ${CYAN}Password:${NC} (the password you entered during setup)"
echo ""
echo " Useful commands:"
echo ""
echo -e " ${YELLOW}docker compose logs -f${NC} # View core service logs"
echo -e " ${YELLOW}docker compose down${NC} # Stop core services"
echo -e " ${YELLOW}docker compose up -d${NC} # Start core services"
echo -e " ${YELLOW}docker compose restart api${NC} # Restart API"
echo ""
echo -e " ${YELLOW}docker compose --profile ai up -d${NC} # Start AI services"
echo -e " ${YELLOW}docker compose --profile ai down${NC} # Stop all (including AI)"
echo -e " ${YELLOW}docker compose --profile ai logs -f ollama${NC} # View Ollama logs"
echo ""
echo " Next steps:"
echo ""
echo " 1. Log into the admin dashboard"
echo " 2. Click 'Scan Library' to index your videos"
echo " 3. Use 'Fractal' section to analyze videos with AI"
echo " 4. Explore the Library, Jobs, and other sections"
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""