Files
dtourolleandClaude Opus 5.5 b0c067ed41
🏗️ Build Plugin / build (push) Successful in 1m15s
Nightly Build / nightly-build (push) Failing after 1m14s
🧪 Test Plugin / test (push) Successful in 40s
Add a Docker test harness for any Jellyfin version
test-image.sh publishes the plugin on the host, bakes it into a stock
jellyfin/jellyfin image via Dockerfile.test, and runs it on :8096, with
optional egress through a LAN gateway (DOCKER_GATEWAY, e.g. the Swiss
VPN box) for geo-blocked streams.

JELLYFIN_TAG (default 12) selects the image and the matching plugin
build: 10.9/10.10 -> net8.0, 10.11 -> net9.0, 12 -> net10.0, with the
generated meta.json targetAbi to match. Images are tagged per version
(srfplay-test:<tag>) so several can coexist.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 01:44:34 +02:00

107 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build the SRF Play plugin from the local checkout, bake it into a stock
# Jellyfin image, and run it on http://localhost:8096.
#
# ./test-image.sh build + run
# ./test-image.sh build build image only (no run)
#
# JELLYFIN_TAG picks the Jellyfin image (default 12) and, with it, the matching
# plugin build (see the TargetFrameworks comment in the plugin csproj):
#
# JELLYFIN_TAG=10.11.0 ./test-image.sh
set -euo pipefail
cd "$(dirname "$0")"
JELLYFIN_TAG="${JELLYFIN_TAG:-12}"
PLUGIN_DIR=".test-plugin/SRFPlay"
IMAGE="srfplay-test:${JELLYFIN_TAG}"
case "$JELLYFIN_TAG" in
10.9*|10.10*) FRAMEWORK="net8.0"; ABI="10.9.0.0" ;;
10.11*) FRAMEWORK="net9.0"; ABI="10.11.0.0" ;;
*) FRAMEWORK="net10.0"; ABI="12.0.0.0" ;;
esac
echo ">> Publishing plugin for Jellyfin ${JELLYFIN_TAG} (${FRAMEWORK}, host build, warm cache)..."
rm -rf .test-plugin
dotnet publish Jellyfin.Plugin.SRFPlay/Jellyfin.Plugin.SRFPlay.csproj \
--configuration Release --framework "$FRAMEWORK" --no-self-contained -o /tmp/srfplay-publish >/dev/null
mkdir -p "$PLUGIN_DIR"
cp /tmp/srfplay-publish/Jellyfin.Plugin.SRFPlay.dll "$PLUGIN_DIR/"
# Generate the meta.json Jellyfin needs to load the plugin, from build.yaml.
# targetAbi follows the framework chosen above rather than build.yaml.
GUID=$(grep -E '^guid:' build.yaml | sed -E 's/^guid:[[:space:]]*"?([^"]+)"?.*/\1/')
VERSION=$(grep -E '^version:' build.yaml | sed -E 's/^version:[[:space:]]*"?([^"]+)"?.*/\1/')
cat > "$PLUGIN_DIR/meta.json" <<EOF
{
"guid": "$GUID",
"name": "SRF Play",
"version": "$VERSION",
"targetAbi": "$ABI",
"owner": "dtourolle",
"overview": "SRF Play plugin for Jellyfin (test build)",
"description": "Local test build of the SRF Play plugin.",
"category": "Live TV",
"status": "Active",
"autoUpdate": false
}
EOF
echo ">> Building image $IMAGE (stock Jellyfin + plugin COPY)..."
docker build -f Dockerfile.test --build-arg JELLYFIN_TAG="$JELLYFIN_TAG" -t "$IMAGE" .
if [ "${1:-run}" = "build" ]; then
echo ">> Built $IMAGE. Run with: docker run --rm -p 8096:8096 $IMAGE"
exit 0
fi
# --- Optional: route the container's egress via a LAN gateway ----------------
# Set DOCKER_GATEWAY to a LAN gateway IP (e.g. the Swiss VPN box 192.168.1.52)
# to send this container's outbound traffic through it. Needs sudo once to add
# host policy-route + masquerade; both are removed automatically on exit.
#
# DOCKER_GATEWAY=192.168.1.52 ./test-image.sh
#
# When unset, the container uses Docker's normal default route (your ISP).
TEST_NET="srfplay-testnet"
TEST_SUBNET="172.31.250.0/24"
TEST_TABLE="250" # dedicated rt table for this subnet
# The uplink is the interface that actually reaches the gateway (the LAN NIC,
# e.g. wlan0) — NOT the host default route, which a VPN killswitch may point
# at the tunnel. Derive it from the route to DOCKER_GATEWAY when set.
if [ -n "${DOCKER_GATEWAY:-}" ]; then
UPLINK_IF="$(ip route get "$DOCKER_GATEWAY" 2>/dev/null | grep -oE 'dev [^ ]+' | awk '{print $2}')"
fi
cleanup() {
echo ">> Cleaning up..."
docker rm -f srfplay-test-run >/dev/null 2>&1 || true
if [ -n "${DOCKER_GATEWAY:-}" ]; then
sudo ip rule del from "$TEST_SUBNET" table "$TEST_TABLE" 2>/dev/null || true
sudo ip route flush table "$TEST_TABLE" 2>/dev/null || true
sudo iptables -t nat -D POSTROUTING -s "$TEST_SUBNET" -o "$UPLINK_IF" -j MASQUERADE 2>/dev/null || true
fi
docker network rm "$TEST_NET" >/dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM
docker network rm "$TEST_NET" >/dev/null 2>&1 || true
docker network create --subnet "$TEST_SUBNET" "$TEST_NET" >/dev/null
if [ -n "${DOCKER_GATEWAY:-}" ]; then
echo ">> Routing container egress via gateway $DOCKER_GATEWAY (uplink $UPLINK_IF)..."
echo " (sudo needed once to install host routes; removed on exit)"
sudo ip route replace default via "$DOCKER_GATEWAY" dev "$UPLINK_IF" table "$TEST_TABLE"
sudo ip rule add from "$TEST_SUBNET" table "$TEST_TABLE" 2>/dev/null || true
sudo iptables -t nat -C POSTROUTING -s "$TEST_SUBNET" -o "$UPLINK_IF" -j MASQUERADE 2>/dev/null \
|| sudo iptables -t nat -A POSTROUTING -s "$TEST_SUBNET" -o "$UPLINK_IF" -j MASQUERADE
fi
echo ">> Starting Jellyfin on http://localhost:8096 (Ctrl-C to stop)..."
docker run --rm --name srfplay-test-run --network "$TEST_NET" -p 8096:8096 "$IMAGE"