From 618aecb676ec3f806f774ce56e756c56dbc9ada4 Mon Sep 17 00:00:00 2001 From: Alex Lebens Date: Fri, 6 Feb 2026 11:06:10 -0600 Subject: [PATCH] feat: add spotisub --- clusters/cl01tl/helm/blocky/values.yaml | 1 + clusters/cl01tl/helm/gatus/values.yaml | 3 + clusters/cl01tl/helm/homepage/values.yaml | 6 + clusters/cl01tl/helm/spotisub/Chart.lock | 6 + clusters/cl01tl/helm/spotisub/Chart.yaml | 21 ++ .../spotisub/templates/external-secret.yaml | 93 +++++++++ .../helm/spotisub/templates/namespace.yaml | 11 + .../templates/persistent-volume-claim.yaml | 17 ++ .../spotisub/templates/persistent-volume.yaml | 23 +++ clusters/cl01tl/helm/spotisub/values.yaml | 194 ++++++++++++++++++ hosts/ps08rp/blocky/config.yml | 1 + hosts/ps09rp/blocky/config.yml | 1 + 12 files changed, 377 insertions(+) create mode 100644 clusters/cl01tl/helm/spotisub/Chart.lock create mode 100644 clusters/cl01tl/helm/spotisub/Chart.yaml create mode 100644 clusters/cl01tl/helm/spotisub/templates/external-secret.yaml create mode 100644 clusters/cl01tl/helm/spotisub/templates/namespace.yaml create mode 100644 clusters/cl01tl/helm/spotisub/templates/persistent-volume-claim.yaml create mode 100644 clusters/cl01tl/helm/spotisub/templates/persistent-volume.yaml create mode 100644 clusters/cl01tl/helm/spotisub/values.yaml diff --git a/clusters/cl01tl/helm/blocky/values.yaml b/clusters/cl01tl/helm/blocky/values.yaml index 0aa9907a8..d90505a9c 100644 --- a/clusters/cl01tl/helm/blocky/values.yaml +++ b/clusters/cl01tl/helm/blocky/values.yaml @@ -157,6 +157,7 @@ blocky: sonarr IN CNAME traefik-cl01tl sonarr-4k IN CNAME traefik-cl01tl sonarr-anime IN CNAME traefik-cl01tl + spotisub IN CNAME traefik-cl01tl stalwart IN CNAME traefik-cl01tl tdarr IN CNAME traefik-cl01tl tubearchivist IN CNAME traefik-cl01tl diff --git a/clusters/cl01tl/helm/gatus/values.yaml b/clusters/cl01tl/helm/gatus/values.yaml index 4162d9495..616f13ef8 100644 --- a/clusters/cl01tl/helm/gatus/values.yaml +++ b/clusters/cl01tl/helm/gatus/values.yaml @@ -310,6 +310,9 @@ gatus: - name: lidarr url: https://lidarr.alexlebens.net <<: *defaults + - name: spotisub + url: https://spotisub.alexlebens.net + <<: *defaults - name: yubal-playlist url: https://yubal-playlist.alexlebens.net <<: *defaults diff --git a/clusters/cl01tl/helm/homepage/values.yaml b/clusters/cl01tl/helm/homepage/values.yaml index fc74ac3a8..1dc075100 100644 --- a/clusters/cl01tl/helm/homepage/values.yaml +++ b/clusters/cl01tl/helm/homepage/values.yaml @@ -661,6 +661,12 @@ homepage: href: https://yubal-playlist.alexlebens.net siteMonitor: http://yubal-playlist.yubal-playlist:80 statusStyle: dot + - Spotisub: + icon: sh-spotify.webp + description: Replicate Spotify playlist + href: https://spotisub.alexlebens.net + siteMonitor: http://spotisub.spotisub:80 + statusStyle: dot - slskd: icon: sh-slskd.webp description: slskd diff --git a/clusters/cl01tl/helm/spotisub/Chart.lock b/clusters/cl01tl/helm/spotisub/Chart.lock new file mode 100644 index 000000000..068abed23 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: app-template + repository: https://bjw-s-labs.github.io/helm-charts/ + version: 4.6.2 +digest: sha256:3b63381e4968f95ce2d99fae620f3d1ae6af295b1bacc4ed0fbe9f1ccb0e9405 +generated: "2026-02-06T11:04:57.311195-06:00" diff --git a/clusters/cl01tl/helm/spotisub/Chart.yaml b/clusters/cl01tl/helm/spotisub/Chart.yaml new file mode 100644 index 000000000..5eb512873 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: spotisub +version: 1.0.0 +description: Spotisub +keywords: + - spotisub + - music + - spotify +home: https://wiki.alexlebens.dev/s/ +sources: + - https://github.com/blastbeng/spotisub + - https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template +maintainers: + - name: alexlebens +dependencies: + - name: app-template + alias: spotisub + repository: https://bjw-s-labs.github.io/helm-charts/ + version: 4.6.2 +# renovate: datasource=github-releases depName=blastbeng/spotisub +appVersion: v0.3.6 diff --git a/clusters/cl01tl/helm/spotisub/templates/external-secret.yaml b/clusters/cl01tl/helm/spotisub/templates/external-secret.yaml new file mode 100644 index 000000000..7c5d66103 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/templates/external-secret.yaml @@ -0,0 +1,93 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: spotisub-config-secret + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: spotisub-config-secret + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Release.Name }} +spec: + secretStoreRef: + kind: ClusterSecretStore + name: vault + data: + - secretKey: spotify-client-id + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /spotify/andrew + metadataPolicy: None + property: client-id + - secretKey: spotify-client-secret + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /spotify/andrew + metadataPolicy: None + property: client-secret + - secretKey: spotify-redirect-uri + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /spotify/andrew + metadataPolicy: None + property: redirect-uri + - secretKey: subsonic-user + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /cl01tl/navidrome/andrew + metadataPolicy: None + property: user + - secretKey: subsonic-password + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /cl01tl/navidrome/andrew + metadataPolicy: None + property: password + - secretKey: lidarr-key + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /cl01tl/lidarr2/key + metadataPolicy: None + property: key + +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: spotisub-wireguard-conf + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: spotisub-wireguard-conf + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Release.Name }} +spec: + secretStoreRef: + kind: ClusterSecretStore + name: vault + data: + - secretKey: private-key + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /protonvpn/conf/cl01tl + metadataPolicy: None + property: private-key + - secretKey: proton-email + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /protonvpn/conf/cl01tl + metadataPolicy: None + property: email + - secretKey: proton-password + remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: /protonvpn/conf/cl01tl + metadataPolicy: None + property: password diff --git a/clusters/cl01tl/helm/spotisub/templates/namespace.yaml b/clusters/cl01tl/helm/spotisub/templates/namespace.yaml new file mode 100644 index 000000000..66c0ad6a3 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/templates/namespace.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: spotisub + labels: + app.kubernetes.io/name: spotisub + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Release.Name }} + pod-security.kubernetes.io/audit: privileged + pod-security.kubernetes.io/enforce: privileged + pod-security.kubernetes.io/warn: privileged diff --git a/clusters/cl01tl/helm/spotisub/templates/persistent-volume-claim.yaml b/clusters/cl01tl/helm/spotisub/templates/persistent-volume-claim.yaml new file mode 100644 index 000000000..b56be1f04 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/templates/persistent-volume-claim.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: spotisub-nfs-storage + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: spotisub-nfs-storage + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Release.Name }} +spec: + volumeName: spotisub-nfs-storage + storageClassName: nfs-client + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi diff --git a/clusters/cl01tl/helm/spotisub/templates/persistent-volume.yaml b/clusters/cl01tl/helm/spotisub/templates/persistent-volume.yaml new file mode 100644 index 000000000..09787f4bb --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/templates/persistent-volume.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: spotisub-nfs-storage + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: spotisub-nfs-storage + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Release.Name }} +spec: + persistentVolumeReclaimPolicy: Retain + storageClassName: nfs-client + capacity: + storage: 1Gi + accessModes: + - ReadWriteMany + nfs: + path: /volume2/Storage/Music Youtube/ + server: synologybond.alexlebens.net + mountOptions: + - vers=4 + - minorversion=1 + - noac diff --git a/clusters/cl01tl/helm/spotisub/values.yaml b/clusters/cl01tl/helm/spotisub/values.yaml new file mode 100644 index 000000000..0f4f86756 --- /dev/null +++ b/clusters/cl01tl/helm/spotisub/values.yaml @@ -0,0 +1,194 @@ +spotisub: + controllers: + main: + type: deployment + replicas: 0 + strategy: Recreate + revisionHistoryLimit: 3 + containers: + main: + image: + repository: blastbeng/spotisub + tag: v0.3.6 + pullPolicy: IfNotPresent + env: + - name: SPOTIPY_CLIENT_ID + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: spotify-client-id + - name: SPOTIPY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: spotify-client-secret + - name: SPOTIPY_REDIRECT_URI + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: spotify-redirect-uri + - name: SUBSONIC_API_HOST + value: http://navidrome-main.navidrome + - name: SUBSONIC_API_PORT + value: 80 + - name: SUBSONIC_API_USER + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: subsonic-user + - name: SUBSONIC_API_PASS + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: subsonic-password + - name: PLAYLIST_PREFIX + value: "Spotify - " + - name: NUM_USER_PLAYLISTS + value: 0 + - name: ARTIST_GEN_SCHED + value: 0 + - name: RECOMEND_GEN_SCHED + value: 0 + - name: SPOTDL_ENABLED + value: 1 + - name: SPOTDL_OUT_FORMAT + value: "/mnt/store/Music Youtube/Andrew Lebens/{artist}/{album} ({year})/{artists} - {album} - {track-number} - {title}.{output-ext}" + - name: LIDARR_ENABLED + value: 1 + - name: LIDARR_IP + value: http://lidarr.lidarr + - name: LIDARR_PORT + value: 80 + - name: LIDARR_TOKEN + valueFrom: + secretKeyRef: + name: spotisub-config-secret + key: lidarr-key + probes: + liveness: + enabled: true + custom: true + spec: + exec: + command: + - /bin/sh + - -c + - "curl -s http://127.0.0.1:5183/api/v1/utils/healthcheck | grep -q 'Ok!'" + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 15 + resources: + requests: + cpu: 10m + memory: 128Mi + gluetun: + image: + repository: ghcr.io/qdm12/gluetun + tag: v3.41.0@sha256:6b54856716d0de56e5bb00a77029b0adea57284cf5a466f23aad5979257d3045 + pullPolicy: IfNotPresent + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "(ip rule del table 51820; ip -6 rule del table 51820) || true"] + env: + - name: VPN_SERVICE_PROVIDER + value: protonvpn + - name: VPN_TYPE + value: wireguard + - name: WIREGUARD_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: spotisub-wireguard-conf + key: private-key + - name: UPDATER_PROTONVPN_EMAIL + valueFrom: + secretKeyRef: + name: spotisub-wireguard-conf + key: proton-email + - name: UPDATER_PROTONVPN_PASSWORD + valueFrom: + secretKeyRef: + name: spotisub-wireguard-conf + key: proton-password + - name: FIREWALL_OUTBOUND_SUBNETS + value: 10.0.0.0/8 + - name: FIREWALL_INPUT_PORTS + value: 5183 + - name: DNS_UPSTREAM_RESOLVER_TYPE + value: dot + securityContext: + privileged: True + capabilities: + add: + - NET_ADMIN + - SYS_MODULE + probes: + liveness: + enabled: true + custom: true + spec: + exec: + command: + - /gluetun-entrypoint + - healthcheck + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 15 + resources: + limits: + devic.es/tun: "1" + requests: + devic.es/tun: "1" + cpu: 10m + memory: 128Mi + service: + main: + controller: main + ports: + http: + port: 80 + targetPort: 5183 + protocol: HTTP + route: + main: + kind: HTTPRoute + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: traefik-gateway + namespace: traefik + hostnames: + - spotisub.alexlebens.net + rules: + - backendRefs: + - group: '' + kind: Service + name: spotisub + port: 80 + weight: 100 + matches: + - path: + type: PathPrefix + value: / + persistence: + cache: + storageClass: ceph-block + accessMode: ReadWriteOnce + size: 1Gi + retain: true + advancedMounts: + main: + main: + - path: /home/user/spotisub/cache + readOnly: false + music: + existingClaim: spotisub-nfs-storage + advancedMounts: + main: + main: + - path: /mnt/store/Music Youtube/ + readOnly: false diff --git a/hosts/ps08rp/blocky/config.yml b/hosts/ps08rp/blocky/config.yml index 67e4b02bf..09e2fd85e 100644 --- a/hosts/ps08rp/blocky/config.yml +++ b/hosts/ps08rp/blocky/config.yml @@ -132,6 +132,7 @@ customDNS: sonarr IN CNAME traefik-cl01tl sonarr-4k IN CNAME traefik-cl01tl sonarr-anime IN CNAME traefik-cl01tl + spotisub IN CNAME traefik-cl01tl stalwart IN CNAME traefik-cl01tl tdarr IN CNAME traefik-cl01tl tubearchivist IN CNAME traefik-cl01tl diff --git a/hosts/ps09rp/blocky/config.yml b/hosts/ps09rp/blocky/config.yml index 4eaa93ce1..8b7b458f1 100644 --- a/hosts/ps09rp/blocky/config.yml +++ b/hosts/ps09rp/blocky/config.yml @@ -153,6 +153,7 @@ customDNS: sonarr IN CNAME traefik-cl01tl sonarr-4k IN CNAME traefik-cl01tl sonarr-anime IN CNAME traefik-cl01tl + spotisub IN CNAME traefik-cl01tl stalwart IN CNAME traefik-cl01tl tdarr IN CNAME traefik-cl01tl tubearchivist IN CNAME traefik-cl01tl