#!/usr/bin/env bash # Migra recordings + Garage da volumi Docker al disco video (es. /media/videos/matchlivetv). # Uso: sudo bash scripts/migrate_videos_storage.sh # Richiede: docker compose prod fermo o in fase di stop; disco montato su MATCHLIVETV_VIDEOS_ROOT. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" VIDEOS_ROOT="${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}" COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}" ENV_FILE="${ENV_FILE:-${ROOT}/.env}" PROJECT="${COMPOSE_PROJECT_NAME:-infra}" VIDEOS_MOUNT="$(dirname "$VIDEOS_ROOT")" if ! mountpoint -q "$VIDEOS_MOUNT" 2>/dev/null && [ ! -d "$VIDEOS_ROOT" ]; then echo "ERRORE: disco video non montato (atteso ${VIDEOS_MOUNT})" exit 1 fi echo "Destinazione: ${VIDEOS_ROOT}" df -h "$VIDEOS_ROOT" 2>/dev/null || df -h "$VIDEOS_MOUNT" fs_mkdir() { if mkdir -p "$@" 2>/dev/null; then return 0 fi sudo mkdir -p "$@" } fs_rsync() { if rsync -aH "$@" 2>/dev/null; then return 0 fi sudo rsync -aH "$@" } fs_mkdir "${VIDEOS_ROOT}/recordings" "${VIDEOS_ROOT}/garage/meta" "${VIDEOS_ROOT}/garage/data" if [ ! -w "$VIDEOS_ROOT" ]; then sudo chown -R "$(id -un)":"$(id -gn)" "$VIDEOS_ROOT" fi volume_path() { local mp mp="$(docker volume inspect "${PROJECT}_$1" --format '{{ .Mountpoint }}' 2>/dev/null || true)" [ -n "$mp" ] && [ -d "$mp" ] && echo "$mp" && return 0 mp="$(sudo docker volume inspect "${PROJECT}_$1" --format '{{ .Mountpoint }}' 2>/dev/null || true)" [ -n "$mp" ] && echo "$mp" } copy_volume() { local vol="$1" dest="$2" local src src="$(volume_path "$vol")" if [ -z "$src" ] || [ ! -d "$src" ]; then echo "Volume ${PROJECT}_${vol} assente, salto copia verso ${dest}" return 0 fi if [ -n "$(sudo ls -A "$dest" 2>/dev/null || true)" ]; then echo "Già presente in ${dest}, salto copia da ${src}" return 0 fi echo "Copia ${src} → ${dest}..." fs_rsync "${src}/" "${dest}/" } echo "Arresto servizi che usano recordings/Garage..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop mediamtx sidekiq rails garage copy_volume recordings "${VIDEOS_ROOT}/recordings" copy_volume garage_meta "${VIDEOS_ROOT}/garage/meta" copy_volume garage_data "${VIDEOS_ROOT}/garage/data" if ! grep -q '^MATCHLIVETV_VIDEOS_ROOT=' "$ENV_FILE" 2>/dev/null; then echo "MATCHLIVETV_VIDEOS_ROOT=${VIDEOS_ROOT}" >> "$ENV_FILE" echo "Aggiunto MATCHLIVETV_VIDEOS_ROOT a ${ENV_FILE}" fi if [ -d "${ROOT}/garage/garage.prod.toml" ]; then echo "ERRORE: ${ROOT}/garage/garage.prod.toml è una directory (bind mount Docker corrotto)." echo "Rimuovi la directory e ripristina il file TOML prima di continuare." exit 1 fi echo "Riavvio stack..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --force-recreate mediamtx garage rails sidekiq echo "Ridimensiono capacità Garage sul disco video..." export MATCHLIVETV_VIDEOS_ROOT="$VIDEOS_ROOT" bash "${ROOT}/scripts/garage_set_capacity.sh" echo "Verifica mount:" docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T rails \ bundle exec rails runner \ 'puts({recordings: `du -sh /recordings 2>/dev/null`.strip, storage: Recordings::Storage.new.backend_name}.inspect)' echo "Fatto. Volumi Docker obsoleti (rimuovi dopo verifica):" for v in recordings garage_data garage_meta; do docker volume inspect "${PROJECT}_${v}" --format '{{ .Name }} ({{ .Mountpoint }})' 2>/dev/null || true done echo " docker volume rm ${PROJECT}_recordings ${PROJECT}_garage_data ${PROJECT}_garage_meta"