diff --git a/Dockerfile b/Dockerfile index fdaf077..787a24b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,8 @@ FROM node:20-alpine WORKDIR /app +RUN apk add --no-cache git + COPY package*.json ./ RUN npm install --omit=dev diff --git a/README.md b/README.md index ae381e5..8711ac2 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Web-Frontend, um Alben aus Spotify zu suchen und an Lidarr zu uebergeben. - Track-Auswahl pro Album - Uebergabe an Lidarr als Album-Download (MissingAlbumSearch) - Einstellung im Frontend: ueberfluessige Dateien nach Download loeschen (optional) +- Button fuer Lidarr Bibliothek-Update +- Button fuer Frontend Git-Update (fetch/pull) - Docker-ready ## Voraussetzungen diff --git a/public/app.js b/public/app.js index b578f8a..1ba1940 100644 --- a/public/app.js +++ b/public/app.js @@ -10,6 +10,7 @@ const trackList = document.getElementById('trackList'); const sendBtn = document.getElementById('sendBtn'); const cleanupToggle = document.getElementById('cleanupToggle'); const updateLibraryBtn = document.getElementById('updateLibraryBtn'); +const updateFrontendBtn = document.getElementById('updateFrontendBtn'); let selectedAlbum = null; @@ -239,6 +240,29 @@ async function updateLibrary() { } } +async function updateFrontendFromGit() { + setStatus('Hole aktuelle Git-Daten...'); + updateFrontendBtn.disabled = true; + + try { + const data = await fetchJson('/api/system/update-frontend', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + if (data.updated) { + setStatus(`Frontend aktualisiert (${data.before} -> ${data.after}). Seite wird neu geladen...`); + setTimeout(() => window.location.reload(), 1200); + } else { + setStatus(`Bereits aktuell (${data.after}).`); + } + } catch (err) { + setStatus(`Frontend-Update fehlgeschlagen: ${err.message}`, true); + } finally { + updateFrontendBtn.disabled = false; + } +} + searchBtn.addEventListener('click', searchSpotify); queryInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { @@ -247,3 +271,4 @@ queryInput.addEventListener('keydown', (event) => { }); sendBtn.addEventListener('click', sendToLidarr); updateLibraryBtn.addEventListener('click', updateLibrary); +updateFrontendBtn.addEventListener('click', updateFrontendFromGit); diff --git a/public/index.html b/public/index.html index 64c8cf9..5dacad5 100644 --- a/public/index.html +++ b/public/index.html @@ -24,6 +24,7 @@
Wenn nur einzelne Songs gewaehlt sind, versucht die App unnoetige Track-Dateien in Lidarr zu entfernen. diff --git a/server.js b/server.js index e6356a3..0eed762 100644 --- a/server.js +++ b/server.js @@ -2,9 +2,12 @@ require('dotenv').config(); const express = require('express'); const path = require('path'); const axios = require('axios'); +const { execFile } = require('child_process'); +const { promisify } = require('util'); const app = express(); const port = Number(process.env.PORT || 3000); +const execFileAsync = promisify(execFile); const lidarrUrl = (process.env.LIDARR_URL || '').replace(/\/$/, ''); const lidarrApiKey = process.env.LIDARR_API_KEY || ''; @@ -40,6 +43,39 @@ function lidarrHeaders() { }; } +async function runGit(args) { + const { stdout } = await execFileAsync('git', args, { + cwd: __dirname, + timeout: 20000 + }); + return String(stdout || '').trim(); +} + +async function updateFrontendFromGit() { + if (!require('fs').existsSync(path.join(__dirname, '.git'))) { + throw new Error('Kein Git-Repository im Container/Projektpfad gefunden.'); + } + + try { + await runGit(['--version']); + } catch (_err) { + throw new Error('git ist nicht installiert.'); + } + + const before = await runGit(['rev-parse', '--short', 'HEAD']); + const branch = await runGit(['rev-parse', '--abbrev-ref', 'HEAD']); + const dirty = await runGit(['status', '--porcelain']); + if (dirty) { + throw new Error('Lokale Aenderungen vorhanden. Update per Button ist blockiert.'); + } + + await runGit(['fetch', '--prune', 'origin']); + await runGit(['pull', '--ff-only', 'origin', branch]); + + const after = await runGit(['rev-parse', '--short', 'HEAD']); + return { before, after, branch, updated: before !== after }; +} + async function lidarrRequest(method, endpoint, data, params) { if (!hasLidarrConfig()) { throw new Error('Lidarr-Konfiguration unvollstaendig.'); @@ -451,6 +487,15 @@ app.post('/api/lidarr/update-library', async (_req, res) => { } }); +app.post('/api/system/update-frontend', async (_req, res) => { + try { + const result = await updateFrontendFromGit(); + res.json({ success: true, ...result }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + app.get('*', (_req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); });