| 1 |
#!/usr/bin/env bash |
|
| 2 | ||
| 3 |
set -euo pipefail |
|
| 4 | ||
| 5 |
VERSION="0.2.0" |
|
| 6 |
APP_NAME="rpi-camera-importer" |
|
| 7 |
PROJECT_DIR="/usr/local/lib/rpi-camera-importer" |
|
| 8 |
MEDIA_IMPORTER_SCRIPT="${PROJECT_DIR}/autonas-media-importer.sh"
|
|
| 9 |
CONFIG_DIR="${RPI_CAMERA_IMPORTER_CONFIG_DIR:-/etc/rpi-camera-importer}"
|
|
| 10 |
CONFIG_FILE="${CONFIG_DIR}/cameras.conf"
|
|
| 11 |
MOUNT_BASE="${RPI_CAMERA_IMPORTER_MOUNT_BASE:-/mnt/rpi-camera-importer}"
|
|
| 12 | ||
| 13 |
DRY_RUN=0 |
|
| 14 |
VERBOSE=0 |
|
| 15 | ||
| 16 |
log_message() {
|
|
| 17 |
local level="$1" |
|
| 18 |
local message="$2" |
|
| 19 |
local ts |
|
| 20 |
ts="$(date '+%Y-%m-%d %H:%M:%S')" |
|
| 21 |
echo "[$ts] [$level] $message" |
|
| 22 |
} |
|
| 23 | ||
| 24 |
usage() {
|
|
| 25 |
cat <<EOF |
|
| 26 |
${APP_NAME} v${VERSION}
|
|
| 27 | ||
| 28 |
Usage: |
|
| 29 |
${APP_NAME} wizard
|
|
| 30 |
${APP_NAME} list
|
|
| 31 |
${APP_NAME} discover
|
|
| 32 |
${APP_NAME} verify
|
|
| 33 |
${APP_NAME} update --profile <name> --destination <path>
|
|
| 34 |
${APP_NAME} import --profile <name> [--dry-run] [--verbose]
|
|
| 35 |
${APP_NAME} import --uuid <uuid> [--dry-run] [--verbose]
|
|
| 36 |
${APP_NAME} import --all [--dry-run] [--verbose]
|
|
| 37 | ||
| 38 |
Config format: |
|
| 39 |
name|uuid|destination_path |
|
| 40 | ||
| 41 |
Notes: |
|
| 42 |
- Import flow is device-trigger compatible (udev + systemd) |
|
| 43 |
- Media importer uses the proven AutoNAS importer script unchanged |
|
| 44 |
EOF |
|
| 45 |
} |
|
| 46 | ||
| 47 |
require_root() {
|
|
| 48 |
if [[ "${EUID}" -ne 0 ]]; then
|
|
| 49 |
log_message "ERROR" "Run as root" |
|
| 50 |
exit 1 |
|
| 51 |
fi |
|
| 52 |
} |
|
| 53 | ||
| 54 |
require_dependency() {
|
|
| 55 |
local bin="$1" |
|
| 56 |
if ! command -v "$bin" >/dev/null 2>&1; then |
|
| 57 |
log_message "ERROR" "Missing dependency: $bin" |
|
| 58 |
exit 1 |
|
| 59 |
fi |
|
| 60 |
} |
|
| 61 | ||
| 62 |
suggest_package_for_bin() {
|
|
| 63 |
local bin="$1" |
|
| 64 |
case "$bin" in |
|
| 65 |
exiftool) echo "libimage-exiftool-perl" ;; |
|
| 66 |
blkid|mount|umount|mountpoint) echo "util-linux" ;; |
|
| 67 |
find) echo "findutils" ;; |
|
| 68 |
awk) echo "mawk" ;; |
|
| 69 |
sed|grep) echo "sed grep" ;; |
|
| 70 |
logger) echo "bsdutils" ;; |
|
| 71 |
*) echo "" ;; |
|
| 72 |
esac |
|
| 73 |
} |
|
| 74 | ||
| 75 |
ensure_runtime_dependencies() {
|
|
| 76 |
local missing_bins=() |
|
| 77 |
local packages=() |
|
| 78 |
local dep pkg |
|
| 79 | ||
| 80 |
for dep in "$@"; do |
|
| 81 |
if ! command -v "$dep" >/dev/null 2>&1; then |
|
| 82 |
missing_bins+=("$dep")
|
|
| 83 |
pkg="$(suggest_package_for_bin "$dep")" |
|
| 84 |
if [[ -n "$pkg" ]]; then |
|
| 85 |
for p in $pkg; do |
|
| 86 |
packages+=("$p")
|
|
| 87 |
done |
|
| 88 |
fi |
|
| 89 |
fi |
|
| 90 |
done |
|
| 91 | ||
| 92 |
if [[ ${#missing_bins[@]} -eq 0 ]]; then
|
|
| 93 |
return 0 |
|
| 94 |
fi |
|
| 95 | ||
| 96 |
log_message "ERROR" "Missing runtime dependencies: ${missing_bins[*]}"
|
|
| 97 | ||
| 98 |
if [[ ${#packages[@]} -gt 0 ]]; then
|
|
| 99 |
local unique_packages |
|
| 100 |
unique_packages="$(printf '%s\n' "${packages[@]}" | awk '!seen[$0]++' | xargs)"
|
|
| 101 |
log_message "ERROR" "Suggested install command: apt update && apt install -y ${unique_packages}"
|
|
| 102 | ||
| 103 |
if [[ -t 0 && -t 1 ]] && command -v apt >/dev/null 2>&1; then |
|
| 104 |
local answer |
|
| 105 |
read -r -p "Install missing dependencies now? [y/N]: " answer |
|
| 106 |
if [[ "${answer,,}" == "y" || "${answer,,}" == "yes" ]]; then
|
|
| 107 |
apt update |
|
| 108 |
apt install -y ${unique_packages}
|
|
| 109 |
fi |
|
| 110 |
fi |
|
| 111 |
fi |
|
| 112 | ||
| 113 |
for dep in "$@"; do |
|
| 114 |
if ! command -v "$dep" >/dev/null 2>&1; then |
|
| 115 |
log_message "ERROR" "Dependency still missing after check: $dep" |
|
| 116 |
return 1 |
|
| 117 |
fi |
|
| 118 |
done |
|
| 119 | ||
| 120 |
return 0 |
|
| 121 |
} |
|
| 122 | ||
| 123 |
ensure_config_file() {
|
|
| 124 |
mkdir -p "$CONFIG_DIR" |
|
| 125 |
if [[ ! -f "$CONFIG_FILE" ]]; then |
|
| 126 |
cat > "$CONFIG_FILE" <<'EOF' |
|
| 127 |
# Camera profiles |
|
| 128 |
# Format: name|uuid|destination_path |
|
| 129 |
# Example: varia_rct715|A1B2-C3D4|/srv/media/varia |
|
| 130 |
EOF |
|
| 131 |
fi |
|
| 132 | ||
| 133 |
# Normalize legacy entries to the current 3-field profile schema. |
|
| 134 |
: > "${CONFIG_FILE}.tmp"
|
|
| 135 |
local line |
|
| 136 |
while IFS= read -r line; do |
|
| 137 |
if [[ -z "$line" || "$line" == \#* ]]; then |
|
| 138 |
echo "$line" >> "${CONFIG_FILE}.tmp"
|
|
| 139 |
continue |
|
| 140 |
fi |
|
| 141 | ||
| 142 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 143 |
if [[ -n "$name" && -n "$uuid" && -n "$destination" ]]; then |
|
| 144 |
echo "${name}|${uuid}|${destination}" >> "${CONFIG_FILE}.tmp"
|
|
| 145 |
fi |
|
| 146 |
done < "$CONFIG_FILE" |
|
| 147 |
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
| 148 |
} |
|
| 149 | ||
| 150 |
sanitize_profile_name() {
|
|
| 151 |
local v="$1" |
|
| 152 |
echo "$v" | tr -cd '[:alnum:]_-' | xargs |
|
| 153 |
} |
|
| 154 | ||
| 155 |
profile_exists_by_name() {
|
|
| 156 |
local profile="$1" |
|
| 157 |
grep -E "^${profile//./\\.}\|" "$CONFIG_FILE" >/dev/null 2>&1
|
|
| 158 |
} |
|
| 159 | ||
| 160 |
profile_exists_by_uuid() {
|
|
| 161 |
local uuid="$1" |
|
| 162 |
grep -E "^[^|]+\|${uuid//./\\.}\|" "$CONFIG_FILE" >/dev/null 2>&1
|
|
| 163 |
} |
|
| 164 | ||
| 165 |
find_profile_line() {
|
|
| 166 |
local mode="$1" |
|
| 167 |
local target="$2" |
|
| 168 |
local line |
|
| 169 | ||
| 170 |
while IFS= read -r line; do |
|
| 171 |
[[ -z "$line" || "$line" == \#* ]] && continue |
|
| 172 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 173 | ||
| 174 |
case "$mode" in |
|
| 175 |
name) |
|
| 176 |
if [[ "$name" == "$target" ]]; then |
|
| 177 |
echo "$line" |
|
| 178 |
return 0 |
|
| 179 |
fi |
|
| 180 |
;; |
|
| 181 |
uuid) |
|
| 182 |
if [[ "$uuid" == "$target" ]]; then |
|
| 183 |
echo "$line" |
|
| 184 |
return 0 |
|
| 185 |
fi |
|
| 186 |
;; |
|
| 187 |
*) |
|
| 188 |
return 1 |
|
| 189 |
;; |
|
| 190 |
esac |
|
| 191 |
done < "$CONFIG_FILE" |
|
| 192 | ||
| 193 |
return 1 |
|
| 194 |
} |
|
| 195 | ||
| 196 |
list_profiles() {
|
|
| 197 |
ensure_config_file |
|
| 198 |
echo "" |
|
| 199 |
echo "Configured camera profiles" |
|
| 200 |
echo "--------------------------------------------------------------------------" |
|
| 201 |
printf "%-20s %-16s %-30s\n" "Name" "UUID" "Destination" |
|
| 202 |
echo "--------------------------------------------------------------------------" |
|
| 203 | ||
| 204 |
local line |
|
| 205 |
while IFS= read -r line; do |
|
| 206 |
[[ -z "$line" || "$line" == \#* ]] && continue |
|
| 207 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 208 |
printf "%-20s %-16s %-30s\n" "$name" "$uuid" "$destination" |
|
| 209 |
done < "$CONFIG_FILE" |
|
| 210 | ||
| 211 |
echo "--------------------------------------------------------------------------" |
|
| 212 |
} |
|
| 213 | ||
| 214 |
discover_devices() {
|
|
| 215 |
ensure_runtime_dependencies "blkid" || return 1 |
|
| 216 |
echo "" |
|
| 217 |
echo "Detected removable/storage block devices" |
|
| 218 |
echo "---------------------------------------------------------------" |
|
| 219 |
blkid -o export 2>/dev/null | awk ' |
|
| 220 |
/^DEVNAME=/ {dev=$0; sub(/^DEVNAME=/, "", dev)}
|
|
| 221 |
/^UUID=/ {uuid=$0; sub(/^UUID=/, "", uuid)}
|
|
| 222 |
/^TYPE=/ {type=$0; sub(/^TYPE=/, "", type)}
|
|
| 223 |
/^$/ {
|
|
| 224 |
if (dev != "" && uuid != "") {
|
|
| 225 |
printf "%s | UUID=%s | FS=%s\n", dev, uuid, type |
|
| 226 |
} |
|
| 227 |
dev=""; uuid=""; type="" |
|
| 228 |
} |
|
| 229 |
END {
|
|
| 230 |
if (dev != "" && uuid != "") {
|
|
| 231 |
printf "%s | UUID=%s | FS=%s\n", dev, uuid, type |
|
| 232 |
} |
|
| 233 |
} |
|
| 234 |
' |
|
| 235 |
echo "---------------------------------------------------------------" |
|
| 236 |
} |
|
| 237 | ||
| 238 |
add_profile_interactive() {
|
|
| 239 |
ensure_config_file |
|
| 240 | ||
| 241 |
echo "" |
|
| 242 |
echo "Tip: conecteaza camera si ruleaza optional 'discover' intr-un alt terminal." |
|
| 243 | ||
| 244 |
local name uuid destination |
|
| 245 |
read -r -p "Profile name (ex: varia_rct715): " name |
|
| 246 |
name="$(sanitize_profile_name "$name")" |
|
| 247 |
if [[ -z "$name" ]]; then |
|
| 248 |
echo "Invalid profile name" |
|
| 249 |
return |
|
| 250 |
fi |
|
| 251 | ||
| 252 |
if profile_exists_by_name "$name"; then |
|
| 253 |
echo "Profile already exists" |
|
| 254 |
return |
|
| 255 |
fi |
|
| 256 | ||
| 257 |
read -r -p "Camera UUID (ID_FS_UUID): " uuid |
|
| 258 |
if [[ -z "$uuid" ]]; then |
|
| 259 |
echo "UUID is required" |
|
| 260 |
return |
|
| 261 |
fi |
|
| 262 | ||
| 263 |
if profile_exists_by_uuid "$uuid"; then |
|
| 264 |
echo "UUID already configured" |
|
| 265 |
return |
|
| 266 |
fi |
|
| 267 | ||
| 268 |
read -r -p "Destination path (ex: /srv/media/varia): " destination |
|
| 269 |
if [[ -z "$destination" ]]; then |
|
| 270 |
echo "Destination is required" |
|
| 271 |
return |
|
| 272 |
fi |
|
| 273 | ||
| 274 |
echo "${name}|${uuid}|${destination}" >> "$CONFIG_FILE"
|
|
| 275 |
echo "Profile '$name' added" |
|
| 276 |
} |
|
| 277 | ||
| 278 |
remove_profile_interactive() {
|
|
| 279 |
ensure_config_file |
|
| 280 |
local name |
|
| 281 |
read -r -p "Profile name to remove: " name |
|
| 282 |
name="$(sanitize_profile_name "$name")" |
|
| 283 | ||
| 284 |
if ! profile_exists_by_name "$name"; then |
|
| 285 |
echo "Profile not found" |
|
| 286 |
return |
|
| 287 |
fi |
|
| 288 | ||
| 289 |
grep -v -E "^${name//./\\.}\|" "$CONFIG_FILE" > "${CONFIG_FILE}.tmp"
|
|
| 290 |
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
| 291 |
echo "Profile '$name' removed" |
|
| 292 |
} |
|
| 293 | ||
| 294 |
update_profile_values() {
|
|
| 295 |
local target_name="$1" |
|
| 296 |
local new_destination="$2" |
|
| 297 | ||
| 298 |
if ! profile_exists_by_name "$target_name"; then |
|
| 299 |
log_message "ERROR" "Profile '$target_name' not found" |
|
| 300 |
return 1 |
|
| 301 |
fi |
|
| 302 | ||
| 303 |
: > "${CONFIG_FILE}.tmp"
|
|
| 304 |
local line |
|
| 305 |
local changed=0 |
|
| 306 |
while IFS= read -r line; do |
|
| 307 |
if [[ -z "$line" || "$line" == \#* ]]; then |
|
| 308 |
echo "$line" >> "${CONFIG_FILE}.tmp"
|
|
| 309 |
continue |
|
| 310 |
fi |
|
| 311 | ||
| 312 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 313 |
if [[ "$name" == "$target_name" ]]; then |
|
| 314 |
[[ -n "$new_destination" ]] && destination="$new_destination" |
|
| 315 |
changed=1 |
|
| 316 |
fi |
|
| 317 | ||
| 318 |
echo "${name}|${uuid}|${destination}" >> "${CONFIG_FILE}.tmp"
|
|
| 319 |
done < "$CONFIG_FILE" |
|
| 320 | ||
| 321 |
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
|
| 322 | ||
| 323 |
if [[ $changed -eq 1 ]]; then |
|
| 324 |
log_message "INFO" "Profile '$target_name' updated" |
|
| 325 |
fi |
|
| 326 |
} |
|
| 327 | ||
| 328 |
update_profile_interactive() {
|
|
| 329 |
ensure_config_file |
|
| 330 | ||
| 331 |
local name |
|
| 332 |
read -r -p "Profile name to update: " name |
|
| 333 |
name="$(sanitize_profile_name "$name")" |
|
| 334 | ||
| 335 |
local line |
|
| 336 |
if ! line="$(find_profile_line "name" "$name")"; then |
|
| 337 |
echo "Profile not found" |
|
| 338 |
return 1 |
|
| 339 |
fi |
|
| 340 | ||
| 341 |
local current_name current_uuid current_destination |
|
| 342 |
IFS='|' read -r current_name current_uuid current_destination _ <<< "$line" |
|
| 343 | ||
| 344 |
echo "Current UUID: $current_uuid" |
|
| 345 |
echo "Current destination: $current_destination" |
|
| 346 | ||
| 347 |
local new_destination |
|
| 348 |
read -r -p "New destination path (leave blank to keep current): " new_destination |
|
| 349 | ||
| 350 |
update_profile_values "$name" "$new_destination" |
|
| 351 |
} |
|
| 352 | ||
| 353 |
run_import_for_entry() {
|
|
| 354 |
local name="$1" |
|
| 355 |
local uuid="$2" |
|
| 356 |
local destination="$3" |
|
| 357 | ||
| 358 |
local mount_point="${MOUNT_BASE}/${name}"
|
|
| 359 |
local device_path="/dev/disk/by-uuid/${uuid}"
|
|
| 360 | ||
| 361 |
if [[ ! -x "$MEDIA_IMPORTER_SCRIPT" ]]; then |
|
| 362 |
log_message "ERROR" "Media importer missing: $MEDIA_IMPORTER_SCRIPT" |
|
| 363 |
return 1 |
|
| 364 |
fi |
|
| 365 | ||
| 366 |
if [[ ! -e "$device_path" ]]; then |
|
| 367 |
log_message "WARN" "Device for UUID $uuid not present, skipping profile '$name'" |
|
| 368 |
return 0 |
|
| 369 |
fi |
|
| 370 | ||
| 371 |
mkdir -p "$mount_point" "$destination" |
|
| 372 | ||
| 373 |
local mounted_here=0 |
|
| 374 |
if ! mountpoint -q "$mount_point" 2>/dev/null; then |
|
| 375 |
if [[ $DRY_RUN -eq 1 ]]; then |
|
| 376 |
log_message "INFO" "[dry-run] would mount $device_path at $mount_point" |
|
| 377 |
else |
|
| 378 |
mount "$device_path" "$mount_point" |
|
| 379 |
mounted_here=1 |
|
| 380 |
fi |
|
| 381 |
fi |
|
| 382 | ||
| 383 |
local args=("$mount_point" "$destination")
|
|
| 384 |
if [[ $DRY_RUN -eq 1 ]]; then |
|
| 385 |
args+=("--dry-run")
|
|
| 386 |
fi |
|
| 387 |
if [[ $VERBOSE -eq 1 ]]; then |
|
| 388 |
args+=("--verbose")
|
|
| 389 |
fi |
|
| 390 | ||
| 391 |
log_message "INFO" "Import start for '$name' (uuid=$uuid)" |
|
| 392 |
if [[ $DRY_RUN -eq 1 ]]; then |
|
| 393 |
"$MEDIA_IMPORTER_SCRIPT" "${args[@]}" || true
|
|
| 394 |
else |
|
| 395 |
"$MEDIA_IMPORTER_SCRIPT" "${args[@]}"
|
|
| 396 |
fi |
|
| 397 |
local importer_rc=$? |
|
| 398 | ||
| 399 |
if [[ $mounted_here -eq 1 ]]; then |
|
| 400 |
sync || true |
|
| 401 |
umount "$mount_point" || log_message "WARN" "Could not unmount $mount_point" |
|
| 402 |
fi |
|
| 403 | ||
| 404 |
if [[ $importer_rc -ne 0 ]]; then |
|
| 405 |
log_message "ERROR" "Import failed for '$name'" |
|
| 406 |
return 1 |
|
| 407 |
fi |
|
| 408 | ||
| 409 |
log_message "INFO" "Import finished for '$name'" |
|
| 410 |
return 0 |
|
| 411 |
} |
|
| 412 | ||
| 413 |
import_profile_by_name() {
|
|
| 414 |
ensure_config_file |
|
| 415 |
local target="$1" |
|
| 416 |
local line |
|
| 417 |
if ! line="$(find_profile_line "name" "$target")"; then |
|
| 418 |
log_message "ERROR" "Profile '$target' not found" |
|
| 419 |
return 1 |
|
| 420 |
fi |
|
| 421 | ||
| 422 |
local name uuid destination |
|
| 423 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 424 |
run_import_for_entry "$name" "$uuid" "$destination" |
|
| 425 |
} |
|
| 426 | ||
| 427 |
import_profile_by_uuid() {
|
|
| 428 |
ensure_config_file |
|
| 429 |
local target_uuid="$1" |
|
| 430 | ||
| 431 |
local line |
|
| 432 |
if ! line="$(find_profile_line "uuid" "$target_uuid")"; then |
|
| 433 |
log_message "INFO" "UUID '$target_uuid' is not configured" |
|
| 434 |
return 0 |
|
| 435 |
fi |
|
| 436 | ||
| 437 |
local name uuid destination |
|
| 438 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 439 |
run_import_for_entry "$name" "$uuid" "$destination" |
|
| 440 |
} |
|
| 441 | ||
| 442 |
verify_pipeline() {
|
|
| 443 |
ensure_config_file |
|
| 444 | ||
| 445 |
local rc=0 |
|
| 446 |
local deps=(mount umount mountpoint exiftool blkid find awk sed grep logger) |
|
| 447 |
local dep |
|
| 448 | ||
| 449 |
log_message "INFO" "Verifying dependencies" |
|
| 450 |
for dep in "${deps[@]}"; do
|
|
| 451 |
if command -v "$dep" >/dev/null 2>&1; then |
|
| 452 |
log_message "INFO" "OK dependency: $dep" |
|
| 453 |
else |
|
| 454 |
log_message "ERROR" "Missing dependency: $dep" |
|
| 455 |
rc=1 |
|
| 456 |
fi |
|
| 457 |
done |
|
| 458 | ||
| 459 |
local paths=( |
|
| 460 |
"$MEDIA_IMPORTER_SCRIPT" |
|
| 461 |
"/usr/local/lib/rpi-camera-importer/rpi-camera-disk-handler.sh" |
|
| 462 |
"/usr/local/lib/rpi-camera-importer/rpi-camera-udev-wrapper.sh" |
|
| 463 |
"/etc/udev/rules.d/99-rpi-camera-importer.rules" |
|
| 464 |
"/etc/systemd/system/rpi-camera-importer-attach@.service" |
|
| 465 |
) |
|
| 466 |
local p |
|
| 467 | ||
| 468 |
log_message "INFO" "Verifying runtime files" |
|
| 469 |
for p in "${paths[@]}"; do
|
|
| 470 |
if [[ -e "$p" ]]; then |
|
| 471 |
log_message "INFO" "OK file: $p" |
|
| 472 |
else |
|
| 473 |
log_message "ERROR" "Missing file: $p" |
|
| 474 |
rc=1 |
|
| 475 |
fi |
|
| 476 |
done |
|
| 477 | ||
| 478 |
if command -v systemctl >/dev/null 2>&1; then |
|
| 479 |
if systemctl daemon-reload >/dev/null 2>&1; then |
|
| 480 |
log_message "INFO" "OK systemd daemon-reload" |
|
| 481 |
else |
|
| 482 |
log_message "WARN" "systemd daemon-reload returned non-zero" |
|
| 483 |
fi |
|
| 484 |
fi |
|
| 485 | ||
| 486 |
log_message "INFO" "Configured profiles overview" |
|
| 487 |
list_profiles |
|
| 488 | ||
| 489 |
log_message "INFO" "Connected block devices overview" |
|
| 490 |
discover_devices |
|
| 491 | ||
| 492 |
if [[ $rc -eq 0 ]]; then |
|
| 493 |
log_message "INFO" "Mechanism verification completed successfully" |
|
| 494 |
else |
|
| 495 |
log_message "ERROR" "Mechanism verification failed" |
|
| 496 |
fi |
|
| 497 | ||
| 498 |
return $rc |
|
| 499 |
} |
|
| 500 | ||
| 501 |
import_all_profiles() {
|
|
| 502 |
ensure_config_file |
|
| 503 |
local line any=0 |
|
| 504 | ||
| 505 |
while IFS= read -r line; do |
|
| 506 |
[[ -z "$line" || "$line" == \#* ]] && continue |
|
| 507 |
IFS='|' read -r name uuid destination _ <<< "$line" |
|
| 508 | ||
| 509 |
any=1 |
|
| 510 |
run_import_for_entry "$name" "$uuid" "$destination" |
|
| 511 |
done < "$CONFIG_FILE" |
|
| 512 | ||
| 513 |
if [[ $any -eq 0 ]]; then |
|
| 514 |
log_message "WARN" "No profiles found" |
|
| 515 |
fi |
|
| 516 |
} |
|
| 517 | ||
| 518 |
wizard() {
|
|
| 519 |
ensure_config_file |
|
| 520 | ||
| 521 |
while true; do |
|
| 522 |
echo "" |
|
| 523 |
echo "=== Raspberry Pi Camera Importer Wizard ===" |
|
| 524 |
echo "1) List profiles" |
|
| 525 |
echo "2) Discover connected devices (UUID)" |
|
| 526 |
echo "3) Add profile" |
|
| 527 |
echo "4) Remove profile" |
|
| 528 |
echo "5) Update profile destination" |
|
| 529 |
echo "6) Run import for one profile" |
|
| 530 |
echo "7) Run import for all profiles" |
|
| 531 |
echo "0) Exit" |
|
| 532 |
echo "" |
|
| 533 | ||
| 534 |
local choice |
|
| 535 |
read -r -p "Choose option: " choice |
|
| 536 | ||
| 537 |
case "$choice" in |
|
| 538 |
1) list_profiles ;; |
|
| 539 |
2) discover_devices ;; |
|
| 540 |
3) add_profile_interactive ;; |
|
| 541 |
4) remove_profile_interactive ;; |
|
| 542 |
5) update_profile_interactive ;; |
|
| 543 |
6) |
|
| 544 |
local p |
|
| 545 |
read -r -p "Profile name: " p |
|
| 546 |
import_profile_by_name "$(sanitize_profile_name "$p")" |
|
| 547 |
;; |
|
| 548 |
7) import_all_profiles ;; |
|
| 549 |
0) break ;; |
|
| 550 |
*) echo "Invalid option" ;; |
|
| 551 |
esac |
|
| 552 |
done |
|
| 553 |
} |
|
| 554 | ||
| 555 |
main() {
|
|
| 556 |
local command="${1:-}"
|
|
| 557 |
if [[ -z "$command" ]]; then |
|
| 558 |
usage |
|
| 559 |
exit 1 |
|
| 560 |
fi |
|
| 561 |
shift || true |
|
| 562 | ||
| 563 |
case "$command" in |
|
| 564 |
--help|-h) |
|
| 565 |
usage |
|
| 566 |
;; |
|
| 567 |
list) |
|
| 568 |
require_root |
|
| 569 |
list_profiles |
|
| 570 |
;; |
|
| 571 |
discover) |
|
| 572 |
require_root |
|
| 573 |
ensure_runtime_dependencies "blkid" || exit 1 |
|
| 574 |
discover_devices |
|
| 575 |
;; |
|
| 576 |
verify) |
|
| 577 |
require_root |
|
| 578 |
verify_pipeline |
|
| 579 |
;; |
|
| 580 |
update) |
|
| 581 |
require_root |
|
| 582 |
ensure_config_file |
|
| 583 | ||
| 584 |
local profile="" |
|
| 585 |
local destination="" |
|
| 586 | ||
| 587 |
while [[ $# -gt 0 ]]; do |
|
| 588 |
case "$1" in |
|
| 589 |
--profile) |
|
| 590 |
profile="$2" |
|
| 591 |
shift 2 |
|
| 592 |
;; |
|
| 593 |
--destination) |
|
| 594 |
destination="$2" |
|
| 595 |
shift 2 |
|
| 596 |
;; |
|
| 597 |
*) |
|
| 598 |
log_message "ERROR" "Unknown option: $1" |
|
| 599 |
exit 1 |
|
| 600 |
;; |
|
| 601 |
esac |
|
| 602 |
done |
|
| 603 | ||
| 604 |
if [[ -z "$profile" ]]; then |
|
| 605 |
log_message "ERROR" "Use --profile <name>" |
|
| 606 |
exit 1 |
|
| 607 |
fi |
|
| 608 | ||
| 609 |
if [[ -z "$destination" ]]; then |
|
| 610 |
log_message "ERROR" "Use --destination <path>" |
|
| 611 |
exit 1 |
|
| 612 |
fi |
|
| 613 | ||
| 614 |
update_profile_values "$profile" "$destination" |
|
| 615 |
;; |
|
| 616 |
wizard) |
|
| 617 |
require_root |
|
| 618 |
ensure_runtime_dependencies "mount" "umount" "mountpoint" "exiftool" "blkid" || exit 1 |
|
| 619 |
wizard |
|
| 620 |
;; |
|
| 621 |
import) |
|
| 622 |
require_root |
|
| 623 |
ensure_runtime_dependencies "mount" "umount" "mountpoint" "exiftool" || exit 1 |
|
| 624 |
local profile="" |
|
| 625 |
local uuid="" |
|
| 626 |
local import_all=0 |
|
| 627 | ||
| 628 |
while [[ $# -gt 0 ]]; do |
|
| 629 |
case "$1" in |
|
| 630 |
--profile) |
|
| 631 |
profile="$2" |
|
| 632 |
shift 2 |
|
| 633 |
;; |
|
| 634 |
--uuid) |
|
| 635 |
uuid="$2" |
|
| 636 |
shift 2 |
|
| 637 |
;; |
|
| 638 |
--all) |
|
| 639 |
import_all=1 |
|
| 640 |
shift |
|
| 641 |
;; |
|
| 642 |
--dry-run) |
|
| 643 |
DRY_RUN=1 |
|
| 644 |
shift |
|
| 645 |
;; |
|
| 646 |
--verbose) |
|
| 647 |
VERBOSE=1 |
|
| 648 |
shift |
|
| 649 |
;; |
|
| 650 |
*) |
|
| 651 |
log_message "ERROR" "Unknown option: $1" |
|
| 652 |
exit 1 |
|
| 653 |
;; |
|
| 654 |
esac |
|
| 655 |
done |
|
| 656 | ||
| 657 |
if [[ $import_all -eq 1 ]]; then |
|
| 658 |
import_all_profiles |
|
| 659 |
elif [[ -n "$profile" ]]; then |
|
| 660 |
import_profile_by_name "$profile" |
|
| 661 |
elif [[ -n "$uuid" ]]; then |
|
| 662 |
import_profile_by_uuid "$uuid" |
|
| 663 |
else |
|
| 664 |
log_message "ERROR" "Use --profile <name>, --uuid <uuid> or --all" |
|
| 665 |
exit 1 |
|
| 666 |
fi |
|
| 667 |
;; |
|
| 668 |
*) |
|
| 669 |
log_message "ERROR" "Unknown command: $command" |
|
| 670 |
usage |
|
| 671 |
exit 1 |
|
| 672 |
;; |
|
| 673 |
esac |
|
| 674 |
} |
|
| 675 | ||
| 676 |
main "$@" |