+E2Piggfy8r+H
M07*qoM6N<$f|;qn+5i9m
literal 0
HcmV?d00001
From c54934ebeee9c3536cce5344ff094e1ee660376c Mon Sep 17 00:00:00 2001
From: pgotta <168211683+pgotta@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:21:08 -0400
Subject: [PATCH 08/12] Use committed icon assets in finalization test
---
.github/workflows/finalize-shutdown-icon.yml | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/.github/workflows/finalize-shutdown-icon.yml b/.github/workflows/finalize-shutdown-icon.yml
index 341ce22..1df8b18 100644
--- a/.github/workflows/finalize-shutdown-icon.yml
+++ b/.github/workflows/finalize-shutdown-icon.yml
@@ -21,13 +21,6 @@ jobs:
with:
python-version: "3.12"
- - name: Restore Parroty icon payload
- run: >-
- python -c "import base64,pathlib;
- data=base64.b64decode(pathlib.Path('tools/parroty_icon.b64').read_text(encoding='utf-8').strip(),validate=True);
- pathlib.Path('parroty.ico').write_bytes(data);
- print('Restored parroty.ico:',len(data),'bytes')"
-
- name: Apply confirmed local changes
id: apply
continue-on-error: true
@@ -124,6 +117,7 @@ jobs:
if: steps.apply.outcome == 'success'
shell: pwsh
run: |
+ if (-not (Test-Path parroty.ico)) { throw "Root Parroty icon missing" }
if (-not (Test-Path app/static/parroty.ico)) { throw "Static Parroty icon missing" }
$rootHash = (Get-FileHash parroty.ico -Algorithm SHA256).Hash
$staticHash = (Get-FileHash app/static/parroty.ico -Algorithm SHA256).Hash
From e65849666af76172587a16b38827022e79cacf39 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 23 Jul 2026 21:21:52 +0000
Subject: [PATCH 09/12] Finalize process-tree shutdown and window icon
---
BUILD.md | 15 +++++----
Quick Start Readme.txt | 37 +++++++++++++++-------
README.md | 15 +++++----
RELEASE_NOTES.md | 12 ++++---
app/templates/index.html | 5 ++-
parroty_window.ps1 | 67 ++++++++++++++++++++++++++++++++++++++--
6 files changed, 119 insertions(+), 32 deletions(-)
diff --git a/BUILD.md b/BUILD.md
index 6a8008d..d02db27 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -22,7 +22,8 @@ The ZIP root should contain the project files directly. Do not wrap them inside
an extra nested `Parroty` directory.
Required source/runtime files include `app/`, `tools/`, `requirements.txt`, the
-tracked launcher helpers, `parroty.ico`, documentation, and `LICENSE`.
+tracked launcher helpers, `parroty.ico`, `app/static/parroty.ico`, documentation,
+and `LICENSE`.
The voice package must include all eight `builtin_*.wav` files and
`app/assets/voices/ATTRIBUTION.md`.
@@ -57,11 +58,13 @@ py -3.12 -m py_compile launch_parroty.pyw app/server.py app/tts.py app/narrate_w
py -3.12 -c "from app.tts import ENGINE_CATALOG; v=ENGINE_CATALOG['chatterbox']['builtin_voices']; assert len(v)==8; print(list(v))"
```
-After installation, verify the monitor identifies CUDA/NVIDIA, the app opens in
-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.
+After installation, verify the monitor identifies CUDA/NVIDIA and the app opens in
+a dedicated maximized window without a visible console. Confirm the desktop
+shortcut, native app window, and taskbar button all use `parroty.ico`. Start a
+short narration, close the app window with X, and verify the Flask listener and
+every `app.narrate_worker` process exit. Also verify the generated `stop.bat` can
+find and stop Parroty workers even when port 5000 is no longer listening. Finally,
+confirm all eight voices appear and previews work.
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 07cc6d6..4c754d3 100644
--- a/Quick Start Readme.txt
+++ b/Quick Start Readme.txt
@@ -16,8 +16,9 @@ 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. Closing the dedicated
-Parroty Chrome/Edge app window with X automatically stops the hidden backend.
+No API key or separate voice download is required. The dedicated Parroty
+Chrome/Edge app window uses the bird icon in its title bar and taskbar. Closing
+it with X stops Flask and any active narration workers.
========================================================================
FILE: install_all.bat
@@ -98,13 +99,27 @@ exit /b 0
========================================================================
@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.)
+setlocal
+cd /d "%~dp0"
+title Stop Parroty
+
+powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command ^
+ "$root=[IO.Path]::GetFullPath('%CD%');" ^
+ "$targets=New-Object 'System.Collections.Generic.HashSet[int]';" ^
+ "try { Get-NetTCPConnection -LocalPort 5000 -State Listen -ErrorAction Stop | ForEach-Object { if ($_.OwningProcess -gt 0) { [void]$targets.Add([int]$_.OwningProcess) } } } catch {};" ^
+ "Get-CimInstance Win32_Process | Where-Object {" ^
+ " $_.Name -match '^pythonw?\.exe$' -and $_.CommandLine -and (" ^
+ " ($_.CommandLine -like ('*' + $root + '*launch_parroty.pyw*')) -or" ^
+ " ($_.CommandLine -like ('*' + $root + '*-m app.narrate_worker*')) -or" ^
+ " ($_.CommandLine -like ('*' + $root + '*-m app.server*'))" ^
+ " )" ^
+ "} | ForEach-Object { [void]$targets.Add([int]$_.ProcessId) };" ^
+ "if ($targets.Count -eq 0) { Write-Host 'Parroty was not running.'; exit 0 };" ^
+ "$ordered=@($targets) | Sort-Object -Descending;" ^
+ "foreach ($id in $ordered) { & taskkill.exe /PID $id /T /F 2>$null | Out-Null };" ^
+ "Remove-Item -LiteralPath (Join-Path $root '.parroty.browser.pid') -Force -ErrorAction SilentlyContinue;" ^
+ "Write-Host 'Parroty stopped, including active narration workers.'"
+
timeout /t 2 >nul
exit /b 0
@@ -141,8 +156,8 @@ exit /b 0
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.
+3. Close the Parroty app window with X to stop Flask and active narration workers.
+4. Use stop.bat if the window is already gone or a forced full 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 1a308fd..93852cb 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,8 @@ timestamps. Everything runs on your own machine and opens in Chrome.
off — finishing 100+ chapter books on a 16 GB machine without crashes.
- **Runs entirely on your own machine** — your files and (for the local engine)
your audio never leave your computer.
+- **Windows desktop-app behavior:** opens maximized with the Parroty bird icon in
+ the window and taskbar; closing X stops Flask and active narration workers.
## Screenshots
@@ -417,12 +419,13 @@ installed). Press **Ctrl+C** to stop.
**Optional Windows launchers:** create the local `.bat` files from
`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. 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.
+Parroty in a dedicated maximized Chrome/Edge app window, applies the Parroty bird
+icon to the native window and taskbar button, and silently creates or refreshes
+the desktop shortcut. Closing that app window with **X** terminates the complete
+Parroty process tree—including an active `app.narrate_worker`—rather than only
+closing the port-5000 Flask parent. Output goes to `parroty.log`; `stop.bat` uses
+the same full-tree behavior and can also find orphaned Parroty workers if the
+listener is already gone. 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 7a7eb7e..331d6d3 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -12,11 +12,13 @@ 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.
-- Corrected that shutdown detection to monitor the exact native Parroty window
- handle rather than waiting for the Chromium process, which may remain alive
- after its app window is closed.
+- Closing the dedicated Parroty app window now monitors the exact native window
+ handle and terminates the complete Parroty process tree, including active
+ `app.narrate_worker` children that Windows would otherwise leave orphaned.
+- Updated the local `stop.bat` template to detect both the port-5000 listener and
+ Parroty launcher/server/narration workers, preventing false “not running” reports.
+- Added the Parroty bird favicon and applies `parroty.ico` directly to the native
+ Chrome/Edge app window so the title bar and Windows taskbar show the app icon.
- 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/app/templates/index.html b/app/templates/index.html
index c12f649..dac98c3 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -4,6 +4,9 @@
Parroty — EPUB to Audiobook
+
+
+
@@ -156,7 +159,7 @@ Choose the voice
-
Bundled male/female voices that run fully offline. Click Preview below to hear them. Or upload your own sample to clone a different voice.
+
Eight reviewed audiobook voices that run fully offline. Click Preview below to hear them. Or upload your own sample to clone a different voice.
diff --git a/parroty_window.ps1 b/parroty_window.ps1
index 431647a..b3c6007 100644
--- a/parroty_window.ps1
+++ b/parroty_window.ps1
@@ -60,14 +60,56 @@ function Stop-ParrotyBackend {
foreach ($backendProcessId in $ProcessIds) {
if ($backendProcessId -le 0 -or $backendProcessId -eq $PID) { continue }
+
+ # Stop-Process kills only the Flask parent on Windows and can leave active
+ # app.narrate_worker children orphaned. taskkill /T terminates the complete
+ # Parroty process tree while the parent relationship still exists.
+ try {
+ & taskkill.exe /PID $backendProcessId /T /F *> $null
+ } catch {}
+
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 {}
+ Wait-Process -Id $backendProcessId -Timeout 5 -ErrorAction SilentlyContinue
} catch {}
}
}
+function Set-ParrotyWindowIcon {
+ param(
+ [IntPtr]$WindowHandle,
+ [string]$IconPath
+ )
+
+ if ($WindowHandle -eq [IntPtr]::Zero) { return }
+ if (-not (Test-Path -LiteralPath $IconPath)) { return }
+
+ try {
+ $IMAGE_ICON = 1
+ $LR_LOADFROMFILE = 0x10
+ $WM_SETICON = 0x0080
+ $ICON_SMALL = 0
+ $ICON_BIG = 1
+
+ $smallIcon = [ParrotyNativeWindow]::LoadImage(
+ [IntPtr]::Zero, $IconPath, $IMAGE_ICON, 16, 16, $LR_LOADFROMFILE
+ )
+ $largeIcon = [ParrotyNativeWindow]::LoadImage(
+ [IntPtr]::Zero, $IconPath, $IMAGE_ICON, 48, 48, $LR_LOADFROMFILE
+ )
+
+ if ($smallIcon -ne [IntPtr]::Zero) {
+ [ParrotyNativeWindow]::SendMessage(
+ $WindowHandle, $WM_SETICON, [IntPtr]$ICON_SMALL, $smallIcon
+ ) | Out-Null
+ }
+ if ($largeIcon -ne [IntPtr]::Zero) {
+ [ParrotyNativeWindow]::SendMessage(
+ $WindowHandle, $WM_SETICON, [IntPtr]$ICON_BIG, $largeIcon
+ ) | Out-Null
+ }
+ } catch {}
+}
+
function Maximize-ParrotyWindow {
param(
[System.Diagnostics.Process]$InitialProcess,
@@ -88,6 +130,24 @@ public static class ParrotyNativeWindow {
[DllImport("user32.dll")]
public static extern bool IsWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern IntPtr LoadImage(
+ IntPtr hInst,
+ string name,
+ uint type,
+ int cx,
+ int cy,
+ uint loadFlags
+ );
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern IntPtr SendMessage(
+ IntPtr hWnd,
+ uint message,
+ IntPtr wParam,
+ IntPtr lParam
+ );
}
"@
} catch {}
@@ -130,6 +190,7 @@ public static class ParrotyNativeWindow {
for ($pass = 0; $pass -lt 5; $pass++) {
[ParrotyNativeWindow]::ShowWindowAsync($chosen.MainWindowHandle, 3) | Out-Null
[ParrotyNativeWindow]::SetForegroundWindow($chosen.MainWindowHandle) | Out-Null
+ Set-ParrotyWindowIcon -WindowHandle $chosen.MainWindowHandle -IconPath (Join-Path $Root "parroty.ico")
Start-Sleep -Milliseconds 300
try { $chosen.Refresh() } catch {}
}
From 3319c7f68b60c24b9547b4e254676a462a1ca616 Mon Sep 17 00:00:00 2001
From: pgotta <168211683+pgotta@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:22:35 -0400
Subject: [PATCH 10/12] Remove temporary finalization workflow
---
.github/workflows/finalize-shutdown-icon.yml | 167 -------------------
1 file changed, 167 deletions(-)
delete mode 100644 .github/workflows/finalize-shutdown-icon.yml
diff --git a/.github/workflows/finalize-shutdown-icon.yml b/.github/workflows/finalize-shutdown-icon.yml
deleted file mode 100644
index 1df8b18..0000000
--- a/.github/workflows/finalize-shutdown-icon.yml
+++ /dev/null
@@ -1,167 +0,0 @@
-name: Finalize shutdown and window icon
-
-on:
- pull_request:
- branches: [main]
-
-permissions:
- contents: write
-
-jobs:
- verify:
- runs-on: windows-latest
- timeout-minutes: 15
- steps:
- - uses: actions/checkout@v4
- with:
- ref: finalize-shutdown-and-window-icon
- fetch-depth: 0
-
- - uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Apply confirmed local changes
- id: apply
- continue-on-error: true
- shell: pwsh
- run: python tools/finalize_shutdown_icon_docs.py *> updater.log
-
- - name: Upload updater diagnostics
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: finalization-updater-diagnostics
- path: updater.log
- if-no-files-found: warn
-
- - name: Parse PowerShell and Python
- if: steps.apply.outcome == 'success'
- shell: pwsh
- run: |
- $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
- }
- python -m py_compile launch_parroty.pyw app/server.py app/narrate_worker.py
-
- - name: Verify full process-tree shutdown
- if: steps.apply.outcome == 'success'
- shell: pwsh
- run: |
- @'
- import pathlib
- import subprocess
- import sys
- import time
-
- child = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(120)"])
- pathlib.Path("child.pid").write_text(str(child.pid), encoding="utf-8")
- time.sleep(120)
- '@ | Set-Content -LiteralPath tree_parent.py -Encoding UTF8
-
- $parent = Start-Process python -ArgumentList "tree_parent.py" -PassThru -WindowStyle Hidden
- try {
- $childPid = $null
- for ($i = 0; $i -lt 40; $i++) {
- Start-Sleep -Milliseconds 250
- if (Test-Path child.pid) {
- $childPid = [int](Get-Content child.pid -Raw)
- break
- }
- }
- if (-not $childPid) { throw "Child PID was not created" }
- if (-not (Get-Process -Id $parent.Id -ErrorAction SilentlyContinue)) { throw "Parent exited too soon" }
- if (-not (Get-Process -Id $childPid -ErrorAction SilentlyContinue)) { throw "Child exited too soon" }
-
- $tokens = $null
- $errors = $null
- $ast = [System.Management.Automation.Language.Parser]::ParseFile(
- (Resolve-Path "parroty_window.ps1"),
- [ref]$tokens,
- [ref]$errors
- )
- $functions = $ast.FindAll({
- param($node)
- $node -is [System.Management.Automation.Language.FunctionDefinitionAst]
- }, $true)
- foreach ($function in $functions) {
- Invoke-Expression $function.Extent.Text
- }
-
- Stop-ParrotyBackend -ProcessIds @($parent.Id)
- Start-Sleep -Milliseconds 750
-
- if (Get-Process -Id $parent.Id -ErrorAction SilentlyContinue) {
- throw "Parent process survived full-tree shutdown"
- }
- if (Get-Process -Id $childPid -ErrorAction SilentlyContinue) {
- throw "Child narration-worker stand-in survived full-tree shutdown"
- }
- Write-Host "Verified taskkill /T stops both parent and child."
- }
- finally {
- Stop-Process -Id $parent.Id -Force -ErrorAction SilentlyContinue
- if ($childPid) { Stop-Process -Id $childPid -Force -ErrorAction SilentlyContinue }
- Remove-Item tree_parent.py,child.pid -Force -ErrorAction SilentlyContinue
- }
-
- - name: Verify icon and documentation assets
- if: steps.apply.outcome == 'success'
- shell: pwsh
- run: |
- if (-not (Test-Path parroty.ico)) { throw "Root Parroty icon missing" }
- if (-not (Test-Path app/static/parroty.ico)) { throw "Static Parroty icon missing" }
- $rootHash = (Get-FileHash parroty.ico -Algorithm SHA256).Hash
- $staticHash = (Get-FileHash app/static/parroty.ico -Algorithm SHA256).Hash
- if ($rootHash -ne $staticHash) { throw "Static icon does not match root parroty.ico" }
-
- $html = Get-Content app/templates/index.html -Raw
- if ($html -notmatch '/static/parroty\.ico') { throw "Favicon link missing" }
-
- $launcher = Get-Content parroty_window.ps1 -Raw
- foreach ($required in @('taskkill.exe /PID $backendProcessId /T /F','Set-ParrotyWindowIcon','WM_SETICON')) {
- if (-not $launcher.Contains($required)) { throw "Launcher is missing: $required" }
- }
-
- $quick = Get-Content "Quick Start Readme.txt" -Raw
- if (-not $quick.Contains('app.narrate_worker')) { throw "Quick Start stop.bat does not detect workers" }
- if (-not $quick.Contains('/T /F')) { throw "Quick Start stop.bat does not terminate process trees" }
-
- - name: Enforce repository policy
- if: steps.apply.outcome == 'success'
- 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: Commit generated final files
- if: steps.apply.outcome == 'success'
- shell: pwsh
- run: |
- git config user.name "github-actions[bot]"
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git add parroty.ico parroty_window.ps1 app/templates/index.html app/static/parroty.ico README.md "Quick Start Readme.txt" BUILD.md RELEASE_NOTES.md
- if (git diff --cached --quiet) {
- Write-Host "Final files already current."
- } else {
- git commit -m "Finalize process-tree shutdown and window icon"
- git push origin HEAD:finalize-shutdown-and-window-icon
- }
-
- - name: Enforce updater result
- if: steps.apply.outcome != 'success'
- run: exit 1
From f87a8a934a82f8936b754f1218f958516f2237bf Mon Sep 17 00:00:00 2001
From: pgotta <168211683+pgotta@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:22:46 -0400
Subject: [PATCH 11/12] Remove temporary finalization updater
---
tools/finalize_shutdown_icon_docs.py | 315 ---------------------------
1 file changed, 315 deletions(-)
delete mode 100644 tools/finalize_shutdown_icon_docs.py
diff --git a/tools/finalize_shutdown_icon_docs.py b/tools/finalize_shutdown_icon_docs.py
deleted file mode 100644
index 68f5acd..0000000
--- a/tools/finalize_shutdown_icon_docs.py
+++ /dev/null
@@ -1,315 +0,0 @@
-from __future__ import annotations
-
-import shutil
-from pathlib import Path
-
-ROOT = Path(__file__).resolve().parents[1]
-
-
-def read(path: str) -> str:
- return (ROOT / path).read_text(encoding="utf-8")
-
-
-def write(path: str, text: str, *, crlf: bool = False) -> None:
- target = ROOT / path
- if crlf:
- normalized = text.replace("\r\n", "\n")
- with target.open("w", encoding="utf-8", newline="") as file:
- file.write(normalized.replace("\n", "\r\n"))
- else:
- target.write_text(text, encoding="utf-8")
-
-
-def replace_once(text: str, old: str, new: str, label: str) -> str:
- if new in text:
- return text
- if old not in text:
- raise RuntimeError(f"Expected text not found while updating {label}")
- return text.replace(old, new, 1)
-
-
-def update_window_launcher() -> None:
- path = "parroty_window.ps1"
- text = read(path)
-
- old_stop = '''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 {}
- }
-}'''
- new_stop = '''function Stop-ParrotyBackend {
- param([int[]]$ProcessIds)
-
- foreach ($backendProcessId in $ProcessIds) {
- if ($backendProcessId -le 0 -or $backendProcessId -eq $PID) { continue }
-
- # Stop-Process kills only the Flask parent on Windows and can leave active
- # app.narrate_worker children orphaned. taskkill /T terminates the complete
- # Parroty process tree while the parent relationship still exists.
- try {
- & taskkill.exe /PID $backendProcessId /T /F *> $null
- } catch {}
-
- try {
- Wait-Process -Id $backendProcessId -Timeout 5 -ErrorAction SilentlyContinue
- } catch {}
- }
-}'''
- text = replace_once(text, old_stop, new_stop, path)
-
- old_native = ''' [DllImport("user32.dll")]
- public static extern bool IsWindow(IntPtr hWnd);
-}'''
- new_native = ''' [DllImport("user32.dll")]
- public static extern bool IsWindow(IntPtr hWnd);
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- public static extern IntPtr LoadImage(
- IntPtr hInst,
- string name,
- uint type,
- int cx,
- int cy,
- uint loadFlags
- );
-
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr SendMessage(
- IntPtr hWnd,
- uint message,
- IntPtr wParam,
- IntPtr lParam
- );
-}'''
- text = replace_once(text, old_native, new_native, path)
-
- helper = '''function Set-ParrotyWindowIcon {
- param(
- [IntPtr]$WindowHandle,
- [string]$IconPath
- )
-
- if ($WindowHandle -eq [IntPtr]::Zero) { return }
- if (-not (Test-Path -LiteralPath $IconPath)) { return }
-
- try {
- $IMAGE_ICON = 1
- $LR_LOADFROMFILE = 0x10
- $WM_SETICON = 0x0080
- $ICON_SMALL = 0
- $ICON_BIG = 1
-
- $smallIcon = [ParrotyNativeWindow]::LoadImage(
- [IntPtr]::Zero, $IconPath, $IMAGE_ICON, 16, 16, $LR_LOADFROMFILE
- )
- $largeIcon = [ParrotyNativeWindow]::LoadImage(
- [IntPtr]::Zero, $IconPath, $IMAGE_ICON, 48, 48, $LR_LOADFROMFILE
- )
-
- if ($smallIcon -ne [IntPtr]::Zero) {
- [ParrotyNativeWindow]::SendMessage(
- $WindowHandle, $WM_SETICON, [IntPtr]$ICON_SMALL, $smallIcon
- ) | Out-Null
- }
- if ($largeIcon -ne [IntPtr]::Zero) {
- [ParrotyNativeWindow]::SendMessage(
- $WindowHandle, $WM_SETICON, [IntPtr]$ICON_BIG, $largeIcon
- ) | Out-Null
- }
- } catch {}
-}
-
-'''
- marker = "function Maximize-ParrotyWindow {"
- if "function Set-ParrotyWindowIcon" not in text:
- if marker not in text:
- raise RuntimeError("Maximize-ParrotyWindow marker not found")
- text = text.replace(marker, helper + marker, 1)
-
- old_loop = ''' [ParrotyNativeWindow]::ShowWindowAsync($chosen.MainWindowHandle, 3) | Out-Null
- [ParrotyNativeWindow]::SetForegroundWindow($chosen.MainWindowHandle) | Out-Null
- Start-Sleep -Milliseconds 300'''
- new_loop = ''' [ParrotyNativeWindow]::ShowWindowAsync($chosen.MainWindowHandle, 3) | Out-Null
- [ParrotyNativeWindow]::SetForegroundWindow($chosen.MainWindowHandle) | Out-Null
- Set-ParrotyWindowIcon -WindowHandle $chosen.MainWindowHandle -IconPath (Join-Path $Root "parroty.ico")
- Start-Sleep -Milliseconds 300'''
- text = replace_once(text, old_loop, new_loop, path)
- write(path, text)
-
-
-def update_template_and_icon() -> None:
- path = "app/templates/index.html"
- text = read(path)
- old = "
Parroty — EPUB to Audiobook"
- new = '''Parroty — EPUB to Audiobook
-
-
-'''
- text = replace_once(text, old, new, path)
- text = text.replace(
- "Bundled male/female voices that run fully offline. Click Preview below to hear them.",
- "Eight reviewed audiobook voices that run fully offline. Click Preview below to hear them.",
- )
- write(path, text)
-
- static_icon = ROOT / "app/static/parroty.ico"
- static_icon.parent.mkdir(parents=True, exist_ok=True)
- shutil.copyfile(ROOT / "parroty.ico", static_icon)
-
-
-def stop_bat_template() -> str:
- return r'''@echo off
-setlocal
-cd /d "%~dp0"
-title Stop Parroty
-
-powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command ^
- "$root=[IO.Path]::GetFullPath('%CD%');" ^
- "$targets=New-Object 'System.Collections.Generic.HashSet[int]';" ^
- "try { Get-NetTCPConnection -LocalPort 5000 -State Listen -ErrorAction Stop | ForEach-Object { if ($_.OwningProcess -gt 0) { [void]$targets.Add([int]$_.OwningProcess) } } } catch {};" ^
- "Get-CimInstance Win32_Process | Where-Object {" ^
- " $_.Name -match '^pythonw?\.exe$' -and $_.CommandLine -and (" ^
- " ($_.CommandLine -like ('*' + $root + '*launch_parroty.pyw*')) -or" ^
- " ($_.CommandLine -like ('*' + $root + '*-m app.narrate_worker*')) -or" ^
- " ($_.CommandLine -like ('*' + $root + '*-m app.server*'))" ^
- " )" ^
- "} | ForEach-Object { [void]$targets.Add([int]$_.ProcessId) };" ^
- "if ($targets.Count -eq 0) { Write-Host 'Parroty was not running.'; exit 0 };" ^
- "$ordered=@($targets) | Sort-Object -Descending;" ^
- "foreach ($id in $ordered) { & taskkill.exe /PID $id /T /F 2>$null | Out-Null };" ^
- "Remove-Item -LiteralPath (Join-Path $root '.parroty.browser.pid') -Force -ErrorAction SilentlyContinue;" ^
- "Write-Host 'Parroty stopped, including active narration workers.'"
-
-timeout /t 2 >nul
-exit /b 0'''
-
-
-def update_quick_start() -> None:
- path = "Quick Start Readme.txt"
- text = read(path)
- text = replace_once(
- text,
- "No API key or separate voice download is required. Closing the dedicated\nParroty Chrome/Edge app window with X automatically stops the hidden backend.",
- "No API key or separate voice download is required. The dedicated Parroty\nChrome/Edge app window uses the bird icon in its title bar and taskbar. Closing\nit with X stops Flask and any active narration workers.",
- path,
- )
-
- start = text.index("@echo off", text.index("FILE: stop.bat"))
- end_marker = "\n\n========================================================================\n FILE: run_debug.bat"
- end = text.index(end_marker, start)
- text = text[:start] + stop_bat_template() + text[end:]
-
- text = text.replace(
- "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.",
- "3. Close the Parroty app window with X to stop Flask and active narration workers.\n"
- "4. Use stop.bat if the window is already gone or a forced full stop is needed.",
- )
- write(path, text, crlf=True)
-
-
-def update_readme() -> None:
- path = "README.md"
- text = read(path)
- highlight = (
- "- **Runs entirely on your own machine** — your files and (for the local engine)\n"
- " your audio never leave your computer."
- )
- replacement = highlight + (
- "\n- **Windows desktop-app behavior:** opens maximized with the Parroty bird icon in\n"
- " the window and taskbar; closing X stops Flask and active narration workers."
- )
- text = replace_once(text, highlight, replacement, path)
-
- old_running = (
- "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."
- )
- new_running = (
- "Parroty in a dedicated maximized Chrome/Edge app window, applies the Parroty bird\n"
- "icon to the native window and taskbar button, and silently creates or refreshes\n"
- "the desktop shortcut. Closing that app window with **X** terminates the complete\n"
- "Parroty process tree—including an active `app.narrate_worker`—rather than only\n"
- "closing the port-5000 Flask parent. Output goes to `parroty.log`; `stop.bat` uses\n"
- "the same full-tree behavior and can also find orphaned Parroty workers if the\n"
- "listener is already gone. These `.bat` files are deliberately ignored by Git."
- )
- text = replace_once(text, old_running, new_running, path)
- write(path, text)
-
-
-def update_build() -> None:
- path = "BUILD.md"
- text = read(path)
- text = replace_once(
- text,
- "tracked launcher helpers, `parroty.ico`, documentation, and `LICENSE`.",
- "tracked launcher helpers, `parroty.ico`, `app/static/parroty.ico`, documentation,\n"
- "and `LICENSE`.",
- path,
- )
- text = replace_once(
- text,
- "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.",
- "After installation, verify the monitor identifies CUDA/NVIDIA and the app opens in\n"
- "a dedicated maximized window without a visible console. Confirm the desktop\n"
- "shortcut, native app window, and taskbar button all use `parroty.ico`. Start a\n"
- "short narration, close the app window with X, and verify the Flask listener and\n"
- "every `app.narrate_worker` process exit. Also verify the generated `stop.bat` can\n"
- "find and stop Parroty workers even when port 5000 is no longer listening. Finally,\n"
- "confirm all eight voices appear and previews work.",
- path,
- )
- write(path, text)
-
-
-def update_release_notes() -> None:
- path = "RELEASE_NOTES.md"
- text = read(path)
- old = (
- "- Closing the dedicated Parroty app window now automatically stops the hidden\n"
- " Flask backend, matching `stop.bat` behavior without affecting normal browser windows.\n"
- "- Corrected that shutdown detection to monitor the exact native Parroty window\n"
- " handle rather than waiting for the Chromium process, which may remain alive\n"
- " after its app window is closed."
- )
- new = (
- "- Closing the dedicated Parroty app window now monitors the exact native window\n"
- " handle and terminates the complete Parroty process tree, including active\n"
- " `app.narrate_worker` children that Windows would otherwise leave orphaned.\n"
- "- Updated the local `stop.bat` template to detect both the port-5000 listener and\n"
- " Parroty launcher/server/narration workers, preventing false “not running” reports.\n"
- "- Added the Parroty bird favicon and applies `parroty.ico` directly to the native\n"
- " Chrome/Edge app window so the title bar and Windows taskbar show the app icon."
- )
- text = replace_once(text, old, new, path)
- write(path, text)
-
-
-def main() -> None:
- update_window_launcher()
- update_template_and_icon()
- update_quick_start()
- update_readme()
- update_build()
- update_release_notes()
- print("Applied confirmed shutdown, icon, and documentation updates")
-
-
-if __name__ == "__main__":
- main()
From 8320e0ae105d15f594acd04db84e851a935956bc Mon Sep 17 00:00:00 2001
From: pgotta <168211683+pgotta@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:22:55 -0400
Subject: [PATCH 12/12] Remove temporary icon payload
---
tools/parroty_icon.b64 | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 tools/parroty_icon.b64
diff --git a/tools/parroty_icon.b64 b/tools/parroty_icon.b64
deleted file mode 100644
index 8c44532..0000000
--- a/tools/parroty_icon.b64
+++ /dev/null
@@ -1 +0,0 @@
-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
\ No newline at end of file