param( [string]$InstallDir = (Join-Path $env:LOCALAPPDATA 'SangLive'), [switch]$ReplaceEnv, [switch]$NoShortcut ) $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() $packageUrl = 'https://slive.misalab.com/sanglive.zip?v=20260511-author2' $tempRoot = Join-Path $env:TEMP ('sanglive-install-' + [guid]::NewGuid().ToString('N')) $zipPath = Join-Path $tempRoot 'sanglive.zip' $extractPath = Join-Path $tempRoot 'extract' $envBackupPath = Join-Path $tempRoot '.env.backup' function Assert-Node { $node = Get-Command node -ErrorAction SilentlyContinue if (-not $node) { throw 'Node.js is required. Install Node.js 20 LTS first: https://nodejs.org/' } } function Resolve-SourceDir { param([string]$ExtractPath) if (Test-Path (Join-Path $ExtractPath 'install.ps1')) { return $ExtractPath } $candidate = Get-ChildItem -Path $ExtractPath -Directory | Where-Object { Test-Path (Join-Path $_.FullName 'install.ps1') } | Select-Object -First 1 if ($candidate) { return $candidate.FullName } throw 'install.ps1 not found in downloaded package.' } New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null try { Assert-Node Write-Host "Downloading S-Live from $packageUrl" Invoke-WebRequest -Uri $packageUrl -OutFile $zipPath -UseBasicParsing Write-Host 'Extracting package...' Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force $sourceDir = Resolve-SourceDir -ExtractPath $extractPath Write-Host "Installing to $InstallDir" $installedEnv = Join-Path $InstallDir '.env' $hasExistingEnv = Test-Path $installedEnv if ($hasExistingEnv -and -not $ReplaceEnv) { Copy-Item -LiteralPath $installedEnv -Destination $envBackupPath -Force } Copy-Item -Path (Join-Path $sourceDir '*') -Destination $InstallDir -Recurse -Force if ($hasExistingEnv -and -not $ReplaceEnv) { Copy-Item -LiteralPath $envBackupPath -Destination $installedEnv -Force } $installer = Join-Path $InstallDir 'install.ps1' if (-not (Test-Path $installer)) { throw 'install.ps1 not found after extraction.' } $installArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $installer) if ($NoShortcut) { $installArgs += '-NoShortcut' } & powershell @installArgs if ($LASTEXITCODE -ne 0) { throw 'S-Live installer failed.' } Write-Host '' Write-Host 'Install complete.' Write-Host 'Run the same command again later to update S-Live.' Write-Host 'Use the Desktop shortcut named "S-Live" to start the app.' } finally { Remove-Item $tempRoot -Recurse -Force -ErrorAction SilentlyContinue }