From 703895dc2090953063c3f2b82159166e99ebb2ba Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:37:20 -0400 Subject: [PATCH 01/12] Stop Parroty backend when app window closes --- parroty_window.ps1 | 59 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) 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 From 1f286fd5abfeeb1384fec36c2dcbe4604a9f5c5c Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:38:38 -0400 Subject: [PATCH 02/12] Add Windows close-to-stop regression test --- .../workflows/test-window-close-shutdown.yml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/test-window-close-shutdown.yml diff --git a/.github/workflows/test-window-close-shutdown.yml b/.github/workflows/test-window-close-shutdown.yml new file mode 100644 index 0000000..7712b33 --- /dev/null +++ b/.github/workflows/test-window-close-shutdown.yml @@ -0,0 +1,98 @@ +name: Window close stops backend + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + windows-close-shutdown: + runs-on: windows-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Parse launcher scripts + shell: pwsh + run: | + python -m py_compile launch_parroty.pyw + $tokens = $null + $errors = $null + [void][System.Management.Automation.Language.Parser]::ParseFile( + (Resolve-Path "parroty_window.ps1"), + [ref]$tokens, + [ref]$errors + ) + if ($errors.Count -gt 0) { + $errors | Format-List | Out-String | Write-Error + exit 1 + } + + - name: Verify recorded listener is stopped + shell: pwsh + run: | + $listener = Start-Process python -ArgumentList @( + "-m", "http.server", "5000", "--bind", "127.0.0.1" + ) -PassThru -WindowStyle Hidden + + try { + $ready = $false + for ($i = 0; $i -lt 40; $i++) { + Start-Sleep -Milliseconds 250 + try { + $client = [System.Net.Sockets.TcpClient]::new("127.0.0.1", 5000) + $client.Dispose() + $ready = $true + break + } catch {} + } + if (-not $ready) { throw "Dummy backend did not start on port 5000" } + + $tokens = $null + $errors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile( + (Resolve-Path "parroty_window.ps1"), + [ref]$tokens, + [ref]$errors + ) + if ($errors.Count -gt 0) { throw "PowerShell parser errors found" } + + $functions = $ast.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.FunctionDefinitionAst] + }, $true) + foreach ($function in $functions) { + Invoke-Expression $function.Extent.Text + } + + $captured = @(Get-ParrotyBackendPids -ListenerPort 5000) + if ($captured -notcontains $listener.Id) { + throw "Listener PID $($listener.Id) was not captured: $($captured -join ', ')" + } + + Stop-ParrotyBackend -ProcessIds $captured + Start-Sleep -Milliseconds 500 + if (Get-Process -Id $listener.Id -ErrorAction SilentlyContinue) { + throw "Recorded backend PID was still running after shutdown" + } + } + finally { + Stop-Process -Id $listener.Id -Force -ErrorAction SilentlyContinue + } + + - name: Enforce BAT exclusion + shell: pwsh + run: | + $trackedBat = @(git ls-files "*.bat") + if ($trackedBat.Count -gt 0) { + throw "Tracked BAT files found: $($trackedBat -join ', ')" + } + $ignore = Get-Content .gitignore -Raw + if ($ignore -notmatch '(?m)^\*\.bat\s*$') { + throw ".gitignore does not block *.bat" + } From 3d93f976f2d93a24ab61c411e8f3f337c07a773e Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:40:58 -0400 Subject: [PATCH 03/12] Add temporary window-close docs updater --- tools/apply_window_close_docs.py | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tools/apply_window_close_docs.py diff --git a/tools/apply_window_close_docs.py b/tools/apply_window_close_docs.py new file mode 100644 index 0000000..2b41162 --- /dev/null +++ b/tools/apply_window_close_docs.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def replace_once(path: Path, old: str, new: str) -> None: + text = path.read_text(encoding="utf-8") + if new in text: + return + if old not in text: + raise RuntimeError(f"Expected text was not found in {path}") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def update_readme() -> None: + replace_once( + ROOT / "README.md", + "Parroty in a dedicated maximized Chrome/Edge app window, and silently creates or\n" + "refreshes the desktop shortcut. Output goes to `parroty.log`; use `stop.bat` to\n" + "shut down the hidden server. These `.bat` files are deliberately ignored by Git.", + "Parroty in a dedicated maximized Chrome/Edge app window, and silently creates or\n" + "refreshes the desktop shortcut. Closing that Parroty app window with **X** now\n" + "automatically stops the hidden Flask backend, the same result as running\n" + "`stop.bat`. Output goes to `parroty.log`; `stop.bat` remains available as a\n" + "fallback if the window is already gone or the backend needs to be forced closed.\n" + "These `.bat` files are deliberately ignored by Git.", + ) + + +def update_quick_start() -> None: + path = ROOT / "Quick Start Readme.txt" + replace_once( + path, + "- stop.bat Stop the hidden backend.", + "- stop.bat Fallback force-stop for the hidden backend.", + ) + replace_once( + path, + "dedicated maximized Chrome/Edge app window and keeps logs in parroty.log.", + "dedicated maximized Chrome/Edge app window and keeps logs in parroty.log.\n" + "Closing that Parroty window with X automatically stops the hidden backend.", + ) + replace_once( + path, + "3. Use stop.bat when you need to stop the hidden backend.\n" + "4. Use run_debug.bat only when you need a visible traceback or log.", + "3. Close the Parroty app window with X to stop the hidden backend automatically.\n" + "4. Use stop.bat only if the window is already gone or a forced stop is needed.\n" + "5. Use run_debug.bat only when you need a visible traceback or log.", + ) + + +def update_build() -> None: + path = ROOT / "BUILD.md" + replace_once( + path, + "- Parroty opens in its own maximized app window.\n" + "- The desktop shortcut uses `parroty.ico`.", + "- Parroty opens in its own maximized app window.\n" + "- Closing the app window with X stops the Flask listener and releases port 5000.\n" + "- The desktop shortcut uses `parroty.ico`.", + ) + replace_once( + path, + "- `stop.bat` shuts down the server on port 5000.", + "- `stop.bat` still shuts down the server on port 5000 as a fallback.", + ) + + +def update_release_notes() -> None: + replace_once( + ROOT / "RELEASE_NOTES.md", + "- Added a dedicated maximized Chrome/Edge app window using its own browser\n" + " profile, rather than reopening inside the user's normal browser session.\n", + "- Added a dedicated maximized Chrome/Edge app window using its own browser\n" + " profile, rather than reopening inside the user's normal browser session.\n" + "- Closing the dedicated Parroty app window now automatically stops the hidden\n" + " Flask backend, matching `stop.bat` behavior without affecting normal browser windows.\n", + ) + + +def main() -> None: + update_readme() + update_quick_start() + update_build() + update_release_notes() + print("Updated window-close shutdown documentation") + + +if __name__ == "__main__": + main() From d51df74663517720f6ecbd346adb76d3efd020c2 Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:41:13 -0400 Subject: [PATCH 04/12] Add temporary window-close docs workflow --- .github/workflows/apply-window-close-docs.yml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/apply-window-close-docs.yml diff --git a/.github/workflows/apply-window-close-docs.yml b/.github/workflows/apply-window-close-docs.yml new file mode 100644 index 0000000..9f8ed07 --- /dev/null +++ b/.github/workflows/apply-window-close-docs.yml @@ -0,0 +1,34 @@ +name: Apply window-close documentation + +on: + push: + branches: [stop-backend-on-window-close] + +permissions: + contents: write + +jobs: + update-docs: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + with: + ref: stop-backend-on-window-close + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Update documentation + run: python tools/apply_window_close_docs.py + - name: Commit documentation + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add README.md "Quick Start Readme.txt" BUILD.md RELEASE_NOTES.md + if git diff --cached --quiet; then + echo "Documentation already current." + else + git commit -m "Document close-window backend shutdown" + git push origin HEAD:stop-backend-on-window-close + fi From e65b1c2c99994bd34f1632ccb3bef88a820917de Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:43:40 -0400 Subject: [PATCH 05/12] Apply close-window docs after Windows test --- .../workflows/test-window-close-shutdown.yml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-window-close-shutdown.yml b/.github/workflows/test-window-close-shutdown.yml index 7712b33..a174d32 100644 --- a/.github/workflows/test-window-close-shutdown.yml +++ b/.github/workflows/test-window-close-shutdown.yml @@ -5,7 +5,7 @@ on: branches: [main] permissions: - contents: read + contents: write jobs: windows-close-shutdown: @@ -13,6 +13,9 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 + with: + ref: stop-backend-on-window-close + fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: "3.12" @@ -96,3 +99,19 @@ jobs: if ($ignore -notmatch '(?m)^\*\.bat\s*$') { throw ".gitignore does not block *.bat" } + + - name: Update documentation + run: python tools/apply_window_close_docs.py + + - name: Commit documentation + shell: pwsh + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add README.md "Quick Start Readme.txt" BUILD.md RELEASE_NOTES.md + if (git diff --cached --quiet) { + Write-Host "Documentation already current." + } else { + git commit -m "Document close-window backend shutdown" + git push origin HEAD:stop-backend-on-window-close + } From 716e48a201b12845dd617ab83525ed7845c23320 Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:45:22 -0400 Subject: [PATCH 06/12] Fix window-close docs updater for current files --- tools/apply_window_close_docs.py | 49 ++++++++++++++------------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/tools/apply_window_close_docs.py b/tools/apply_window_close_docs.py index 2b41162..552bc04 100644 --- a/tools/apply_window_close_docs.py +++ b/tools/apply_window_close_docs.py @@ -33,51 +33,44 @@ def update_quick_start() -> None: path = ROOT / "Quick Start Readme.txt" replace_once( path, - "- stop.bat Stop the hidden backend.", - "- stop.bat Fallback force-stop for the hidden backend.", + "The eight bundled Chatterbox voices already live under app\\assets\\voices.\n" + "No API key or separate voice download is required.", + "The eight bundled Chatterbox voices already live under app\\assets\\voices.\n" + "No API key or separate voice download is required. Closing the dedicated\n" + "Parroty Chrome/Edge app window with X automatically stops the hidden backend.", ) replace_once( path, - "dedicated maximized Chrome/Edge app window and keeps logs in parroty.log.", - "dedicated maximized Chrome/Edge app window and keeps logs in parroty.log.\n" - "Closing that Parroty window with X automatically stops the hidden backend.", - ) - replace_once( - path, - "3. Use stop.bat when you need to stop the hidden backend.\n" - "4. Use run_debug.bat only when you need a visible traceback or log.", + "3. Use stop.bat to stop the hidden backend.\n" + "4. Use run_debug.bat only for troubleshooting.", "3. Close the Parroty app window with X to stop the hidden backend automatically.\n" "4. Use stop.bat only if the window is already gone or a forced stop is needed.\n" - "5. Use run_debug.bat only when you need a visible traceback or log.", + "5. Use run_debug.bat only for troubleshooting.", ) def update_build() -> None: - path = ROOT / "BUILD.md" replace_once( - path, - "- Parroty opens in its own maximized app window.\n" - "- The desktop shortcut uses `parroty.ico`.", - "- Parroty opens in its own maximized app window.\n" - "- Closing the app window with X stops the Flask listener and releases port 5000.\n" - "- The desktop shortcut uses `parroty.ico`.", - ) - replace_once( - path, - "- `stop.bat` shuts down the server on port 5000.", - "- `stop.bat` still shuts down the server on port 5000 as a fallback.", + ROOT / "BUILD.md", + "After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in\n" + "a dedicated maximized window without a visible console, the desktop shortcut\n" + "uses `parroty.ico`, all eight voices appear, previews work, and `stop.bat` shuts\n" + "down port 5000.", + "After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in\n" + "a dedicated maximized window without a visible console, and closing that app\n" + "window with X stops the Flask listener and releases port 5000. Also verify the\n" + "desktop shortcut uses `parroty.ico`, all eight voices appear, previews work, and\n" + "`stop.bat` still shuts down port 5000 as a fallback.", ) def update_release_notes() -> None: replace_once( ROOT / "RELEASE_NOTES.md", - "- Added a dedicated maximized Chrome/Edge app window using its own browser\n" - " profile, rather than reopening inside the user's normal browser session.\n", - "- Added a dedicated maximized Chrome/Edge app window using its own browser\n" - " profile, rather than reopening inside the user's normal browser session.\n" + "- Added a dedicated maximized Chrome/Edge app window using its own browser profile.", + "- Added a dedicated maximized Chrome/Edge app window using its own browser profile.\n" "- Closing the dedicated Parroty app window now automatically stops the hidden\n" - " Flask backend, matching `stop.bat` behavior without affecting normal browser windows.\n", + " Flask backend, matching `stop.bat` behavior without affecting normal browser windows.", ) From 8f68462febc0e826cc4a4e0babb148f27826ab72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 02:45:30 +0000 Subject: [PATCH 07/12] Document close-window backend shutdown --- BUILD.md | 7 +- Quick Start Readme.txt | 294 +++++++++++++++++++++-------------------- README.md | 7 +- RELEASE_NOTES.md | 2 + 4 files changed, 159 insertions(+), 151 deletions(-) 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..f658f76 100644 --- a/Quick Start Readme.txt +++ b/Quick Start Readme.txt @@ -1,146 +1,148 @@ -======================================================================== - PARROTY - WINDOWS QUICK START -======================================================================== - -The Parroty source repository intentionally does NOT track .bat files. -The repository .gitignore blocks *.bat everywhere. Downloadable Windows ZIPs -may include these wrappers for convenience, but they are never source files. - -Create any BAT file below inside the Parroty folder, beside requirements.txt, -launch_parroty.pyw, and the app folder: - -1. Open Notepad. -2. Copy the complete block for the file. -3. File > Save As. -4. Set "Save as type" to "All Files (*.*)". -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. - -======================================================================== - FILE: install_all.bat -======================================================================== - -@echo off -setlocal EnableExtensions EnableDelayedExpansion -cd /d "%~dp0" -title Parroty - Install All - -set "HAVE_WINGET=" -where winget >nul 2>&1 && set "HAVE_WINGET=1" - -set "PY=" -for %%V in (3.12 3.11 3.10) do ( - if not defined PY py -%%V -c "import sys" >nul 2>&1 && set "PY=py -%%V" -) -if not defined PY python -c "import sys; raise SystemExit(0 if (3,10) <= sys.version_info[:2] <= (3,12) else 1)" >nul 2>&1 && set "PY=python" - -if not defined PY if defined HAVE_WINGET ( - winget install -e --id Python.Python.3.12 --scope user --accept-package-agreements --accept-source-agreements - py -3.12 -c "import sys" >nul 2>&1 && set "PY=py -3.12" -) -if not defined PY ( - echo Python 3.10-3.12 is required. Install Python 3.12 and rerun this file. - pause - exit /b 1 -) - -where ffmpeg >nul 2>&1 -if errorlevel 1 if defined HAVE_WINGET winget install -e --id Gyan.FFmpeg --accept-package-agreements --accept-source-agreements - -if not exist "venv\Scripts\python.exe" %PY% -m venv venv -if errorlevel 1 ( - echo Could not create the virtual environment. - pause - exit /b 1 -) - -set "VPY=venv\Scripts\python.exe" -"%VPY%" -m pip install --upgrade pip setuptools wheel -"%VPY%" -m pip install -r requirements.txt -if errorlevel 1 ( - echo Core requirements failed to install. - pause - exit /b 1 -) - -"%VPY%" -m pip install --upgrade --force-reinstall --no-cache-dir torch==2.11.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 -if errorlevel 1 ( - echo CUDA PyTorch failed to install. Cloud voices may still work. -) - -"%VPY%" "tools\install_chatterbox_compat.py" -if errorlevel 1 echo Chatterbox installation failed. Cloud voices may still work. - -"%VPY%" -c "import torch; print('PyTorch:',torch.__version__); print('CUDA:',torch.version.cuda); print('GPU available:',torch.cuda.is_available()); print('GPU:',torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU only')" - -if exist "create_desktop_shortcut.vbs" wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet - -echo. -echo Installation complete. Start Parroty with run.bat or the desktop shortcut. -pause - -======================================================================== - FILE: run.bat -======================================================================== - -@echo off -setlocal -cd /d "%~dp0" -wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet -start "" wscript.exe //nologo "%~dp0launch_parroty.vbs" -exit /b 0 - -======================================================================== - FILE: stop.bat -======================================================================== - -@echo off -setlocal enabledelayedexpansion -set "FOUND=0" -for /f "tokens=5" %%P in ('netstat -ano ^| findstr ":5000" ^| findstr LISTENING') do ( - taskkill /F /PID %%P >nul 2>&1 - set "FOUND=1" -) -if "!FOUND!"=="0" (echo Parroty was not running.) else (echo Parroty stopped.) -timeout /t 2 >nul -exit /b 0 - -======================================================================== - FILE: run_debug.bat -======================================================================== - -@echo off -setlocal -cd /d "%~dp0" -title Parroty - Debug Launcher -set "PY=venv\Scripts\python.exe" -if not exist "%PY%" set "PY=.venv\Scripts\python.exe" -if not exist "%PY%" set "PY=python.exe" -set "PARROTY_HIDDEN=" -set "PARROTY_NO_BROWSER=" -"%PY%" -m app.server -echo. -echo Parroty stopped. Review the error above or parroty.log. -pause - -======================================================================== - FILE: Create Desktop Shortcut.bat -======================================================================== - -@echo off -cd /d "%~dp0" -wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" -exit /b 0 - -======================================================================== - FINISHED -======================================================================== - -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. - -Do not commit the generated BAT files. They are local/package conveniences. +======================================================================== + PARROTY - WINDOWS QUICK START +======================================================================== + +The Parroty source repository intentionally does NOT track .bat files. +The repository .gitignore blocks *.bat everywhere. Downloadable Windows ZIPs +may include these wrappers for convenience, but they are never source files. + +Create any BAT file below inside the Parroty folder, beside requirements.txt, +launch_parroty.pyw, and the app folder: + +1. Open Notepad. +2. Copy the complete block for the file. +3. File > Save As. +4. Set "Save as type" to "All Files (*.*)". +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. Closing the dedicated +Parroty Chrome/Edge app window with X automatically stops the hidden backend. + +======================================================================== + FILE: install_all.bat +======================================================================== + +@echo off +setlocal EnableExtensions EnableDelayedExpansion +cd /d "%~dp0" +title Parroty - Install All + +set "HAVE_WINGET=" +where winget >nul 2>&1 && set "HAVE_WINGET=1" + +set "PY=" +for %%V in (3.12 3.11 3.10) do ( + if not defined PY py -%%V -c "import sys" >nul 2>&1 && set "PY=py -%%V" +) +if not defined PY python -c "import sys; raise SystemExit(0 if (3,10) <= sys.version_info[:2] <= (3,12) else 1)" >nul 2>&1 && set "PY=python" + +if not defined PY if defined HAVE_WINGET ( + winget install -e --id Python.Python.3.12 --scope user --accept-package-agreements --accept-source-agreements + py -3.12 -c "import sys" >nul 2>&1 && set "PY=py -3.12" +) +if not defined PY ( + echo Python 3.10-3.12 is required. Install Python 3.12 and rerun this file. + pause + exit /b 1 +) + +where ffmpeg >nul 2>&1 +if errorlevel 1 if defined HAVE_WINGET winget install -e --id Gyan.FFmpeg --accept-package-agreements --accept-source-agreements + +if not exist "venv\Scripts\python.exe" %PY% -m venv venv +if errorlevel 1 ( + echo Could not create the virtual environment. + pause + exit /b 1 +) + +set "VPY=venv\Scripts\python.exe" +"%VPY%" -m pip install --upgrade pip setuptools wheel +"%VPY%" -m pip install -r requirements.txt +if errorlevel 1 ( + echo Core requirements failed to install. + pause + exit /b 1 +) + +"%VPY%" -m pip install --upgrade --force-reinstall --no-cache-dir torch==2.11.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 +if errorlevel 1 ( + echo CUDA PyTorch failed to install. Cloud voices may still work. +) + +"%VPY%" "tools\install_chatterbox_compat.py" +if errorlevel 1 echo Chatterbox installation failed. Cloud voices may still work. + +"%VPY%" -c "import torch; print('PyTorch:',torch.__version__); print('CUDA:',torch.version.cuda); print('GPU available:',torch.cuda.is_available()); print('GPU:',torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU only')" + +if exist "create_desktop_shortcut.vbs" wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet + +echo. +echo Installation complete. Start Parroty with run.bat or the desktop shortcut. +pause + +======================================================================== + FILE: run.bat +======================================================================== + +@echo off +setlocal +cd /d "%~dp0" +wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet +start "" wscript.exe //nologo "%~dp0launch_parroty.vbs" +exit /b 0 + +======================================================================== + FILE: stop.bat +======================================================================== + +@echo off +setlocal enabledelayedexpansion +set "FOUND=0" +for /f "tokens=5" %%P in ('netstat -ano ^| findstr ":5000" ^| findstr LISTENING') do ( + taskkill /F /PID %%P >nul 2>&1 + set "FOUND=1" +) +if "!FOUND!"=="0" (echo Parroty was not running.) else (echo Parroty stopped.) +timeout /t 2 >nul +exit /b 0 + +======================================================================== + FILE: run_debug.bat +======================================================================== + +@echo off +setlocal +cd /d "%~dp0" +title Parroty - Debug Launcher +set "PY=venv\Scripts\python.exe" +if not exist "%PY%" set "PY=.venv\Scripts\python.exe" +if not exist "%PY%" set "PY=python.exe" +set "PARROTY_HIDDEN=" +set "PARROTY_NO_BROWSER=" +"%PY%" -m app.server +echo. +echo Parroty stopped. Review the error above or parroty.log. +pause + +======================================================================== + FILE: Create Desktop Shortcut.bat +======================================================================== + +@echo off +cd /d "%~dp0" +wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" +exit /b 0 + +======================================================================== + FINISHED +======================================================================== + +1. Run install_all.bat once. +2. Start Parroty with run.bat or the desktop shortcut. +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. From f1113f2b2e544ffaacac735590fddbb6508357af Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:46:53 -0400 Subject: [PATCH 08/12] Preserve Quick Start Windows line endings --- tools/apply_window_close_docs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/apply_window_close_docs.py b/tools/apply_window_close_docs.py index 552bc04..dcfdc88 100644 --- a/tools/apply_window_close_docs.py +++ b/tools/apply_window_close_docs.py @@ -48,6 +48,12 @@ def update_quick_start() -> None: "5. Use run_debug.bat only for troubleshooting.", ) + # Keep this Windows copy/paste guide in its original CRLF format so changing + # two instructions does not appear as a whole-file rewrite in Git. + normalized = path.read_text(encoding="utf-8").replace("\r\n", "\n") + with path.open("w", encoding="utf-8", newline="") as file: + file.write(normalized.replace("\n", "\r\n")) + def update_build() -> None: replace_once( From 3e3d905855b342bfd26feb0d1314c01ef3e427d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 02:47:01 +0000 Subject: [PATCH 09/12] Document close-window backend shutdown --- Quick Start Readme.txt | 296 ++++++++++++++++++++--------------------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/Quick Start Readme.txt b/Quick Start Readme.txt index f658f76..07cc6d6 100644 --- a/Quick Start Readme.txt +++ b/Quick Start Readme.txt @@ -1,148 +1,148 @@ -======================================================================== - PARROTY - WINDOWS QUICK START -======================================================================== - -The Parroty source repository intentionally does NOT track .bat files. -The repository .gitignore blocks *.bat everywhere. Downloadable Windows ZIPs -may include these wrappers for convenience, but they are never source files. - -Create any BAT file below inside the Parroty folder, beside requirements.txt, -launch_parroty.pyw, and the app folder: - -1. Open Notepad. -2. Copy the complete block for the file. -3. File > Save As. -4. Set "Save as type" to "All Files (*.*)". -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. Closing the dedicated -Parroty Chrome/Edge app window with X automatically stops the hidden backend. - -======================================================================== - FILE: install_all.bat -======================================================================== - -@echo off -setlocal EnableExtensions EnableDelayedExpansion -cd /d "%~dp0" -title Parroty - Install All - -set "HAVE_WINGET=" -where winget >nul 2>&1 && set "HAVE_WINGET=1" - -set "PY=" -for %%V in (3.12 3.11 3.10) do ( - if not defined PY py -%%V -c "import sys" >nul 2>&1 && set "PY=py -%%V" -) -if not defined PY python -c "import sys; raise SystemExit(0 if (3,10) <= sys.version_info[:2] <= (3,12) else 1)" >nul 2>&1 && set "PY=python" - -if not defined PY if defined HAVE_WINGET ( - winget install -e --id Python.Python.3.12 --scope user --accept-package-agreements --accept-source-agreements - py -3.12 -c "import sys" >nul 2>&1 && set "PY=py -3.12" -) -if not defined PY ( - echo Python 3.10-3.12 is required. Install Python 3.12 and rerun this file. - pause - exit /b 1 -) - -where ffmpeg >nul 2>&1 -if errorlevel 1 if defined HAVE_WINGET winget install -e --id Gyan.FFmpeg --accept-package-agreements --accept-source-agreements - -if not exist "venv\Scripts\python.exe" %PY% -m venv venv -if errorlevel 1 ( - echo Could not create the virtual environment. - pause - exit /b 1 -) - -set "VPY=venv\Scripts\python.exe" -"%VPY%" -m pip install --upgrade pip setuptools wheel -"%VPY%" -m pip install -r requirements.txt -if errorlevel 1 ( - echo Core requirements failed to install. - pause - exit /b 1 -) - -"%VPY%" -m pip install --upgrade --force-reinstall --no-cache-dir torch==2.11.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 -if errorlevel 1 ( - echo CUDA PyTorch failed to install. Cloud voices may still work. -) - -"%VPY%" "tools\install_chatterbox_compat.py" -if errorlevel 1 echo Chatterbox installation failed. Cloud voices may still work. - -"%VPY%" -c "import torch; print('PyTorch:',torch.__version__); print('CUDA:',torch.version.cuda); print('GPU available:',torch.cuda.is_available()); print('GPU:',torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU only')" - -if exist "create_desktop_shortcut.vbs" wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet - -echo. -echo Installation complete. Start Parroty with run.bat or the desktop shortcut. -pause - -======================================================================== - FILE: run.bat -======================================================================== - -@echo off -setlocal -cd /d "%~dp0" -wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet -start "" wscript.exe //nologo "%~dp0launch_parroty.vbs" -exit /b 0 - -======================================================================== - FILE: stop.bat -======================================================================== - -@echo off -setlocal enabledelayedexpansion -set "FOUND=0" -for /f "tokens=5" %%P in ('netstat -ano ^| findstr ":5000" ^| findstr LISTENING') do ( - taskkill /F /PID %%P >nul 2>&1 - set "FOUND=1" -) -if "!FOUND!"=="0" (echo Parroty was not running.) else (echo Parroty stopped.) -timeout /t 2 >nul -exit /b 0 - -======================================================================== - FILE: run_debug.bat -======================================================================== - -@echo off -setlocal -cd /d "%~dp0" -title Parroty - Debug Launcher -set "PY=venv\Scripts\python.exe" -if not exist "%PY%" set "PY=.venv\Scripts\python.exe" -if not exist "%PY%" set "PY=python.exe" -set "PARROTY_HIDDEN=" -set "PARROTY_NO_BROWSER=" -"%PY%" -m app.server -echo. -echo Parroty stopped. Review the error above or parroty.log. -pause - -======================================================================== - FILE: Create Desktop Shortcut.bat -======================================================================== - -@echo off -cd /d "%~dp0" -wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" -exit /b 0 - -======================================================================== - FINISHED -======================================================================== - -1. Run install_all.bat once. -2. Start Parroty with run.bat or the desktop shortcut. -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. +======================================================================== + PARROTY - WINDOWS QUICK START +======================================================================== + +The Parroty source repository intentionally does NOT track .bat files. +The repository .gitignore blocks *.bat everywhere. Downloadable Windows ZIPs +may include these wrappers for convenience, but they are never source files. + +Create any BAT file below inside the Parroty folder, beside requirements.txt, +launch_parroty.pyw, and the app folder: + +1. Open Notepad. +2. Copy the complete block for the file. +3. File > Save As. +4. Set "Save as type" to "All Files (*.*)". +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. Closing the dedicated +Parroty Chrome/Edge app window with X automatically stops the hidden backend. + +======================================================================== + FILE: install_all.bat +======================================================================== + +@echo off +setlocal EnableExtensions EnableDelayedExpansion +cd /d "%~dp0" +title Parroty - Install All + +set "HAVE_WINGET=" +where winget >nul 2>&1 && set "HAVE_WINGET=1" + +set "PY=" +for %%V in (3.12 3.11 3.10) do ( + if not defined PY py -%%V -c "import sys" >nul 2>&1 && set "PY=py -%%V" +) +if not defined PY python -c "import sys; raise SystemExit(0 if (3,10) <= sys.version_info[:2] <= (3,12) else 1)" >nul 2>&1 && set "PY=python" + +if not defined PY if defined HAVE_WINGET ( + winget install -e --id Python.Python.3.12 --scope user --accept-package-agreements --accept-source-agreements + py -3.12 -c "import sys" >nul 2>&1 && set "PY=py -3.12" +) +if not defined PY ( + echo Python 3.10-3.12 is required. Install Python 3.12 and rerun this file. + pause + exit /b 1 +) + +where ffmpeg >nul 2>&1 +if errorlevel 1 if defined HAVE_WINGET winget install -e --id Gyan.FFmpeg --accept-package-agreements --accept-source-agreements + +if not exist "venv\Scripts\python.exe" %PY% -m venv venv +if errorlevel 1 ( + echo Could not create the virtual environment. + pause + exit /b 1 +) + +set "VPY=venv\Scripts\python.exe" +"%VPY%" -m pip install --upgrade pip setuptools wheel +"%VPY%" -m pip install -r requirements.txt +if errorlevel 1 ( + echo Core requirements failed to install. + pause + exit /b 1 +) + +"%VPY%" -m pip install --upgrade --force-reinstall --no-cache-dir torch==2.11.0 torchaudio==2.11.0 --index-url https://download.pytorch.org/whl/cu128 +if errorlevel 1 ( + echo CUDA PyTorch failed to install. Cloud voices may still work. +) + +"%VPY%" "tools\install_chatterbox_compat.py" +if errorlevel 1 echo Chatterbox installation failed. Cloud voices may still work. + +"%VPY%" -c "import torch; print('PyTorch:',torch.__version__); print('CUDA:',torch.version.cuda); print('GPU available:',torch.cuda.is_available()); print('GPU:',torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU only')" + +if exist "create_desktop_shortcut.vbs" wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet + +echo. +echo Installation complete. Start Parroty with run.bat or the desktop shortcut. +pause + +======================================================================== + FILE: run.bat +======================================================================== + +@echo off +setlocal +cd /d "%~dp0" +wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" /quiet +start "" wscript.exe //nologo "%~dp0launch_parroty.vbs" +exit /b 0 + +======================================================================== + FILE: stop.bat +======================================================================== + +@echo off +setlocal enabledelayedexpansion +set "FOUND=0" +for /f "tokens=5" %%P in ('netstat -ano ^| findstr ":5000" ^| findstr LISTENING') do ( + taskkill /F /PID %%P >nul 2>&1 + set "FOUND=1" +) +if "!FOUND!"=="0" (echo Parroty was not running.) else (echo Parroty stopped.) +timeout /t 2 >nul +exit /b 0 + +======================================================================== + FILE: run_debug.bat +======================================================================== + +@echo off +setlocal +cd /d "%~dp0" +title Parroty - Debug Launcher +set "PY=venv\Scripts\python.exe" +if not exist "%PY%" set "PY=.venv\Scripts\python.exe" +if not exist "%PY%" set "PY=python.exe" +set "PARROTY_HIDDEN=" +set "PARROTY_NO_BROWSER=" +"%PY%" -m app.server +echo. +echo Parroty stopped. Review the error above or parroty.log. +pause + +======================================================================== + FILE: Create Desktop Shortcut.bat +======================================================================== + +@echo off +cd /d "%~dp0" +wscript.exe //nologo "%~dp0create_desktop_shortcut.vbs" +exit /b 0 + +======================================================================== + FINISHED +======================================================================== + +1. Run install_all.bat once. +2. Start Parroty with run.bat or the desktop shortcut. +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. From 3bdf234417a4319685c634fc44ac1cc5e5801bd3 Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:48:11 -0400 Subject: [PATCH 10/12] Remove temporary documentation workflow --- .github/workflows/apply-window-close-docs.yml | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 .github/workflows/apply-window-close-docs.yml diff --git a/.github/workflows/apply-window-close-docs.yml b/.github/workflows/apply-window-close-docs.yml deleted file mode 100644 index 9f8ed07..0000000 --- a/.github/workflows/apply-window-close-docs.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Apply window-close documentation - -on: - push: - branches: [stop-backend-on-window-close] - -permissions: - contents: write - -jobs: - update-docs: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@v4 - with: - ref: stop-backend-on-window-close - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - name: Update documentation - run: python tools/apply_window_close_docs.py - - name: Commit documentation - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add README.md "Quick Start Readme.txt" BUILD.md RELEASE_NOTES.md - if git diff --cached --quiet; then - echo "Documentation already current." - else - git commit -m "Document close-window backend shutdown" - git push origin HEAD:stop-backend-on-window-close - fi From c1acc8585f6569a54545921bbefc2043322027b2 Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:48:19 -0400 Subject: [PATCH 11/12] Remove temporary window-close regression workflow --- .../workflows/test-window-close-shutdown.yml | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 .github/workflows/test-window-close-shutdown.yml diff --git a/.github/workflows/test-window-close-shutdown.yml b/.github/workflows/test-window-close-shutdown.yml deleted file mode 100644 index a174d32..0000000 --- a/.github/workflows/test-window-close-shutdown.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: Window close stops backend - -on: - pull_request: - branches: [main] - -permissions: - contents: write - -jobs: - windows-close-shutdown: - runs-on: windows-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - with: - ref: stop-backend-on-window-close - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Parse launcher scripts - shell: pwsh - run: | - python -m py_compile launch_parroty.pyw - $tokens = $null - $errors = $null - [void][System.Management.Automation.Language.Parser]::ParseFile( - (Resolve-Path "parroty_window.ps1"), - [ref]$tokens, - [ref]$errors - ) - if ($errors.Count -gt 0) { - $errors | Format-List | Out-String | Write-Error - exit 1 - } - - - name: Verify recorded listener is stopped - shell: pwsh - run: | - $listener = Start-Process python -ArgumentList @( - "-m", "http.server", "5000", "--bind", "127.0.0.1" - ) -PassThru -WindowStyle Hidden - - try { - $ready = $false - for ($i = 0; $i -lt 40; $i++) { - Start-Sleep -Milliseconds 250 - try { - $client = [System.Net.Sockets.TcpClient]::new("127.0.0.1", 5000) - $client.Dispose() - $ready = $true - break - } catch {} - } - if (-not $ready) { throw "Dummy backend did not start on port 5000" } - - $tokens = $null - $errors = $null - $ast = [System.Management.Automation.Language.Parser]::ParseFile( - (Resolve-Path "parroty_window.ps1"), - [ref]$tokens, - [ref]$errors - ) - if ($errors.Count -gt 0) { throw "PowerShell parser errors found" } - - $functions = $ast.FindAll({ - param($node) - $node -is [System.Management.Automation.Language.FunctionDefinitionAst] - }, $true) - foreach ($function in $functions) { - Invoke-Expression $function.Extent.Text - } - - $captured = @(Get-ParrotyBackendPids -ListenerPort 5000) - if ($captured -notcontains $listener.Id) { - throw "Listener PID $($listener.Id) was not captured: $($captured -join ', ')" - } - - Stop-ParrotyBackend -ProcessIds $captured - Start-Sleep -Milliseconds 500 - if (Get-Process -Id $listener.Id -ErrorAction SilentlyContinue) { - throw "Recorded backend PID was still running after shutdown" - } - } - finally { - Stop-Process -Id $listener.Id -Force -ErrorAction SilentlyContinue - } - - - name: Enforce BAT exclusion - shell: pwsh - run: | - $trackedBat = @(git ls-files "*.bat") - if ($trackedBat.Count -gt 0) { - throw "Tracked BAT files found: $($trackedBat -join ', ')" - } - $ignore = Get-Content .gitignore -Raw - if ($ignore -notmatch '(?m)^\*\.bat\s*$') { - throw ".gitignore does not block *.bat" - } - - - name: Update documentation - run: python tools/apply_window_close_docs.py - - - name: Commit documentation - shell: pwsh - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add README.md "Quick Start Readme.txt" BUILD.md RELEASE_NOTES.md - if (git diff --cached --quiet) { - Write-Host "Documentation already current." - } else { - git commit -m "Document close-window backend shutdown" - git push origin HEAD:stop-backend-on-window-close - } From 696b893b104edea5d985d4e5a4b3565a0c216d1e Mon Sep 17 00:00:00 2001 From: pgotta <168211683+pgotta@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:48:29 -0400 Subject: [PATCH 12/12] Remove temporary documentation updater --- tools/apply_window_close_docs.py | 92 -------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 tools/apply_window_close_docs.py diff --git a/tools/apply_window_close_docs.py b/tools/apply_window_close_docs.py deleted file mode 100644 index dcfdc88..0000000 --- a/tools/apply_window_close_docs.py +++ /dev/null @@ -1,92 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def replace_once(path: Path, old: str, new: str) -> None: - text = path.read_text(encoding="utf-8") - if new in text: - return - if old not in text: - raise RuntimeError(f"Expected text was not found in {path}") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def update_readme() -> None: - replace_once( - ROOT / "README.md", - "Parroty in a dedicated maximized Chrome/Edge app window, and silently creates or\n" - "refreshes the desktop shortcut. Output goes to `parroty.log`; use `stop.bat` to\n" - "shut down the hidden server. These `.bat` files are deliberately ignored by Git.", - "Parroty in a dedicated maximized Chrome/Edge app window, and silently creates or\n" - "refreshes the desktop shortcut. Closing that Parroty app window with **X** now\n" - "automatically stops the hidden Flask backend, the same result as running\n" - "`stop.bat`. Output goes to `parroty.log`; `stop.bat` remains available as a\n" - "fallback if the window is already gone or the backend needs to be forced closed.\n" - "These `.bat` files are deliberately ignored by Git.", - ) - - -def update_quick_start() -> None: - path = ROOT / "Quick Start Readme.txt" - replace_once( - path, - "The eight bundled Chatterbox voices already live under app\\assets\\voices.\n" - "No API key or separate voice download is required.", - "The eight bundled Chatterbox voices already live under app\\assets\\voices.\n" - "No API key or separate voice download is required. Closing the dedicated\n" - "Parroty Chrome/Edge app window with X automatically stops the hidden backend.", - ) - replace_once( - path, - "3. Use stop.bat to stop the hidden backend.\n" - "4. Use run_debug.bat only for troubleshooting.", - "3. Close the Parroty app window with X to stop the hidden backend automatically.\n" - "4. Use stop.bat only if the window is already gone or a forced stop is needed.\n" - "5. Use run_debug.bat only for troubleshooting.", - ) - - # Keep this Windows copy/paste guide in its original CRLF format so changing - # two instructions does not appear as a whole-file rewrite in Git. - normalized = path.read_text(encoding="utf-8").replace("\r\n", "\n") - with path.open("w", encoding="utf-8", newline="") as file: - file.write(normalized.replace("\n", "\r\n")) - - -def update_build() -> None: - replace_once( - ROOT / "BUILD.md", - "After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in\n" - "a dedicated maximized window without a visible console, the desktop shortcut\n" - "uses `parroty.ico`, all eight voices appear, previews work, and `stop.bat` shuts\n" - "down port 5000.", - "After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in\n" - "a dedicated maximized window without a visible console, and closing that app\n" - "window with X stops the Flask listener and releases port 5000. Also verify the\n" - "desktop shortcut uses `parroty.ico`, all eight voices appear, previews work, and\n" - "`stop.bat` still shuts down port 5000 as a fallback.", - ) - - -def update_release_notes() -> None: - replace_once( - ROOT / "RELEASE_NOTES.md", - "- Added a dedicated maximized Chrome/Edge app window using its own browser profile.", - "- Added a dedicated maximized Chrome/Edge app window using its own browser profile.\n" - "- Closing the dedicated Parroty app window now automatically stops the hidden\n" - " Flask backend, matching `stop.bat` behavior without affecting normal browser windows.", - ) - - -def main() -> None: - update_readme() - update_quick_start() - update_build() - update_release_notes() - print("Updated window-close shutdown documentation") - - -if __name__ == "__main__": - main()