Docker faellt weg. Das Projekt hat keine npm-Abhaengigkeiten und Node fuehrt TypeScript direkt aus - der Container schrieb nur noch drei Programme fest, und dafuer braucht es keine Virtualisierung, sondern einen Ordner. Der Anlass war, dass ADR-0004 zwar `docker compose pull` versprach, es aber nie ein veroeffentlichtes Image gab. Wer installieren wollte, musste whisper.cpp aus dem Quelltext uebersetzen. Plattformschicht (packages/core/src/platform.ts): - Programme werden neben der Anwendung gesucht, nicht im PATH - Daten unter %LOCALAPPDATA%, damit Installation und Update ohne Administratorrechte auskommen - Schriften mitgeliefert: ohne fontsdir setzt libass wortlos eine Ersatzschrift ein - gemessen 720 statt 826 Pixel Zeilenbreite Windows-Eigenheiten, an echtem ffmpeg 8.1 durchprobiert: - Pfade im Filtergraphen brauchen alle drei Schritte (Vorwaertsschraegstriche, escapter Doppelpunkt, Anfuehrungszeichen); je zwei davon reichen nicht - `nice` gibt es nicht, ersetzt durch os.setPriority Auslieferung: - scripts/build-package.ts baut das Paket, laeuft auch unter Linux - install.ps1 ohne Administratorrechte, Autostart ueber den Aufgabenplaner - update legt neue Fassungen bereit; eingespielt wird beim naechsten Start, nie mitten im Stream - ZIP lesen und schreiben ohne Abhaengigkeiten (packages/core/src/zip.ts) Kein `irm ... | iex`-Einzeiler: git.zeasy.dev antwortete instanzweit mit einer Umleitung auf /user/login, und `iex` fuehrte die Anmeldeseite als PowerShell aus. Der Updater verfolgt Umleitungen jetzt nicht und hat keine erfundene Vorgabeadresse mehr. Ausserdem: backup.ps1 war an zwei Stellen dokumentiert, aber nie geschrieben. ADR-0007 ersetzt ADR-0004. 84 Tests, Typpruefung sauber. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AhVSfMFdWnXKCv5JfLnArZ
188 lines
8.2 KiB
PowerShell
188 lines
8.2 KiB
PowerShell
# Installiert abdulive auf dem Stream-PC.
|
|
#
|
|
# .\install.ps1
|
|
#
|
|
# Aus einem entpackten Paket heraus ausfuehren - install.ps1 liegt darin neben
|
|
# node.exe.
|
|
#
|
|
# Einen `irm ... | iex`-Einzeiler gibt es bewusst nicht: git.zeasy.dev verlangt
|
|
# instanzweit eine Anmeldung und antwortet auf jede Adresse mit einer Umleitung
|
|
# auf /user/login. `iex` fuehrt die Anmeldeseite dann als PowerShell aus, mit
|
|
# einer Fehlermeldung, aus der niemand die Ursache ablesen kann. Wer eine
|
|
# Ablage ohne Anmeldung hat, gibt sie mit -Quelle an; dann laedt das Skript
|
|
# das Paket selbst.
|
|
#
|
|
# Ohne Administratorrechte, mit Absicht: Programm und Daten liegen im Profil
|
|
# des Benutzers, damit auch Updates ohne Rueckfrage durchlaufen. Siehe
|
|
# ADR-0007.
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
# Nur noetig, wenn nicht aus einem entpackten Paket heraus installiert
|
|
# wird. Dort muessen abdulive.json und die ZIP-Datei ohne Anmeldung
|
|
# erreichbar sein.
|
|
[string]$Quelle,
|
|
[string]$Ziel = (Join-Path $env:LOCALAPPDATA 'Programs\abdulive'),
|
|
[switch]$KeinAutostart
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$daten = Join-Path $env:LOCALAPPDATA 'abdulive'
|
|
|
|
function Schritt($text) { Write-Host "==> $text" -ForegroundColor Cyan }
|
|
function Hinweis($text) { Write-Host " $text" -ForegroundColor DarkGray }
|
|
|
|
# ---------------------------------------------------------------- Pruefen
|
|
|
|
Schritt 'Umgebung pruefen'
|
|
if ([Environment]::Is64BitOperatingSystem -ne $true) {
|
|
throw 'Das Paket gibt es nur fuer 64-Bit-Windows.'
|
|
}
|
|
$obs = Get-Process obs64, obs -ErrorAction SilentlyContinue
|
|
Hinweis ("OBS laeuft: " + $(if ($obs) { 'ja' } else { 'nein - macht nichts, wird spaeter gebraucht' }))
|
|
|
|
# ---------------------------------------------------------------- Quelle
|
|
|
|
$hier = if ($PSScriptRoot) { $PSScriptRoot } else { $null }
|
|
$ausPaket = $hier -and (Test-Path (Join-Path $hier 'node.exe'))
|
|
|
|
if ($ausPaket) {
|
|
Schritt "Installiere aus dem entpackten Paket ($hier)"
|
|
$quellordner = $hier
|
|
} elseif (-not $Quelle) {
|
|
Write-Host ''
|
|
Write-Host 'Dieses Skript liegt nicht neben einem Paket, und -Quelle ist nicht gesetzt.' -ForegroundColor Red
|
|
Write-Host ''
|
|
Write-Host 'Entweder:'
|
|
Write-Host ' - das Paket herunterladen, entpacken und darin install.ps1 ausfuehren'
|
|
Write-Host ' - oder eine Ablage angeben, unter der abdulive.json ohne Anmeldung liegt:'
|
|
Write-Host ' .\install.ps1 -Quelle https://.../abdulive'
|
|
Write-Host ''
|
|
exit 1
|
|
} else {
|
|
Schritt 'Lade das aktuelle Paket'
|
|
$tmp = Join-Path $env:TEMP ("abdulive-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
|
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
|
|
|
# Nicht auf die Form der Ausnahme verlassen: `-MaximumRedirection 0` wirft
|
|
# in Windows PowerShell 5.1 eine WebException ganz ohne `Response`, und ein
|
|
# Zugriff auf .Response.StatusCode laeuft dann selbst auf die Nase.
|
|
# Stattdessen holen, was kommt, und den Inhalt ansehen.
|
|
try {
|
|
$antwort = Invoke-WebRequest "$Quelle/abdulive.json" -UseBasicParsing -ErrorAction Stop
|
|
} catch {
|
|
Write-Host ''
|
|
Write-Host "$Quelle/abdulive.json ist nicht erreichbar." -ForegroundColor Red
|
|
Write-Host " $($_.Exception.Message)"
|
|
Write-Host ''
|
|
exit 1
|
|
}
|
|
|
|
# Eine Anmeldeseite ist HTML und kommt mit Status 200 an, wenn der Umleitung
|
|
# gefolgt wurde. Genau das ist bei git.zeasy.dev passiert.
|
|
$typ = [string]$antwort.Headers['Content-Type']
|
|
$anfang = [string]$antwort.Content
|
|
if ($typ -like 'text/html*' -or $anfang -match '(?i)^\s*(<!doctype|<html|<a href="/user/login)') {
|
|
Write-Host ''
|
|
Write-Host "$Quelle/abdulive.json liefert HTML statt JSON." -ForegroundColor Red
|
|
Write-Host ' Vermutlich eine Anmeldeseite - die Ablage ist nicht oeffentlich.'
|
|
Write-Host ' Die Adresse einmal im Browser aufrufen zeigt, was dort steht.'
|
|
Write-Host ''
|
|
exit 1
|
|
}
|
|
$manifest = $antwort.Content | ConvertFrom-Json
|
|
Hinweis "Version $($manifest.version), $([math]::Round($manifest.groesse / 1MB)) MB"
|
|
|
|
$zip = Join-Path $tmp $manifest.datei
|
|
Invoke-WebRequest "$Quelle/$($manifest.datei)" -OutFile $zip
|
|
|
|
$ist = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLower()
|
|
if ($ist -ne $manifest.sha256.ToLower()) {
|
|
throw "Pruefsumme stimmt nicht. Erwartet $($manifest.sha256), bekommen $ist."
|
|
}
|
|
Hinweis 'Pruefsumme stimmt.'
|
|
|
|
Expand-Archive -Path $zip -DestinationPath (Join-Path $tmp 'paket') -Force
|
|
$quellordner = Join-Path $tmp 'paket'
|
|
}
|
|
|
|
# ---------------------------------------------------------------- Kopieren
|
|
|
|
Schritt "Programm nach $Ziel"
|
|
# Laeuft gerade eine aeltere Fassung, muss sie erst weichen - Windows sperrt
|
|
# die Dateien laufender Programme.
|
|
Get-Process node -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Path -and $_.Path.StartsWith($Ziel, 'OrdinalIgnoreCase') } |
|
|
ForEach-Object { Hinweis "beende laufende Fassung (PID $($_.Id))"; Stop-Process -Id $_.Id -Force }
|
|
|
|
New-Item -ItemType Directory -Force -Path $Ziel | Out-Null
|
|
foreach ($item in @('app', 'bin', 'fonts', 'node.exe', 'version.json',
|
|
'abdulive.cmd', 'launcher.ps1', 'install.ps1', 'backup.ps1')) {
|
|
$von = Join-Path $quellordner $item
|
|
if (-not (Test-Path $von)) { continue }
|
|
$nach = Join-Path $Ziel $item
|
|
if (Test-Path $nach) { Remove-Item -Recurse -Force $nach }
|
|
Copy-Item -Recurse -Force -Path $von -Destination $nach
|
|
}
|
|
|
|
# ---------------------------------------------------------------- Daten
|
|
|
|
Schritt "Daten nach $daten"
|
|
foreach ($unter in @('config', 'db', 'work', 'clips', 'models', 'logs')) {
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $daten $unter) | Out-Null
|
|
}
|
|
# Eine vorhandene config.yaml wird nie ueberschrieben - da stehen Abdus
|
|
# Einstellungen drin.
|
|
$cfg = Join-Path $daten 'config\config.yaml'
|
|
if (-not (Test-Path $cfg)) {
|
|
Copy-Item (Join-Path $Ziel 'app\config\config.example.yaml') $cfg
|
|
Hinweis 'config.yaml aus der Vorlage angelegt.'
|
|
} else {
|
|
Hinweis 'config.yaml war schon da, bleibt unveraendert.'
|
|
}
|
|
Copy-Item (Join-Path $Ziel 'app\config\layout.yaml') (Join-Path $daten 'config\layout.yaml') -Force
|
|
|
|
# ---------------------------------------------------------------- Autostart
|
|
|
|
if (-not $KeinAutostart) {
|
|
Schritt 'Autostart einrichten'
|
|
# Aufgabenplaner statt Windows-Dienst: die Anwendung muss in derselben
|
|
# Sitzung laufen wie OBS, und ein Dienst braeuchte Administratorrechte
|
|
# samt eigenem Wrapper-Programm.
|
|
$ps = (Get-Command powershell.exe).Source
|
|
$start = New-ScheduledTaskAction -Execute $ps `
|
|
-Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$Ziel\launcher.ps1`""
|
|
$beiAnmeldung = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
|
|
$einst = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries `
|
|
-DontStopIfGoingOnBatteries -StartWhenAvailable -RestartInterval (New-TimeSpan -Minutes 2) `
|
|
-RestartCount 3 -ExecutionTimeLimit ([TimeSpan]::Zero)
|
|
Register-ScheduledTask -TaskName 'abdulive' -Action $start -Trigger $beiAnmeldung `
|
|
-Settings $einst -Force | Out-Null
|
|
Hinweis 'Aufgabe "abdulive" laeuft ab der naechsten Anmeldung mit.'
|
|
|
|
# Taeglich nach einem Update sehen. Angewendet wird es erst beim naechsten
|
|
# Start - waehrend eines Streams wird nichts ausgetauscht.
|
|
$pruefen = New-ScheduledTaskAction -Execute (Join-Path $Ziel 'node.exe') `
|
|
-Argument "`"$Ziel\app\apps\cli\main.ts`" update"
|
|
$taeglich = New-ScheduledTaskTrigger -Daily -At '05:00'
|
|
Register-ScheduledTask -TaskName 'abdulive-update' -Action $pruefen -Trigger $taeglich `
|
|
-Settings $einst -Force | Out-Null
|
|
Hinweis 'Aufgabe "abdulive-update" sieht taeglich um 05:00 nach.'
|
|
}
|
|
|
|
# ---------------------------------------------------------------- Fertig
|
|
|
|
$version = (Get-Content (Join-Path $Ziel 'version.json') -Raw | ConvertFrom-Json).version
|
|
Write-Host ''
|
|
Write-Host "abdulive $version ist installiert." -ForegroundColor Green
|
|
Write-Host ''
|
|
Write-Host 'Naechste Schritte:'
|
|
Write-Host " 1. Sprachmodell laden (einmalig, ca. 465 MB):"
|
|
Write-Host " & '$Ziel\abdulive.cmd' fetch-model"
|
|
Write-Host " 2. Umgebung pruefen:"
|
|
Write-Host " & '$Ziel\abdulive.cmd' check-env"
|
|
Write-Host ' 3. Starten und einrichten:'
|
|
Write-Host ' Start-ScheduledTask -TaskName abdulive'
|
|
Write-Host ' dann http://localhost:4792/setup im Browser oeffnen'
|
|
Write-Host ''
|