File the owner's containers on the mirror during onboard, with the icon resolved where the Emby key is

This commit is contained in:
Gmer4Lfe
2026-08-17 05:33:52 -04:00
parent 6dbb076a1e
commit fe58fb7826
3 changed files with 116 additions and 4 deletions
+25
View File
@@ -454,6 +454,31 @@ deploy_xml_stack() {
#
# Usage: ensure_stack_networks_on_remote "$MIRROR_IP" "$MIRROR_SSH_KEY"
# ==============================================================================================
# ==============================================================================================
# ── Container names this onboard deploys, read from the templates it deploys them from ────────
#
# Echoes one name per line. The <Name> element is the same value deploy_xml_stack() passes to
# `docker create --name`, so this is the deployed set by construction rather than by asking the
# mirror what it ended up with — which would also pick up whatever the mirror already ran.
#
# Usage: mapfile -t names < <(deployed_stack_container_names)
# ==============================================================================================
deployed_stack_container_names() {
local -a xml_names=()
[[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}")
[[ ${#PARTNERSHIP_ARR_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}")
[[ ${#PARTNERSHIP_SERVICES_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_SERVICES_STACK[@]}")
local xml_name xml_file cname
for xml_name in "${xml_names[@]}"; do
[[ -z "$xml_name" ]] && continue
xml_file="${TEMPLATES_DIR}/${xml_name}"
[[ -f "$xml_file" ]] || continue
cname=$(awk 'match($0,/<Name>([^<]+)<\/Name>/,a){print a[1];exit}' "$xml_file")
[[ -n "$cname" ]] && echo "$cname"
done
}
ensure_stack_networks_on_remote() {
local remote_ip="$1" ssh_key="$2"
local -a xml_names=() nets=()
+38 -4
View File
@@ -40,7 +40,8 @@
// Icon resolution failing never blocks the folder: no image is a cosmetic loss, no folder is not.
//
// REQUEST
// fallback_folder.php --host=HOST2 [--containers=A,B,C] [--dry-run]
// fallback_folder.php --host=HOST2 [--containers=A,B,C] [--icon=URL] [--dry-run]
// fallback_folder.php --host=HOST2 --icon-only resolve and print the icon URL, write nothing
//
// CONFIGURATION
// HOST1/HOST2… master.conf — the hostname the folder is named after
@@ -57,9 +58,10 @@ require_once $pluginDir . '/include/config.php';
define('FV3_JSON', '/boot/config/plugins/folder.view3/docker.json');
// ── Args ──────────────────────────────────────────────────────────────────────
$opts = ['host' => '', 'containers' => '', 'dry-run' => false];
$opts = ['host' => '', 'containers' => '', 'icon' => '', 'dry-run' => false, 'icon-only' => false];
foreach (array_slice($argv, 1) as $a) {
if ($a === '--dry-run') { $opts['dry-run'] = true; continue; }
if ($a === '--dry-run') { $opts['dry-run'] = true; continue; }
if ($a === '--icon-only'){ $opts['icon-only'] = true; continue; }
if (preg_match('/^--([a-z-]+)=(.*)$/', $a, $m)) $opts[$m[1]] = $m[2];
}
$hostId = strtoupper(trim($opts['host']));
@@ -72,6 +74,7 @@ $vars = vv_conf_vars();
$hostname = trim($vars[$hostId] ?? '');
if ($hostname === '') { fwrite(STDERR, "$hostId is not set in master.conf\n"); exit(1); }
// derive_short_name() in common.sh: lowercase, strip a leading "unraid-", capitalise.
$short = preg_replace('/^unraid-/i', '', $hostname);
$short = ucfirst($short);
@@ -124,9 +127,40 @@ function vv_closest_emby_icon(string $short, array $vars, string $meId): array {
}
$meId = strtoupper(vv_detect_host());
[$icon, $iconNote] = vv_closest_emby_icon($short, $vars, $meId);
// --icon= skips the lookup entirely. The mirror needs this: it is being given a folder named
// after the OWNER, and the avatar lives in the owner's Emby — which the mirror has no key for and
// may not run at all. So the owner resolves the URL with --icon-only and hands it over, rather
// than the mirror guessing from a userbase it cannot see.
if (trim($opts['icon']) !== '') {
$icon = trim($opts['icon']);
$iconNote = 'supplied by caller';
} else {
[$icon, $iconNote] = vv_closest_emby_icon($short, $vars, $meId);
}
// --icon-only: resolve and print, touch nothing. Exits non-zero when there is no icon, so a
// caller can tell "no image" from "empty string because something broke".
if ($opts['icon-only']) {
if ($icon === '') { fwrite(STDERR, "no icon: $iconNote\n"); exit(1); }
echo $icon . "\n";
exit(0);
}
// ── Upsert the folder ─────────────────────────────────────────────────────────
// A host does not keep a fallback folder for itself — the folder means "containers I run on
// SOMEONE ELSE's behalf", so naming it after this machine is always wrong. Checked here rather
// than at argument parsing, because --icon-only legitimately asks for THIS host's own avatar:
// the owner resolves its own picture to hand to the mirror, which is the whole point of that mode.
//
// Refused rather than created, because the failure is otherwise silent — an empty folder named
// after yourself looks plausible enough to survive a glance. One appeared on HOST1 exactly this
// way, when an older copy of this script ignored an unrecognised flag and ran the upsert anyway.
if (strcasecmp($hostId, strtoupper(vv_detect_host())) === 0) {
fwrite(STDERR, "$hostId is this host — a fallback folder is named after the PARTNER, not self\n");
exit(2);
}
if (!file_exists(FV3_JSON)) {
echo "folder.view3 is not installed — nothing to do\n";
exit(0);