style: echte Umlaute im ganzen Projekt + Installer im Rippy-Look
Ampel / ampel (push) Successful in 27s
Ampel / ampel (push) Successful in 27s
Commander-Rueckmeldung: Umlaute fehlen. Ausloeser war sichtbar der Installer -
"Diese Maschine uebernimmt die Video-Kompression fuer Rippy" stand woertlich im
Screenshot.
## Die .ps1-Falle war loesbar, nicht unumgehbar
Bisher galt: ausgelieferte .ps1 MUESSEN ASCII sein, weil PowerShell 5.1 sie
ohne BOM als ANSI liest. Das ist nur die halbe Wahrheit - gemessen mit echtem
powershell.exe 5.1:
ohne BOM: $s = "Größe: äöü" + Unerwartetes Token -> Skript kaputt
mit BOM: Groesse: aeoeue + laeuft, Length 16 korrekt
Alle drei Skripte sind jetzt UTF-8 MIT BOM und tragen echte Umlaute; unter 5.1
gegengeprueft (BOM vorhanden, Parser fehlerfrei, Text korrekt gelesen). Der
Kopfkommentar sagt das jetzt richtig statt "ASCII-only".
## Umstellung: Text ja, Bezeichner nein
Umlaute gehoeren in Kommentare und Anzeigetexte, nicht in Funktionsnamen oder
Datenschluessel. Deshalb je Sprache das passende Werkzeug:
- Python: ueber den TOKENIZER - angefasst wurden ausschliesslich COMMENT- und
STRING-Tokens. 156 Stellen. Code ist damit garantiert unberuehrt.
- TypeScript: nur // und /* */ Kommentare sowie JSX-Text (kann per Definition
kein Bezeichner sein). 24 Stellen. `const waehlen`, `let laeuft`, `plaetze`,
`GeraetInfo` sind nachweislich unversehrt.
- install.sh: Anzeigetext, aber die Shell-Funktionen (gruen/rot/gelb/titel) und
der Schalter --nur-pruefen bleiben ASCII - das sind Schnittstellen.
- Markdown: 0 Aenderungen, die Doku hatte schon Umlaute.
ZWEI FEHLER MEINES KONVERTERS, beide von Werkzeugen gefangen:
1. In f-Strings steht in {...} CODE, kein Text. Aus f"{groesse}" wurde
f"{größe}", waehrend die Variable groesse hiess - Ruff meldete F821
"Undefined name". Der Konverter lagert Einsetzungen jetzt aus.
2. Ein Dict-Schluessel wurde umbenannt: die Wortliste enthaelt das PRAEFIX
"uebersprung", der Tabu-Schutz prueft aber ganze Woerter. bericht[...] ist
wieder ASCII - Umlaute in Datenschluesseln brechen JSON-Runden und DB-Felder.
## Installer im Rippy-Look
Statt hellgrau jetzt dieselben Toene wie das Web-UI (aus lib/design.ts
uebernommen): slate-900 Flaeche, dunkle Eingabefelder, Amber-Hauptknopf wie
"Los geht's" im Wizard. Oben ein Kopfbereich mit dem Farbverlauf
amber -> indigo -> purple und dem Disc-Symbol - in WinForms per Paint-Ereignis
gezeichnet, weil es dort keine Verlaeufe von der Stange gibt.
Geprueft, nicht gehofft: Das Fenster wurde headless in ein PNG gerendert
(DrawToBitmap) und angesehen - Verlauf, Symbol, Umlaute und Farben sitzen.
RippyWorkerSetup.exe neu gebaut (57344 -> 60416 Bytes); Umlaute und das
requireAdministrator-Manifest sind in der .exe verifiziert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
# Baut rippy.ico + RippyWorkerSetup.exe aus install-gui.ps1 (ps2exe).
|
||||
# Baut rippy.ico + RippyWorkerSetup.exe aus install-gui.ps1 (ps2exe).
|
||||
#
|
||||
# WANN NEU BAUEN: nur wenn sich install-gui.ps1 (die Installer-Oberflaeche)
|
||||
# aendert. NICHT bei MakeMKV-/HandBrake-Updates - die .exe holt HandBrake zur
|
||||
# WANN NEU BAUEN: nur wenn sich install-gui.ps1 (die Installer-Oberfläche)
|
||||
# ändert. NICHT bei MakeMKV-/HandBrake-Updates - die .exe holt HandBrake zur
|
||||
# Laufzeit (GitHub latest) und den Worker-Code live von Rippy, friert also
|
||||
# keine Versionen ein.
|
||||
# AUSFUEHREN AUF WINDOWS (eine Windows-.exe kann nicht von Linux gebaut werden):
|
||||
@@ -35,7 +35,7 @@ $bw.Write($png); $bw.Flush()
|
||||
[System.IO.File]::WriteAllBytes("$PSScriptRoot\rippy.ico", $icoStream.ToArray())
|
||||
Write-Host "rippy.ico erzeugt ($($png.Length) Bytes PNG)"
|
||||
|
||||
# 2. ps2exe sicherstellen - TLS 1.2 ist der Fix fuer den NuGet-Bootstrap-Fehler
|
||||
# 2. ps2exe sicherstellen - TLS 1.2 ist der Fix für den NuGet-Bootstrap-Fehler
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
if (-not (Get-Module -ListAvailable ps2exe)) {
|
||||
Write-Host "Bootstrappe NuGet + PSGallery ..."
|
||||
@@ -49,13 +49,13 @@ Import-Module ps2exe
|
||||
|
||||
# 3. Kompilieren (WinForms -> noConsole + STA, Icon eingebettet)
|
||||
#
|
||||
# -requireAdmin (seit 25.07.2026): Der Worker wird standardmaessig nach
|
||||
# -requireAdmin (seit 25.07.2026): Der Worker wird standardmäßig nach
|
||||
# "C:\Program Files\Rippy Worker" installiert, und dorthin darf nur ein
|
||||
# Administrator schreiben. Mit dem Schalter tragt die .exe ein Manifest, das
|
||||
# Windows beim Doppelklick EINMAL per UAC fragen laesst - danach stimmen die
|
||||
# Rechte fuer alles Weitere. Ohne ihn liefe der Installer unerhoeht und
|
||||
# Windows beim Doppelklick EINMAL per UAC fragen lässt - danach stimmen die
|
||||
# Rechte für alles Weitere. Ohne ihn liefe der Installer unerhöht und
|
||||
# scheiterte beim Anlegen des Ordners; wer das Ziel ins eigene Profil legt,
|
||||
# braucht die Erhoehung nicht, wird aber trotzdem gefragt (normal fuer einen
|
||||
# braucht die Erhöhung nicht, wird aber trotzdem gefragt (normal für einen
|
||||
# Installer und besser als ein Fehlschlag mitten im Lauf).
|
||||
Invoke-ps2exe -inputFile $guiSrc -outputFile "$PSScriptRoot\RippyWorkerSetup.exe" `
|
||||
-iconFile "$PSScriptRoot\rippy.ico" -noConsole -STA -requireAdmin `
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Rippy Windows-Worker - GRAFISCHER Installer (WinForms, keine externe Runtime).
|
||||
# Wird ueber rippy-worker-setup.bat gestartet (Doppelklick). Braucht nur
|
||||
# Rippy Windows-Worker - GRAFISCHER Installer (WinForms, keine externe Runtime).
|
||||
# Wird über rippy-worker-setup.bat gestartet (Doppelklick). Braucht nur
|
||||
# Python 3.10+ auf der Maschine - kein Docker, kein git.
|
||||
#
|
||||
# ASCII-only (PowerShell 5.1 liest .ps1 als ANSI - Sonderzeichen zerschiessen
|
||||
# das Skript).
|
||||
# WICHTIG - KODIERUNG: Diese Datei MUSS als UTF-8 MIT BOM gespeichert werden.
|
||||
# PowerShell 5.1 liest .ps1 ohne BOM als ANSI; Umlaute werden dann zu Kauderwelsch
|
||||
# UND das Skript wirft einen Parser-Fehler, weil in "ö" ein Anführungszeichen
|
||||
# steckt. Mit BOM liest 5.1 korrekt UTF-8 - am 25.07.2026 beidseitig gemessen:
|
||||
# ohne BOM: "Größe" + Unerwartetes Token
|
||||
# mit BOM: "Größe" + laeuft
|
||||
# Nach jeder Änderung pruefen: powershell.exe -Command "Parser::ParseFile(...)"
|
||||
|
||||
param(
|
||||
[string]$RippyHost = "",
|
||||
@@ -16,28 +21,42 @@ Add-Type -AssemblyName System.Drawing
|
||||
|
||||
# Standard-Zielverzeichnis: wie jedes andere Programm unter "Programme"
|
||||
# (Commander-Wunsch 25.07.2026 - vorher lag der Worker im Benutzerprofil, was
|
||||
# fuer ein Programm unueblich ist). GetFolderPath liefert den ECHTEN Pfad;
|
||||
# deutsche Windows-Versionen zeigen im Explorer "Programme" an, der Pfad heisst
|
||||
# für ein Programm unüblich ist). GetFolderPath liefert den ECHTEN Pfad;
|
||||
# deutsche Windows-Versionen zeigen im Explorer "Programme" an, der Pfad heißt
|
||||
# trotzdem "C:\Program Files".
|
||||
#
|
||||
# Schreiben dorthin braucht Administratorrechte - deshalb ist die .exe mit
|
||||
# -requireAdmin gebaut (build-exe.ps1) und fragt beim Start einmal per UAC.
|
||||
# Ohne das waere hier der naechste "Zugriff verweigert" programmiert.
|
||||
# Ohne das wäre hier der nächste "Zugriff verweigert" programmiert.
|
||||
$StandardZiel = Join-Path ([Environment]::GetFolderPath("ProgramFiles")) "Rippy Worker"
|
||||
$InstallDir = $StandardZiel
|
||||
|
||||
# --- Farben: dieselben Töne wie das Rippy-Web-UI ---------------------------
|
||||
# Übernommen aus docker/ui/src/lib/design.ts bzw. den Tailwind-Klassen des UI,
|
||||
# damit der Installer nicht wie ein Fremdkörper wirkt.
|
||||
$cBg = [System.Drawing.Color]::FromArgb(15, 23, 42) # slate-900, Fensterfläche
|
||||
$cPanel = [System.Drawing.Color]::FromArgb(2, 6, 23) # slate-950, Log + Eingaben
|
||||
$cFeld = [System.Drawing.Color]::FromArgb(30, 41, 59) # slate-800, Eingabefelder
|
||||
$cText = [System.Drawing.Color]::FromArgb(226, 232, 240) # slate-200
|
||||
$cGedaempft= [System.Drawing.Color]::FromArgb(148, 163, 184) # slate-400, Beschriftungen
|
||||
$cIndigo = [System.Drawing.Color]::FromArgb(99, 102, 241)
|
||||
$cAmber = [System.Drawing.Color]::FromArgb(245, 158, 11)
|
||||
$cEmerald = [System.Drawing.Color]::FromArgb(16, 185, 129)
|
||||
$cPurple = [System.Drawing.Color]::FromArgb(126, 34, 206)
|
||||
|
||||
# --- Fenster ---------------------------------------------------------------
|
||||
$form = New-Object System.Windows.Forms.Form
|
||||
$form.Text = "Rippy Encoding-Worker - Installation"
|
||||
$form.Size = New-Object System.Drawing.Size(560, 575)
|
||||
$form.ClientSize = New-Object System.Drawing.Size(560, 560)
|
||||
$form.StartPosition = "CenterScreen"
|
||||
$form.FormBorderStyle = "FixedDialog"
|
||||
$form.MaximizeBox = $false
|
||||
$form.BackColor = [System.Drawing.Color]::FromArgb(248, 250, 252)
|
||||
$form.BackColor = $cBg
|
||||
$form.ForeColor = $cText
|
||||
$form.Font = New-Object System.Drawing.Font("Segoe UI", 9)
|
||||
|
||||
# Fenster-Icon: Rippy-Disc zur Laufzeit zeichnen (Indigo-Kreis + weisses Loch)
|
||||
# - kein externes .ico noetig. Erscheint in Titelleiste und Taskleiste.
|
||||
# Fenster-Icon: Rippy-Disc zur Laufzeit zeichnen (Indigo-Kreis + weißes Loch)
|
||||
# - kein externes .ico nötig. Erscheint in Titelleiste und Taskleiste.
|
||||
try {
|
||||
$ibmp = New-Object System.Drawing.Bitmap 32, 32
|
||||
$ig = [System.Drawing.Graphics]::FromImage($ibmp)
|
||||
@@ -47,75 +66,116 @@ try {
|
||||
$form.Icon = [System.Drawing.Icon]::FromHandle($ibmp.GetHicon())
|
||||
} catch {}
|
||||
|
||||
$titel = New-Object System.Windows.Forms.Label
|
||||
$titel.Text = "Rippy Encoding-Worker einrichten"
|
||||
$titel.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)
|
||||
$titel.ForeColor = [System.Drawing.Color]::FromArgb(79, 70, 229)
|
||||
$titel.Location = New-Object System.Drawing.Point(24, 20)
|
||||
$titel.Size = New-Object System.Drawing.Size(500, 30)
|
||||
$form.Controls.Add($titel)
|
||||
# --- Kopfbereich mit Farbverlauf --------------------------------------------
|
||||
# Das Rippy-UI hat oben einen Verlauf amber -> indigo -> purple (siehe
|
||||
# FirstRunWizard.tsx). WinForms kann das nicht von sich aus, also wird der
|
||||
# Verlauf im Paint-Ereignis gezeichnet - inklusive Disc-Symbol.
|
||||
$kopf = New-Object System.Windows.Forms.Panel
|
||||
$kopf.Dock = "Top"
|
||||
$kopf.Height = 104
|
||||
$form.Controls.Add($kopf)
|
||||
$kopf.Add_Paint({
|
||||
param($absender, $e)
|
||||
$flaeche = New-Object System.Drawing.Rectangle 0, 0, $absender.Width, $absender.Height
|
||||
$pinsel = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
|
||||
$flaeche, $cAmber, $cPurple, [System.Drawing.Drawing2D.LinearGradientMode]::Horizontal)
|
||||
# Dreifarbig wie im UI: der Indigo-Ton in der Mitte
|
||||
$mischung = New-Object System.Drawing.Drawing2D.ColorBlend 3
|
||||
$mischung.Colors = @($cAmber, $cIndigo, $cPurple)
|
||||
$mischung.Positions = @(0.0, 0.45, 1.0)
|
||||
$pinsel.InterpolationColors = $mischung
|
||||
$e.Graphics.FillRectangle($pinsel, $flaeche)
|
||||
$pinsel.Dispose()
|
||||
|
||||
$info = New-Object System.Windows.Forms.Label
|
||||
$info.Text = "Diese Maschine uebernimmt die Video-Kompression fuer Rippy. Gerippt wird weiter auf der Rippy-Hauptmaschine."
|
||||
$info.Location = New-Object System.Drawing.Point(24, 52)
|
||||
$info.Size = New-Object System.Drawing.Size(500, 34)
|
||||
$info.ForeColor = [System.Drawing.Color]::FromArgb(100, 116, 139)
|
||||
$form.Controls.Add($info)
|
||||
# Disc-Symbol: Ring aussen, weisses Loch in der Mitte
|
||||
$e.Graphics.SmoothingMode = "AntiAlias"
|
||||
$weiss = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(235, 255, 255, 255))
|
||||
$e.Graphics.FillEllipse($weiss, 24, 28, 48, 48)
|
||||
$e.Graphics.FillEllipse((New-Object System.Drawing.SolidBrush $cIndigo), 36, 40, 24, 24)
|
||||
$e.Graphics.FillEllipse($weiss, 45, 49, 6, 6)
|
||||
$weiss.Dispose()
|
||||
|
||||
# Texte direkt zeichnen - so bleibt der Verlauf ohne Kästchen sichtbar
|
||||
$fTitel = New-Object System.Drawing.Font("Segoe UI", 15, [System.Drawing.FontStyle]::Bold)
|
||||
$fUnter = New-Object System.Drawing.Font("Segoe UI", 9)
|
||||
$e.Graphics.DrawString("Rippy Encoding-Worker", $fTitel,
|
||||
[System.Drawing.Brushes]::White, 88, 26)
|
||||
$e.Graphics.DrawString(
|
||||
"Diese Maschine übernimmt die Video-Kompression für Rippy." + [char]10 +
|
||||
"Gerippt wird weiter auf der Rippy-Hauptmaschine.", $fUnter,
|
||||
(New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(225, 255, 255, 255))), 90, 56)
|
||||
$fTitel.Dispose(); $fUnter.Dispose()
|
||||
})
|
||||
|
||||
# Rippy-Adresse
|
||||
$lblHost = New-Object System.Windows.Forms.Label
|
||||
$lblHost.Text = "LAN-IP der Rippy-Maschine (Docker-Host) - NICHT dieser PC:"
|
||||
$lblHost.Location = New-Object System.Drawing.Point(24, 96)
|
||||
$lblHost.Location = New-Object System.Drawing.Point(24, 120)
|
||||
$lblHost.Size = New-Object System.Drawing.Size(500, 18)
|
||||
$lblHost.ForeColor = $cGedaempft
|
||||
$form.Controls.Add($lblHost)
|
||||
|
||||
$txtHost = New-Object System.Windows.Forms.TextBox
|
||||
$txtHost.Location = New-Object System.Drawing.Point(24, 116)
|
||||
$txtHost.Location = New-Object System.Drawing.Point(24, 140)
|
||||
$txtHost.Size = New-Object System.Drawing.Size(300, 26)
|
||||
$txtHost.Text = $RippyHost
|
||||
$txtHost.Font = New-Object System.Drawing.Font("Consolas", 10)
|
||||
$txtHost.BackColor = $cFeld
|
||||
$txtHost.ForeColor = $cText
|
||||
$txtHost.BorderStyle = "FixedSingle"
|
||||
$form.Controls.Add($txtHost)
|
||||
|
||||
# Worker-Name
|
||||
$lblName = New-Object System.Windows.Forms.Label
|
||||
$lblName.Text = "Name dieses Workers (frei waehlbar):"
|
||||
$lblName.Location = New-Object System.Drawing.Point(24, 152)
|
||||
$lblName.Text = "Name dieses Workers (frei wählbar):"
|
||||
$lblName.Location = New-Object System.Drawing.Point(24, 176)
|
||||
$lblName.Size = New-Object System.Drawing.Size(500, 18)
|
||||
$lblName.ForeColor = $cGedaempft
|
||||
$form.Controls.Add($lblName)
|
||||
|
||||
$txtName = New-Object System.Windows.Forms.TextBox
|
||||
$txtName.Location = New-Object System.Drawing.Point(24, 172)
|
||||
$txtName.Location = New-Object System.Drawing.Point(24, 196)
|
||||
$txtName.Size = New-Object System.Drawing.Size(300, 26)
|
||||
$txtName.Text = $WorkerName
|
||||
$txtName.BackColor = $cFeld
|
||||
$txtName.ForeColor = $cText
|
||||
$txtName.BorderStyle = "FixedSingle"
|
||||
$form.Controls.Add($txtName)
|
||||
|
||||
# Zielverzeichnis - frei waehlbar, Standard "Programme"
|
||||
# Zielverzeichnis - frei wählbar, Standard "Programme"
|
||||
$lblPfad = New-Object System.Windows.Forms.Label
|
||||
$lblPfad.Text = "Installieren nach:"
|
||||
$lblPfad.Location = New-Object System.Drawing.Point(24, 206)
|
||||
$lblPfad.Location = New-Object System.Drawing.Point(24, 232)
|
||||
$lblPfad.Size = New-Object System.Drawing.Size(500, 18)
|
||||
$lblPfad.ForeColor = $cGedaempft
|
||||
$form.Controls.Add($lblPfad)
|
||||
|
||||
$txtPfad = New-Object System.Windows.Forms.TextBox
|
||||
$txtPfad.Location = New-Object System.Drawing.Point(24, 226)
|
||||
$txtPfad.Location = New-Object System.Drawing.Point(24, 252)
|
||||
$txtPfad.Size = New-Object System.Drawing.Size(390, 26)
|
||||
$txtPfad.Text = $StandardZiel
|
||||
$txtPfad.Font = New-Object System.Drawing.Font("Consolas", 9)
|
||||
$txtPfad.BackColor = $cFeld
|
||||
$txtPfad.ForeColor = $cText
|
||||
$txtPfad.BorderStyle = "FixedSingle"
|
||||
$form.Controls.Add($txtPfad)
|
||||
|
||||
$btnPfad = New-Object System.Windows.Forms.Button
|
||||
$btnPfad.Text = "Aendern ..."
|
||||
$btnPfad.Location = New-Object System.Drawing.Point(424, 225)
|
||||
$btnPfad.Text = "Ändern ..."
|
||||
$btnPfad.Location = New-Object System.Drawing.Point(424, 251)
|
||||
$btnPfad.Size = New-Object System.Drawing.Size(100, 28)
|
||||
$btnPfad.FlatStyle = "Flat"
|
||||
$btnPfad.BackColor = $cFeld
|
||||
$btnPfad.ForeColor = $cText
|
||||
$btnPfad.FlatAppearance.BorderColor = $cGedaempft
|
||||
$form.Controls.Add($btnPfad)
|
||||
$btnPfad.Add_Click({
|
||||
$dlg = New-Object System.Windows.Forms.FolderBrowserDialog
|
||||
$dlg.Description = "Ordner waehlen - darin wird 'Rippy Worker' angelegt"
|
||||
$dlg.Description = "Ordner wählen - darin wird 'Rippy Worker' angelegt"
|
||||
$dlg.ShowNewFolderButton = $true
|
||||
if ($dlg.ShowDialog() -eq "OK") {
|
||||
# Der Dialog liefert den ELTERN-Ordner; der Unterordner kommt von uns,
|
||||
# damit die Deinstallation nie einen fremden Ordner mitloescht.
|
||||
# damit die Deinstallation nie einen fremden Ordner mitlöscht.
|
||||
$txtPfad.Text = (Join-Path $dlg.SelectedPath "Rippy Worker")
|
||||
}
|
||||
})
|
||||
@@ -123,39 +183,41 @@ $btnPfad.Add_Click({
|
||||
# Autostart
|
||||
$chkAuto = New-Object System.Windows.Forms.CheckBox
|
||||
$chkAuto.Text = "Beim Anmelden automatisch starten (Tray-Symbol)"
|
||||
$chkAuto.Location = New-Object System.Drawing.Point(24, 264)
|
||||
$chkAuto.Location = New-Object System.Drawing.Point(24, 290)
|
||||
$chkAuto.Size = New-Object System.Drawing.Size(400, 22)
|
||||
$chkAuto.Checked = $true
|
||||
$chkAuto.ForeColor = $cText
|
||||
$form.Controls.Add($chkAuto)
|
||||
|
||||
# Log-Bereich
|
||||
$log = New-Object System.Windows.Forms.TextBox
|
||||
$log.Location = New-Object System.Drawing.Point(24, 296)
|
||||
$log.Size = New-Object System.Drawing.Size(500, 160)
|
||||
$log.Location = New-Object System.Drawing.Point(24, 322)
|
||||
$log.Size = New-Object System.Drawing.Size(500, 150)
|
||||
$log.Multiline = $true
|
||||
$log.ReadOnly = $true
|
||||
$log.ScrollBars = "Vertical"
|
||||
$log.BackColor = [System.Drawing.Color]::FromArgb(15, 23, 42)
|
||||
$log.ForeColor = [System.Drawing.Color]::FromArgb(226, 232, 240)
|
||||
$log.BackColor = $cPanel
|
||||
$log.ForeColor = $cText
|
||||
$log.BorderStyle = "FixedSingle"
|
||||
$log.Font = New-Object System.Drawing.Font("Consolas", 8.5)
|
||||
$form.Controls.Add($log)
|
||||
|
||||
# Knoepfe
|
||||
# Knöpfe
|
||||
$btnInstall = New-Object System.Windows.Forms.Button
|
||||
$btnInstall.Text = "Installieren"
|
||||
$btnInstall.Location = New-Object System.Drawing.Point(24, 470)
|
||||
$btnInstall.Location = New-Object System.Drawing.Point(24, 486)
|
||||
$btnInstall.Size = New-Object System.Drawing.Size(150, 40)
|
||||
$btnInstall.BackColor = [System.Drawing.Color]::FromArgb(79, 70, 229)
|
||||
$btnInstall.ForeColor = [System.Drawing.Color]::White
|
||||
$btnInstall.BackColor = $cAmber
|
||||
$btnInstall.ForeColor = $cBg
|
||||
$btnInstall.FlatStyle = "Flat"
|
||||
$btnInstall.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
|
||||
$form.Controls.Add($btnInstall)
|
||||
|
||||
$btnStart = New-Object System.Windows.Forms.Button
|
||||
$btnStart.Text = "Worker starten"
|
||||
$btnStart.Location = New-Object System.Drawing.Point(190, 470)
|
||||
$btnStart.Location = New-Object System.Drawing.Point(190, 486)
|
||||
$btnStart.Size = New-Object System.Drawing.Size(150, 40)
|
||||
$btnStart.BackColor = [System.Drawing.Color]::FromArgb(16, 185, 129)
|
||||
$btnStart.BackColor = $cEmerald
|
||||
$btnStart.ForeColor = [System.Drawing.Color]::White
|
||||
$btnStart.FlatStyle = "Flat"
|
||||
$btnStart.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
|
||||
@@ -176,15 +238,15 @@ function Do-Install {
|
||||
|
||||
# Zielverzeichnis aus dem Feld. script:-Bereich, weil "Worker starten"
|
||||
# und die Abschlussmeldung denselben Pfad brauchen.
|
||||
$gewaehlt = $txtPfad.Text.Trim()
|
||||
if (-not $gewaehlt) { $gewaehlt = $StandardZiel }
|
||||
if (-not [System.IO.Path]::IsPathRooted($gewaehlt)) {
|
||||
$zielEingabe = $txtPfad.Text.Trim()
|
||||
if (-not $zielEingabe) { $zielEingabe = $StandardZiel }
|
||||
if (-not [System.IO.Path]::IsPathRooted($zielEingabe)) {
|
||||
[System.Windows.Forms.MessageBox]::Show(
|
||||
"Bitte einen vollstaendigen Pfad angeben, z. B. C:\Program Files\Rippy Worker.",
|
||||
"Pfad unvollstaendig")
|
||||
"Bitte einen vollständigen Pfad angeben, z. B. C:\Program Files\Rippy Worker.",
|
||||
"Pfad unvollständig")
|
||||
return
|
||||
}
|
||||
$script:InstallDir = $gewaehlt
|
||||
$script:InstallDir = $zielEingabe
|
||||
|
||||
$btnInstall.Enabled = $false
|
||||
$log.Clear()
|
||||
@@ -209,7 +271,7 @@ function Do-Install {
|
||||
Log "Python gefunden: $python"
|
||||
|
||||
# 2. Ordner + Worker-Code
|
||||
# Erst PRUEFEN, ob wir dort ueberhaupt schreiben duerfen. Die .exe ist mit
|
||||
# Erst PRUEFEN, ob wir dort überhaupt schreiben dürfen. Die .exe ist mit
|
||||
# -requireAdmin gebaut und fragt per UAC, aber wer install-gui.ps1 direkt
|
||||
# startet, hat die Rechte nicht - dann soll hier ein brauchbarer Satz
|
||||
# stehen und nicht "Zugriff verweigert" aus irgendeiner Tiefe.
|
||||
@@ -221,12 +283,12 @@ function Do-Install {
|
||||
} catch {
|
||||
Log "FEHLER: In '$InstallDir' darf nicht geschrieben werden."
|
||||
Log ""
|
||||
Log "Das ist normal bei Ordnern unter 'Programme' - dafuer braucht es"
|
||||
Log "Das ist normal bei Ordnern unter 'Programme' - dafür braucht es"
|
||||
Log "Administratorrechte. Zwei Wege:"
|
||||
Log " 1. Diesen Installer mit Rechtsklick > 'Als Administrator"
|
||||
Log " ausfuehren' starten (der empfohlene Weg), ODER"
|
||||
Log " 2. oben ueber 'Aendern ...' einen Ordner im eigenen"
|
||||
Log " Benutzerprofil waehlen - dort geht es ohne Adminrechte."
|
||||
Log " ausführen' starten (der empfohlene Weg), ODER"
|
||||
Log " 2. oben über 'Ändern ...' einen Ordner im eigenen"
|
||||
Log " Benutzerprofil wählen - dort geht es ohne Adminrechte."
|
||||
$btnInstall.Enabled = $true
|
||||
return
|
||||
}
|
||||
@@ -242,13 +304,13 @@ function Do-Install {
|
||||
Expand-Archive worker.zip -DestinationPath . -Force
|
||||
Remove-Item worker.zip
|
||||
|
||||
# 3. venv + Abhaengigkeiten
|
||||
# 3. venv + Abhängigkeiten
|
||||
if (-not (Test-Path "venv")) { Log "Lege Python-Umgebung an ..."; Invoke-Expression "$python -m venv venv" }
|
||||
Log "Installiere Python-Abhaengigkeiten (kann 1-2 min dauern) ..."
|
||||
Log "Installiere Python-Abhängigkeiten (kann 1-2 min dauern) ..."
|
||||
& .\venv\Scripts\python.exe -m pip install --quiet --upgrade pip
|
||||
& .\venv\Scripts\pip.exe install --quiet -r requirements.txt
|
||||
& .\venv\Scripts\pip.exe install --quiet pystray pillow
|
||||
Log "Abhaengigkeiten installiert."
|
||||
Log "Abhängigkeiten installiert."
|
||||
|
||||
# 4. HandBrakeCLI
|
||||
if (-not (Test-Path ".\HandBrakeCLI.exe") -and -not (Get-Command HandBrakeCLI.exe -ErrorAction SilentlyContinue)) {
|
||||
@@ -273,12 +335,12 @@ if (-not `$Force) { if ([System.Windows.Forms.MessageBox]::Show("Rippy-Worker '$
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Get-CimInstance Win32_Process | Where-Object { `$_.ExecutablePath -like "`$PSScriptRoot*" } | ForEach-Object { Stop-Process -Id `$_.ProcessId -Force -ErrorAction SilentlyContinue }
|
||||
Start-Sleep 2
|
||||
# Autostart-Verknuepfung entfernen - BEIDE moeglichen Orte, weil die
|
||||
# Installation je nach Zielverzeichnis persoenlich oder maschinenweit war.
|
||||
# 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 aelteren Installationen, falls vorhanden.
|
||||
# ... und die geplante Aufgabe aus älteren Installationen, falls vorhanden.
|
||||
schtasks /delete /tn "RippyWorker" /f 2>`$null | Out-Null
|
||||
try { Invoke-RestMethod -Method Delete "http://$rHost/api/workers/$wName" -TimeoutSec 5 | Out-Null } catch {}
|
||||
Set-Location (Split-Path `$PSScriptRoot -Parent)
|
||||
@@ -287,55 +349,55 @@ Remove-Item -Recurse -Force `$PSScriptRoot
|
||||
"@
|
||||
Set-Content -Path "uninstall.ps1" -Value $uninstall -Encoding ASCII
|
||||
|
||||
# 6. Autostart ueber den Autostart-ORDNER, nicht ueber schtasks.
|
||||
# 6. Autostart über den Autostart-ORDNER, nicht über schtasks.
|
||||
#
|
||||
# Befund 25.07.2026 (auf dem Commander-PC aufgetreten): schtasks /create
|
||||
# mit /tn "RippyWorker" legt die Aufgabe im WURZELORDNER der
|
||||
# Aufgabenplanung an, und das verlangt Administratorrechte. Der
|
||||
# Installer laeuft normal ohne. schtasks schrieb deshalb
|
||||
# Installer läuft normal ohne. schtasks schrieb deshalb
|
||||
# "FEHLER: Zugriff verweigert." nach stderr, und weil oben
|
||||
# $ErrorActionPreference = "Stop" steht, wurde daraus ein Abbruch der
|
||||
# GESAMTEN Installation - obwohl alles ausser dem Autostart fertig war.
|
||||
# GESAMTEN Installation - obwohl alles außer dem Autostart fertig war.
|
||||
# Der Nutzer sah nur "FEHLER: FEHLER: Zugriff verweigert" samt dem
|
||||
# irrefuehrenden Tipp, die IP koenne falsch sein.
|
||||
# irreführenden Tipp, die IP könne falsch sein.
|
||||
#
|
||||
# Der Autostart-Ordner braucht NIE Adminrechte, und der Nutzer kann die
|
||||
# Verknuepfung dort selbst sehen und loeschen. Zwei Fliegen.
|
||||
# Verknüpfung dort selbst sehen und löschen. Zwei Fliegen.
|
||||
if ($chkAuto.Checked) {
|
||||
try {
|
||||
# Liegt das Programm unter "Programme", ist es eine Installation
|
||||
# fuer die ganze Maschine - dann gehoert der Autostart in den
|
||||
# für die ganze Maschine - dann gehört der Autostart in den
|
||||
# Autostart-Ordner ALLER Benutzer. Das passt auch zur Rechtelage:
|
||||
# der Installer laeuft dafuer ohnehin erhoeht, und bei einer
|
||||
# Erhoehung ueber ein FREMDES Administratorkonto waere der
|
||||
# der Installer läuft dafür ohnehin erhöht, und bei einer
|
||||
# Erhöhung über ein FREMDES Administratorkonto wäre der
|
||||
# benutzereigene Ordner der des Admins - also der falsche.
|
||||
# Bei einer Installation ins eigene Profil bleibt es persoenlich.
|
||||
# Bei einer Installation ins eigene Profil bleibt es persönlich.
|
||||
$programme = [Environment]::GetFolderPath("ProgramFiles")
|
||||
$programmeX86 = [Environment]::GetFolderPath("ProgramFilesX86")
|
||||
$fuerAlle = $InstallDir.StartsWith($programme, "OrdinalIgnoreCase") `
|
||||
$maschinenweit = $InstallDir.StartsWith($programme, "OrdinalIgnoreCase") `
|
||||
-or ($programmeX86 -and $InstallDir.StartsWith($programmeX86, "OrdinalIgnoreCase"))
|
||||
$autostartOrdner = if ($fuerAlle) {
|
||||
$autostartDir = if ($maschinenweit) {
|
||||
[Environment]::GetFolderPath("CommonStartup")
|
||||
} else {
|
||||
[Environment]::GetFolderPath("Startup")
|
||||
}
|
||||
$verknuepfung = Join-Path $autostartOrdner "RippyWorker.lnk"
|
||||
$lnkPfad = Join-Path $autostartDir "RippyWorker.lnk"
|
||||
$wsh = New-Object -ComObject WScript.Shell
|
||||
$lnk = $wsh.CreateShortcut($verknuepfung)
|
||||
$lnk = $wsh.CreateShortcut($lnkPfad)
|
||||
$lnk.TargetPath = (Join-Path $InstallDir "start-tray.bat")
|
||||
$lnk.WorkingDirectory = $InstallDir
|
||||
$lnk.Description = "Rippy Encoding-Worker"
|
||||
$lnk.Save()
|
||||
if ($fuerAlle) {
|
||||
Log "Autostart eingerichtet (fuer alle Benutzer dieser Maschine)."
|
||||
if ($maschinenweit) {
|
||||
Log "Autostart eingerichtet (für alle Benutzer dieser Maschine)."
|
||||
} else {
|
||||
Log "Autostart eingerichtet (fuer dich, startet bei der Anmeldung)."
|
||||
Log "Autostart eingerichtet (für dich, startet bei der Anmeldung)."
|
||||
}
|
||||
} catch {
|
||||
# Nur eine Warnung: der Worker selbst ist fertig und startbar.
|
||||
Log "HINWEIS: Autostart konnte nicht eingerichtet werden ($($_.Exception.Message))."
|
||||
Log " Der Worker ist trotzdem fertig installiert und laeuft."
|
||||
Log " Von Hand: start-tray.bat in den Autostart-Ordner verknuepfen"
|
||||
Log " Der Worker ist trotzdem fertig installiert und läuft."
|
||||
Log " Von Hand: start-tray.bat in den Autostart-Ordner verknüpfen"
|
||||
Log " (Windows-Taste + R, dann shell:startup eingeben)."
|
||||
}
|
||||
}
|
||||
|
||||
+211
-211
@@ -1,211 +1,211 @@
|
||||
# Rippy: Nativer Windows-Transcode-Worker - Installation OHNE Docker.
|
||||
#
|
||||
# Was das Skript tut (alles nach -InstallDir, Standard "C:\Program Files\Rippy Worker"):
|
||||
# 1. Prueft Python (3.10+), legt ein venv an, installiert die Abhaengigkeiten
|
||||
# 2. Laedt den Worker-Code direkt von deiner Rippy-Instanz (/api/worker-setup/paket)
|
||||
# 3. Laedt HandBrakeCLI (gepinnte Version, offizielles GitHub-Release),
|
||||
# falls nicht schon im PATH oder im Ordner vorhanden
|
||||
# 4. Erzeugt start-worker.bat - Doppelklick startet den Worker
|
||||
#
|
||||
# Der Worker bedient NUR die Kompressions-Queue (transcode) - gerippt wird
|
||||
# weiterhin auf der Rippy-Hauptmaschine (dort haengt das Laufwerk).
|
||||
#
|
||||
# Aufruf (PowerShell):
|
||||
# .\install.ps1 -RippyHost 192.168.178.162 [-WorkerName mein-pc] [-Autostart]
|
||||
#
|
||||
# -Autostart registriert eine Aufgabe "RippyWorker" (Start bei Anmeldung).
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)] [string]$RippyHost,
|
||||
[string]$WorkerName = $env:COMPUTERNAME.ToLower(),
|
||||
[switch]$Autostart,
|
||||
# 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 (frueher war hier
|
||||
# eine feste 1.9.2 hardcodiert, was verwirrte). Faellt auf eine bekannte
|
||||
# Version zurueck, 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 ausfuehren)"
|
||||
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 + Abhaengigkeiten
|
||||
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 fuer den Windows-Worker: Tray-Symbol (Status, Start/Stopp, Beenden)
|
||||
& .\venv\Scripts\pip.exe install --quiet pystray pillow
|
||||
Write-Host "Python-Abhaengigkeiten installiert."
|
||||
|
||||
# 4. HandBrakeCLI besorgen (falls noetig)
|
||||
$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. 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 PATH=%~dp0;%PATH%
|
||||
venv\Scripts\celery.exe -A celery_app worker --loglevel=info -Q transcode --pool=solo -n ${WorkerName}@%%h
|
||||
"@
|
||||
Set-Content -Path "start-worker.bat" -Value $bat -Encoding ASCII
|
||||
|
||||
# 6. 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 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."
|
||||
|
||||
# 7. Deinstaller - MUSS-Kriterium: rueckstandsfrei entfernbar
|
||||
$uninstall = @"
|
||||
# Rippy-Worker DEINSTALLIEREN: stoppt Prozesse, entfernt Autostart,
|
||||
# meldet den Worker in Rippy ab und loescht 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-Verknuepfung entfernen - BEIDE moeglichen Orte, weil die
|
||||
# Installation je nach Zielverzeichnis persoenlich 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 aelteren 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 loeschen (Einstellungen -> Worker)." }
|
||||
Set-Location (Split-Path `$PSScriptRoot -Parent)
|
||||
Remove-Item -Recurse -Force `$PSScriptRoot
|
||||
Write-Host "Rippy-Worker deinstalliert." -ForegroundColor Green
|
||||
"@
|
||||
Set-Content -Path "uninstall.ps1" -Value $uninstall -Encoding ASCII
|
||||
Write-Host "uninstall.ps1 erzeugt."
|
||||
|
||||
# 8. Optional: Autostart bei Anmeldung (mit Tray)
|
||||
#
|
||||
# Ueber den Autostart-ORDNER, nicht ueber schtasks: Eine Aufgabe im
|
||||
# Wurzelordner der Aufgabenplanung anzulegen verlangt Administratorrechte, und
|
||||
# der Installer laeuft 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 fuer die Maschine, also Autostart fuer
|
||||
# alle Benutzer. Sonst persoenlich. Siehe install-gui.ps1 fuer die
|
||||
# ausfuehrliche Begruendung (Rechtelage bei fremdem Admin-Konto).
|
||||
$programme = [Environment]::GetFolderPath("ProgramFiles")
|
||||
$fuerAlle = $ziel.StartsWith($programme, "OrdinalIgnoreCase")
|
||||
$autostartOrdner = if ($fuerAlle) {
|
||||
[Environment]::GetFolderPath("CommonStartup")
|
||||
} else {
|
||||
[Environment]::GetFolderPath("Startup")
|
||||
}
|
||||
$wsh = New-Object -ComObject WScript.Shell
|
||||
$lnk = $wsh.CreateShortcut((Join-Path $autostartOrdner "RippyWorker.lnk"))
|
||||
$lnk.TargetPath = (Join-Path $ziel "start-tray.bat")
|
||||
$lnk.WorkingDirectory = $ziel
|
||||
$lnk.Description = "Rippy Encoding-Worker"
|
||||
$lnk.Save()
|
||||
Write-Host "Autostart eingerichtet (Verknuepfung 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 verknuepfen." -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'."
|
||||
# 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. 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 192.168.178.162 [-WorkerName mein-pc] [-Autostart]
|
||||
#
|
||||
# -Autostart registriert eine Aufgabe "RippyWorker" (Start bei Anmeldung).
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)] [string]$RippyHost,
|
||||
[string]$WorkerName = $env:COMPUTERNAME.ToLower(),
|
||||
[switch]$Autostart,
|
||||
# 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. 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 PATH=%~dp0;%PATH%
|
||||
venv\Scripts\celery.exe -A celery_app worker --loglevel=info -Q transcode --pool=solo -n ${WorkerName}@%%h
|
||||
"@
|
||||
Set-Content -Path "start-worker.bat" -Value $bat -Encoding ASCII
|
||||
|
||||
# 6. 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 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."
|
||||
|
||||
# 7. 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)." }
|
||||
Set-Location (Split-Path `$PSScriptRoot -Parent)
|
||||
Remove-Item -Recurse -Force `$PSScriptRoot
|
||||
Write-Host "Rippy-Worker deinstalliert." -ForegroundColor Green
|
||||
"@
|
||||
Set-Content -Path "uninstall.ps1" -Value $uninstall -Encoding ASCII
|
||||
Write-Host "uninstall.ps1 erzeugt."
|
||||
|
||||
# 8. 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'."
|
||||
|
||||
Reference in New Issue
Block a user