From c1965de87363331b07256ee4e4918d76125c3cf8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 07:01:05 +0000 Subject: [PATCH 1/4] Add Spotify to Plex add-on New add-on wrapping the upstream jjdenhertog/spotify-to-plex Docker image (requested in issue #2814). Keeps Spotify playlists synced to Plex. - Wraps the multi-arch upstream image via BUILD_FROM (amd64 + aarch64) - Uses the shared ha_entrypoint framework with 00-global_var.sh so add-on options (Spotify credentials, redirect URI, encryption key) become the environment variables the app expects - 99-run.sh persists /app/config into the add-on config dir, auto-generates and stores a stable ENCRYPTION_KEY when left blank, then hands off to the upstream supervisord - Web UI exposed directly on port 9030 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW --- spotify_to_plex/CHANGELOG.md | 2 + spotify_to_plex/Dockerfile | 135 ++++++++++++++++++ spotify_to_plex/README.md | 75 ++++++++++ spotify_to_plex/build.json | 6 + spotify_to_plex/config.yaml | 34 +++++ .../rootfs/etc/cont-init.d/99-run.sh | 29 ++++ spotify_to_plex/updater.json | 8 ++ 7 files changed, 289 insertions(+) create mode 100644 spotify_to_plex/CHANGELOG.md create mode 100644 spotify_to_plex/Dockerfile create mode 100644 spotify_to_plex/README.md create mode 100644 spotify_to_plex/build.json create mode 100644 spotify_to_plex/config.yaml create mode 100644 spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh create mode 100644 spotify_to_plex/updater.json diff --git a/spotify_to_plex/CHANGELOG.md b/spotify_to_plex/CHANGELOG.md new file mode 100644 index 0000000000..784415f55b --- /dev/null +++ b/spotify_to_plex/CHANGELOG.md @@ -0,0 +1,2 @@ +## 1.0.104 (2026-07-06) +- Initial release: Spotify to Plex add-on based on jjdenhertog/spotify-to-plex diff --git a/spotify_to_plex/Dockerfile b/spotify_to_plex/Dockerfile new file mode 100644 index 0000000000..644d14ed8d --- /dev/null +++ b/spotify_to_plex/Dockerfile @@ -0,0 +1,135 @@ +#============================# +# ALEXBELGIUM'S DOCKERFILE # +#============================# +# _.------. +# _.-` ('>.-`"""-. +# '.--'` _'` _ .--.) +# -' '-.-';` ` +# ' - _.' ``'--. +# '---` .-'""` +# /` +#=== Home Assistant Addon ===# + +################# +# 1 Build Image # +################# + +ARG BUILD_FROM + +FROM ${BUILD_FROM} + +################## +# 2 Modify Image # +################## + +# Set S6 wait time +ENV S6_CMD_WAIT_FOR_SERVICES=1 \ + S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ + S6_SERVICES_GRACETIME=0 + +USER root + +################## +# 3 Install apps # +################## + +# Copy local files +COPY rootfs/ / +RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \; + +# Uses /bin for compatibility purposes +# hadolint ignore=DL4005 +RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \ + if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi + +# Modules +ARG MODULES="00-banner.sh 00-global_var.sh 01-custom_script.sh" + +# Automatic modules download +COPY ha_automodules.sh /ha_automodules.sh +RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh + +# Manual apps +ENV PACKAGES="jq" + +# Automatic apps & bashio +COPY ha_autoapps.sh /ha_autoapps.sh +RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh + +EXPOSE 9030 + +################ +# 4 Entrypoint # +################ + +# Add entrypoint +ENV S6_STAGE2_HOOK=/ha_entrypoint.sh +COPY ha_entrypoint.sh /ha_entrypoint.sh +RUN chmod 777 /ha_entrypoint.sh + +# Install bashio +COPY bashio-standalone.sh /usr/local/lib/bashio-standalone.sh +RUN chmod 0755 /usr/local/lib/bashio-standalone.sh + +ENTRYPOINT [ "/usr/bin/env" ] +CMD [ "/ha_entrypoint.sh" ] + +############ +# 5 Labels # +############ + +ARG BUILD_ARCH +ARG BUILD_DATE +ARG BUILD_DESCRIPTION +ARG BUILD_NAME +ARG BUILD_REF +ARG BUILD_REPOSITORY +ARG BUILD_VERSION +ENV BUILD_VERSION="${BUILD_VERSION}" +LABEL \ + io.hass.name="${BUILD_NAME}" \ + io.hass.description="${BUILD_DESCRIPTION}" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.title="${BUILD_NAME}" \ + org.opencontainers.image.description="${BUILD_DESCRIPTION}" \ + org.opencontainers.image.vendor="Home Assistant Add-ons" \ + org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.url="https://github.com/alexbelgium" \ + org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \ + org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${BUILD_REF} \ + org.opencontainers.image.version=${BUILD_VERSION} + +################# +# 6 Healthcheck # +################# + +# Avoid spamming logs +# hadolint ignore=SC2016 +RUN \ + # Handle Apache configuration + if [ -d /etc/apache2/sites-available ]; then \ + for file in /etc/apache2/sites-*/*.conf; do \ + sed -i '/ /etc/nginx/nginx.conf.new && \ + mv /etc/nginx/nginx.conf.new /etc/nginx/nginx.conf; \ + fi + +ENV HEALTH_PORT="9030" \ + HEALTH_URL="" +HEALTHCHECK \ + --interval=5s \ + --retries=5 \ + --start-period=30s \ + --timeout=25s \ + CMD curl -A "HealthCheck: Docker/1.0" -s -f "http://127.0.0.1:${HEALTH_PORT}${HEALTH_URL}" &>/dev/null || exit 1 diff --git a/spotify_to_plex/README.md b/spotify_to_plex/README.md new file mode 100644 index 0000000000..e6ba819b0c --- /dev/null +++ b/spotify_to_plex/README.md @@ -0,0 +1,75 @@ +# Home assistant add-on: Spotify to Plex + + +I maintain this and other Home Assistant add-ons in my free time: keeping up with upstream changes, HA changes, and testing on real hardware takes a lot of time (and some money). I use around 5-10 of my >110 addons so regularly I install test machines (and purchase some test services such as vpn) that I don't use myself to troubleshoot and improve the addons + +If this add-on saves you time or makes your setup easier, I would be very grateful for your support! + +[![Buy me a coffee][donation-badge]](https://www.buymeacoffee.com/alexbelgium) +[![Donate via PayPal][paypal-badge]](https://www.paypal.com/donate/?hosted_button_id=DZFULJZTP3UQA) + +## Addon informations + +![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotify_to_plex%2Fconfig.yaml) +![Ingress](https://img.shields.io/badge/dynamic/yaml?label=Ingress&query=%24.ingress&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotify_to_plex%2Fconfig.yaml) +![Arch](https://img.shields.io/badge/dynamic/yaml?color=success&label=Arch&query=%24.arch&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fspotify_to_plex%2Fconfig.yaml) + +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9c6cf10bdbba45ecb202d7f579b5be0e)](https://www.codacy.com/gh/alexbelgium/hassio-addons/dashboard?utm_source=github.com&utm_medium=referral&utm_content=alexbelgium/hassio-addons&utm_campaign=Badge_Grade) +[![GitHub Super-Linter](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/weekly-supelinter.yaml?label=Lint%20code%20base)](https://github.com/alexbelgium/hassio-addons/actions/workflows/weekly-supelinter.yaml) +[![Builder](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/onpush_builder.yaml?label=Builder)](https://github.com/alexbelgium/hassio-addons/actions/workflows/onpush_builder.yaml) + +[donation-badge]: https://img.shields.io/badge/Buy%20me%20a%20coffee-%23d32f2f?logo=buy-me-a-coffee&style=flat&logoColor=white +[paypal-badge]: https://img.shields.io/badge/Buy%20me%20a%20coffee%20Paypal-%23d32f2f?logo=buy-me-a-coffee&style=flat&logoColor=white + +_Thanks to everyone having starred my repo! To star it click on the image below, then it will be on top right. Thanks!_ + +[![Stargazers repo roster for @alexbelgium/hassio-addons](https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.github/stars2.svg)](https://github.com/alexbelgium/hassio-addons/stargazers) + +## About + +This addon is based on the [docker image](https://hub.docker.com/r/jjdenhertog/spotify-to-plex) from [jjdenhertog/spotify-to-plex](https://github.com/jjdenhertog/spotify-to-plex). + +It keeps your Spotify playlists synced to Plex automatically: syncing any Spotify playlist (including Spotify-owned ones), supporting multiple Spotify users, scheduled automatic synchronization, smart caching and optional downloading of missing tracks via Lidarr, SLSKD or Tidal. + +## Configuration + +Before starting the add-on you need a Spotify developer application (https://developer.spotify.com/dashboard): + +1. Create an app and note its `Client ID` and `Client Secret`. +1. In the app settings, add the redirect URI `https://jjdenhertog.github.io/spotify-to-plex/callback.html` (this is the default `SPOTIFY_API_REDIRECT_URI`; change it only if you self-host the callback page). + +Fill the add-on options: + +| Option | Description | +|--------|-------------| +| `SPOTIFY_API_CLIENT_ID` | Client ID of your Spotify developer application | +| `SPOTIFY_API_CLIENT_SECRET` | Client secret of your Spotify developer application | +| `SPOTIFY_API_REDIRECT_URI` | OAuth redirect URI (must match the one configured in your Spotify app) | +| `ENCRYPTION_KEY` | Key used to encrypt stored secrets. **Leave empty** to have the add-on generate a random key on first start and persist it in the add-on config folder. Provide your own only if you want to reuse an existing configuration. | + +Use the add-on `env_vars` option to pass any extra upstream environment variables (for example Tidal, SLSKD, Lidarr or Plex settings). See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details. + +The configuration and cache are stored in the add-on config folder (`/addon_configs/`), so they survive restarts and updates. + +Webui can be found at `:9030`. + +## Installation + +The installation of this add-on is pretty straightforward and not different in +comparison to installing any other Hass.io add-on. + +1. Add my add-ons repository to your home assistant instance (in supervisor addons store at top right, or click button below if you have configured my HA) + [![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons) +1. Install this add-on. +1. Set the required options (Spotify Client ID and Secret). +1. Click the `Save` button to store your configuration. +1. Start the add-on. +1. Check the logs of the add-on to see if everything went well. +1. Open the webui, where you will finish the setup and connect your Spotify and Plex accounts. + +## Support + +For issues related to the add-on packaging, open an issue on [alexbelgium/hassio-addons](https://github.com/alexbelgium/hassio-addons/issues). +For issues related to the application itself, refer to the [upstream project](https://github.com/jjdenhertog/spotify-to-plex). + +[repository]: https://github.com/alexbelgium/hassio-addons diff --git a/spotify_to_plex/build.json b/spotify_to_plex/build.json new file mode 100644 index 0000000000..f54b721e47 --- /dev/null +++ b/spotify_to_plex/build.json @@ -0,0 +1,6 @@ +{ + "build_from": { + "aarch64": "jjdenhertog/spotify-to-plex:1.0.104", + "amd64": "jjdenhertog/spotify-to-plex:1.0.104" + } +} diff --git a/spotify_to_plex/config.yaml b/spotify_to_plex/config.yaml new file mode 100644 index 0000000000..c5137ce273 --- /dev/null +++ b/spotify_to_plex/config.yaml @@ -0,0 +1,34 @@ +arch: + - amd64 + - aarch64 +description: Sync Spotify playlists to Plex +environment: + STORAGE_DIR: /config +image: ghcr.io/alexbelgium/spotify_to_plex-{arch} +init: false +map: + - addon_config:rw +name: Spotify to Plex +options: + env_vars: [] + SPOTIFY_API_CLIENT_ID: "" + SPOTIFY_API_CLIENT_SECRET: "" + SPOTIFY_API_REDIRECT_URI: "https://jjdenhertog.github.io/spotify-to-plex/callback.html" + ENCRYPTION_KEY: "" +panel_icon: mdi:spotify +ports: + 9030/tcp: 9030 +ports_description: + 9030/tcp: Web interface +schema: + env_vars: + - name: match(^[A-Za-z0-9_]+$) + value: str? + SPOTIFY_API_CLIENT_ID: str? + SPOTIFY_API_CLIENT_SECRET: str? + SPOTIFY_API_REDIRECT_URI: str? + ENCRYPTION_KEY: str? +slug: spotify_to_plex +url: https://github.com/alexbelgium/hassio-addons/tree/master/spotify_to_plex +version: "1.0.104" +webui: "http://[HOST]:[PORT:9030]" diff --git a/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh new file mode 100644 index 0000000000..c4436ce16b --- /dev/null +++ b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bashio +# shellcheck shell=bash + +bashio::log.info "Starting Spotify to Plex" + +# Persist the app's /app/config into the HA add-on config dir (/config) +CONFIG_TARGET="/config" +mkdir -p "$CONFIG_TARGET" +if [ -d /app/config ] && [ ! -L /app/config ]; then + cp -rn /app/config/. "$CONFIG_TARGET/" 2> /dev/null || true + rm -rf /app/config +fi +ln -sfn "$CONFIG_TARGET" /app/config + +# Auto-generate a persistent ENCRYPTION_KEY when the user leaves it blank +if [ -z "${ENCRYPTION_KEY:-}" ]; then + if [ -f "$CONFIG_TARGET/.encryption_key" ]; then + ENCRYPTION_KEY="$(cat "$CONFIG_TARGET/.encryption_key")" + else + ENCRYPTION_KEY="$(head -c 32 /dev/urandom | base64)" + echo "$ENCRYPTION_KEY" > "$CONFIG_TARGET/.encryption_key" + bashio::log.info "Generated a new ENCRYPTION_KEY (stored in the add-on config dir)" + fi + export ENCRYPTION_KEY +fi + +# Hand off to the upstream supervisord (web + scraper + scheduler) +cd /app || true +exec /docker-entrypoint.sh supervisord -n -c /etc/supervisor/conf.d/supervisord.conf diff --git a/spotify_to_plex/updater.json b/spotify_to_plex/updater.json new file mode 100644 index 0000000000..cf1deb6a8f --- /dev/null +++ b/spotify_to_plex/updater.json @@ -0,0 +1,8 @@ +{ + "last_update": "2026-07-06", + "repository": "alexbelgium/hassio-addons", + "slug": "spotify_to_plex", + "source": "dockerhub", + "upstream_repo": "jjdenhertog/spotify-to-plex", + "upstream_version": "1.0.104" +} From 725f42932d701a5f6e26cd0e722d99485a4307c2 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Mon, 6 Jul 2026 09:14:01 +0200 Subject: [PATCH 2/4] Add files via upload --- spotify_to_plex/icon.png | Bin 0 -> 18187 bytes spotify_to_plex/logo.png | Bin 0 -> 18187 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 spotify_to_plex/icon.png create mode 100644 spotify_to_plex/logo.png diff --git a/spotify_to_plex/icon.png b/spotify_to_plex/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6b5b81f124c9d26c2f58becbf701a0c047a993 GIT binary patch literal 18187 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 Date: Mon, 6 Jul 2026 07:14:29 +0000 Subject: [PATCH 3/4] Fix encryption key format and config migration safety in spotify_to_plex - Generate ENCRYPTION_KEY as 64 hex chars instead of base64: upstream reads it via Buffer.from(ENCRYPTION_KEY, 'hex') for aes-256-cbc, so a base64 value silently broke Spotify token encryption for anyone leaving the option blank (the default path). - Store the key without a trailing newline and chmod 600 it. - Only delete /app/config after a successful copy into /config, so a failed migration (permissions, disk full) can't silently wipe the upstream default config. Addresses review feedback from PR #2816. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW --- .../rootfs/etc/cont-init.d/99-run.sh | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh index c4436ce16b..f329bc9254 100644 --- a/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh +++ b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh @@ -6,19 +6,30 @@ bashio::log.info "Starting Spotify to Plex" # Persist the app's /app/config into the HA add-on config dir (/config) CONFIG_TARGET="/config" mkdir -p "$CONFIG_TARGET" -if [ -d /app/config ] && [ ! -L /app/config ]; then - cp -rn /app/config/. "$CONFIG_TARGET/" 2> /dev/null || true - rm -rf /app/config -fi -ln -sfn "$CONFIG_TARGET" /app/config -# Auto-generate a persistent ENCRYPTION_KEY when the user leaves it blank -if [ -z "${ENCRYPTION_KEY:-}" ]; then - if [ -f "$CONFIG_TARGET/.encryption_key" ]; then - ENCRYPTION_KEY="$(cat "$CONFIG_TARGET/.encryption_key")" +if [ -d /app/config ] && [ ! -L /app/config ]; then + if cp -rn /app/config/. "$CONFIG_TARGET/"; then + rm -rf /app/config else - ENCRYPTION_KEY="$(head -c 32 /dev/urandom | base64)" - echo "$ENCRYPTION_KEY" > "$CONFIG_TARGET/.encryption_key" + bashio::log.error "Failed to migrate /app/config to $CONFIG_TARGET; leaving original config in place" + fi +fi + +if [ ! -e /app/config ] || [ -L /app/config ]; then + ln -sfn "$CONFIG_TARGET" /app/config +fi + +# Auto-generate a persistent ENCRYPTION_KEY when the user leaves it blank. +# Upstream reads this via Buffer.from(ENCRYPTION_KEY, 'hex') for aes-256-cbc, +# so it must be exactly 64 hex characters (not base64). +if [ -z "${ENCRYPTION_KEY:-}" ]; then + KEY_FILE="$CONFIG_TARGET/.encryption_key" + if [ -f "$KEY_FILE" ]; then + ENCRYPTION_KEY="$(cat "$KEY_FILE")" + else + ENCRYPTION_KEY="$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" + printf '%s' "$ENCRYPTION_KEY" > "$KEY_FILE" + chmod 600 "$KEY_FILE" bashio::log.info "Generated a new ENCRYPTION_KEY (stored in the add-on config dir)" fi export ENCRYPTION_KEY From 15c0c296f4617a48424e0897cc7e49addd26aaf6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 07:20:09 +0000 Subject: [PATCH 4/4] Fail safely instead of degrading silently in spotify_to_plex startup - Exit with a clear error if the /app/config migration fails, rather than continuing to run against the non-persistent source directory (user changes would otherwise silently vanish on the next restart). - Exit with a clear error if /app is missing instead of swallowing a broken upstream image layout with `|| true`. Addresses further review feedback from PR #2816. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01NZTfSk3GQRU7oD85TnjsmW --- spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh index f329bc9254..4b53e38372 100644 --- a/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh +++ b/spotify_to_plex/rootfs/etc/cont-init.d/99-run.sh @@ -11,7 +11,7 @@ if [ -d /app/config ] && [ ! -L /app/config ]; then if cp -rn /app/config/. "$CONFIG_TARGET/"; then rm -rf /app/config else - bashio::log.error "Failed to migrate /app/config to $CONFIG_TARGET; leaving original config in place" + bashio::exit.nok "Failed to migrate /app/config to $CONFIG_TARGET; refusing to start with a non-persistent config" fi fi @@ -36,5 +36,5 @@ if [ -z "${ENCRYPTION_KEY:-}" ]; then fi # Hand off to the upstream supervisord (web + scraper + scheduler) -cd /app || true +cd /app || bashio::exit.nok "Cannot find /app (upstream image layout may have changed)" exec /docker-entrypoint.sh supervisord -n -c /etc/supervisor/conf.d/supervisord.conf