diff --git a/CHANGELOG.md b/CHANGELOG.md index 709948c..b23b217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,13 @@ like Android), concert events become **reliable across web/Android**, several An **YouTube downloads are recovered** after they broke on two independent fronts. ### Added +- **Android APK download in Settings (Web)** β a "π€ Android App" section in Settings offers the + latest APK so co-users self-serve instead of the maintainer hand-delivering builds. The backend + serves the staged APK (`GET /api/apk`, metadata at `/api/apk/info`); the button appears only when an + APK is present and shows version/size/date. **Maintainer workflow:** drop the release build into the + backend's data volume β `docker cp app-release.apk novasonic-backend:/data/novasonic.apk` (optional + `docker cp version.txt novasonic-backend:/data/novasonic.version.txt`). The Android *source* is + already in the repo; only build artifacts stay out of git. - **Unified Global Search field (Web)** β the separate Song-Importer page/sidebar entry is gone. The one search field now auto-detects: a **link** (Spotify/SoundCloud/YouTube) opens the importer preview, plain **text** runs a Deezer search. Cleaner left nav, same behaviour as Android. diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js index 5e3f32f..a26e2ef 100644 --- a/backend/novasonic-server.js +++ b/backend/novasonic-server.js @@ -466,6 +466,33 @@ app.get('/api/events-debug/:artist', (req, res) => { }).on('error', e => res.json({ error: e.message })); }); +// ββ NovaSonic Android APK distribution ββββββββββββββββββββββββββββββββββββββ +// The web app offers a "download the Android app" button that points here, so co-users always get +// the latest APK without the maintainer hand-delivering it. Drop the latest release build into the +// writable data volume (APK_PATH, default /data/novasonic.apk): +// docker cp app-release.apk novasonic-backend:/data/novasonic.apk +// Optional: a sidecar /data/novasonic.version.txt with a version string (e.g. "2.2") shown in the UI. +const APK_PATH = process.env.APK_PATH || path.join(CONFIG.dataPath, 'novasonic.apk'); +const APK_VER_PATH = path.join(CONFIG.dataPath, 'novasonic.version.txt'); + +app.get('/api/apk/info', (req, res) => { + fs.stat(APK_PATH, (err, st) => { + if (err) return res.json({ available: false }); + let version = null; + try { version = fs.readFileSync(APK_VER_PATH, 'utf8').trim() || null; } catch {} + res.json({ available: true, size: st.size, updatedAt: st.mtimeMs, version }); + }); +}); + +app.get('/api/apk', (req, res) => { + fs.stat(APK_PATH, (err) => { + if (err) return res.status(404).json({ error: 'Keine APK hinterlegt' }); + res.setHeader('Content-Type', 'application/vnd.android.package-archive'); + res.setHeader('Content-Disposition', 'attachment; filename="NovaSonic.apk"'); + res.sendFile(APK_PATH); + }); +}); + app.get('/api/health', (req, res) => { res.json({ status: 'ok', ytdlpVersion, diff --git a/index.html b/index.html index c9d9325..f0686c7 100644 --- a/index.html +++ b/index.html @@ -605,6 +605,18 @@
+