diff --git a/package/diy/docker-restore/Makefile b/package/diy/docker-restore/Makefile new file mode 100644 index 0000000000..33f1f84a8b --- /dev/null +++ b/package/diy/docker-restore/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2026 xiexiaobao +# +# This is free software, licensed under the GPL 2.0 license. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=docker-restore +PKG_VERSION:=1.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=xiexiaobao +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/docker-restore + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Docker container restore script for R6C + DEPENDS:=+docker + PKGARCH:=all +endef + +define Package/docker-restore/description + One-click restore script for all 16 Docker containers on NanoPi R6C. + Saves docker run commands for Jellyfin, Plex, Emby, Immich, Navidrome, + n8n, HomeAssistant, PhotoPrism, LANraragi, UptimeKuma, Aria2, etc. +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/docker-restore/install + $(INSTALL_DIR) $(1)/root + $(INSTALL_BIN) ./files/restore-docker.sh $(1)/root/restore-docker.sh +endef + +$(eval $(call BuildPackage,docker-restore)) diff --git a/package/diy/docker-restore/files/restore-docker.sh b/package/diy/docker-restore/files/restore-docker.sh new file mode 100644 index 0000000000..285f7876a4 --- /dev/null +++ b/package/diy/docker-restore/files/restore-docker.sh @@ -0,0 +1,231 @@ +#!/bin/sh +# R6C Docker Container Restore Script +# Pre-installed in firmware by docker-restore package +# Run after flashing iStoreOS: sh /root/restore-docker.sh +# +# EDIT THIS FILE: replace CHANGEME values with your actual passwords. +# If you preserved /mnt/usb1-0/, container data survives reflash. +# Immich DB password and Aria2 RPC secret can be found from your old install. + +set -e +echo "=== R6C Docker Restore ===" +echo "" + +# ---- Wait for dockerd ---- +for i in 1 2 3 4 5 6 7 8 9 10 15 20; do + docker info >/dev/null 2>&1 && break + echo "Waiting for dockerd... (${i}s)" + sleep 3 +done +docker info >/dev/null 2>&1 || { echo "dockerd not ready"; exit 1; } +echo "dockerd ready" +echo "" + +# ---- Check Docker data root ---- +CURRENT_ROOT=$(docker info --format "{{.DockerRootDir}}" 2>/dev/null) +if [ "$CURRENT_ROOT" = "/opt/docker" ] && [ -d "/mnt/usb1-0/docker" ]; then + echo "Docker data root is on eMMC but TF card has data." + echo "Run this to fix:" + echo ' uci set dockerd.globals.data_root=/mnt/usb1-0/docker' + echo ' uci add_list dockerd.globals.registry_mirrors=https://docker.1ms.run' + echo ' uci commit dockerd && /etc/init.d/dockerd restart' + echo "" +fi + +restore_container() { + local name="$1" + shift + if docker inspect "$name" >/dev/null 2>&1; then + echo "SKIP: $name already exists" + return + fi + echo "Creating: $name" + docker run -d --name "$name" "$@" + echo "DONE: $name" + echo "" +} + +# ========== Preparation ========== +# Create Immich network if missing +if ! docker network inspect immich_default >/dev/null 2>&1; then + echo "Creating network: immich_default" + docker network create immich_default + echo "" +fi + +# ========== CONTAINERS ========== + +# ---- n8n ---- +restore_container n8n \ + --restart=unless-stopped \ + -e WEBHOOK_URL=https://n8n.maplemaple.top \ + -e N8N_AI_ENABLED=true \ + -e N8N_SECURE_COOKIE=false \ + -v /opt/n8n/data:/home/node/.n8n \ + -p 5678:5678 \ + n8nio/n8n:latest + +# ---- ChangeDetection ---- +restore_container changedetection \ + --restart=unless-stopped \ + -e BASE_URL=https://watch.maplemaple.top \ + -v /opt/changedetection/data:/datastore \ + -p 127.0.0.1:5000:5000 \ + ghcr.io/dgtlmoon/changedetection.io:latest + +# ---- Jellyfin (with WebRootPath fix) ---- +restore_container jellyfin \ + --restart=unless-stopped \ + --network=host \ + -e TZ=Asia/Shanghai \ + -v /mnt/usb1-0/Configs/Jellyfin:/config \ + -v /mnt/usb1-0/Caches/Jellyfin:/config/transcodes \ + -v /mnt/usb1-0:/mnt/usb1-0 \ + --entrypoint sh \ + jellyfin/jellyfin:2026062206-arm64 \ + -c "ln -sf /jellyfin/jellyfin-web /jellyfin/wwwroot && exec /jellyfin/jellyfin" + +# ---- Emby ---- +restore_container emby \ + --restart=unless-stopped \ + --network=host \ + -v /mnt/usb1-0/Configs/Emby:/config \ + -v /mnt:/mnt \ + emby/embyserver:latest + +# ---- HomeAssistant ---- +restore_container homeassistant \ + --restart=unless-stopped \ + --network=host \ + -v /mnt/usb1-0/homeassistant:/config \ + homeassistant/home-assistant:latest + +# ---- Immich (4 containers) ---- +# Note: Update DB_PASSWORD if your Immich DB password differs +IMMICH_DB_PASS="${IMMICH_DB_PASS:-CHANGEME_IMMICH_DB_PASSWORD}" + +restore_container immich_postgres \ + --restart=unless-stopped \ + --network=immich_default \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_DB=immich \ + -e POSTGRES_PASSWORD="$IMMICH_DB_PASS" \ + -e POSTGRES_INITDB_ARGS="--data-checksums" \ + -v /opt/immich/postgres:/var/lib/postgresql/data:rw \ + ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 + +restore_container immich_redis \ + --restart=unless-stopped \ + --network=immich_default \ + docker.io/valkey/valkey:9 + +restore_container immich_server \ + --restart=unless-stopped \ + --network=immich_default \ + -e TZ=Asia/Shanghai \ + -e DB_USERNAME=postgres \ + -e DB_DATABASE_NAME=immich \ + -e DB_PASSWORD="$IMMICH_DB_PASS" \ + -e UPLOAD_LOCATION=/mnt/usb1-0/Public/Immich \ + -v /mnt/usb1-0/Public/Immich:/data:rw \ + -p 2283:2283 \ + ghcr.io/immich-app/immich-server:v2 + +restore_container immich_machine_learning \ + --restart=unless-stopped \ + --network=immich_default \ + -e TZ=Asia/Shanghai \ + -e DB_PASSWORD="$IMMICH_DB_PASS" \ + -v immich_model-cache:/cache:rw \ + ghcr.io/immich-app/immich-machine-learning:v2 + +# ---- UptimeKuma ---- +restore_container uptime-kuma \ + --restart=unless-stopped \ + -v /mnt/usb1-0/uptime-kuma:/app/data \ + -p 3001:3001 \ + louislam/uptime-kuma:latest + +# ---- Navidrome ---- +restore_container navidrome \ + --restart=unless-stopped \ + -e ND_PORT=4533 \ + -v /mnt/usb1-0/navidrome:/data \ + -v /mnt/usb1-0/music:/music:ro \ + -p 4533:4533 \ + deluan/navidrome:latest + +# ---- LANraragi ---- +restore_container lanraragi \ + --restart=unless-stopped \ + -e TZ=Asia/Shanghai \ + -v /mnt/usb1-0/Configs/LANraragi:/root/lanraragi/database \ + -v /mnt/usb1-0/Public/Comics:/root/lanraragi/content \ + -v /mnt:/mnt:rslave \ + -p 3000:3000 \ + dezhao/lanraragi_cn + +# ---- PhotoPrism ---- +restore_container photoprism \ + --restart=unless-stopped \ + -e PHOTOPRISM_UPLOAD_NSFW=true \ + -e PHOTOPRISM_ADMIN_PASSWORD=CHANGEME_PHOTOPRISM_PASS \ + -e TZ=Asia/Shanghai \ + -v /mnt/usb1-0/Configs/PhotoPrism:/photoprism/storage \ + -v /mnt/usb1-0/Public/Photo:/photoprism/originals \ + -p 2342:2342 \ + photoprism/photoprism + +# ---- Plex ---- +restore_container plex \ + --restart=unless-stopped \ + -e TZ=Asia/Shanghai \ + -e PLEX_CLAIM= \ + -v /mnt/usb1-0/Configs/Plex:/config \ + -v /mnt/usb1-0/Caches/Plex:/transcode \ + -v /mnt:/mnt:rslave \ + -p 3005:3005 \ + -p 32400:32400 \ + -p 32410:32410/udp \ + -p 32412:32412/udp \ + -p 32413:32413/udp \ + -p 32414:32414/udp \ + -p 32469:32469 \ + -p 8324:8324 \ + linuxserver/plex:latest + +# ---- Aria2 ---- +# Update RPC_SECRET if yours differs +Aria2_RPC_SECRET="${ARIA2_SECRET:-CHANGEME_ARIA2_SECRET}" +restore_container aria2 \ + --restart=unless-stopped \ + -e PUID=0 -e PGID=0 \ + -e RPC_SECRET="$Aria2_RPC_SECRET" \ + -e TZ=Asia/Shanghai \ + -v /opt/aria2/downloads:/downloads \ + -v /opt/aria2/config:/config \ + -p 6889:6888 \ + -p 6889:6888/udp \ + -p 6880:8080 \ + p3terx/aria2-pro:latest + +# ========== Jellyfin JS patch ========== +echo "" +echo "Patching Jellyfin web UI bug..." +for i in 1 2 3 4 5 6 7 8 9 10; do + docker exec jellyfin curl -s http://127.0.0.1:8096/system/info/public >/dev/null 2>&1 && break + sleep 3 +done +docker exec jellyfin sh -c 'sed -i "s/key:\"getApi\",value:function(e){var t,n=this.getApiClient(e);return/key:\"getApi\",value:function(e){if(!e)return;var t,n=this.getApiClient(e);return/g" /wwwroot/main.jellyfin.bundle.js' 2>/dev/null || true +docker restart jellyfin 2>/dev/null || true + +echo "" +echo "=== Restore complete ===" +echo "" +docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" +echo "" +echo "Next steps:" +echo " 1. Set PLEX_CLAIM (visit plex.tv/claim)" +echo " 2. Change PhotoPrism admin password in web UI" +echo " 3. Check Immich admin setup at http://192.168.100.1:2283" +echo " 4. Restart changedetection to pick up API key changes" diff --git a/r6c.config b/r6c.config index 9fe02e4d12..c5d347fe6c 100644 --- a/r6c.config +++ b/r6c.config @@ -1 +1,2 @@ CONFIG_PACKAGE_jellyfin-autoinit=y +CONFIG_PACKAGE_docker-restore=y