- Add 4-step modal wizard for first-time configuration - Step 1: Welcome with Docker/container status checks - Step 2: Add/remove media library paths with type presets - Step 3: Network configuration (domain, HAProxy, ACME) - Step 4: Complete with link to Jellyfin Web UI - Add RPCD methods: get_wizard_status, set_wizard_complete, add_media_path, remove_media_path, get_media_paths - Auto-trigger wizard when installed but not configured - Add wizard.css with step indicators and form styling - Update Makefile to install CSS resources Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
310 lines
8.5 KiB
Bash
310 lines
8.5 KiB
Bash
#!/bin/sh
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
CONTAINER="secbx-jellyfin"
|
|
CONFIG="jellyfin"
|
|
|
|
case "$1" in
|
|
list)
|
|
echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"uninstall":{},"update":{},"configure_haproxy":{},"backup":{},"restore":{"path":"str"},"logs":{"lines":"int"},"get_wizard_status":{},"set_wizard_complete":{"complete":"int"},"add_media_path":{"path":"str","name":"str","type":"str"},"remove_media_path":{"section":"str"},"get_media_paths":{}}'
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
json_init
|
|
|
|
enabled=$(uci -q get ${CONFIG}.main.enabled)
|
|
image=$(uci -q get ${CONFIG}.main.image)
|
|
port=$(uci -q get ${CONFIG}.main.port)
|
|
data_path=$(uci -q get ${CONFIG}.main.data_path)
|
|
timezone=$(uci -q get ${CONFIG}.main.timezone)
|
|
hw_accel=$(uci -q get ${CONFIG}.transcoding.hw_accel)
|
|
|
|
# Network/domain config
|
|
domain=$(uci -q get ${CONFIG}.network.domain)
|
|
haproxy=$(uci -q get ${CONFIG}.network.haproxy)
|
|
firewall_wan=$(uci -q get ${CONFIG}.network.firewall_wan)
|
|
|
|
# Mesh config
|
|
mesh_enabled=$(uci -q get ${CONFIG}.mesh.enabled)
|
|
|
|
json_add_boolean "enabled" ${enabled:-0}
|
|
json_add_string "image" "${image:-jellyfin/jellyfin:latest}"
|
|
json_add_int "port" ${port:-8096}
|
|
json_add_string "data_path" "${data_path:-/srv/jellyfin}"
|
|
json_add_string "timezone" "${timezone:-Europe/Paris}"
|
|
json_add_boolean "hw_accel" ${hw_accel:-0}
|
|
json_add_string "domain" "${domain:-jellyfin.secubox.local}"
|
|
json_add_boolean "haproxy" ${haproxy:-0}
|
|
json_add_boolean "firewall_wan" ${firewall_wan:-0}
|
|
json_add_boolean "mesh_enabled" ${mesh_enabled:-0}
|
|
|
|
# Docker availability
|
|
if command -v docker >/dev/null 2>&1; then
|
|
json_add_boolean "docker_available" 1
|
|
else
|
|
json_add_boolean "docker_available" 0
|
|
fi
|
|
|
|
# Container status
|
|
if docker ps --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null | grep -q .; then
|
|
json_add_string "container_status" "running"
|
|
uptime=$(docker ps --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null)
|
|
json_add_string "container_uptime" "$uptime"
|
|
elif docker ps -a --filter "name=$CONTAINER" --format '{{.Status}}' 2>/dev/null | grep -q .; then
|
|
json_add_string "container_status" "stopped"
|
|
json_add_string "container_uptime" ""
|
|
else
|
|
json_add_string "container_status" "not_installed"
|
|
json_add_string "container_uptime" ""
|
|
fi
|
|
|
|
# HAProxy vhost status
|
|
if [ "${haproxy:-0}" = "1" ]; then
|
|
vhost_exists=$(uci show haproxy 2>/dev/null | grep "\.domain='${domain:-jellyfin.secubox.local}'" | head -1)
|
|
if [ -n "$vhost_exists" ]; then
|
|
json_add_string "haproxy_status" "configured"
|
|
else
|
|
json_add_string "haproxy_status" "pending"
|
|
fi
|
|
else
|
|
json_add_string "haproxy_status" "disabled"
|
|
fi
|
|
|
|
# Disk usage
|
|
dp="${data_path:-/srv/jellyfin}"
|
|
if [ -d "$dp" ]; then
|
|
disk_usage=$(du -sh "$dp" 2>/dev/null | cut -f1)
|
|
json_add_string "disk_usage" "${disk_usage:-0}"
|
|
else
|
|
json_add_string "disk_usage" ""
|
|
fi
|
|
|
|
# Media paths
|
|
json_add_array "media_paths"
|
|
for mp in $(uci -q get ${CONFIG}.media.media_path); do
|
|
json_add_string "" "$mp"
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
;;
|
|
|
|
start)
|
|
/etc/init.d/jellyfin start >/dev/null 2>&1
|
|
echo '{"success":true}'
|
|
;;
|
|
|
|
stop)
|
|
/etc/init.d/jellyfin stop >/dev/null 2>&1
|
|
echo '{"success":true}'
|
|
;;
|
|
|
|
restart)
|
|
/etc/init.d/jellyfin restart >/dev/null 2>&1
|
|
echo '{"success":true}'
|
|
;;
|
|
|
|
install)
|
|
output=$(/usr/sbin/jellyfinctl install 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
;;
|
|
|
|
uninstall)
|
|
output=$(/usr/sbin/jellyfinctl uninstall 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
;;
|
|
|
|
update)
|
|
output=$(/usr/sbin/jellyfinctl update 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
;;
|
|
|
|
configure_haproxy)
|
|
output=$(/usr/sbin/jellyfinctl configure-haproxy 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
;;
|
|
|
|
backup)
|
|
backup_file="/tmp/jellyfin-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
|
|
output=$(/usr/sbin/jellyfinctl backup "$backup_file" 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "path" "$backup_file"
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
;;
|
|
|
|
restore)
|
|
read -r input
|
|
path=$(echo "$input" | jsonfilter -e '@.path' 2>/dev/null)
|
|
if [ -z "$path" ]; then
|
|
echo '{"success":false,"output":"No backup path specified"}'
|
|
else
|
|
output=$(/usr/sbin/jellyfinctl restore "$path" 2>&1)
|
|
code=$?
|
|
json_init
|
|
json_add_boolean "success" $((code == 0))
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
fi
|
|
;;
|
|
|
|
logs)
|
|
read -r input
|
|
lines=$(echo "$input" | jsonfilter -e '@.lines' 2>/dev/null)
|
|
[ -z "$lines" ] && lines=50
|
|
|
|
logs=$(docker logs --tail "$lines" "$CONTAINER" 2>&1 | tail -100)
|
|
json_init
|
|
json_add_string "logs" "$logs"
|
|
json_dump
|
|
;;
|
|
|
|
get_wizard_status)
|
|
json_init
|
|
|
|
# Check if container exists and running
|
|
installed=0
|
|
running=0
|
|
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER}$"; then
|
|
installed=1
|
|
docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER}$" && running=1
|
|
fi
|
|
|
|
# Check if any media paths configured
|
|
media_count=0
|
|
for section in $(uci show ${CONFIG} 2>/dev/null | grep "=media$" | cut -d'.' -f2 | cut -d'=' -f1); do
|
|
media_count=$((media_count + 1))
|
|
done
|
|
|
|
# Check if wizard completed
|
|
wizard_complete=$(uci -q get ${CONFIG}.main.wizard_complete)
|
|
|
|
json_add_boolean "installed" "$installed"
|
|
json_add_boolean "running" "$running"
|
|
json_add_int "media_count" "$media_count"
|
|
json_add_boolean "wizard_complete" "${wizard_complete:-0}"
|
|
|
|
# Show wizard if installed but not complete
|
|
show=0
|
|
[ "$installed" = "1" ] && [ "${wizard_complete:-0}" != "1" ] && show=1
|
|
json_add_boolean "show_wizard" "$show"
|
|
|
|
json_dump
|
|
;;
|
|
|
|
set_wizard_complete)
|
|
read -r input
|
|
complete=$(echo "$input" | jsonfilter -e '@.complete' 2>/dev/null)
|
|
|
|
uci set ${CONFIG}.main.wizard_complete="${complete:-1}"
|
|
uci commit ${CONFIG}
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
;;
|
|
|
|
add_media_path)
|
|
read -r input
|
|
path=$(echo "$input" | jsonfilter -e '@.path' 2>/dev/null)
|
|
name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
|
type=$(echo "$input" | jsonfilter -e '@.type' 2>/dev/null)
|
|
|
|
if [ -z "$path" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Path required"
|
|
json_dump
|
|
exit 0
|
|
fi
|
|
|
|
# Generate unique section name
|
|
section="media_$(echo "${name:-library}" | tr ' ' '_' | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9_')"
|
|
|
|
# Ensure unique
|
|
count=1
|
|
base_section="$section"
|
|
while uci -q get "${CONFIG}.${section}" >/dev/null 2>&1; do
|
|
section="${base_section}_${count}"
|
|
count=$((count + 1))
|
|
done
|
|
|
|
uci set "${CONFIG}.${section}=media"
|
|
uci set "${CONFIG}.${section}.path=$path"
|
|
uci set "${CONFIG}.${section}.name=${name:-Library}"
|
|
uci set "${CONFIG}.${section}.type=${type:-movies}"
|
|
uci commit ${CONFIG}
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "section" "$section"
|
|
json_dump
|
|
;;
|
|
|
|
remove_media_path)
|
|
read -r input
|
|
section=$(echo "$input" | jsonfilter -e '@.section' 2>/dev/null)
|
|
|
|
if [ -z "$section" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Section required"
|
|
json_dump
|
|
exit 0
|
|
fi
|
|
|
|
uci delete "${CONFIG}.${section}" 2>/dev/null
|
|
uci commit ${CONFIG}
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_dump
|
|
;;
|
|
|
|
get_media_paths)
|
|
json_init
|
|
json_add_array "paths"
|
|
|
|
for section in $(uci show ${CONFIG} 2>/dev/null | grep "=media$" | cut -d'.' -f2 | cut -d'=' -f1); do
|
|
path=$(uci -q get "${CONFIG}.${section}.path")
|
|
name=$(uci -q get "${CONFIG}.${section}.name")
|
|
type=$(uci -q get "${CONFIG}.${section}.type")
|
|
|
|
json_add_object ""
|
|
json_add_string "section" "$section"
|
|
json_add_string "path" "$path"
|
|
json_add_string "name" "${name:-$section}"
|
|
json_add_string "type" "${type:-movies}"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
|
|
exit 0
|