#!/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" <> 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"