9230d0dd7a
Ampel / ampel (push) Successful in 29s
Weitergabe nicht durch Lesen geprueft, sondern indem ich mich wie ein fremder Rechner verhalten habe: frischer `git clone` von Gitea in ein leeres Verzeichnis, dann `./install.sh --nur-pruefen`. Ergebnis: 2,6 MB, alles gruen, Laufwerk samt richtigem sg-Knoten ueber die SCSI-Adresse erkannt. Der Weg selbst traegt. Drei Luecken sind dabei aufgefallen: 1. IN BEISPIELBEFEHLEN STAND MEINE IP. `install.ps1` und `remote-transcode-worker.yml` nannten im Aufruf-Beispiel 192.168.178.162 - also genau die Zeile, die ein Fremder kopiert. Jetzt Platzhalter, beim Windows-Installer mit dem Hinweis, wo die richtige IP steht (und dass es NICHT die des eigenen PCs ist - der haeufigste Irrtum). 2. DER ASSISTENT FRAGTE DEN MAKEMKV-BETA-KEY NICHT. Er stand in der README, in der .env und in den Einstellungen - nur nicht dort, wo man beim Einrichten hinsieht. Ein Fremder installiert also, legt eine Blu-ray ein und bekommt spaeter einen Fehlschlag, ohne dass ihn jemand darauf hingewiesen haette. Das ist die wahrscheinlichste Stolperstelle einer frischen Installation. Jetzt fragt der Assistent ihn ab, mit dem Unterschied im Klartext: DVDs gehen ohne, Blu-ray braucht ihn - und er ist NICHT der Disc-Schluessel einer 4K-Disc. 3. Eine Docstring nannte meine NAS-IP als Beispiel - jetzt neutral (//NAS/rippy). Tests und Log-Beispiele behalten die echten Namen: Sie dokumentieren Messungen, und genau das ist ihr Wert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
285 lines
14 KiB
PowerShell
285 lines
14 KiB
PowerShell
# Rippy: Nativer Windows-Transcode-Worker - Installation OHNE Docker.
|
|
#
|
|
# Was das Skript tut (alles nach -InstallDir, Standard "C:\Program Files\Rippy Worker"):
|
|
# 1. Prüft Python (3.10+), legt ein venv an, installiert die Abhängigkeiten
|
|
# 2. Lädt den Worker-Code direkt von deiner Rippy-Instanz (/api/worker-setup/paket)
|
|
# 3. Lädt HandBrakeCLI (gepinnte Version, offizielles GitHub-Release),
|
|
# falls nicht schon im PATH oder im Ordner vorhanden
|
|
# 4. Fragt Rippy, über welche Netzwerk-Freigabe die Rohdaten erreichbar sind,
|
|
# und prüft, ob DIESER PC sie wirklich sieht (RIPPY_PATH_MAP)
|
|
# 5. Erzeugt start-worker.bat - Doppelklick startet den Worker
|
|
#
|
|
# Der Worker bedient NUR die Kompressions-Queue (transcode) - gerippt wird
|
|
# weiterhin auf der Rippy-Hauptmaschine (dort hängt das Laufwerk).
|
|
#
|
|
# Aufruf (PowerShell):
|
|
# .\install.ps1 -RippyHost <LAN-IP-der-Rippy-Maschine> [-WorkerName mein-pc] [-Autostart]
|
|
# (die IP steht in Rippy unter Einstellungen -> Worker; NICHT die dieses PCs)
|
|
#
|
|
# -Autostart legt eine Verknüpfung im Autostart-Ordner an (Start bei Anmeldung).
|
|
|
|
param(
|
|
[Parameter(Mandatory = $true)] [string]$RippyHost,
|
|
[string]$WorkerName = $env:COMPUTERNAME.ToLower(),
|
|
[switch]$Autostart,
|
|
# Übersetzung der Rippy-Container-Pfade auf Freigaben, die DIESER PC sieht.
|
|
# Leer lassen: dann holt der Installer den Wert von Rippy selbst
|
|
# (GET /worker-setup/pfad-map, abgeleitet aus dessen eigenen Mounts).
|
|
# Von Hand nur, wenn dieser PC die Freigabe anders erreicht, z. B.
|
|
# -PfadMap "/app/media/rippy=Z:\"
|
|
[string]$PfadMap = "",
|
|
# Wie viele Kompressions-Aufträge dieser PC gleichzeitig annimmt. 0 = nach
|
|
# Kernzahl entscheiden (ab 12 Kernen zwei, sonst einer): HandBrake nutzt
|
|
# schon alle Kerne, aber x265 skaliert nicht linear — auf einer breiten CPU
|
|
# bringt ein zweiter Encode mehr Durchsatz, auf einer schmalen bremst er nur.
|
|
[int]$Slots = 0,
|
|
# Zielverzeichnis. Standard ist "Programme", wie bei jedem anderen Programm
|
|
# (Commander-Wunsch 25.07.2026). Dorthin schreiben braucht Adminrechte -
|
|
# PowerShell also "Als Administrator" starten, oder hier einen Ordner im
|
|
# eigenen Profil angeben.
|
|
[string]$InstallDir = (Join-Path ([Environment]::GetFolderPath("ProgramFiles")) "Rippy Worker")
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
# HandBrake-Version: die NEUESTE offizielle (GitHub-Release) - so passt der
|
|
# Windows-Worker zu dem, was der Update-Check im UI meldet (früher war hier
|
|
# eine feste 1.9.2 hardcodiert, was verwirrte). Fällt auf eine bekannte
|
|
# Version zurück, falls die GitHub-API gerade nicht erreichbar ist.
|
|
$HandBrakeVersion = "1.11.2"
|
|
try {
|
|
$rel = Invoke-RestMethod "https://api.github.com/repos/HandBrake/HandBrake/releases/latest" `
|
|
-TimeoutSec 15 -Headers @{ "User-Agent" = "rippy-installer" }
|
|
if ($rel.tag_name) { $HandBrakeVersion = ([string]$rel.tag_name).TrimStart("v") }
|
|
Write-Host "Neueste HandBrake-Version: $HandBrakeVersion"
|
|
} catch { Write-Host "GitHub-API nicht erreichbar - nutze HandBrake $HandBrakeVersion" }
|
|
$HandBrakeUrl = "https://github.com/HandBrake/HandBrake/releases/download/$HandBrakeVersion/HandBrakeCLI-$HandBrakeVersion-win-x86_64.zip"
|
|
|
|
Write-Host "== Rippy Windows-Worker Installation ==" -ForegroundColor Cyan
|
|
|
|
# 1. Python finden (py-Launcher oder python im PATH)
|
|
$python = $null
|
|
foreach ($kandidat in @("py -3", "python")) {
|
|
try {
|
|
$version = Invoke-Expression "$kandidat --version" 2>&1
|
|
if ("$version" -match "Python 3\.(\d+)") {
|
|
if ([int]$Matches[1] -ge 10) { $python = $kandidat; break }
|
|
}
|
|
} catch {}
|
|
}
|
|
if (-not $python) {
|
|
Write-Host "FEHLER: Python 3.10+ nicht gefunden." -ForegroundColor Red
|
|
Write-Host "Installieren mit: winget install Python.Python.3.12 (dann Skript erneut ausführen)"
|
|
exit 1
|
|
}
|
|
Write-Host "Python gefunden: $python"
|
|
|
|
# 2. Zielordner + Worker-Code von der Rippy-Instanz laden
|
|
$ziel = $InstallDir
|
|
try {
|
|
New-Item -ItemType Directory -Force $ziel -ErrorAction Stop | Out-Null
|
|
$probe = Join-Path $ziel ".schreibtest"
|
|
Set-Content -Path $probe -Value "x" -ErrorAction Stop
|
|
Remove-Item -Force $probe -ErrorAction SilentlyContinue
|
|
} catch {
|
|
Write-Host ""
|
|
Write-Host "FEHLER: In '$ziel' darf nicht geschrieben werden." -ForegroundColor Red
|
|
Write-Host "Das ist normal bei Ordnern unter 'Programme'. Zwei Wege:" -ForegroundColor Red
|
|
Write-Host " 1. PowerShell als Administrator starten und erneut aufrufen, ODER" -ForegroundColor Red
|
|
Write-Host " 2. ein eigenes Ziel angeben, z. B.:" -ForegroundColor Red
|
|
Write-Host " -InstallDir `"`$env:LOCALAPPDATA\Rippy Worker`"" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host "Ziel: $ziel"
|
|
Set-Location $ziel
|
|
|
|
Write-Host "Lade Worker-Code von http://$RippyHost/api/worker-setup/paket ..."
|
|
Invoke-WebRequest "http://$RippyHost/api/worker-setup/paket" -OutFile worker.zip -UseBasicParsing
|
|
Expand-Archive worker.zip -DestinationPath . -Force
|
|
Remove-Item worker.zip
|
|
|
|
# 3. venv + Abhängigkeiten
|
|
if (-not (Test-Path "venv")) {
|
|
Write-Host "Lege Python-Umgebung an ..."
|
|
Invoke-Expression "$python -m venv venv"
|
|
}
|
|
& .\venv\Scripts\python.exe -m pip install --quiet --upgrade pip
|
|
& .\venv\Scripts\pip.exe install --quiet -r requirements.txt
|
|
# Nur für den Windows-Worker: Tray-Symbol (Status, Start/Stopp, Beenden)
|
|
& .\venv\Scripts\pip.exe install --quiet pystray pillow
|
|
Write-Host "Python-Abhängigkeiten installiert."
|
|
|
|
# 4. HandBrakeCLI besorgen (falls nötig)
|
|
$hb = Get-Command HandBrakeCLI.exe -ErrorAction SilentlyContinue
|
|
if (-not $hb -and -not (Test-Path ".\HandBrakeCLI.exe")) {
|
|
Write-Host "Lade HandBrakeCLI $HandBrakeVersion (offizielles GitHub-Release) ..."
|
|
Invoke-WebRequest $HandBrakeUrl -OutFile hb.zip -UseBasicParsing
|
|
Expand-Archive hb.zip -DestinationPath . -Force
|
|
Remove-Item hb.zip
|
|
}
|
|
if (-not (Test-Path ".\HandBrakeCLI.exe") -and -not $hb) {
|
|
Write-Host "FEHLER: HandBrakeCLI.exe fehlt." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Host "HandBrakeCLI bereit."
|
|
|
|
# 5. Netzwerk-Freigabe klären (RIPPY_PATH_MAP) - der stille Blocker bis 26.07.2026
|
|
#
|
|
# Rippy schickt dem Worker Pfade wie /app/media/rippy/<job>. Das sind Pfade
|
|
# INNERHALB des Rippy-Containers; dieser PC sieht sie nur, wenn sie auf eine
|
|
# Netzwerk-Freigabe übersetzt werden. Ohne das nimmt der Worker die Aufgabe an
|
|
# und lehnt sie Millisekunden später ab ("Keine Roh-MKVs gefunden") - gemessen
|
|
# am Job 95afdc89. Geraten wird nichts: Rippy hat die Freigabe selbst
|
|
# eingehängt und kennt ihre Quelle.
|
|
if (-not $PfadMap) {
|
|
Write-Host "Frage Rippy, über welche Freigabe die Rohdaten erreichbar sind ..."
|
|
try {
|
|
$antwort = Invoke-RestMethod "http://$RippyHost/api/worker-setup/pfad-map" -TimeoutSec 15
|
|
if ($antwort.hinweis) { Write-Host $antwort.hinweis -ForegroundColor Yellow }
|
|
if ($antwort.mapping) { $PfadMap = [string]$antwort.mapping }
|
|
} catch {
|
|
Write-Host "HINWEIS: Abfrage fehlgeschlagen ($($_.Exception.Message))." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
if ($PfadMap) {
|
|
Write-Host "Freigabe: $PfadMap"
|
|
foreach ($paar in ($PfadMap -split ";")) {
|
|
if ($paar -notmatch "=") { continue }
|
|
$zielPfad = ($paar -split "=", 2)[1]
|
|
if (Test-Path -LiteralPath $zielPfad -ErrorAction SilentlyContinue) {
|
|
Write-Host " OK: $zielPfad ist von hier erreichbar." -ForegroundColor Green
|
|
} else {
|
|
Write-Host " NICHT erreichbar: $zielPfad" -ForegroundColor Yellow
|
|
Write-Host " Meist fehlen nur die Zugangsdaten: die Freigabe einmal im" -ForegroundColor Yellow
|
|
Write-Host " Explorer öffnen und 'Anmeldedaten speichern' anhaken." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host "ACHTUNG: Ohne Freigabe kann dieser Worker NICHTS komprimieren." -ForegroundColor Yellow
|
|
Write-Host " Er wird trotzdem installiert; Jobs schlagen dann mit einer" -ForegroundColor Yellow
|
|
Write-Host " Klartext-Meldung fehl statt still zu hängen." -ForegroundColor Yellow
|
|
}
|
|
# Leeres RIPPY_PATH_MAP nicht setzen: pfad_lokal() liest es als "kein Mapping",
|
|
# und die Fehlermeldung im Worker unterscheidet genau diese beiden Fälle.
|
|
$mapZeile = if ($PfadMap) { "set RIPPY_PATH_MAP=$PfadMap`r`n" } else { "" }
|
|
|
|
# Gleichzeitige Aufträge. Auf Windows gibt es KEINEN prefork-Pool (kein fork) —
|
|
# `solo` bedient genau einen Auftrag, `threads` mehrere. Das passt hier, weil die
|
|
# eigentliche Arbeit ein Kind-Prozess ist (HandBrake) und der Thread nur wartet.
|
|
if ($Slots -le 0) {
|
|
$Slots = if ([Environment]::ProcessorCount -ge 12) { 2 } else { 1 }
|
|
}
|
|
$poolArg = if ($Slots -le 1) { "--pool=solo" } else { "--pool=threads --concurrency=$Slots" }
|
|
Write-Host "Gleichzeitige Aufträge: $Slots (von $([Environment]::ProcessorCount) Kernen)"
|
|
|
|
# 6. Start-Skript erzeugen
|
|
$bat = @"
|
|
@echo off
|
|
rem Rippy Windows-Transcode-Worker - verbindet sich mit $RippyHost
|
|
cd /d "%~dp0"
|
|
set REDIS_URL=redis://${RippyHost}:6379/0
|
|
set DATABASE_URL=postgresql://rippy:rippy@${RippyHost}:5432/rippy
|
|
set API_URL=http://${RippyHost}:8000
|
|
set WORKER_NAME=$WorkerName
|
|
set RIPPY_SLOTS=$Slots
|
|
${mapZeile}set PATH=%~dp0;%PATH%
|
|
venv\Scripts\celery.exe -A celery_app worker --loglevel=info -Q transcode $poolArg -n ${WorkerName}@%%h
|
|
"@
|
|
Set-Content -Path "start-worker.bat" -Value $bat -Encoding ASCII
|
|
|
|
# 7. Tray-Start (empfohlen): Symbol neben der Uhr, kein Konsolenfenster
|
|
$trayBat = @"
|
|
@echo off
|
|
rem Rippy-Worker mit Tray-Symbol - verbindet sich mit $RippyHost
|
|
cd /d "%~dp0"
|
|
set REDIS_URL=redis://${RippyHost}:6379/0
|
|
set DATABASE_URL=postgresql://rippy:rippy@${RippyHost}:5432/rippy
|
|
set API_URL=http://${RippyHost}:8000
|
|
set WORKER_NAME=$WorkerName
|
|
set RIPPY_TRAY_HOST=$RippyHost
|
|
set RIPPY_SLOTS=$Slots
|
|
${mapZeile}set PATH=%~dp0;%PATH%
|
|
start "" venv\Scripts\pythonw.exe tray.py
|
|
"@
|
|
Set-Content -Path "start-tray.bat" -Value $trayBat -Encoding ASCII
|
|
Write-Host "start-worker.bat + start-tray.bat erzeugt."
|
|
|
|
# 8. Deinstaller - MUSS-Kriterium: rückstandsfrei entfernbar
|
|
$uninstall = @"
|
|
# Rippy-Worker DEINSTALLIEREN: stoppt Prozesse, entfernt Autostart,
|
|
# meldet den Worker in Rippy ab und löscht diesen Ordner komplett.
|
|
param([switch]`$Force)
|
|
if (-not `$Force) {
|
|
`$antwort = Read-Host "Rippy-Worker '$WorkerName' wirklich deinstallieren? (j/n)"
|
|
if (`$antwort -ne "j") { Write-Host "Abgebrochen."; exit }
|
|
}
|
|
Get-CimInstance Win32_Process | Where-Object {
|
|
`$_.ExecutablePath -like "`$PSScriptRoot*"
|
|
} | ForEach-Object { Stop-Process -Id `$_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
Start-Sleep 2
|
|
# Autostart-Verknüpfung entfernen - BEIDE möglichen Orte, weil die
|
|
# Installation je nach Zielverzeichnis persönlich oder maschinenweit war.
|
|
foreach (`$ordner in @("Startup", "CommonStartup")) {
|
|
try { Remove-Item -Force (Join-Path ([Environment]::GetFolderPath(`$ordner)) "RippyWorker.lnk") -ErrorAction Stop } catch {}
|
|
}
|
|
# ... und die geplante Aufgabe aus älteren Installationen, falls vorhanden.
|
|
schtasks /delete /tn "RippyWorker" /f 2>`$null | Out-Null
|
|
try {
|
|
Invoke-RestMethod -Method Delete "http://$RippyHost/api/workers/$WorkerName" -TimeoutSec 5 | Out-Null
|
|
Write-Host "Worker-Eintrag in Rippy entfernt."
|
|
} catch { Write-Host "Hinweis: Eintrag in Rippy ggf. von Hand löschen (Einstellungen -> Worker)." }
|
|
# Ordner abräumen — NICHT von hier aus: Dieses Skript liegt darin, und die
|
|
# venv-DLLs sind noch geladen. `Remove-Item -Recurse -Force` auf den eigenen
|
|
# Ordner klappt auf Windows nicht zuverlässig (Befund 26.07.2026). Ein
|
|
# losgelöstes cmd wartet, bis PowerShell weg ist, und räumt dann auf.
|
|
Set-Location `$env:TEMP
|
|
Start-Process cmd.exe -ArgumentList '/c timeout /t 4 /nobreak >nul & rd /s /q "'"`$PSScriptRoot"'"' -WindowStyle Hidden
|
|
Write-Host "Rippy-Worker deinstalliert (Ordner verschwindet in wenigen Sekunden)." -ForegroundColor Green
|
|
"@
|
|
# UTF-8 MIT BOM statt ASCII: die Datei enthält Umlaute, und PowerShell 5.1 liest
|
|
# .ps1 ohne BOM als ANSI.
|
|
[System.IO.File]::WriteAllText(
|
|
(Join-Path $ziel "uninstall.ps1"), $uninstall,
|
|
(New-Object System.Text.UTF8Encoding $true))
|
|
Write-Host "uninstall.ps1 erzeugt."
|
|
|
|
# 9. Optional: Autostart bei Anmeldung (mit Tray)
|
|
#
|
|
# Über den Autostart-ORDNER, nicht über schtasks: Eine Aufgabe im
|
|
# Wurzelordner der Aufgabenplanung anzulegen verlangt Administratorrechte, und
|
|
# der Installer läuft normal ohne. Auf dem Commander-PC scheiterte das am
|
|
# 25.07.2026 mit "FEHLER: Zugriff verweigert." - und weil oben
|
|
# $ErrorActionPreference = "Stop" steht, riss es die ganze Installation mit,
|
|
# obwohl nur der Autostart fehlte. Der Autostart-Ordner braucht nie Adminrechte.
|
|
if ($Autostart) {
|
|
try {
|
|
# Unter "Programme" = Installation für die Maschine, also Autostart für
|
|
# alle Benutzer. Sonst persönlich. Siehe install-gui.ps1 für die
|
|
# ausführliche Begründung (Rechtelage bei fremdem Admin-Konto).
|
|
$programme = [Environment]::GetFolderPath("ProgramFiles")
|
|
$maschinenweit = $ziel.StartsWith($programme, "OrdinalIgnoreCase")
|
|
$autostartDir = if ($maschinenweit) {
|
|
[Environment]::GetFolderPath("CommonStartup")
|
|
} else {
|
|
[Environment]::GetFolderPath("Startup")
|
|
}
|
|
$wsh = New-Object -ComObject WScript.Shell
|
|
$lnk = $wsh.CreateShortcut((Join-Path $autostartDir "RippyWorker.lnk"))
|
|
$lnk.TargetPath = (Join-Path $ziel "start-tray.bat")
|
|
$lnk.WorkingDirectory = $ziel
|
|
$lnk.Description = "Rippy Encoding-Worker"
|
|
$lnk.Save()
|
|
Write-Host "Autostart eingerichtet (Verknüpfung im Autostart-Ordner)."
|
|
} catch {
|
|
Write-Host "HINWEIS: Autostart konnte nicht eingerichtet werden ($($_.Exception.Message))." -ForegroundColor Yellow
|
|
Write-Host " Der Worker ist trotzdem fertig installiert." -ForegroundColor Yellow
|
|
Write-Host " Von Hand: start-tray.bat in shell:startup verknüpfen." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "FERTIG." -ForegroundColor Green
|
|
Write-Host " Starten (mit Tray-Symbol): $ziel\start-tray.bat"
|
|
Write-Host " Starten (Konsole/Debug): $ziel\start-worker.bat"
|
|
Write-Host " Deinstallieren: $ziel\uninstall.ps1"
|
|
Write-Host "Der Worker erscheint in Rippy unter Einstellungen -> Worker als '$WorkerName'."
|