From a6ff18c8c33512c4e085e2a34bcf7c933de9003a Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Wed, 29 Apr 2026 10:25:41 +0000 Subject: [PATCH] 0.16.1-2 Installer Fix --- backend/package-lock.json | 4 +- backend/package.json | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/src/App.jsx | 37 ++++++++++++- install.sh | 105 +++++++++++++++++++++++++++++++------ package-lock.json | 4 +- package.json | 2 +- 8 files changed, 134 insertions(+), 26 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 287a6fa..8a67c96 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.16.1-1", + "version": "0.16.1-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.16.1-1", + "version": "0.16.1-2", "dependencies": { "archiver": "^7.0.1", "cors": "^2.8.5", diff --git a/backend/package.json b/backend/package.json index 602f4c2..6694ae0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.16.1-1", + "version": "0.16.1-2", "private": true, "type": "commonjs", "scripts": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5f60109..4179604 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.16.1-1", + "version": "0.16.1-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.16.1-1", + "version": "0.16.1-2", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index b5e446f..5fe4f95 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.16.1-1", + "version": "0.16.1-2", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3e43c22..d68a922 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -36,6 +36,17 @@ function normalizeStage(value) { return String(value || '').trim().toUpperCase(); } +function isRunningStage(value) { + const normalized = normalizeStage(value); + return normalized === 'ANALYZING' + || normalized === 'RIPPING' + || normalized === 'MEDIAINFO_CHECK' + || normalized === 'ENCODING' + || normalized === 'CD_ANALYZING' + || normalized === 'CD_RIPPING' + || normalized === 'CD_ENCODING'; +} + function isTerminalStage(value) { const normalized = normalizeStage(value); return normalized === 'FINISHED' || normalized === 'ERROR' || normalized === 'CANCELLED'; @@ -315,17 +326,24 @@ function App() { const isCdProgressStage = progressStage === 'CD_ANALYZING' || progressStage === 'CD_RIPPING' || progressStage === 'CD_ENCODING'; + const incomingIsRunning = isRunningStage(progressStage); const incomingIsTerminal = isTerminalStage(progressStage); const prevActiveJobId = normalizeJobId(prev?.activeJobId || prev?.context?.jobId); const prevJobProgress = normalizedProgressJobId ? (prev?.jobProgress?.[normalizedProgressJobId] || null) : null; const prevJobProgressState = normalizeStage(prevJobProgress?.state); + const prevJobProgressIsRunning = isRunningStage(prevJobProgressState); const prevJobProgressIsTerminal = isTerminalStage(prevJobProgressState); const matchingCdDriveEntry = normalizedProgressJobId ? Object.values(prev?.cdDrives || {}).find((driveState) => normalizeJobId(driveState?.jobId) === normalizedProgressJobId) : null; + const matchingCdDriveState = normalizeStage(matchingCdDriveEntry?.state); + const matchingCdDriveIsRunning = isRunningStage(matchingCdDriveState); const matchingCdDriveIsTerminal = isTerminalStage(matchingCdDriveEntry?.state); + const previousGlobalStage = normalizeStage(prev?.state); + const previousGlobalIsRunning = isRunningStage(previousGlobalStage); + const previousGlobalIsTerminal = isTerminalStage(previousGlobalStage); const hasKnownBinding = Boolean( normalizedProgressJobId && ( @@ -334,9 +352,24 @@ function App() { || matchingCdDriveEntry ) ); + const regressesStableJobState = Boolean( + normalizedProgressJobId + && incomingIsRunning + && ( + (prevJobProgressState && !prevJobProgressIsRunning && !prevJobProgressIsTerminal) + || (matchingCdDriveState && !matchingCdDriveIsRunning && !matchingCdDriveIsTerminal) + || ( + prevActiveJobId === normalizedProgressJobId + && previousGlobalStage + && !previousGlobalIsRunning + && !previousGlobalIsTerminal + ) + ) + ); // Ignore late/stale progress packets that arrive after a terminal state - // or for jobs that are no longer bound in pipeline state. + // or regress a job from an interactive/ready state back into an + // earlier running stage because an older progress packet arrived late. if ( normalizedProgressJobId && !incomingIsTerminal @@ -344,6 +377,7 @@ function App() { prevJobProgressIsTerminal || matchingCdDriveIsTerminal || !hasKnownBinding + || regressesStableJobState ) ) { return prev; @@ -376,7 +410,6 @@ function App() { }; } if (progressJobId === prev?.activeJobId || progressJobId == null) { - const previousGlobalStage = normalizeStage(prev?.state); const keepPreviousGlobalStage = isTerminalStage(previousGlobalStage) && !incomingIsTerminal; next.state = keepPreviousGlobalStage ? prev?.state : (payload.state ?? prev?.state); next.progress = keepPreviousGlobalStage ? prev?.progress : (payload.progress ?? prev?.progress); diff --git a/install.sh b/install.sh index b29280a..6bb1513 100644 --- a/install.sh +++ b/install.sh @@ -55,6 +55,11 @@ SKIP_HANDBRAKE=false HANDBRAKE_INSTALL_MODE="" SKIP_NGINX=false REINSTALL=false +GIT_COMMAND_TIMEOUT_SEC=180 + +# Keine interaktiven Git-Prompts im Installer (z.B. Credentials) - stattdessen +# sauber mit Fehler abbrechen. +export GIT_TERMINAL_PROMPT=0 # --- Container-Optionen ------------------------------------------------------- CONTAINER_TYPE="none" @@ -135,6 +140,30 @@ detect_container_env() { command_exists() { command -v "$1" &>/dev/null; } +run_git_command() { + local description="$1" + shift + local status=0 + + info "$description..." + if command_exists timeout; then + timeout --foreground "${GIT_COMMAND_TIMEOUT_SEC}" "$@" + status=$? + if [[ "$status" -ne 0 ]]; then + if [[ "$status" -eq 124 ]]; then + fatal "Git-Kommando lief in Timeout (${GIT_COMMAND_TIMEOUT_SEC}s): $*" + fi + fatal "Git-Kommando fehlgeschlagen (Exit $status): $*" + fi + else + "$@" + status=$? + if [[ "$status" -ne 0 ]]; then + fatal "Git-Kommando fehlgeschlagen (Exit $status): $*" + fi + fi +} + service_unit_exists() { local unit="$1" systemctl list-unit-files --type=service --all --no-legend 2>/dev/null \ @@ -188,6 +217,36 @@ backup_and_remove_backend_src() { fi } +backup_persistent_backend_data() { + local src_dir="$INSTALL_DIR/backend/data" + local backup_root="/tmp/ripster-data-backup" + local backup_dir="${backup_root}/data_bak_$(date +%Y%m%d%H%M%S)" + local copied_any=false + local candidate + + if [[ ! -d "$src_dir" ]]; then + warn "Kein Backend-Datenverzeichnis gefunden: $src_dir" + return 0 + fi + + # Nur kleine, kritische Metadaten sichern. Medienordner bleiben im + # Installationspfad bestehen und werden hier bewusst nicht kopiert. + mkdir -p "$backup_dir" + for candidate in "ripster.db" "ripster.db-shm" "ripster.db-wal" "thumbnails"; do + if [[ -e "${src_dir}/${candidate}" ]]; then + cp -a "${src_dir}/${candidate}" "$backup_dir/" + copied_any=true + fi + done + + if [[ "$copied_any" == true ]]; then + DATA_BACKUP="$backup_dir" + ok "Persistente Backend-Daten gesichert nach: $DATA_BACKUP" + else + warn "Keine zu sichernden Kern-Daten in $src_dir gefunden (ripster.db/thumbnails)." + fi +} + enforce_default_output_dirs_permissions() { local default_dirs=( "$INSTALL_DIR/backend/data/output/raw" @@ -823,10 +882,24 @@ header "Repository holen (Git)" info "Prüfe Branch '$GIT_BRANCH' auf Remote..." branch_check_output="" branch_check_status=0 -if ! branch_check_output=$(git ls-remote --exit-code --heads "$REPO_URL" "$GIT_BRANCH" 2>&1); then - branch_check_status=$? +if command_exists timeout; then + branch_check_output=$(timeout --foreground "${GIT_COMMAND_TIMEOUT_SEC}" \ + git ls-remote --exit-code --heads "$REPO_URL" "$GIT_BRANCH" 2>&1) || branch_check_status=$? +else + branch_check_output=$(git ls-remote --exit-code --heads "$REPO_URL" "$GIT_BRANCH" 2>&1) || branch_check_status=$? +fi + +if [[ "$branch_check_status" -ne 0 ]]; then + if [[ "$branch_check_status" -eq 124 ]]; then + fatal "Timeout beim Prüfen des Branches '${GIT_BRANCH}' (${GIT_COMMAND_TIMEOUT_SEC}s)." + fi if [[ "$branch_check_status" -eq 2 ]]; then - available_branches=$(git ls-remote --heads "$REPO_URL" 2>/dev/null | awk '{print $2}' | sed 's|refs/heads/||' | tr '\n' ' ') + if command_exists timeout; then + available_branches=$(timeout --foreground "${GIT_COMMAND_TIMEOUT_SEC}" \ + git ls-remote --heads "$REPO_URL" 2>/dev/null | awk '{print $2}' | sed 's|refs/heads/||' | tr '\n' ' ') + else + available_branches=$(git ls-remote --heads "$REPO_URL" 2>/dev/null | awk '{print $2}' | sed 's|refs/heads/||' | tr '\n' ' ') + fi fatal "Branch '$GIT_BRANCH' existiert nicht im Repository $REPO_URL.\nVerfügbare Branches: ${available_branches:-keine oder Remote nicht erreichbar}" fi fatal "Remote $REPO_URL ist nicht erreichbar.\nGit-Fehler: ${branch_check_output:-unbekannt}" @@ -836,22 +909,24 @@ ok "Branch '$GIT_BRANCH' gefunden" if [[ -d "$INSTALL_DIR/.git" ]]; then if [[ "$REINSTALL" == true ]]; then info "Aktualisiere bestehendes Repository..." - # Daten sichern - if [[ -d "$INSTALL_DIR/backend/data" ]]; then - DATA_BACKUP="/tmp/ripster-data-backup-$(date +%Y%m%d%H%M%S)" - cp -a "$INSTALL_DIR/backend/data" "$DATA_BACKUP" - info "Datenbank gesichert nach: $DATA_BACKUP" - fi + info "Sichere persistente Backend-Daten (ohne große Medienordner)..." + backup_persistent_backend_data backup_and_remove_backend_src # safe.directory nötig wenn das Verzeichnis einem anderen User gehört # (z.B. ripster-Serviceuser nach erstem Install) git config --global --add safe.directory "$INSTALL_DIR" 2>/dev/null || true - git -C "$INSTALL_DIR" remote set-url origin "$REPO_URL" - git -C "$INSTALL_DIR" remote set-branches origin '*' - git -C "$INSTALL_DIR" fetch --quiet origin - git -C "$INSTALL_DIR" reset --hard HEAD - git -C "$INSTALL_DIR" checkout --quiet -B "$GIT_BRANCH" "origin/$GIT_BRANCH" - git -C "$INSTALL_DIR" reset --hard "origin/$GIT_BRANCH" + run_git_command "Setze Git-Remote-URL" \ + git -C "$INSTALL_DIR" remote set-url origin "$REPO_URL" + run_git_command "Aktualisiere Remote-Branch-Mapping" \ + git -C "$INSTALL_DIR" remote set-branches origin '*' + run_git_command "Hole Änderungen vom Remote" \ + git -C "$INSTALL_DIR" fetch --quiet origin + run_git_command "Setze Arbeitsverzeichnis auf aktuellen Stand (HEAD)" \ + git -C "$INSTALL_DIR" reset --hard HEAD + run_git_command "Checkout Branch '$GIT_BRANCH'" \ + git -C "$INSTALL_DIR" checkout --quiet -B "$GIT_BRANCH" "origin/$GIT_BRANCH" + run_git_command "Setze Branch auf origin/$GIT_BRANCH" \ + git -C "$INSTALL_DIR" reset --hard "origin/$GIT_BRANCH" ok "Repository aktualisiert auf Branch '$GIT_BRANCH'" else fatal "$INSTALL_DIR enthält bereits ein Git-Repository.\nVerwende --reinstall um zu aktualisieren." diff --git a/package-lock.json b/package-lock.json index 34ff2ae..cb07829 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster", - "version": "0.16.1-1", + "version": "0.16.1-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster", - "version": "0.16.1-1", + "version": "0.16.1-2", "devDependencies": { "concurrently": "^9.1.2" } diff --git a/package.json b/package.json index 2ff335f..d9d9978 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ripster", "private": true, - "version": "0.16.1-1", + "version": "0.16.1-2", "scripts": { "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev:backend": "npm run dev --prefix backend",