|
42e9b20…
|
noreply
|
1 |
#!/usr/bin/env bash |
|
42e9b20…
|
noreply
|
2 |
# PlanOpticon installer — cross-platform one-command setup |
|
42e9b20…
|
noreply
|
3 |
# Usage: curl -fsSL https://planopticon.dev/install.sh | bash |
|
42e9b20…
|
noreply
|
4 |
# |
|
42e9b20…
|
noreply
|
5 |
# Supports: macOS (Homebrew), Ubuntu/Debian (apt), Fedora/RHEL (dnf), Arch (pacman) |
|
42e9b20…
|
noreply
|
6 |
# Idempotent — safe to re-run. |
|
42e9b20…
|
noreply
|
7 |
|
|
42e9b20…
|
noreply
|
8 |
set -euo pipefail |
|
42e9b20…
|
noreply
|
9 |
|
|
42e9b20…
|
noreply
|
10 |
# --- Colors & helpers -------------------------------------------------------- |
|
42e9b20…
|
noreply
|
11 |
|
|
42e9b20…
|
noreply
|
12 |
RED='\033[0;31m' |
|
42e9b20…
|
noreply
|
13 |
GREEN='\033[0;32m' |
|
42e9b20…
|
noreply
|
14 |
YELLOW='\033[1;33m' |
|
42e9b20…
|
noreply
|
15 |
BLUE='\033[0;34m' |
|
42e9b20…
|
noreply
|
16 |
BOLD='\033[1m' |
|
42e9b20…
|
noreply
|
17 |
NC='\033[0m' |
|
42e9b20…
|
noreply
|
18 |
|
|
42e9b20…
|
noreply
|
19 |
info() { printf "${BLUE}[info]${NC} %s\n" "$*"; } |
|
42e9b20…
|
noreply
|
20 |
ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; } |
|
42e9b20…
|
noreply
|
21 |
warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; } |
|
42e9b20…
|
noreply
|
22 |
err() { printf "${RED}[error]${NC} %s\n" "$*" >&2; } |
|
42e9b20…
|
noreply
|
23 |
step() { printf "\n${BOLD}==> %s${NC}\n" "$*"; } |
|
42e9b20…
|
noreply
|
24 |
|
|
42e9b20…
|
noreply
|
25 |
command_exists() { command -v "$1" >/dev/null 2>&1; } |
|
42e9b20…
|
noreply
|
26 |
|
|
42e9b20…
|
noreply
|
27 |
# --- Detect OS & package manager --------------------------------------------- |
|
42e9b20…
|
noreply
|
28 |
|
|
42e9b20…
|
noreply
|
29 |
detect_os() { |
|
42e9b20…
|
noreply
|
30 |
case "$(uname -s)" in |
|
42e9b20…
|
noreply
|
31 |
Darwin) OS="macos" ;; |
|
42e9b20…
|
noreply
|
32 |
Linux) |
|
42e9b20…
|
noreply
|
33 |
if [ -f /etc/os-release ]; then |
|
42e9b20…
|
noreply
|
34 |
. /etc/os-release |
|
42e9b20…
|
noreply
|
35 |
case "$ID" in |
|
42e9b20…
|
noreply
|
36 |
ubuntu|debian|pop|linuxmint|elementary) OS="debian" ;; |
|
42e9b20…
|
noreply
|
37 |
fedora|rhel|centos|rocky|alma) OS="fedora" ;; |
|
42e9b20…
|
noreply
|
38 |
arch|manjaro|endeavouros) OS="arch" ;; |
|
42e9b20…
|
noreply
|
39 |
*) OS="linux-unknown" ;; |
|
42e9b20…
|
noreply
|
40 |
esac |
|
42e9b20…
|
noreply
|
41 |
else |
|
42e9b20…
|
noreply
|
42 |
OS="linux-unknown" |
|
42e9b20…
|
noreply
|
43 |
fi |
|
42e9b20…
|
noreply
|
44 |
;; |
|
42e9b20…
|
noreply
|
45 |
MINGW*|MSYS*|CYGWIN*) |
|
42e9b20…
|
noreply
|
46 |
err "Windows detected. Please use install.ps1 instead:" |
|
42e9b20…
|
noreply
|
47 |
err " irm https://planopticon.dev/install.ps1 | iex" |
|
42e9b20…
|
noreply
|
48 |
exit 1 |
|
42e9b20…
|
noreply
|
49 |
;; |
|
42e9b20…
|
noreply
|
50 |
*) |
|
42e9b20…
|
noreply
|
51 |
err "Unsupported OS: $(uname -s)" |
|
42e9b20…
|
noreply
|
52 |
exit 1 |
|
42e9b20…
|
noreply
|
53 |
;; |
|
42e9b20…
|
noreply
|
54 |
esac |
|
42e9b20…
|
noreply
|
55 |
} |
|
42e9b20…
|
noreply
|
56 |
|
|
42e9b20…
|
noreply
|
57 |
# --- Python ------------------------------------------------------------------ |
|
42e9b20…
|
noreply
|
58 |
|
|
42e9b20…
|
noreply
|
59 |
MIN_PYTHON_MAJOR=3 |
|
42e9b20…
|
noreply
|
60 |
MIN_PYTHON_MINOR=10 |
|
42e9b20…
|
noreply
|
61 |
|
|
42e9b20…
|
noreply
|
62 |
find_python() { |
|
42e9b20…
|
noreply
|
63 |
for cmd in python3 python; do |
|
42e9b20…
|
noreply
|
64 |
if command_exists "$cmd"; then |
|
42e9b20…
|
noreply
|
65 |
local ver |
|
42e9b20…
|
noreply
|
66 |
ver=$("$cmd" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null) || continue |
|
42e9b20…
|
noreply
|
67 |
local major minor |
|
42e9b20…
|
noreply
|
68 |
major=$(echo "$ver" | cut -d. -f1) |
|
42e9b20…
|
noreply
|
69 |
minor=$(echo "$ver" | cut -d. -f2) |
|
42e9b20…
|
noreply
|
70 |
if [ "$major" -ge "$MIN_PYTHON_MAJOR" ] && [ "$minor" -ge "$MIN_PYTHON_MINOR" ]; then |
|
42e9b20…
|
noreply
|
71 |
PYTHON="$cmd" |
|
42e9b20…
|
noreply
|
72 |
PYTHON_VERSION="$ver" |
|
42e9b20…
|
noreply
|
73 |
return 0 |
|
42e9b20…
|
noreply
|
74 |
fi |
|
42e9b20…
|
noreply
|
75 |
fi |
|
42e9b20…
|
noreply
|
76 |
done |
|
42e9b20…
|
noreply
|
77 |
return 1 |
|
42e9b20…
|
noreply
|
78 |
} |
|
42e9b20…
|
noreply
|
79 |
|
|
42e9b20…
|
noreply
|
80 |
install_python() { |
|
42e9b20…
|
noreply
|
81 |
step "Installing Python 3.10+" |
|
42e9b20…
|
noreply
|
82 |
case "$OS" in |
|
42e9b20…
|
noreply
|
83 |
macos) |
|
42e9b20…
|
noreply
|
84 |
if command_exists brew; then |
|
42e9b20…
|
noreply
|
85 |
brew install [email protected] |
|
42e9b20…
|
noreply
|
86 |
else |
|
42e9b20…
|
noreply
|
87 |
err "Homebrew not found. Install it first: https://brew.sh" |
|
42e9b20…
|
noreply
|
88 |
exit 1 |
|
42e9b20…
|
noreply
|
89 |
fi |
|
42e9b20…
|
noreply
|
90 |
;; |
|
42e9b20…
|
noreply
|
91 |
debian) |
|
42e9b20…
|
noreply
|
92 |
sudo apt-get update -qq |
|
42e9b20…
|
noreply
|
93 |
sudo apt-get install -y -qq python3 python3-pip python3-venv |
|
42e9b20…
|
noreply
|
94 |
;; |
|
42e9b20…
|
noreply
|
95 |
fedora) |
|
42e9b20…
|
noreply
|
96 |
sudo dnf install -y -q python3 python3-pip |
|
42e9b20…
|
noreply
|
97 |
;; |
|
42e9b20…
|
noreply
|
98 |
arch) |
|
42e9b20…
|
noreply
|
99 |
sudo pacman -S --noconfirm python python-pip |
|
42e9b20…
|
noreply
|
100 |
;; |
|
42e9b20…
|
noreply
|
101 |
*) |
|
42e9b20…
|
noreply
|
102 |
err "Cannot auto-install Python on this OS." |
|
42e9b20…
|
noreply
|
103 |
err "Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually." |
|
42e9b20…
|
noreply
|
104 |
exit 1 |
|
42e9b20…
|
noreply
|
105 |
;; |
|
42e9b20…
|
noreply
|
106 |
esac |
|
42e9b20…
|
noreply
|
107 |
} |
|
42e9b20…
|
noreply
|
108 |
|
|
42e9b20…
|
noreply
|
109 |
# --- FFmpeg ------------------------------------------------------------------ |
|
42e9b20…
|
noreply
|
110 |
|
|
42e9b20…
|
noreply
|
111 |
install_ffmpeg() { |
|
42e9b20…
|
noreply
|
112 |
step "Installing FFmpeg" |
|
42e9b20…
|
noreply
|
113 |
case "$OS" in |
|
42e9b20…
|
noreply
|
114 |
macos) |
|
42e9b20…
|
noreply
|
115 |
if command_exists brew; then |
|
42e9b20…
|
noreply
|
116 |
brew install ffmpeg |
|
42e9b20…
|
noreply
|
117 |
else |
|
42e9b20…
|
noreply
|
118 |
err "Homebrew not found. Install it first: https://brew.sh" |
|
42e9b20…
|
noreply
|
119 |
exit 1 |
|
42e9b20…
|
noreply
|
120 |
fi |
|
42e9b20…
|
noreply
|
121 |
;; |
|
42e9b20…
|
noreply
|
122 |
debian) |
|
42e9b20…
|
noreply
|
123 |
sudo apt-get update -qq |
|
42e9b20…
|
noreply
|
124 |
sudo apt-get install -y -qq ffmpeg |
|
42e9b20…
|
noreply
|
125 |
;; |
|
42e9b20…
|
noreply
|
126 |
fedora) |
|
42e9b20…
|
noreply
|
127 |
sudo dnf install -y -q ffmpeg-free || { |
|
42e9b20…
|
noreply
|
128 |
warn "ffmpeg-free not available, trying RPM Fusion..." |
|
42e9b20…
|
noreply
|
129 |
sudo dnf install -y -q \ |
|
42e9b20…
|
noreply
|
130 |
"https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" 2>/dev/null || true |
|
42e9b20…
|
noreply
|
131 |
sudo dnf install -y -q ffmpeg |
|
42e9b20…
|
noreply
|
132 |
} |
|
42e9b20…
|
noreply
|
133 |
;; |
|
42e9b20…
|
noreply
|
134 |
arch) |
|
42e9b20…
|
noreply
|
135 |
sudo pacman -S --noconfirm ffmpeg |
|
42e9b20…
|
noreply
|
136 |
;; |
|
42e9b20…
|
noreply
|
137 |
*) |
|
42e9b20…
|
noreply
|
138 |
err "Cannot auto-install FFmpeg on this OS." |
|
42e9b20…
|
noreply
|
139 |
err "Please install FFmpeg manually: https://ffmpeg.org/download.html" |
|
42e9b20…
|
noreply
|
140 |
exit 1 |
|
42e9b20…
|
noreply
|
141 |
;; |
|
42e9b20…
|
noreply
|
142 |
esac |
|
42e9b20…
|
noreply
|
143 |
} |
|
42e9b20…
|
noreply
|
144 |
|
|
42e9b20…
|
noreply
|
145 |
# --- Ollama (optional) ------------------------------------------------------- |
|
42e9b20…
|
noreply
|
146 |
|
|
42e9b20…
|
noreply
|
147 |
install_ollama() { |
|
42e9b20…
|
noreply
|
148 |
step "Installing Ollama" |
|
42e9b20…
|
noreply
|
149 |
if command_exists ollama; then |
|
42e9b20…
|
noreply
|
150 |
ok "Ollama already installed" |
|
42e9b20…
|
noreply
|
151 |
return 0 |
|
42e9b20…
|
noreply
|
152 |
fi |
|
42e9b20…
|
noreply
|
153 |
case "$OS" in |
|
42e9b20…
|
noreply
|
154 |
macos) |
|
42e9b20…
|
noreply
|
155 |
if command_exists brew; then |
|
42e9b20…
|
noreply
|
156 |
brew install ollama |
|
42e9b20…
|
noreply
|
157 |
else |
|
42e9b20…
|
noreply
|
158 |
curl -fsSL https://ollama.com/install.sh | sh |
|
42e9b20…
|
noreply
|
159 |
fi |
|
42e9b20…
|
noreply
|
160 |
;; |
|
42e9b20…
|
noreply
|
161 |
debian|fedora|arch|linux-unknown) |
|
42e9b20…
|
noreply
|
162 |
curl -fsSL https://ollama.com/install.sh | sh |
|
42e9b20…
|
noreply
|
163 |
;; |
|
42e9b20…
|
noreply
|
164 |
esac |
|
42e9b20…
|
noreply
|
165 |
} |
|
42e9b20…
|
noreply
|
166 |
|
|
42e9b20…
|
noreply
|
167 |
pull_ollama_models() { |
|
42e9b20…
|
noreply
|
168 |
info "Pulling recommended local models..." |
|
42e9b20…
|
noreply
|
169 |
ollama pull llama3.2 2>/dev/null || warn "Failed to pull llama3.2" |
|
42e9b20…
|
noreply
|
170 |
ollama pull llava 2>/dev/null || warn "Failed to pull llava (vision model)" |
|
42e9b20…
|
noreply
|
171 |
ok "Ollama models ready" |
|
42e9b20…
|
noreply
|
172 |
} |
|
42e9b20…
|
noreply
|
173 |
|
|
42e9b20…
|
noreply
|
174 |
# --- Extras selection -------------------------------------------------------- |
|
42e9b20…
|
noreply
|
175 |
|
|
42e9b20…
|
noreply
|
176 |
select_extras() { |
|
42e9b20…
|
noreply
|
177 |
EXTRAS="" |
|
42e9b20…
|
noreply
|
178 |
printf "\n${BOLD}Optional extras:${NC}\n" |
|
42e9b20…
|
noreply
|
179 |
echo " 1) core — just the basics (default)" |
|
42e9b20…
|
noreply
|
180 |
echo " 2) cloud — Google Drive, Dropbox, S3 integrations" |
|
42e9b20…
|
noreply
|
181 |
echo " 3) pdf — PDF document ingestion" |
|
42e9b20…
|
noreply
|
182 |
echo " 4) sources — YouTube, RSS, web scraping" |
|
42e9b20…
|
noreply
|
183 |
echo " 5) all — everything" |
|
42e9b20…
|
noreply
|
184 |
echo "" |
|
42e9b20…
|
noreply
|
185 |
printf "Choose extras [1-5, default=1]: " |
|
42e9b20…
|
noreply
|
186 |
|
|
42e9b20…
|
noreply
|
187 |
if [ -t 0 ]; then |
|
42e9b20…
|
noreply
|
188 |
read -r choice |
|
42e9b20…
|
noreply
|
189 |
else |
|
42e9b20…
|
noreply
|
190 |
choice="1" |
|
42e9b20…
|
noreply
|
191 |
echo "1 (non-interactive, using default)" |
|
42e9b20…
|
noreply
|
192 |
fi |
|
42e9b20…
|
noreply
|
193 |
|
|
42e9b20…
|
noreply
|
194 |
case "${choice:-1}" in |
|
42e9b20…
|
noreply
|
195 |
2) EXTRAS="[cloud]" ;; |
|
42e9b20…
|
noreply
|
196 |
3) EXTRAS="[pdf]" ;; |
|
42e9b20…
|
noreply
|
197 |
4) EXTRAS="[sources]" ;; |
|
42e9b20…
|
noreply
|
198 |
5) EXTRAS="[all]" ;; |
|
42e9b20…
|
noreply
|
199 |
*) EXTRAS="" ;; |
|
42e9b20…
|
noreply
|
200 |
esac |
|
42e9b20…
|
noreply
|
201 |
} |
|
42e9b20…
|
noreply
|
202 |
|
|
42e9b20…
|
noreply
|
203 |
# --- API key setup ----------------------------------------------------------- |
|
42e9b20…
|
noreply
|
204 |
|
|
42e9b20…
|
noreply
|
205 |
setup_api_keys() { |
|
42e9b20…
|
noreply
|
206 |
step "API key setup" |
|
42e9b20…
|
noreply
|
207 |
local env_file=".env" |
|
42e9b20…
|
noreply
|
208 |
|
|
42e9b20…
|
noreply
|
209 |
if [ -f "$env_file" ]; then |
|
42e9b20…
|
noreply
|
210 |
ok "Found existing .env file, skipping API key setup" |
|
42e9b20…
|
noreply
|
211 |
return 0 |
|
42e9b20…
|
noreply
|
212 |
fi |
|
42e9b20…
|
noreply
|
213 |
|
|
42e9b20…
|
noreply
|
214 |
if [ ! -t 0 ]; then |
|
42e9b20…
|
noreply
|
215 |
warn "Non-interactive mode — skipping API key setup" |
|
42e9b20…
|
noreply
|
216 |
info "Create a .env file with at least one API key:" |
|
42e9b20…
|
noreply
|
217 |
info " OPENAI_API_KEY=sk-..." |
|
42e9b20…
|
noreply
|
218 |
info " ANTHROPIC_API_KEY=sk-ant-..." |
|
42e9b20…
|
noreply
|
219 |
info " GEMINI_API_KEY=..." |
|
42e9b20…
|
noreply
|
220 |
return 0 |
|
42e9b20…
|
noreply
|
221 |
fi |
|
42e9b20…
|
noreply
|
222 |
|
|
42e9b20…
|
noreply
|
223 |
printf "\n${BOLD}PlanOpticon needs at least one AI provider API key.${NC}\n" |
|
42e9b20…
|
noreply
|
224 |
echo "You can add more later in .env" |
|
42e9b20…
|
noreply
|
225 |
echo "" |
|
42e9b20…
|
noreply
|
226 |
echo " 1) OpenAI (OPENAI_API_KEY)" |
|
42e9b20…
|
noreply
|
227 |
echo " 2) Anthropic (ANTHROPIC_API_KEY)" |
|
42e9b20…
|
noreply
|
228 |
echo " 3) Google (GEMINI_API_KEY)" |
|
42e9b20…
|
noreply
|
229 |
echo " 4) Ollama (local, no key needed)" |
|
42e9b20…
|
noreply
|
230 |
echo " 5) Skip for now" |
|
42e9b20…
|
noreply
|
231 |
echo "" |
|
42e9b20…
|
noreply
|
232 |
printf "Choose provider [1-5]: " |
|
42e9b20…
|
noreply
|
233 |
read -r provider_choice |
|
42e9b20…
|
noreply
|
234 |
|
|
42e9b20…
|
noreply
|
235 |
case "${provider_choice:-5}" in |
|
42e9b20…
|
noreply
|
236 |
1) |
|
42e9b20…
|
noreply
|
237 |
printf "Enter your OpenAI API key: " |
|
42e9b20…
|
noreply
|
238 |
read -r api_key |
|
42e9b20…
|
noreply
|
239 |
if [ -n "$api_key" ]; then |
|
42e9b20…
|
noreply
|
240 |
echo "OPENAI_API_KEY=$api_key" > "$env_file" |
|
42e9b20…
|
noreply
|
241 |
ok "Saved to .env" |
|
42e9b20…
|
noreply
|
242 |
fi |
|
42e9b20…
|
noreply
|
243 |
;; |
|
42e9b20…
|
noreply
|
244 |
2) |
|
42e9b20…
|
noreply
|
245 |
printf "Enter your Anthropic API key: " |
|
42e9b20…
|
noreply
|
246 |
read -r api_key |
|
42e9b20…
|
noreply
|
247 |
if [ -n "$api_key" ]; then |
|
42e9b20…
|
noreply
|
248 |
echo "ANTHROPIC_API_KEY=$api_key" > "$env_file" |
|
42e9b20…
|
noreply
|
249 |
ok "Saved to .env" |
|
42e9b20…
|
noreply
|
250 |
fi |
|
42e9b20…
|
noreply
|
251 |
;; |
|
42e9b20…
|
noreply
|
252 |
3) |
|
42e9b20…
|
noreply
|
253 |
printf "Enter your Google/Gemini API key: " |
|
42e9b20…
|
noreply
|
254 |
read -r api_key |
|
42e9b20…
|
noreply
|
255 |
if [ -n "$api_key" ]; then |
|
42e9b20…
|
noreply
|
256 |
echo "GEMINI_API_KEY=$api_key" > "$env_file" |
|
42e9b20…
|
noreply
|
257 |
ok "Saved to .env" |
|
42e9b20…
|
noreply
|
258 |
fi |
|
42e9b20…
|
noreply
|
259 |
;; |
|
42e9b20…
|
noreply
|
260 |
4) |
|
42e9b20…
|
noreply
|
261 |
info "Using Ollama — no API key needed" |
|
42e9b20…
|
noreply
|
262 |
echo "OLLAMA_HOST=http://localhost:11434" > "$env_file" |
|
42e9b20…
|
noreply
|
263 |
;; |
|
42e9b20…
|
noreply
|
264 |
5) |
|
42e9b20…
|
noreply
|
265 |
warn "Skipping API key setup. Add keys to .env later." |
|
42e9b20…
|
noreply
|
266 |
;; |
|
42e9b20…
|
noreply
|
267 |
esac |
|
42e9b20…
|
noreply
|
268 |
} |
|
42e9b20…
|
noreply
|
269 |
|
|
42e9b20…
|
noreply
|
270 |
# --- Main -------------------------------------------------------------------- |
|
42e9b20…
|
noreply
|
271 |
|
|
42e9b20…
|
noreply
|
272 |
main() { |
|
42e9b20…
|
noreply
|
273 |
printf "\n${BOLD}PlanOpticon Installer${NC}\n" |
|
42e9b20…
|
noreply
|
274 |
echo "=====================================" |
|
42e9b20…
|
noreply
|
275 |
echo "" |
|
42e9b20…
|
noreply
|
276 |
|
|
42e9b20…
|
noreply
|
277 |
detect_os |
|
42e9b20…
|
noreply
|
278 |
info "Detected OS: $OS" |
|
42e9b20…
|
noreply
|
279 |
|
|
42e9b20…
|
noreply
|
280 |
# --- Python --- |
|
42e9b20…
|
noreply
|
281 |
step "Checking Python" |
|
42e9b20…
|
noreply
|
282 |
if find_python; then |
|
42e9b20…
|
noreply
|
283 |
ok "Python $PYTHON_VERSION found ($PYTHON)" |
|
42e9b20…
|
noreply
|
284 |
else |
|
42e9b20…
|
noreply
|
285 |
warn "Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ not found" |
|
42e9b20…
|
noreply
|
286 |
install_python |
|
42e9b20…
|
noreply
|
287 |
if ! find_python; then |
|
42e9b20…
|
noreply
|
288 |
err "Python installation failed. Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually." |
|
42e9b20…
|
noreply
|
289 |
exit 1 |
|
42e9b20…
|
noreply
|
290 |
fi |
|
42e9b20…
|
noreply
|
291 |
ok "Python $PYTHON_VERSION installed" |
|
42e9b20…
|
noreply
|
292 |
fi |
|
42e9b20…
|
noreply
|
293 |
|
|
42e9b20…
|
noreply
|
294 |
# --- FFmpeg --- |
|
42e9b20…
|
noreply
|
295 |
step "Checking FFmpeg" |
|
42e9b20…
|
noreply
|
296 |
if command_exists ffmpeg; then |
|
42e9b20…
|
noreply
|
297 |
local ffmpeg_ver |
|
42e9b20…
|
noreply
|
298 |
ffmpeg_ver=$(ffmpeg -version 2>/dev/null | head -1 | awk '{print $3}') || ffmpeg_ver="unknown" |
|
42e9b20…
|
noreply
|
299 |
ok "FFmpeg found ($ffmpeg_ver)" |
|
42e9b20…
|
noreply
|
300 |
else |
|
42e9b20…
|
noreply
|
301 |
warn "FFmpeg not found" |
|
42e9b20…
|
noreply
|
302 |
install_ffmpeg |
|
42e9b20…
|
noreply
|
303 |
if command_exists ffmpeg; then |
|
42e9b20…
|
noreply
|
304 |
ok "FFmpeg installed" |
|
42e9b20…
|
noreply
|
305 |
else |
|
42e9b20…
|
noreply
|
306 |
err "FFmpeg installation failed. Please install manually." |
|
42e9b20…
|
noreply
|
307 |
exit 1 |
|
42e9b20…
|
noreply
|
308 |
fi |
|
42e9b20…
|
noreply
|
309 |
fi |
|
42e9b20…
|
noreply
|
310 |
|
|
42e9b20…
|
noreply
|
311 |
# --- pip install planopticon --- |
|
42e9b20…
|
noreply
|
312 |
step "Installing PlanOpticon" |
|
42e9b20…
|
noreply
|
313 |
select_extras |
|
42e9b20…
|
noreply
|
314 |
|
|
42e9b20…
|
noreply
|
315 |
local pip_target="planopticon${EXTRAS}" |
|
42e9b20…
|
noreply
|
316 |
info "Installing: $pip_target" |
|
42e9b20…
|
noreply
|
317 |
|
|
42e9b20…
|
noreply
|
318 |
if "$PYTHON" -m pip install --upgrade "$pip_target" 2>&1; then |
|
42e9b20…
|
noreply
|
319 |
ok "PlanOpticon installed" |
|
42e9b20…
|
noreply
|
320 |
else |
|
42e9b20…
|
noreply
|
321 |
warn "pip install failed, trying with --user flag" |
|
42e9b20…
|
noreply
|
322 |
"$PYTHON" -m pip install --user --upgrade "$pip_target" |
|
42e9b20…
|
noreply
|
323 |
ok "PlanOpticon installed (user)" |
|
42e9b20…
|
noreply
|
324 |
fi |
|
42e9b20…
|
noreply
|
325 |
|
|
42e9b20…
|
noreply
|
326 |
# --- Ollama (optional) --- |
|
42e9b20…
|
noreply
|
327 |
if [ -t 0 ]; then |
|
42e9b20…
|
noreply
|
328 |
printf "\n${BOLD}Would you like to install Ollama for local AI? [y/N]: ${NC}" |
|
42e9b20…
|
noreply
|
329 |
read -r install_ollama_choice |
|
42e9b20…
|
noreply
|
330 |
if [[ "${install_ollama_choice:-n}" =~ ^[Yy] ]]; then |
|
42e9b20…
|
noreply
|
331 |
install_ollama |
|
42e9b20…
|
noreply
|
332 |
if command_exists ollama; then |
|
42e9b20…
|
noreply
|
333 |
printf "Pull recommended models? (llama3.2 + llava, ~6GB) [y/N]: " |
|
42e9b20…
|
noreply
|
334 |
read -r pull_choice |
|
42e9b20…
|
noreply
|
335 |
if [[ "${pull_choice:-n}" =~ ^[Yy] ]]; then |
|
42e9b20…
|
noreply
|
336 |
pull_ollama_models |
|
42e9b20…
|
noreply
|
337 |
fi |
|
42e9b20…
|
noreply
|
338 |
fi |
|
42e9b20…
|
noreply
|
339 |
fi |
|
42e9b20…
|
noreply
|
340 |
fi |
|
42e9b20…
|
noreply
|
341 |
|
|
42e9b20…
|
noreply
|
342 |
# --- API keys --- |
|
42e9b20…
|
noreply
|
343 |
setup_api_keys |
|
42e9b20…
|
noreply
|
344 |
|
|
42e9b20…
|
noreply
|
345 |
# --- Verify --- |
|
42e9b20…
|
noreply
|
346 |
step "Verifying installation" |
|
42e9b20…
|
noreply
|
347 |
if command_exists planopticon; then |
|
42e9b20…
|
noreply
|
348 |
local version |
|
42e9b20…
|
noreply
|
349 |
version=$(planopticon --version 2>/dev/null || echo "installed") |
|
42e9b20…
|
noreply
|
350 |
ok "planopticon CLI ready ($version)" |
|
42e9b20…
|
noreply
|
351 |
else |
|
42e9b20…
|
noreply
|
352 |
# Might need PATH update |
|
42e9b20…
|
noreply
|
353 |
warn "planopticon not in PATH — you may need to restart your shell" |
|
42e9b20…
|
noreply
|
354 |
info "Or run: $PYTHON -m video_processor.cli.commands --version" |
|
42e9b20…
|
noreply
|
355 |
fi |
|
42e9b20…
|
noreply
|
356 |
|
|
42e9b20…
|
noreply
|
357 |
if planopticon list-models >/dev/null 2>&1; then |
|
42e9b20…
|
noreply
|
358 |
ok "Provider check passed" |
|
42e9b20…
|
noreply
|
359 |
else |
|
42e9b20…
|
noreply
|
360 |
warn "No AI providers configured yet — add an API key to .env" |
|
42e9b20…
|
noreply
|
361 |
fi |
|
42e9b20…
|
noreply
|
362 |
|
|
42e9b20…
|
noreply
|
363 |
# --- Done --- |
|
42e9b20…
|
noreply
|
364 |
printf "\n${GREEN}${BOLD}Installation complete!${NC}\n" |
|
42e9b20…
|
noreply
|
365 |
echo "" |
|
42e9b20…
|
noreply
|
366 |
echo "Quick start:" |
|
42e9b20…
|
noreply
|
367 |
echo " planopticon process video.mp4 # Analyze a video" |
|
42e9b20…
|
noreply
|
368 |
echo " planopticon companion # Start AI companion" |
|
42e9b20…
|
noreply
|
369 |
echo " planopticon list-models # Check available models" |
|
42e9b20…
|
noreply
|
370 |
echo "" |
|
42e9b20…
|
noreply
|
371 |
echo "Docs: https://planopticon.dev" |
|
42e9b20…
|
noreply
|
372 |
echo "" |
|
42e9b20…
|
noreply
|
373 |
} |
|
42e9b20…
|
noreply
|
374 |
|
|
42e9b20…
|
noreply
|
375 |
main "$@" |