Files
hassio-addons/immich_frame/rootfs/etc/cont-init.d/99-run.sh
copilot-swe-agent[bot] 0b4da732ca Fix Settings.yaml corruption: move bashio::log calls outside stdout redirect block
The bashio-standalone.sh library writes log output to stdout (fd 1), not to
a preserved LOG_FD like the real bashio library. When bashio::log.info calls
were inside the { ... } > Settings.yaml block in immich_frame's 99-run.sh,
log messages leaked into the generated YAML file, corrupting it and causing
ImmichFrame to fail to parse account configuration correctly.

Changes:
- bashio-standalone.sh: redirect log output to LOG_FD (if available) or
  stderr, matching real bashio behavior
- bashio-standalone.sh: add missing bashio::log.fatal and bashio::log.notice
  aliases
- immich_frame 99-run.sh: move all bashio::log calls outside the
  { ... } > Settings.yaml block so only YAML content goes to the file

Agent-Logs-Url: https://github.com/alexbelgium/hassio-addons/sessions/87f542ba-1e4a-4b71-a787-e979818997df

Co-authored-by: alexbelgium <44178713+alexbelgium@users.noreply.github.com>
2026-04-17 12:06:52 +00:00

224 lines
8.2 KiB
Bash
Executable File

#!/usr/bin/env bashio
bashio::log.info "Starting Immich Frame"
mkdir -p /config/Config
# Handle legacy installs where /config/Config was a symlink to /app/Config
if [ -L /config/Config ]; then
bashio::log.info "Migrating legacy /config/Config symlink to real directory"
mkdir -p /config/Config.migrate
# Copy contents from the symlink target into the new real directory
cp -a /config/Config/. /config/Config.migrate/ 2>/dev/null || true
rm -f /config/Config
mv /config/Config.migrate /config/Config
fi
if [ -d /app/Config ] && [ ! -L /app/Config ]; then
cp -n /app/Config/* /config/Config/ 2>/dev/null || true
rm -rf /app/Config
fi
if [ ! -e /app/Config ]; then
ln -sf /config/Config /app/Config
fi
# ---- Settings.yaml generation ----
SETTINGS_FILE="/config/Config/Settings.yaml"
# Known account-level setting names (ImmichFrame v2 config)
ACCOUNT_KEYS=" ImmichServerUrl ApiKey ApiKeyFile Albums ExcludedAlbums People Tags ShowFavorites ShowMemories ShowArchived ShowVideos ImagesFromDays ImagesFromDate ImagesUntilDate Rating "
# Settings that accept comma-separated values and should become YAML lists
LIST_KEYS=" Albums ExcludedAlbums People Tags Webcalendars "
# Helper: check if word is in a space-padded list
in_list() { [[ "$2" == *" $1 "* ]]; }
# Helper: read a value from options.json handling booleans and nulls correctly
config_val() {
jq -r "($1) as \$v | if \$v == null then \"\" else (\$v | tostring) end" /data/options.json 2>/dev/null
}
config_has() {
jq -e "($1) != null" /data/options.json >/dev/null 2>&1
}
# Helper: write a YAML key-value pair with proper formatting
yaml_kv() {
local indent="$1" key="$2" value="$3"
# List-type settings -> YAML array
if in_list "$key" "$LIST_KEYS"; then
echo "${indent}${key}:"
IFS=',' read -ra ITEMS <<< "$value"
for item in "${ITEMS[@]}"; do
item="$(echo "$item" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
[ -n "$item" ] && echo "${indent} - '${item//\'/\'\'}'"
done
return
fi
# Boolean
if [ "$value" = "true" ] || [ "$value" = "false" ]; then
echo "${indent}${key}: ${value}"
return
fi
# Integer
if [[ "$value" =~ ^-?[0-9]+$ ]]; then
echo "${indent}${key}: ${value}"
return
fi
# Float
if [[ "$value" =~ ^-?[0-9]+\.[0-9]+$ ]]; then
echo "${indent}${key}: ${value}"
return
fi
# String (single-quoted with escaping)
echo "${indent}${key}: '${value//\'/\'\'}'"
}
# ---- Classify env_vars into general vs account settings ----
declare -A GENERAL_ENVS
declare -A ACCOUNT_ENVS
ENV_COUNT=$(jq '.env_vars // [] | length' /data/options.json 2>/dev/null || echo 0)
if [ "$ENV_COUNT" -gt 0 ]; then
bashio::log.info "Processing ${ENV_COUNT} env_var(s) for Settings.yaml"
fi
for idx in $(seq 0 $((ENV_COUNT - 1))); do
ENAME=$(jq -r ".env_vars[${idx}].name" /data/options.json)
EVALUE=$(jq -r ".env_vars[${idx}].value // \"\"" /data/options.json)
[ -z "$ENAME" ] && continue
[ -z "$EVALUE" ] && continue
[ "$ENAME" = "TZ" ] && continue # TZ is a system env var, not an ImmichFrame setting
if in_list "$ENAME" "$ACCOUNT_KEYS"; then
ACCOUNT_ENVS["$ENAME"]="$EVALUE"
bashio::log.info " env_var ${ENAME} -> Account setting"
else
GENERAL_ENVS["$ENAME"]="$EVALUE"
bashio::log.info " env_var ${ENAME} -> General setting"
fi
done
# General options from the addon schema
GENERAL_SCHEMA_OPTS="Interval TransitionDuration ShowClock ClockFormat ClockDateFormat
ShowProgressBar ShowPhotoDate PhotoDateFormat ShowImageDesc ShowPeopleDesc
ShowTagsDesc ShowAlbumName ShowImageLocation ShowWeatherDescription
ImageZoom ImagePan ImageFill PlayAudio PrimaryColor SecondaryColor Style
Layout BaseFontSize Language WeatherApiKey UnitSystem WeatherLatLong
ImageLocationFormat DownloadImages RenewImagesDuration RefreshAlbumPeopleInterval"
# Per-account options from the addon schema (besides ImmichServerUrl/ApiKey)
ACCOUNT_SCHEMA_OPTS="Albums ExcludedAlbums People Tags ShowFavorites ShowMemories
ShowArchived ShowVideos ImagesFromDays ImagesFromDate ImagesUntilDate Rating"
# ---- Build Settings.yaml ----
# Determine account configuration mode (and validate) before writing the file
ACCOUNT_COUNT=$(jq '.Accounts // [] | length' /data/options.json 2>/dev/null || echo 0)
if [ "$ACCOUNT_COUNT" -gt 0 ]; then
ACCOUNT_MODE="accounts_list"
bashio::log.info "Configuring ${ACCOUNT_COUNT} account(s) from Accounts list"
elif config_has '.ApiKey' && config_has '.ImmichServerUrl'; then
ACCOUNT_MODE="single"
bashio::log.info "Using single account configuration"
elif [ -n "${ACCOUNT_ENVS[ImmichServerUrl]:-}" ] && [ -n "${ACCOUNT_ENVS[ApiKey]:-}" ]; then
ACCOUNT_MODE="env_vars"
bashio::log.info "Using account configuration from env_vars"
else
bashio::log.fatal "No accounts configured! Set either 'Accounts' list or both 'ApiKey' and 'ImmichServerUrl'"
exit 1
fi
# Generate Settings.yaml — only echo/yaml_kv inside this block (no bashio::log)
{
# -- General section --
GENERAL_STARTED=false
for opt in $GENERAL_SCHEMA_OPTS; do
if config_has ".$opt"; then
$GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; }
yaml_kv " " "$opt" "$(config_val ".$opt")"
fi
done
# Add general env_vars (skip if already set via schema option)
for key in "${!GENERAL_ENVS[@]}"; do
if ! config_has ".$key"; then
$GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; }
yaml_kv " " "$key" "${GENERAL_ENVS[$key]}"
fi
done
# -- Accounts section --
if [ "$ACCOUNT_MODE" = "accounts_list" ]; then
echo "Accounts:"
for i in $(seq 0 $((ACCOUNT_COUNT - 1))); do
SRV="$(config_val ".Accounts[${i}].ImmichServerUrl")"
KEY="$(config_val ".Accounts[${i}].ApiKey")"
echo " - ImmichServerUrl: '${SRV//\'/\'\'}'"
echo " ApiKey: '${KEY//\'/\'\'}'"
for opt in $ACCOUNT_SCHEMA_OPTS; do
if config_has ".Accounts[${i}].${opt}"; then
yaml_kv " " "$opt" "$(config_val ".Accounts[${i}].${opt}")"
fi
done
# Apply account-level env_vars (only if not already set in this account's schema)
for key in "${!ACCOUNT_ENVS[@]}"; do
in_list "$key" " ImmichServerUrl ApiKey " && continue
if ! config_has ".Accounts[${i}].${key}"; then
yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}"
fi
done
done
elif [ "$ACCOUNT_MODE" = "single" ]; then
SRV="$(config_val '.ImmichServerUrl')"
KEY="$(config_val '.ApiKey')"
echo "Accounts:"
echo " - ImmichServerUrl: '${SRV//\'/\'\'}'"
echo " ApiKey: '${KEY//\'/\'\'}'"
# Apply account-level env_vars to the single account
for key in "${!ACCOUNT_ENVS[@]}"; do
in_list "$key" " ImmichServerUrl ApiKey " && continue
yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}"
done
elif [ "$ACCOUNT_MODE" = "env_vars" ]; then
echo "Accounts:"
echo " - ImmichServerUrl: '${ACCOUNT_ENVS[ImmichServerUrl]//\'/\'\'}'"
echo " ApiKey: '${ACCOUNT_ENVS[ApiKey]//\'/\'\'}'"
for key in "${!ACCOUNT_ENVS[@]}"; do
in_list "$key" " ImmichServerUrl ApiKey " && continue
yaml_kv " " "$key" "${ACCOUNT_ENVS[$key]}"
done
fi
} > "${SETTINGS_FILE}"
# Log account details after YAML generation
if [ "$ACCOUNT_MODE" = "accounts_list" ]; then
for i in $(seq 0 $((ACCOUNT_COUNT - 1))); do
bashio::log.info " Account $((i + 1)): $(config_val ".Accounts[${i}].ImmichServerUrl")"
done
fi
chmod 600 "${SETTINGS_FILE}"
bashio::log.info "Settings.yaml generated at ${SETTINGS_FILE}"
# Log contents (mask sensitive values)
bashio::log.info "--- Generated Settings.yaml ---"
sed -E 's/(ApiKey:).*/\1 *****/;s/(AuthenticationSecret:).*/\1 *****/;s/(WeatherApiKey:).*/\1 *****/' "${SETTINGS_FILE}" | while IFS= read -r line; do
bashio::log.info "$line"
done
bashio::log.info "-------------------------------"
export IMMICHFRAME_CONFIG_PATH=/config/Config
exec dotnet ImmichFrame.WebApi.dll