Album: Download im Profil — zwei ZIPs (DE+EN) mit Cover, ID3-Tags, Liner Notes (v1302)

- Profil-Karte "Ban Yaro — das Album" in settings.js mit Cover-Thumbnail +
  zwei Download-Buttons (Deutsch/English), rein deklarativ (CSP-safe)
- /downloads StaticFiles-Mount in main.py (makedirs-Schutz); ZIPs matchen
  keine SW-Cache-Regel -> fluten den Cache nicht
- backend/static/downloads/ban-yaro-album-{de,en}.zip: je 7 MP3s mit ID3-Tags
  + eingebettetem Cover, cover.jpg, LIESMICH.txt/README.txt (Tracklist + Lizenz)
- Cover aus Fruehling-Playdate-Foto (quadr. Crop + Wortmarke), DE/EN-Variante;
  textfreies album-thumb.jpg fuer die Karte
- Reproduzierbar: make album (tools/album-build/build.sh + Liner-Notes)
- LIVE auf Prod + Staging v1302
This commit is contained in:
rene 2026-06-17 06:25:34 +02:00
parent aea489aa5a
commit 1a03cab5dd
15 changed files with 213 additions and 18 deletions

3
.gitignore vendored
View file

@ -21,3 +21,6 @@ tiles/build/
*.mbtiles *.mbtiles
tiles/build.log tiles/build.log
tiles/.DS_Store tiles/.DS_Store
# Album-Build: Zwischendateien (ZIPs + Thumbnail werden committet)
tools/album-build/dist/

View file

@ -28,7 +28,7 @@ TAR_EXCLUDE := --exclude='.git' \
--exclude='./.DS_Store' --exclude='./.DS_Store'
.PHONY: help deploy deploy-clean staging release sync push restart build stop status \ .PHONY: help deploy deploy-clean staging release sync push restart build stop status \
logs logs-f shell db dev clean-cache check-ssh reports bump test tiles tiles-deploy logs logs-f shell db dev clean-cache check-ssh reports bump test tiles tiles-deploy album
# ---------------------------------------------------------- # ----------------------------------------------------------
# SSH-Prüfung — Abhängigkeit aller DS-Befehle # SSH-Prüfung — Abhängigkeit aller DS-Befehle
@ -343,6 +343,13 @@ bump:
sed -i.bak -E "s/\?v=[0-9]+/?v=$$NEW/g" backend/static/landing.html && rm -f backend/static/landing.html.bak; \ sed -i.bak -E "s/\?v=[0-9]+/?v=$$NEW/g" backend/static/landing.html && rm -f backend/static/landing.html.bak; \
echo " ✓ APP_VER $$CUR → $$NEW (VERSION, sw.js, app.js, index.html, landing.html aktualisiert)" echo " ✓ APP_VER $$CUR → $$NEW (VERSION, sw.js, app.js, index.html, landing.html aktualisiert)"
# ----------------------------------------------------------
# ALBUM — die zwei Download-ZIPs (DE+EN) neu bauen (Cover, ID3-Tags, Liner Notes)
# Nur nötig, wenn sich Songs/Cover/Beschreibung ändern. Braucht ImageMagick + ffmpeg.
# ----------------------------------------------------------
album:
@bash tools/album-build/build.sh
# ---------------------------------------------------------- # ----------------------------------------------------------
# TEST — Smoke-Tests gegen isolierte Test-DB (kein Docker, kein DS) # TEST — Smoke-Tests gegen isolierte Test-DB (kein Docker, kein DS)
# ---------------------------------------------------------- # ----------------------------------------------------------

View file

@ -1 +1 @@
1301 1302

View file

@ -375,6 +375,8 @@ app.mount("/js", StaticFiles(directory=f"{STATIC_DIR}/js"), name="js")
app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons") app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons")
app.mount("/img", StaticFiles(directory=f"{STATIC_DIR}/img"), name="img") app.mount("/img", StaticFiles(directory=f"{STATIC_DIR}/img"), name="img")
app.mount("/sounds", StaticFiles(directory=f"{STATIC_DIR}/sounds"), name="sounds") # Yaro-Navi-Sounds app.mount("/sounds", StaticFiles(directory=f"{STATIC_DIR}/sounds"), name="sounds") # Yaro-Navi-Sounds
os.makedirs(f"{STATIC_DIR}/downloads", exist_ok=True) # Album-ZIPs (vom Build-Skript erzeugt)
app.mount("/downloads", StaticFiles(directory=f"{STATIC_DIR}/downloads"), name="downloads")
# Selbst-gehostete Vektor-Tiles (.pmtiles) — liegen im data-Volume, NICHT im Image. # Selbst-gehostete Vektor-Tiles (.pmtiles) — liegen im data-Volume, NICHT im Image.
# WICHTIG: Starlettes StaticFiles/FileResponse liefert hinter unserer BaseHTTPMiddleware # WICHTIG: Starlettes StaticFiles/FileResponse liefert hinter unserer BaseHTTPMiddleware

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View file

@ -86,14 +86,14 @@
<title>Ban Yaro</title> <title>Ban Yaro</title>
<!-- Theme + theme-color Statusleiste vor CSS setzen --> <!-- Theme + theme-color Statusleiste vor CSS setzen -->
<script src="/js/boot-early.js?v=1301"></script> <script src="/js/boot-early.js?v=1302"></script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung --> <!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=1301"> <link rel="stylesheet" href="/css/design-system.css?v=1302">
<link rel="stylesheet" href="/css/layout.css?v=1301"> <link rel="stylesheet" href="/css/layout.css?v=1302">
<link rel="stylesheet" href="/css/components.css?v=1301"> <link rel="stylesheet" href="/css/components.css?v=1302">
<link rel="stylesheet" href="/css/utilities.css?v=1301"> <link rel="stylesheet" href="/css/utilities.css?v=1302">
<link rel="stylesheet" href="/css/lists.css?v=1301"> <link rel="stylesheet" href="/css/lists.css?v=1302">
</head> </head>
<body> <body>
@ -624,12 +624,12 @@
<div id="modal-container"></div> <div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features --> <!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js?v=1301"></script> <script src="/js/api.js?v=1302"></script>
<script src="/js/ui.js?v=1301"></script> <script src="/js/ui.js?v=1302"></script>
<script src="/js/app.js?v=1301"></script> <script src="/js/app.js?v=1302"></script>
<script src="/js/worlds.js?v=1301"></script> <script src="/js/worlds.js?v=1302"></script>
<script src="/js/offline-indicator.js?v=1301"></script> <script src="/js/offline-indicator.js?v=1302"></script>
<script src="/js/contact-form.js?v=1301"></script> <script src="/js/contact-form.js?v=1302"></script>
<!-- Feature-Seiten werden lazy geladen --> <!-- Feature-Seiten werden lazy geladen -->
@ -639,7 +639,7 @@
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) --> <!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
<script src="/js/boot.js?v=1301"></script> <script src="/js/boot.js?v=1302"></script>
</body> </body>

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung. Router, State-Management, Navigation, Initialisierung.
============================================================ */ ============================================================ */
const APP_VER = '1301'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VER = '1302'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator) window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
window.APP_VERSION = APP_VERSION; window.APP_VERSION = APP_VERSION;

View file

@ -877,6 +877,35 @@ window.Page_settings = (() => {
</div> </div>
</div> </div>
<!-- Album herunterladen -->
<div class="card mb-4">
<div class="by-card-section-header">Ban Yaro das Album</div>
<div style="padding:var(--space-4);display:flex;gap:var(--space-4);align-items:center">
<img src="/img/banyaro/album-thumb.jpg" alt="Ban Yaro — Album-Cover"
width="84" height="84" loading="lazy"
style="width:84px;height:84px;border-radius:var(--radius-md);object-fit:cover;flex-shrink:0">
<div style="min-width:0">
<div style="font-weight:600">7 Songs zum Behalten 🎸</div>
<div class="text-xs-secondary" style="margin-top:2px">
Das ganze Album als Download auf Deutsch oder Englisch.
Behalten &amp; teilen ausdrücklich erwünscht.
</div>
</div>
</div>
<div style="padding:0 var(--space-4) var(--space-4);display:flex;gap:var(--space-2);flex-wrap:wrap">
<a class="btn btn-primary" href="/downloads/ban-yaro-album-de.zip"
download="Ban Yaro - Das Album.zip"
style="flex:1;min-width:140px;justify-content:center;text-decoration:none">
${UI.icon('download-simple')} Deutsch · 33 MB
</a>
<a class="btn btn-secondary" href="/downloads/ban-yaro-album-en.zip"
download="Ban Yaro - The Album.zip"
style="flex:1;min-width:140px;justify-content:center;text-decoration:none">
${UI.icon('download-simple')} English · 36 MB
</a>
</div>
</div>
<!-- App empfehlen --> <!-- App empfehlen -->
<div class="card" style="margin-bottom:var(--space-5)" id="referral-card"> <div class="card" style="margin-bottom:var(--space-5)" id="referral-card">
<div style="padding:var(--space-4);border-bottom:1px solid var(--c-border)"> <div style="padding:var(--space-4);border-bottom:1px solid var(--c-border)">

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<script src="/js/landing-init.js?v=1301"></script> <script src="/js/landing-init.js?v=1302"></script>
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title> <title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, direkt im Browser oder als native iPhone-App (Ban Yaro Go)."> <meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, direkt im Browser oder als native iPhone-App (Ban Yaro Go).">
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz"> <meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">

View file

@ -4,7 +4,7 @@
============================================================ */ ============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab // ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
const VER = '1301'; const VER = '1302';
const CACHE_VERSION = `by-v${VER}`; const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten

86
tools/album-build/build.sh Executable file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Baut die zwei Album-Downloads (DE + EN) reproduzierbar:
# Cover (Foto quadr. + Wortmarke) → ID3-Tags + eingebettetes Cover → ZIP mit Liner Notes.
# Quelle: die committeten MP3s in backend/static/sounds/ + ein Hi-Res-Foto.
# Ausgabe: backend/static/downloads/ban-yaro-album-{de,en}.zip + img/banyaro/album-thumb.jpg
# Benötigt: ImageMagick (magick), ffmpeg, zip. Aufruf: make album (oder bash tools/album-build/build.sh)
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SND="$ROOT/backend/static/sounds"
PHOTO="$ROOT/backend/static/img/banyaro/hires/banyaro_fruehling_playdate_hires.jpg"
FONT="/System/Library/Fonts/Supplemental/Arial Bold.ttf"
BUILD="$ROOT/tools/album-build"
DIST="$BUILD/dist"
DL="$ROOT/backend/static/downloads"
IMG="$ROOT/backend/static/img/banyaro"
rm -rf "$DIST"; mkdir -p "$DIST" "$DL" "$IMG"
# --- 1) Cover (quadratischer Crop auf die Hunde + Verlauf unten + Wortmarke) ---
make_cover() { # $1=subtitle $2=outfile
magick "$PHOTO" -auto-orient -crop 2648x2648+551+0 +repage -resize 2000x2000 \
\( -size 2000x950 gradient:none-'rgba(0,0,0,0.80)' \) -gravity south -compose over -composite \
-gravity south -font "$FONT" -kerning 10 \
-fill 'rgba(0,0,0,0.55)' -pointsize 168 -annotate +4+254 "BAN YARO" \
-fill white -pointsize 168 -annotate +0+250 "BAN YARO" \
-kerning 7 -fill 'rgba(255,255,255,0.92)' -pointsize 60 -annotate +0+160 "$1" \
-quality 90 "$2"
}
make_cover "DAS ALBUM · 7 SONGS" "$DIST/cover-de.jpg"
make_cover "THE ALBUM · 7 SONGS" "$DIST/cover-en.jpg"
# Eingebettetes Albumart kleiner halten (sonst blähen sich die MP3s auf)
magick "$DIST/cover-de.jpg" -resize 800x800 -quality 85 "$DIST/art-de.jpg"
magick "$DIST/cover-en.jpg" -resize 800x800 -quality 85 "$DIST/art-en.jpg"
# Neutrales Thumbnail (ohne Text) für die Profil-Karte — der Titel steht dort im HTML
magick "$PHOTO" -auto-orient -crop 2648x2648+551+0 +repage -resize 600x600 -quality 85 "$IMG/album-thumb.jpg"
# --- 2)+3) pro Sprache: taggen + zippen ---
# Zeilenformat: "quelldatei(ohne .mp3)|Titel|Untertitel"
DE_TRACKS=(
"ban-yaro-blues|Ban Yaro Blues|Die Hymne"
"ban-yaro-mobil|Ban Yaro Mobil|Erste Fahrt im Anhänger"
"amy|Amy|Eine Liebesromanze"
"beim-friseur|Beim Friseur|Halbes Fell, Energie pur"
"leckerli-paradies|Leckerli-Paradies|Voller Napf, volles Glück"
"platsch|Platsch!|Ab ins kühle Nass"
"bester-freund|Bester Freund|Du und ich"
)
EN_TRACKS=(
"ban-yaro-blues-en|Ban Yaro Blues|The anthem"
"ban-yaro-mobil-en|Ban Yaro Mobile|First ride in the trailer"
"amy-en|Amy|A love duet"
"at-the-groomers-en|At the Groomer's|Half the fur, all the energy"
"treat-paradise-en|Treat Paradise|Full bowl, full heart"
"splash-en|Splash!|Into the cool water"
"best-friend-en|Best Friend|You and me"
)
zip_album() { # $1=lang $2=AlbumName(ID3, Em-Dash ok) $3=Ordner(ASCII) $4=art $5=liner shift 5; rest=tracks
local lang="$1" album="$2" fname="$3" art="$4" liner="$5"; shift 5
local folder="$DIST/$fname"; rm -rf "$folder"; mkdir -p "$folder"
if [ "$lang" = "de" ]; then cp "$liner" "$folder/LIESMICH.txt"; else cp "$liner" "$folder/README.txt"; fi
cp "$art" "$folder/cover.jpg"
local n=0 line src title sub nn
for line in "$@"; do
n=$((n+1)); nn=$(printf "%02d" "$n")
IFS='|' read -r src title sub <<<"$line"
ffmpeg -y -loglevel error -i "$SND/$src.mp3" -i "$art" \
-map 0:a -map 1:v -c copy -id3v2_version 3 \
-metadata title="$title" -metadata artist="Ban Yaro" \
-metadata album="$album" -metadata album_artist="Ban Yaro" \
-metadata track="$n/7" -metadata date="2026" -metadata genre="Blues" \
-metadata comment="$sub" -disposition:v:0 attached_pic \
"$folder/$nn $title.mp3"
done
( cd "$DIST" && rm -f "$DL/ban-yaro-album-$lang.zip" && zip -r -X -q "$DL/ban-yaro-album-$lang.zip" "$fname" )
echo "$fname → ban-yaro-album-$lang.zip ($(du -h "$DL/ban-yaro-album-$lang.zip" | cut -f1))"
}
# LIESMICH.txt für DE, README.txt für EN
cp "$BUILD/liner-de.txt" "$DIST/_liner-de.txt"
cp "$BUILD/liner-en.txt" "$DIST/_liner-en.txt"
zip_album "de" "Ban Yaro — Das Album" "Ban Yaro - Das Album" "$DIST/art-de.jpg" "$DIST/_liner-de.txt" "${DE_TRACKS[@]}"
zip_album "en" "Ban Yaro — The Album" "Ban Yaro - The Album" "$DIST/art-en.jpg" "$DIST/_liner-en.txt" "${EN_TRACKS[@]}"
echo "Fertig. Downloads in backend/static/downloads/, Thumbnail in img/banyaro/album-thumb.jpg"

View file

@ -0,0 +1,34 @@
==================================================
BAN YARO — DAS ALBUM
7 Songs
==================================================
Sieben Lieder über einen Hund namens Ban Yaro:
übers Gassigehen bei jedem Wetter, die erste Fahrt
im Anhänger, die große Liebe, den Friseur, den
vollen Napf, das kühle Wasser — und den besten
Freund, den ein Mensch haben kann.
Selbst gemacht, mit viel Herz. Aus der Ban-Yaro-App.
--------------------------------------------------
TRACKLIST
--------------------------------------------------
1. Ban Yaro Blues Die Hymne
2. Ban Yaro Mobil Erste Fahrt im Anhänger
3. Amy Eine Liebesromanze
4. Beim Friseur Halbes Fell, Energie pur
5. Leckerli-Paradies Voller Napf, volles Glück
6. Platsch! Ab ins kühle Nass
7. Bester Freund Du und ich
--------------------------------------------------
Ein Ban-Yaro-Original · banyaro.app
(c) 2026 Ban Yaro
Diese Aufnahmen sind ein kleines Geschenk:
behalten und teilen ausdrucklich erwunscht —
nur bitte nicht kommerziell verwenden.
Viel Freude beim Horen und einen schonen
Gassi-Gang! Wuff. 🐾

View file

@ -0,0 +1,34 @@
==================================================
BAN YARO — THE ALBUM
7 Songs
==================================================
Seven songs about a dog named Ban Yaro:
about walks in every kind of weather, the first
ride in the trailer, falling in love, the groomer,
a full bowl, cool water — and the best friend a
human could ever ask for.
Homemade, with all our heart. From the Ban Yaro app.
--------------------------------------------------
TRACKLIST
--------------------------------------------------
1. Ban Yaro Blues The anthem
2. Ban Yaro Mobile First ride in the trailer
3. Amy A love duet
4. At the Groomer's Half the fur, all the energy
5. Treat Paradise Full bowl, full heart
6. Splash! Into the cool water
7. Best Friend You and me
--------------------------------------------------
A Ban Yaro original · banyaro.app
(c) 2026 Ban Yaro
These recordings are a little gift:
keep them and share them freely —
just please don't use them commercially.
Enjoy the music, and have a wonderful walk!
Woof. 🐾