#!/usr/bin/env bash # # Kino — one-line installer # # Usage (interactive): # curl -fsSL https://raw.githubusercontent.com/YOU/kino-media-server/main/install.sh | sudo bash # # Usage (non-interactive, override defaults with env): # curl -fsSL .../install.sh | sudo KINO_HTTP_PORT=9000 MEDIA_ROOT_HOST=/mnt/nas/movies bash # # What it does: # 1. Installs Docker + Compose if missing (apt/dnf/yum) # 2. Clones the repo into $KINO_DIR (default /opt/kino) # 3. Generates a strong JWT_SECRET and (if omitted) ADMIN_PASSWORD # 4. Writes .env # 5. Runs `docker compose up -d` # 6. Prints URL + credentials # set -euo pipefail # ---------- Defaults (override via env) ---------- KINO_REPO="${KINO_REPO:-https://github.com/myronblair/kino-app.git}" KINO_BRANCH="${KINO_BRANCH:-main}" KINO_DIR="${KINO_DIR:-/opt/kino}" KINO_HTTP_PORT="${KINO_HTTP_PORT:-8080}" MEDIA_ROOT_HOST="${MEDIA_ROOT_HOST:-${KINO_DIR}/media}" DB_NAME="${DB_NAME:-kino}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin@kino.local}" ADMIN_NAME="${ADMIN_NAME:-Admin}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-}" JWT_SECRET="${JWT_SECRET:-}" # ---------- Pretty output ---------- c_red() { printf "\033[31m%s\033[0m" "$*"; } c_grn() { printf "\033[32m%s\033[0m" "$*"; } c_ylw() { printf "\033[33m%s\033[0m" "$*"; } c_cyn() { printf "\033[36m%s\033[0m" "$*"; } say() { printf "%s %s\n" "$(c_cyn '›')" "$*"; } ok() { printf "%s %s\n" "$(c_grn '✓')" "$*"; } warn() { printf "%s %s\n" "$(c_ylw '!')" "$*"; } die() { printf "%s %s\n" "$(c_red '✗')" "$*" >&2; exit 1; } # ---------- Preflight ---------- [ "$(id -u)" -eq 0 ] || die "Run as root (or via sudo). Try: curl -fsSL ... | sudo bash" if [ "$KINO_REPO" = "https://github.com/CHANGE_ME/kino-media-server.git" ]; then die "KINO_REPO placeholder — pass KINO_REPO=... env var to install.sh" fi # ---------- OS detection & package manager ---------- if [ -r /etc/os-release ]; then . /etc/os-release; fi OS_ID="${ID:-unknown}" say "Detected OS: ${OS_ID} ${VERSION_ID:-}" pkg_install() { if command -v apt-get >/dev/null 2>&1; then apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@" elif command -v dnf >/dev/null 2>&1; then dnf install -y "$@" elif command -v yum >/dev/null 2>&1; then yum install -y "$@" else die "No supported package manager (apt/dnf/yum) found." fi } # ---------- Ensure prerequisites ---------- need_pkgs=() command -v curl >/dev/null 2>&1 || need_pkgs+=(curl) command -v git >/dev/null 2>&1 || need_pkgs+=(git) command -v openssl >/dev/null 2>&1 || need_pkgs+=(openssl) command -v ca-certificates >/dev/null 2>&1 || need_pkgs+=(ca-certificates) if [ ${#need_pkgs[@]} -gt 0 ]; then say "Installing prerequisites: ${need_pkgs[*]}" pkg_install "${need_pkgs[@]}" >/dev/null ok "Prerequisites installed" fi # ---------- Docker ---------- if ! command -v docker >/dev/null 2>&1; then say "Installing Docker via official convenience script" curl -fsSL https://get.docker.com | sh systemctl enable --now docker ok "Docker installed" else ok "Docker already installed ($(docker --version))" fi if ! docker compose version >/dev/null 2>&1; then # docker compose plugin missing — install say "Installing docker compose plugin" if command -v apt-get >/dev/null 2>&1; then pkg_install docker-compose-plugin >/dev/null else die "docker compose plugin not found — install it manually for your distro." fi ok "Compose plugin installed" else ok "Docker Compose already available ($(docker compose version --short))" fi # ---------- Clone or update repo ---------- if [ -d "$KINO_DIR/.git" ]; then say "Updating existing checkout at $KINO_DIR" git -C "$KINO_DIR" fetch --quiet origin "$KINO_BRANCH" git -C "$KINO_DIR" checkout --quiet "$KINO_BRANCH" git -C "$KINO_DIR" pull --quiet --ff-only origin "$KINO_BRANCH" || warn "git pull skipped (local changes?)" else say "Cloning $KINO_REPO → $KINO_DIR" mkdir -p "$(dirname "$KINO_DIR")" git clone --depth 1 --branch "$KINO_BRANCH" "$KINO_REPO" "$KINO_DIR" ok "Repo cloned" fi cd "$KINO_DIR" # ---------- Media root ---------- mkdir -p "$MEDIA_ROOT_HOST" ok "Media dir ready: $MEDIA_ROOT_HOST" # ---------- Secrets ---------- if [ -z "$JWT_SECRET" ]; then JWT_SECRET="$(openssl rand -hex 32)" ok "Generated random JWT_SECRET" fi if [ -z "$ADMIN_PASSWORD" ]; then ADMIN_PASSWORD="$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-20)" GENERATED_PASSWORD=1 ok "Generated random ADMIN_PASSWORD" fi # ---------- Write .env ---------- if [ -f .env ]; then cp .env ".env.bak.$(date +%Y%m%d%H%M%S)" warn ".env exists — backed up. Overwriting with new values." fi cat > .env </dev/null 2>&1; then ok "Backend healthy at $URL/api/" else warn "Backend not responding yet — check: cd $KINO_DIR && docker compose logs -f backend" fi # ---------- Done ---------- cat <