#!/bin/bash # Idempotent install script — skips steps already completed on previous runs. # Model weights and repo are stored in /data for persistence across restarts. set -euo pipefail DATA_ROOT="${DATA_ROOT:-/data}" REPO_DIR="${DATA_ROOT}/app" SENTINEL="${REPO_DIR}/.install_complete" WEIGHTS_DIR="${DATA_ROOT}/models/make-it-animatable" mkdir -p "${DATA_ROOT}/temp" "${WEIGHTS_DIR}" # ── 1. Clone repo (once) ────────────────────────────────────────────────────── if [ ! -f "$SENTINEL" ]; then echo "==> Cloning Make-It-Animatable..." git lfs install rm -rf "$REPO_DIR" git clone https://github.com/jasongzy/Make-It-Animatable --recursive --single-branch --depth 1 "$REPO_DIR" else echo "==> Repo already present, skipping clone." fi # Symlink so the Space entrypoint can find the upstream package as ./app/. # `ln -sfn SOURCE EXISTING_DIR` creates EXISTING_DIR/app instead of replacing # the directory, so always resolve the destination explicitly. if [ -L "app" ]; then ln -sfn "$REPO_DIR" app elif [ ! -e "app" ]; then ln -s "$REPO_DIR" app fi # ── 2. FBX2glTF binary (once) ───────────────────────────────────────────────── if [ ! -f "${REPO_DIR}/util/FBX2glTF" ]; then echo "==> Downloading FBX2glTF..." wget -q https://github.com/facebookincubator/FBX2glTF/releases/download/v0.9.7/FBX2glTF-linux-x64 \ -O "${REPO_DIR}/util/FBX2glTF" chmod +x "${REPO_DIR}/util/FBX2glTF" else echo "==> FBX2glTF already present." fi # ── 3. Model weights (once) ─────────────────────────────────────────────────── if [ -d "${WEIGHTS_DIR}/data" ] && [ "$(ls -A ${WEIGHTS_DIR}/data 2>/dev/null)" ]; then echo "==> Model weights already present, skipping download." rm -rf "${REPO_DIR}/data" "${REPO_DIR}/output" ln -s "${WEIGHTS_DIR}/data" "${REPO_DIR}/data" ln -s "${WEIGHTS_DIR}/output" "${REPO_DIR}/output" else echo "==> Downloading model weights (one-time only)..." TMPDIR="${DATA_ROOT}/temp/hf-weights" rm -rf "$TMPDIR" python3 -c " from huggingface_hub import snapshot_download snapshot_download('jasongzy/Make-It-Animatable', local_dir='${TMPDIR}', ignore_patterns=['*.git*', '.gitattributes']) print('Download complete.') " [ -d "${TMPDIR}/data" ] && mv "${TMPDIR}/data" "${WEIGHTS_DIR}/data" [ -d "${TMPDIR}/output" ] && mv "${TMPDIR}/output" "${WEIGHTS_DIR}/output" rm -rf "$TMPDIR" rm -rf "${REPO_DIR}/data" "${REPO_DIR}/output" ln -s "${WEIGHTS_DIR}/data" "${REPO_DIR}/data" ln -s "${WEIGHTS_DIR}/output" "${REPO_DIR}/output" echo "==> Weights saved to ${WEIGHTS_DIR}" fi # ── 4. Mixamo bones ─────────────────────────────────────────────────────────── mkdir -p "${REPO_DIR}/data/Mixamo/animation" 2>/dev/null || true BONES_SRC="${REPO_DIR}/data/Standard Run.fbx" BONES_DST="${REPO_DIR}/data/Mixamo/bones.fbx" if [ -f "$BONES_SRC" ] && [ ! -f "$BONES_DST" ]; then cp "$BONES_SRC" "$BONES_DST" fi test -s "$BONES_DST" # ── 5. Runtime patches ──────────────────────────────────────────────────────── python3 patch_runtime.py # ── 6. Mark complete ────────────────────────────────────────────────────────── touch "$SENTINEL" echo "==> install.sh done."