135 lines
3.6 KiB
PowerShell
135 lines
3.6 KiB
PowerShell
$ErrorActionPreference = "Stop"
|
|
|
|
$toolRoot = Split-Path -Parent $PSCommandPath
|
|
$projectRoot = (Resolve-Path (Join-Path $toolRoot "..\..")).Path
|
|
$serverPath = Join-Path $toolRoot "server.py"
|
|
$reviewRoot = Join-Path $projectRoot "epub_review_sessions"
|
|
$venvRoot = Join-Path $toolRoot ".venv"
|
|
$venvPython = Join-Path $venvRoot "Scripts\python.exe"
|
|
$requirements = Join-Path $toolRoot "requirements.txt"
|
|
$versionFile = Join-Path $toolRoot "version.py"
|
|
$defaultPort = 5177
|
|
|
|
$env:PYTHONIOENCODING = "utf-8"
|
|
|
|
function Get-PythonCommand {
|
|
$pyLauncher = Get-Command py -ErrorAction SilentlyContinue
|
|
if ($pyLauncher) {
|
|
return [pscustomobject]@{ Exe = $pyLauncher.Source; Args = @("-3") }
|
|
}
|
|
|
|
$python = Get-Command python -ErrorAction SilentlyContinue
|
|
if ($python) {
|
|
return [pscustomobject]@{ Exe = $python.Source; Args = @() }
|
|
}
|
|
|
|
throw "Python was not found. Install Python 3, then run this launcher again."
|
|
}
|
|
|
|
function Invoke-PythonCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Exe,
|
|
[string[]]$Args = @()
|
|
)
|
|
|
|
& $Exe @Args
|
|
return $LASTEXITCODE
|
|
}
|
|
|
|
function Test-FlaskInstalled {
|
|
& $venvPython -c "import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('flask') else 1)" 2>$null
|
|
return ($LASTEXITCODE -eq 0)
|
|
}
|
|
|
|
function Get-ExpectedVersion {
|
|
if (!(Test-Path $versionFile)) {
|
|
return ""
|
|
}
|
|
$content = Get-Content -Raw -Encoding UTF8 $versionFile
|
|
$match = [regex]::Match($content, 'version\s*=\s*"([^"]+)"')
|
|
if ($match.Success) {
|
|
return $match.Groups[1].Value
|
|
}
|
|
return ""
|
|
}
|
|
|
|
function Test-LocalPortOpen {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[int]$Port
|
|
)
|
|
|
|
$client = New-Object System.Net.Sockets.TcpClient
|
|
try {
|
|
$async = $client.BeginConnect("127.0.0.1", $Port, $null, $null)
|
|
if (!$async.AsyncWaitHandle.WaitOne(100, $false)) {
|
|
return $false
|
|
}
|
|
$client.EndConnect($async)
|
|
return $true
|
|
} catch {
|
|
return $false
|
|
} finally {
|
|
$client.Close()
|
|
}
|
|
}
|
|
|
|
function Get-RunningEditorUrl {
|
|
$expectedVersion = Get-ExpectedVersion
|
|
for ($port = $defaultPort; $port -lt ($defaultPort + 100); $port++) {
|
|
if (!(Test-LocalPortOpen -Port $port)) {
|
|
continue
|
|
}
|
|
$url = "http://127.0.0.1:$port"
|
|
try {
|
|
$versionPayload = Invoke-RestMethod -Uri "$url/api/version" -TimeoutSec 1 -ErrorAction Stop
|
|
if (!$expectedVersion -or $versionPayload.version -eq $expectedVersion) {
|
|
return "http://localhost:$port"
|
|
}
|
|
} catch {
|
|
continue
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
$runningUrl = Get-RunningEditorUrl
|
|
if ($runningUrl) {
|
|
Write-Host "EPUB review editor is already running."
|
|
Start-Process $runningUrl
|
|
Write-Host $runningUrl
|
|
Write-Host "review root: $reviewRoot"
|
|
exit 0
|
|
}
|
|
|
|
if (!(Test-Path $venvPython)) {
|
|
Write-Host "Creating local Python environment..."
|
|
$python = Get-PythonCommand
|
|
$venvArgs = @()
|
|
if ($python.Args) {
|
|
$venvArgs += $python.Args
|
|
}
|
|
$venvArgs += @("-m", "venv", $venvRoot)
|
|
if ((Invoke-PythonCommand -Exe $python.Exe -Args $venvArgs) -ne 0) {
|
|
throw "Failed to create local Python environment."
|
|
}
|
|
}
|
|
|
|
if (!(Test-FlaskInstalled)) {
|
|
Write-Host "Installing review editor dependencies..."
|
|
& $venvPython -m pip install -r $requirements
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to install dependencies from requirements.txt."
|
|
}
|
|
if (!(Test-FlaskInstalled)) {
|
|
throw "Dependencies were installed, but Flask is still unavailable in the local Python environment."
|
|
}
|
|
}
|
|
|
|
Write-Host "Starting EPUB review editor..."
|
|
& $venvPython $serverPath --review-root $reviewRoot --daemon
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to start EPUB review editor."
|
|
}
|