diff --git a/BUILD.md b/BUILD.md index 53bacb6..6a8008d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -58,9 +58,10 @@ py -3.12 -c "from app.tts import ENGINE_CATALOG; v=ENGINE_CATALOG['chatterbox'][ ``` After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in -a dedicated maximized window without a visible console, the desktop shortcut -uses `parroty.ico`, all eight voices appear, previews work, and `stop.bat` shuts -down port 5000. +a dedicated maximized window without a visible console, and closing that app +window with X stops the Flask listener and releases port 5000. Also verify the +desktop shortcut uses `parroty.ico`, all eight voices appear, previews work, and +`stop.bat` still shuts down port 5000 as a fallback. Before publishing, review `README.md`, `Quick Start Readme.txt`, `BUILD.md`, `RELEASE_NOTES.md`, and the voice attribution. No version bump or GitHub Release diff --git a/Quick Start Readme.txt b/Quick Start Readme.txt index d4a98d3..07cc6d6 100644 --- a/Quick Start Readme.txt +++ b/Quick Start Readme.txt @@ -16,7 +16,8 @@ launch_parroty.pyw, and the app folder: 5. Save with the exact .bat filename shown. The eight bundled Chatterbox voices already live under app\assets\voices. -No API key or separate voice download is required. +No API key or separate voice download is required. Closing the dedicated +Parroty Chrome/Edge app window with X automatically stops the hidden backend. ======================================================================== FILE: install_all.bat @@ -140,7 +141,8 @@ exit /b 0 1. Run install_all.bat once. 2. Start Parroty with run.bat or the desktop shortcut. -3. Use stop.bat to stop the hidden backend. -4. Use run_debug.bat only for troubleshooting. +3. Close the Parroty app window with X to stop the hidden backend automatically. +4. Use stop.bat only if the window is already gone or a forced stop is needed. +5. Use run_debug.bat only for troubleshooting. Do not commit the generated BAT files. They are local/package conveniences. diff --git a/README.md b/README.md index 32cfe9c..1a308fd 100644 --- a/README.md +++ b/README.md @@ -418,8 +418,11 @@ installed). Press **Ctrl+C** to stop. `Quick Start Readme.txt`, or use a packaged Windows ZIP that already includes them. `run.bat` starts the backend windowlessly through `pythonw.exe`, opens Parroty in a dedicated maximized Chrome/Edge app window, and silently creates or -refreshes the desktop shortcut. Output goes to `parroty.log`; use `stop.bat` to -shut down the hidden server. These `.bat` files are deliberately ignored by Git. +refreshes the desktop shortcut. Closing that Parroty app window with **X** now +automatically stops the hidden Flask backend, the same result as running +`stop.bat`. Output goes to `parroty.log`; `stop.bat` remains available as a +fallback if the window is already gone or the backend needs to be forced closed. +These `.bat` files are deliberately ignored by Git. Starting again later needs no reinstall — setup is one-time. Open a terminal, `cd` into the Parroty folder, activate the venv, and run the server: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 71d0c84..f25bc6c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,8 @@ No version number is assigned to this update. - Added the bottom-left CPU/GPU/VRAM system monitor. - Added hidden background launching with persistent `parroty.log` diagnostics. - Added a dedicated maximized Chrome/Edge app window using its own browser profile. +- Closing the dedicated Parroty app window now automatically stops the hidden + Flask backend, matching `stop.bat` behavior without affecting normal browser windows. - Added desktop-shortcut creation using the Parroty icon. - Corrected hidden-launch template/static resolution. - Kept narration workers GPU-enabled when the app window is hidden or inactive. diff --git a/parroty_window.ps1 b/parroty_window.ps1 index 21c6d00..382f5de 100644 --- a/parroty_window.ps1 +++ b/parroty_window.ps1 @@ -1,6 +1,7 @@ param( [string]$Url = "http://127.0.0.1:5000", - [string]$Root = $PSScriptRoot + [string]$Root = $PSScriptRoot, + [int]$Port = 5000 ) $ErrorActionPreference = "SilentlyContinue" @@ -21,6 +22,52 @@ function Find-Browser { return $null } +function Get-ParrotyBackendPids { + param([int]$ListenerPort) + + # Capture the exact process already listening for Parroty before opening the + # browser. When the app window closes we stop only these recorded PIDs, never + # an unrelated process that might later reuse the port. + $ids = [System.Collections.Generic.HashSet[int]]::new() + + try { + Get-NetTCPConnection -LocalPort $ListenerPort -State Listen -ErrorAction Stop | + ForEach-Object { + if ($_.OwningProcess -gt 0) { + [void]$ids.Add([int]$_.OwningProcess) + } + } + } catch {} + + # Fallback for systems where Get-NetTCPConnection is unavailable or restricted. + if ($ids.Count -eq 0) { + try { + $escapedPort = [regex]::Escape([string]$ListenerPort) + $pattern = "^\s*TCP\s+\S+:$escapedPort\s+\S+\s+LISTENING\s+(\d+)\s*$" + foreach ($line in (& netstat.exe -ano -p tcp 2>$null)) { + if ($line -match $pattern) { + [void]$ids.Add([int]$Matches[1]) + } + } + } catch {} + } + + return @($ids) +} + +function Stop-ParrotyBackend { + param([int[]]$ProcessIds) + + foreach ($backendProcessId in $ProcessIds) { + if ($backendProcessId -le 0 -or $backendProcessId -eq $PID) { continue } + try { + Get-Process -Id $backendProcessId -ErrorAction Stop | Out-Null + Stop-Process -Id $backendProcessId -Force -ErrorAction Stop + try { Wait-Process -Id $backendProcessId -Timeout 5 -ErrorAction SilentlyContinue } catch {} + } catch {} + } +} + function Maximize-ParrotyWindow { param( [System.Diagnostics.Process]$InitialProcess, @@ -92,10 +139,17 @@ public static class ParrotyNativeWindow { $browser = Find-Browser if (-not $browser) { + # A normal browser tab cannot be reliably monitored for its close event, so + # retain the browser fallback without automatic backend shutdown. Start-Process $Url exit 0 } +# Record the currently healthy Parroty listener before starting the app window. +# This mirrors stop.bat when the window later closes, while avoiding broad +# process-name matching or interference with normal browser windows. +$backendPids = @(Get-ParrotyBackendPids -ListenerPort $Port) + # A dedicated Chromium profile prevents an existing normal browser session from # restoring Parroty to a remembered windowed size. This is the same launcher # pattern used by Stemmy. @@ -122,5 +176,8 @@ if (-not $process) { $windowProcess = Maximize-ParrotyWindow -InitialProcess $process -BrowserPath $browser -LaunchTime $launchTime Set-Content -LiteralPath (Join-Path $Root ".parroty.browser.pid") -Value $windowProcess.Id +# Wait for the dedicated Parroty app window—not the user's normal browser—to be +# closed with X. Closing it is treated the same as running stop.bat. try { $windowProcess.WaitForExit() } catch {} Remove-Item -LiteralPath (Join-Path $Root ".parroty.browser.pid") -Force -ErrorAction SilentlyContinue +Stop-ParrotyBackend -ProcessIds $backendPids