Compare commits
6 Commits
master
...
wip/n1-new
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42431c3fc8 | ||
|
|
19e2fcf570 | ||
|
|
b4340aa3ff | ||
|
|
7734f3068d | ||
|
|
70b7a8e8e3 | ||
|
|
2cbc998803 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,4 +36,3 @@ git-src
|
||||
.cproject
|
||||
.ccache
|
||||
.vscode*
|
||||
.config.bak
|
||||
|
||||
@ -133,8 +133,7 @@ _ip2str START "$start"
|
||||
_ip2str END "$end"
|
||||
|
||||
if [ "$start" -le "$ipaddr" ] && [ "$ipaddr" -le "$end" ]; then
|
||||
echo "error: address $IP inside range $START..$END" >&2
|
||||
exit 1
|
||||
echo "warning: address $IP inside range $START..$END" >&2
|
||||
fi
|
||||
|
||||
echo "START=$START"
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
#
|
||||
# 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))
|
||||
@ -1,231 +0,0 @@
|
||||
#!/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"
|
||||
@ -1,47 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2026 xiexiaobao
|
||||
#
|
||||
# This is free software, licensed under the GPL 2.0 license.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=expand-overlay
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=xiexiaobao
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/expand-overlay
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=eMMC partition layout: 3G overlay + data partition
|
||||
DEPENDS:=+sfdisk +e2fsprogs +block-mount
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/expand-overlay/description
|
||||
First-boot eMMC partition layout for NanoPi R6C:
|
||||
- p3 (3GB): overlay filesystem (system/config)
|
||||
- p4 (~54GB): data partition (mounted at /mnt/data, for Docker)
|
||||
Stage 1 (uci-defaults): resizes p3 to 3GB, creates p4.
|
||||
Stage 2 (init.d): resizes p3 fs, formats & mounts p4.
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/expand-overlay/install
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) ./files/expand-overlay.sh $(1)/etc/uci-defaults/99-expand-overlay
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/expand-overlay.init $(1)/etc/init.d/expand-overlay
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,expand-overlay))
|
||||
@ -1,51 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# /etc/init.d/expand-overlay
|
||||
# Stage 2: Resize p3 filesystem, format & mount p4, configure Docker.
|
||||
# Runs at START=98 (before dockerd at 99) so Docker picks up data_root.
|
||||
|
||||
START=98
|
||||
|
||||
boot() {
|
||||
local flag="/etc/resize2fs.done"
|
||||
local p4flag="/etc/data-partition.done"
|
||||
|
||||
# ---- Stage 2a: Resize p3 filesystem ----
|
||||
if [ ! -f "$flag" ]; then
|
||||
local i=0
|
||||
while [ ! -f "/etc/expand-overlay.done" ] && [ $i -lt 30 ]; do
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
if [ -f "/etc/expand-overlay.done" ]; then
|
||||
resize2fs /dev/mmcblk2p3 2>/dev/null
|
||||
touch "$flag"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Stage 2b: Format and mount p4 data partition ----
|
||||
if [ ! -f "$p4flag" ]; then
|
||||
if dd if=/dev/mmcblk2p4 bs=5 count=1 2>/dev/null | grep -qF "RESET"; then
|
||||
mkfs.ext4 -F -L data /dev/mmcblk2p4 2>/dev/null
|
||||
fi
|
||||
|
||||
mkdir -p /mnt/data
|
||||
mount /dev/mmcblk2p4 /mnt/data 2>/dev/null
|
||||
|
||||
mkdir -p /mnt/data/docker
|
||||
|
||||
uci -q set dockerd.globals.data_root="/mnt/data/docker"
|
||||
uci -q commit dockerd
|
||||
|
||||
if ! grep -q "mmcblk2p4" /etc/config/fstab 2>/dev/null; then
|
||||
block detect > /dev/null 2>&1
|
||||
local idx=$(( $(uci show fstab | grep -c "fstab.@mount") - 1 ))
|
||||
[ "$idx" -ge 0 ] && uci -q set fstab.@mount[$idx].target="/mnt/data"
|
||||
uci -q commit fstab
|
||||
fi
|
||||
|
||||
touch "$p4flag"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
# /etc/uci-defaults/99-expand-overlay
|
||||
# Stage 1: Resize p3 to 3GB, create p4 for Docker data.
|
||||
# Auto-removed by uci-defaults after first run.
|
||||
|
||||
FLAG_FILE="/etc/expand-overlay.done"
|
||||
|
||||
[ -f "$FLAG_FILE" ] && exit 0
|
||||
|
||||
# Only run if root is on eMMC
|
||||
ROOT_DEV=$(mount | grep ' / ' | awk '{print $1}' | sed 's/p[0-9]*$//')
|
||||
[ "$ROOT_DEV" != "/dev/mmcblk2" ] && exit 0
|
||||
|
||||
# Save backup of original partition table
|
||||
sfdisk -d /dev/mmcblk2 > /etc/sfdisk.bak 2>/dev/null
|
||||
|
||||
# Step 1: Resize partition 3 to exactly 3GB (3072 MiB)
|
||||
# instead of expanding to fill the entire disk
|
||||
echo ",3072M" | sfdisk -N 3 /dev/mmcblk2 2>/dev/null
|
||||
|
||||
# Step 2: Create partition 4 using the remaining space
|
||||
# sfdisk -N 4 with ",+" = start after p3, fill to end
|
||||
echo ",+" | sfdisk -N 4 /dev/mmcblk2 2>/dev/null
|
||||
|
||||
# Step 3: Write RESET marker on p4 so stage 2 knows to format it
|
||||
echo "RESET000" | dd of=/dev/mmcblk2p4 bs=512 count=1 conv=notrunc,sync 2>/dev/null
|
||||
|
||||
touch "$FLAG_FILE"
|
||||
exit 0
|
||||
@ -1,42 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2026 xiexiaobao
|
||||
#
|
||||
# This is free software, licensed under the GPL 2.0 license.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=frpc-config
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=xiexiaobao
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/frpc-config
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Pre-configured frpc tunnels (SSH + Jellyfin + Navidrome)
|
||||
DEPENDS:=+frpc
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/frpc-config/description
|
||||
Pre-configure frpc tunnels for VPS 43.134.64.107:7000.
|
||||
Tunnels: SSH(:2222), Jellyfin(:8096), Navidrome(:4533).
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/frpc-config/install
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DATA) ./files/frpc $(1)/etc/config/frpc
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,frpc-config))
|
||||
@ -1,32 +0,0 @@
|
||||
config init
|
||||
option stdout '1'
|
||||
option stderr '1'
|
||||
option user 'frpc'
|
||||
option group 'frpc'
|
||||
option respawn '1'
|
||||
|
||||
config conf 'common'
|
||||
option server_addr '43.134.64.107'
|
||||
option server_port '7000'
|
||||
option token '@@xiefeng'
|
||||
|
||||
config conf 'ssh'
|
||||
option type 'tcp'
|
||||
option local_ip '127.0.0.1'
|
||||
option local_port '22'
|
||||
option remote_port '2222'
|
||||
option name 'ssh'
|
||||
|
||||
config conf 'jellyfin'
|
||||
option type 'tcp'
|
||||
option local_ip '127.0.0.1'
|
||||
option local_port '8096'
|
||||
option remote_port '8096'
|
||||
option name 'jellyfin'
|
||||
|
||||
config conf 'navidrome'
|
||||
option type 'tcp'
|
||||
option local_ip '127.0.0.1'
|
||||
option local_port '4533'
|
||||
option remote_port '4533'
|
||||
option name 'navidrome'
|
||||
@ -1,34 +0,0 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=jellyfin-autoinit
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/jellyfin-autoinit
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Auto-pull and start Jellyfin Docker container on first boot
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+docker
|
||||
endef
|
||||
|
||||
define Package/jellyfin-autoinit/description
|
||||
On first boot, after eMMC expansion is complete, this package will:
|
||||
1) docker pull jellyfin/jellyfin:2026062206-arm64
|
||||
2) create the Jellyfin container (host network, correct webroot symlink)
|
||||
3) patch the JS startup crash bug (PR #8088)
|
||||
4) start with rkmpp hardware acceleration
|
||||
|
||||
Re-runs are guarded by /etc/jellyfin-autoinit.done.
|
||||
endef
|
||||
|
||||
define Package/jellyfin-autoinit/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./files/jellyfin-autoinit.sh $(1)/usr/bin/jellyfin-autoinit.sh
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/jellyfin-autoinit.init $(1)/etc/init.d/jellyfin-autoinit
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,jellyfin-autoinit))
|
||||
@ -1,9 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Jellyfin container auto-start on boot
|
||||
# START=98 = after dockerd, before user services
|
||||
# Stage ordering: dockerd → expand-overlay(99) → this(98) → user services
|
||||
START=98
|
||||
|
||||
start() {
|
||||
/usr/bin/jellyfin-autoinit.sh
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Auto-pull and start Jellyfin Docker container on first boot
|
||||
# Fixes two known bugs in jellyfin/jellyfin:2026062206-arm64:
|
||||
# 1) WebRootPath points to empty /jellyfin/wwwroot/ — workaround: symlink
|
||||
# 2) JS crash "item or serverId cannot be null" (PR #8088) — workaround: sed patch
|
||||
#
|
||||
# Gates on expand-overlay.done so it only runs after the eMMC is resized
|
||||
# Idempotent: skips if container or flag file exists
|
||||
# Forgiving: pull failure exits 0 so init retries next boot
|
||||
# Tolerates missing expand-overlay.done (may not be created on mismatched firmware)
|
||||
|
||||
FLAG_FILE="/etc/jellyfin-autoinit.done"
|
||||
|
||||
# Wait up to 30s for expand-overlay to finish
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ -f /etc/expand-overlay.done ] && break
|
||||
sleep 3
|
||||
done
|
||||
|
||||
# Already done
|
||||
[ -f "$FLAG_FILE" ] && exit 0
|
||||
|
||||
# Docker available? Wait for dockerd to actually be ready
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "[jellyfin-autoinit] docker binary not found, exiting"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
docker info >/dev/null 2>&1 && break
|
||||
[ "$i" -eq 10 ] && { echo "[jellyfin-autoinit] dockerd not ready after 30s, exiting"; exit 0; }
|
||||
sleep 3
|
||||
done
|
||||
|
||||
# Container already exists
|
||||
if docker inspect jellyfin >/dev/null 2>&1; then
|
||||
echo "[jellyfin-autoinit] container already exists, skipping"
|
||||
touch "$FLAG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[jellyfin-autoinit] pulling jellyfin/jellyfin:2026062206-arm64..."
|
||||
docker pull jellyfin/jellyfin:2026062206-arm64 2>&1 | tail -3 || {
|
||||
echo "[jellyfin-autoinit] pull failed, will retry next boot"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Ensure mount points exist
|
||||
mkdir -p /mnt/usb1-0/Configs/Jellyfin/config
|
||||
mkdir -p /mnt/usb1-0/Configs/Jellyfin/cache
|
||||
mkdir -p /mnt/usb1-0/Caches/Jellyfin
|
||||
|
||||
echo "[jellyfin-autoinit] creating container with webroot fix..."
|
||||
docker run -d \
|
||||
--name 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 /wwwroot && exec /jellyfin/jellyfin' 2>&1 | tail -3
|
||||
|
||||
# Wait for Jellyfin API to be ready, then patch the JS startup crash bug
|
||||
echo "[jellyfin-autoinit] waiting for Jellyfin to start..."
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 15 20; do
|
||||
docker exec jellyfin curl -s http://127.0.0.1:8096/system/info/public >/dev/null 2>&1 && break
|
||||
[ "$i" -eq 20 ] && { echo "[jellyfin-autoinit] Jellyfin not ready after 60s"; break; }
|
||||
sleep 3
|
||||
done
|
||||
|
||||
echo "[jellyfin-autoinit] patching JS startup crash bug (PR #8088)..."
|
||||
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'
|
||||
|
||||
docker restart jellyfin
|
||||
|
||||
if docker ps | grep -q jellyfin; then
|
||||
echo "[jellyfin-autoinit] jellyfin started successfully with all fixes applied"
|
||||
touch "$FLAG_FILE"
|
||||
else
|
||||
echo "[jellyfin-autoinit] failed to start, will retry next boot"
|
||||
fi
|
||||
@ -1,42 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2026 xiexiaobao
|
||||
#
|
||||
# This is free software, licensed under the GPL 2.0 license.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=navidrome-autoinit
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=xiexiaobao
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/navidrome-autoinit
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Auto-create Navidrome container on first boot
|
||||
DEPENDS:=+dockerd
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/navidrome-autoinit/description
|
||||
Automatically pull and start the Navidrome music streaming container
|
||||
on first boot after flashing. Music and data directories on /mnt/usb1-0.
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/navidrome-autoinit/install
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/navidrome.init $(1)/etc/init.d/navidrome-autoinit
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,navidrome-autoinit))
|
||||
@ -1,70 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# /etc/init.d/navidrome-autoinit
|
||||
# Auto-create Navidrome Docker container on first boot.
|
||||
# Idempotent: skips if container or FLAG_FILE exists.
|
||||
# Tolerates missing TF card: exits gracefully.
|
||||
|
||||
START=97
|
||||
FLAG_FILE="/etc/navidrome-autoinit.done"
|
||||
CONTAINER="navidrome"
|
||||
IMAGE="deluan/navidrome:latest"
|
||||
MUSIC_DIR="/mnt/usb1-0/music"
|
||||
DATA_DIR="/mnt/usb1-0/navidrome"
|
||||
PORT="4533"
|
||||
|
||||
boot() {
|
||||
[ -f "$FLAG_FILE" ] && return 0
|
||||
|
||||
# Verify Docker is installed
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
logger -t navidrome-autoinit "docker binary not found, skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait up to 30s for dockerd to be ready
|
||||
local i=0
|
||||
while ! docker info >/dev/null 2>&1 && [ $i -lt 10 ]; do
|
||||
sleep 3
|
||||
i=$((i + 1))
|
||||
done
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
logger -t navidrome-autoinit "dockerd not ready after 30s, will retry next boot"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Container already exists (e.g. Docker data root restored from TF card)
|
||||
if docker inspect "$CONTAINER" >/dev/null 2>&1; then
|
||||
logger -t navidrome-autoinit "container $CONTAINER already exists, skipping"
|
||||
touch "$FLAG_FILE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create music and data directories on TF card
|
||||
mkdir -p "$MUSIC_DIR" "$DATA_DIR"
|
||||
|
||||
# Pull the image (tolerate network failure — will retry next boot)
|
||||
logger -t navidrome-autoinit "pulling $IMAGE..."
|
||||
if ! docker pull "$IMAGE" 2>&1 | logger -t navidrome-autoinit; then
|
||||
logger -t navidrome-autoinit "pull failed, will retry next boot"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create and start the container
|
||||
logger -t navidrome-autoinit "creating container $CONTAINER..."
|
||||
docker run -d \
|
||||
--name "$CONTAINER" \
|
||||
--restart unless-stopped \
|
||||
-p ${PORT}:${PORT} \
|
||||
-v "$DATA_DIR":/data \
|
||||
-v "$MUSIC_DIR":/music:ro \
|
||||
"$IMAGE" 2>&1 | logger -t navidrome-autoinit
|
||||
|
||||
if docker ps | grep -q "$CONTAINER"; then
|
||||
logger -t navidrome-autoinit "$CONTAINER started successfully on port $PORT"
|
||||
touch "$FLAG_FILE"
|
||||
else
|
||||
logger -t navidrome-autoinit "$CONTAINER failed to start, will retry next boot"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
|
||||
PKG_NAME:=dnsmasq
|
||||
PKG_UPSTREAM_VERSION:=2.90
|
||||
PKG_VERSION:=$(subst test,~~test,$(subst rc,~rc,$(PKG_UPSTREAM_VERSION)))
|
||||
PKG_RELEASE:=5
|
||||
PKG_RELEASE:=6
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_UPSTREAM_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=https://thekelleys.org.uk/dnsmasq/
|
||||
|
||||
@ -597,10 +597,20 @@ dhcp_add() {
|
||||
nettag="${networkid:+set:${networkid},}"
|
||||
|
||||
# make sure the DHCP range is not empty
|
||||
if [ "$dhcpv4" != "disabled" ] && ipcalc "$ipaddr/$prefix_or_netmask" "$start" "$limit" ; then
|
||||
[ "$dynamicdhcpv4" = "0" ] && END="static"
|
||||
if [ "$dhcpv4" != "disabled" ]; then
|
||||
unset START
|
||||
unset END
|
||||
unset NETMASK
|
||||
ipcalc "$ipaddr/$prefix_or_netmask" "$start" "$limit"
|
||||
|
||||
xappend "--dhcp-range=$tags$nettag$START,$END,$NETMASK,$leasetime${options:+ $options}"
|
||||
if [ -z "$START" ] || [ -z "$END" ] || [ -z "$NETMASK" ]; then
|
||||
logger -t dnsmasq \
|
||||
"unable to set dhcp-range for dhcp uci config section '$cfg'" \
|
||||
"on interface '$ifname', please check your config"
|
||||
else
|
||||
[ "$dynamicdhcpv4" = "0" ] && END="static"
|
||||
xappend "--dhcp-range=$tags$nettag$START,$END,$NETMASK,$leasetime${options:+ $options}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$dynamicdhcpv6" = "0" ] ; then
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
CONFIG_PACKAGE_jellyfin-autoinit=y
|
||||
CONFIG_PACKAGE_docker-restore=y
|
||||
CONFIG_PACKAGE_kmod-fuse=y
|
||||
CONFIG_PACKAGE_fuse-utils=y
|
||||
CONFIG_PACKAGE_ntfs-3g=y
|
||||
@ -24,11 +24,11 @@
|
||||
# danger zones
|
||||
# 0-117
|
||||
|
||||
# part start size end
|
||||
# boot 0x7500000/117M 0x4000000/64M 0xB500000/181M
|
||||
# rootfs 0xB500000/181M 0x18F00000/399M 0x24400000/580M
|
||||
# part start size end
|
||||
# boot 0x7500000/117M (239616s) 0x4000000/64M 0xB500000/181M (370687s)
|
||||
# rootfs 0xB500000/181M (370688s) 0x18F00000/399M 0x24400000/580M (1187839s)
|
||||
# (gap for amlogic bootloader 0x24400000 - 0x32000000)
|
||||
# overlay 0x32000000/800M 0x80000000/2G 0xB2000000/2848M
|
||||
# overlay 0x32000000/800M (1638400s) 0x80000000/2G 0xB2000000/2848M (4194304s)
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/functions/system.sh
|
||||
@ -51,6 +51,19 @@ function umount_disk() {
|
||||
|
||||
function part_disk() {
|
||||
local to="${1#/dev/}"
|
||||
#dd if="/dev/$to" of=/tmp/mmc-diskid.bin bs=1 skip=440 count=4
|
||||
dd if=/dev/zero of="/dev/$to" bs=1 seek=446 count=64 conv=notrunc
|
||||
{
|
||||
# echo o;
|
||||
echo n; echo p; echo 1; echo 239616; echo 370687;
|
||||
echo n; echo p; echo 2; echo 370688; echo 1187839;
|
||||
echo n; echo p; echo 3; echo 1638400; echo 4194304;
|
||||
echo t; echo 1; echo c;
|
||||
echo w;
|
||||
} | fdisk "/dev/$to"
|
||||
#dd if=/tmp/mmc-diskid.bin of="/dev/$to" bs=1 seek=440 count=4 conv=notrunc
|
||||
return 0
|
||||
|
||||
dd if=/dev/zero of="/dev/$to" bs=512 count=1 conv=notrunc 2>/dev/null
|
||||
parted --script "/dev/$to" \
|
||||
mktable msdos \
|
||||
|
||||
@ -20,6 +20,9 @@ export_ota_url() {
|
||||
lyt,t68m)
|
||||
export -n OTA_URL_BASE="https://fw0.koolcenter.com/iStoreOS/${board##*,}"
|
||||
;;
|
||||
easepi,a2)
|
||||
export -n OTA_URL_BASE="https://fw0.koolcenter.com/iStoreOS/easepi-a2"
|
||||
;;
|
||||
easepi,r1)
|
||||
export -n OTA_URL_BASE="https://fw0.koolcenter.com/iStoreOS/easepi-r1"
|
||||
;;
|
||||
|
||||
@ -739,4 +739,3 @@ CONFIG_XZ_DEC_BCJ=y
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
||||
CONFIG_ZONE_DMA32=y
|
||||
CONFIG_FUSE_FS=m
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
aliases {
|
||||
ethernet0 = &pcie_eth0;
|
||||
/delete-property/ ethernet1;
|
||||
mmc2 = &sdmmc2;
|
||||
};
|
||||
|
||||
chosen {
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
aliases {
|
||||
ethernet2 = &pcie_eth2;
|
||||
ethernet3 = &pcie_eth3;
|
||||
mmc2 = &sdmmc2;
|
||||
};
|
||||
|
||||
pwr-net-25g {
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
#include <dt-bindings/pinctrl/rockchip.h>
|
||||
#include <dt-bindings/soc/rockchip,vop2.h>
|
||||
#include <dt-bindings/usb/pd.h>
|
||||
@ -21,6 +22,7 @@
|
||||
/ {
|
||||
model = "Xunlong Orange Pi 5 Plus";
|
||||
compatible = "xunlong,orangepi-5-plus", "rockchip,rk3588";
|
||||
eth_order = "0004:41:00.0,0003:31:00.0";
|
||||
|
||||
aliases {
|
||||
mmc0 = &sdhci;
|
||||
@ -416,7 +418,7 @@
|
||||
op-sink-microwatt = <1000000>;
|
||||
source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
|
||||
sink-pdos = <PDO_FIXED(5000, 1000, PDO_FIXED_USB_COMM)>;
|
||||
try-power-role = "source";
|
||||
try-power-role = "sink";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
||||
@ -132,7 +132,7 @@ define Device/friendlyarm_nanopi-r6c
|
||||
SOC := rk3588s
|
||||
DEVICE_PACKAGES := kmod-r8169
|
||||
endef
|
||||
TARGET_DEVICES += friendlyarm_nanopi-r6c
|
||||
# TARGET_DEVICES += friendlyarm_nanopi-r6c
|
||||
|
||||
define Device/friendlyarm_nanopi-r6s
|
||||
DEVICE_VENDOR := FriendlyARM
|
||||
|
||||
Loading…
Reference in New Issue
Block a user