Skip to content

auto update edge-tts#39

Merged
pladisdev merged 82 commits intomainfrom
development
Apr 5, 2026
Merged

auto update edge-tts#39
pladisdev merged 82 commits intomainfrom
development

Conversation

@pladisdev
Copy link
Copy Markdown
Owner

No description provided.

pladisdev and others added 30 commits November 2, 2025 09:42
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
…4b50-93c4-0e11b13594f4

Clarify error handler ordering is correct - no changes needed
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
Co-authored-by: pladisdev <127021507+pladisdev@users.noreply.github.com>
…47b6-b204-5bf8892748d3

Extract magic numbers to named constants in audio playback code
…4606-8149-97ceaa22873d

Use platform.system() instead of sys.platform for OS detection
Extract duplicated hex opacity calculation to utility function
Extract avatar active offset magic number into configurable setting
Fix audio reference cleanup on play() failure in popup mode
Fix race condition in popup avatar lifecycle
Copilot AI review requested due to automatic review settings April 5, 2026 17:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates dependencies and build/config behavior across the backend, frontend, and CI to address Edge TTS compatibility and release pipeline changes.

Changes:

  • Bump edge-tts version and add backend logic to retry synthesis by attempting a runtime edge-tts upgrade on certain 403 errors.
  • Frontend: fix Vite ESM __dirname handling and add a default / route redirect to /settings.
  • CI/Release: move Windows MSI build to WiX v5 and reduce Docker involvement in releases.

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
requirements.txt Bumps edge-tts pinned version.
frontend/vite.config.js Defines __dirname for ESM-based Vite config path resolution.
frontend/src/app.jsx Adds root route redirect to /settings.
frontend/package-lock.json Updates lockfile entries from dependency refresh.
deployment/build_msi.py Adjusts WiX extension detection to include global installs.
backend/modules/tts.py Adds 403-triggered retry + runtime edge-tts upgrade attempt.
.github/workflows/build-and-release.yml Updates WiX install to v5 and reduces Docker usage in release flow.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

deployment/build_msi.py:56

  • check_wix_extensions() now checks global extensions, but the auto-install path still runs wix extension add WixToolset.UI.wixext without --global (and without a version), which can diverge from the CI setup (--global WixToolset.UI.wixext/5.0.1). Align the install command with the expected scope/version so local builds match CI.
        # Check both local and global extensions
        result = subprocess.run("wix extension list", shell=True, capture_output=True, text=True)
        global_result = subprocess.run("wix extension list --global", shell=True, capture_output=True, text=True)
        
        if "WixToolset.UI.wixext" in result.stdout or "WixToolset.UI.wixext" in global_result.stdout:
            print("WiX UI extension found")
            return True
        else:
            print("\nWARNING: WiX UI extension not found!")
            print("Installing WixToolset.UI.wixext...")
            install_result = subprocess.run("wix extension add WixToolset.UI.wixext", 
                                          shell=True, capture_output=True, text=True)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import uuid
import time
import sys
import subprocess
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subprocess is imported but not used in this module; please remove it to avoid lint/packaging noise.

Suggested change
import subprocess

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +73
# Track edge-tts update attempts to avoid repeated updates
_edge_tts_update_attempted = False

async def try_update_edge_tts():
"""Attempt to update edge-tts package when API compatibility issues occur"""
global _edge_tts_update_attempted

if _edge_tts_update_attempted:
logger.info("edge-tts update already attempted this session, skipping")
return False

_edge_tts_update_attempted = True
logger.info("Attempting to update edge-tts package to fix API compatibility...")

try:
# Run pip upgrade in subprocess
python_exe = sys.executable
result = await asyncio.create_subprocess_exec(
python_exe, "-m", "pip", "install", "--upgrade", "edge-tts",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await result.communicate()

if result.returncode == 0:
logger.info(f"edge-tts successfully updated: {stdout.decode()}")

# Reload the edge_tts module
try:
import importlib
global edge_tts
if edge_tts:
importlib.reload(edge_tts)
logger.info("edge-tts module reloaded successfully")
else:
import edge_tts as new_edge_tts
edge_tts = new_edge_tts
logger.info("edge-tts module imported successfully")
return True
except Exception as e:
logger.warning(f"edge-tts updated but module reload failed: {e}")
logger.info("Restart the application to use the updated edge-tts")
return False
else:
logger.error(f"edge-tts update failed: {stderr.decode()}")
return False
except Exception as e:
logger.error(f"Failed to update edge-tts: {e}")
return False
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a runtime pip install --upgrade edge-tts inside the server process. That can break reproducible deployments (conflicts with the pinned edge-tts==... in requirements), fail in read-only/container environments, and expands the attack surface by fetching code at runtime. Consider removing the auto-upgrade and instead surface a clear error/instruction (or gate it behind an explicit admin setting/env var and pin to an expected version).

Suggested change
# Track edge-tts update attempts to avoid repeated updates
_edge_tts_update_attempted = False
async def try_update_edge_tts():
"""Attempt to update edge-tts package when API compatibility issues occur"""
global _edge_tts_update_attempted
if _edge_tts_update_attempted:
logger.info("edge-tts update already attempted this session, skipping")
return False
_edge_tts_update_attempted = True
logger.info("Attempting to update edge-tts package to fix API compatibility...")
try:
# Run pip upgrade in subprocess
python_exe = sys.executable
result = await asyncio.create_subprocess_exec(
python_exe, "-m", "pip", "install", "--upgrade", "edge-tts",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await result.communicate()
if result.returncode == 0:
logger.info(f"edge-tts successfully updated: {stdout.decode()}")
# Reload the edge_tts module
try:
import importlib
global edge_tts
if edge_tts:
importlib.reload(edge_tts)
logger.info("edge-tts module reloaded successfully")
else:
import edge_tts as new_edge_tts
edge_tts = new_edge_tts
logger.info("edge-tts module imported successfully")
return True
except Exception as e:
logger.warning(f"edge-tts updated but module reload failed: {e}")
logger.info("Restart the application to use the updated edge-tts")
return False
else:
logger.error(f"edge-tts update failed: {stderr.decode()}")
return False
except Exception as e:
logger.error(f"Failed to update edge-tts: {e}")
return False
# Track edge-tts update attempts to avoid repeated update instructions
_edge_tts_update_attempted = False
async def try_update_edge_tts():
"""Log a one-time instruction to update edge-tts outside the running process."""
global _edge_tts_update_attempted
if _edge_tts_update_attempted:
logger.info("edge-tts update already flagged this session, skipping")
return False
_edge_tts_update_attempted = True
logger.error(
"edge-tts appears incompatible, but automatic runtime upgrades are disabled. "
"Please update the pinned 'edge-tts' dependency through your normal build/deploy "
"process and restart the application."
)
return False

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +38

async def try_update_edge_tts():
"""Attempt to update edge-tts package when API compatibility issues occur"""
global _edge_tts_update_attempted

if _edge_tts_update_attempted:
logger.info("edge-tts update already attempted this session, skipping")
return False

_edge_tts_update_attempted = True
logger.info("Attempting to update edge-tts package to fix API compatibility...")

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _edge_tts_update_attempted guard is a shared global without synchronization. With concurrent synth requests, multiple tasks can still race into try_update_edge_tts() and run upgrades simultaneously. Consider guarding with an asyncio.Lock (or similar) to ensure only one update attempt runs at a time.

Suggested change
async def try_update_edge_tts():
"""Attempt to update edge-tts package when API compatibility issues occur"""
global _edge_tts_update_attempted
if _edge_tts_update_attempted:
logger.info("edge-tts update already attempted this session, skipping")
return False
_edge_tts_update_attempted = True
logger.info("Attempting to update edge-tts package to fix API compatibility...")
_edge_tts_update_lock = asyncio.Lock()
async def try_update_edge_tts():
"""Attempt to update edge-tts package when API compatibility issues occur"""
global _edge_tts_update_attempted
async with _edge_tts_update_lock:
if _edge_tts_update_attempted:
logger.info("edge-tts update already attempted this session, skipping")
return False
_edge_tts_update_attempted = True
logger.info("Attempting to update edge-tts package to fix API compatibility...")

Copilot uses AI. Check for mistakes.
Comment on lines +396 to +414
# Helper function to attempt synthesis with retry logic
async def attempt_synthesis(voice_id: str, retry_on_403: bool = True):
try:
communicate = edge_tts.Communicate(job.text, voice_id)
await communicate.save(outpath)
return True
except Exception as e:
# Check if it's a 403 error (API compatibility issue)
error_str = str(e)
if "403" in error_str and "Invalid response status" in error_str and retry_on_403:
logger.warning(f"Edge TTS returned 403 error - API may need updating")
# Attempt to update edge-tts
if await try_update_edge_tts():
logger.info("Retrying synthesis after edge-tts update...")
# Retry once after update (without further retries)
return await attempt_synthesis(voice_id, retry_on_403=False)
else:
logger.error("edge-tts update failed or requires restart. Please restart the application.")
raise
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New 403-detection + auto-update retry logic isn’t covered by existing unit tests. Add a unit test that mocks edge_tts.Communicate.save to raise the 403 error once, asserts try_update_edge_tts() is invoked at most once per session, and verifies synthesis is retried (or that a clear error is surfaced when update fails).

Copilot uses AI. Check for mistakes.
Comment on lines +672 to +685
# Commented out to speed up CI - Docker build takes too long
# - name: Build and push Docker image
# uses: docker/build-push-action@v5
# with:
# context: .
# file: ./docker/Dockerfile
# push: true
# tags: ${{ steps.meta.outputs.tags }}
# labels: ${{ steps.meta.outputs.labels }}
# platforms: linux/amd64,linux/arm64
# cache-from: type=gha
# cache-to: type=gha,mode=max
# build-args: |
# APP_VERSION=${{ steps.docker_version.outputs.app_version }}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build step is commented out, but the job still logs into GHCR, computes metadata, and later posts a PR comment claiming an image was built/published. Either disable the whole docker-build job, or gate the downstream steps/comments/artifacts on actually building/pushing the image to avoid misleading CI output.

Copilot uses AI. Check for mistakes.
Comment on lines 119 to 135
@@ -128,10 +128,9 @@ jobs:
wix --version

Write-Host "Installing WiX UI extension globally..."
wix extension add --global WixToolset.UI.wixext
wix extension add --global WixToolset.UI.wixext/5.0.1

Write-Host "Verifying UI extension..."
wix extension list
wix extension list --global

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR title indicates an edge-tts update, but this workflow change also upgrades WiX to v5 and changes release/build dependencies (including de-emphasizing Docker). If these are intentional, consider splitting them into a separate PR or updating the PR title/description so reviewers can assess CI/release changes explicitly.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 5, 2026

✅ Windows Build Successful

Executable: ChatYapper.exe (67.00 MB)
MSI Installer: ChatYapper-1.3.1.msi (66.32 MB)
App Version: 1.3.1
Build ID: build-v1.3.1-2026.04.05-5b4c76f
Commit: 5b4c76f

Build Status

  • ✅ Windows executable & MSI installer
  • 🔄 Linux build (check separate job)
  • 🔄 Docker image (check separate job)

Download the artifacts from the workflow run to test before merging.

Once merged to main, an official release will be created automatically with tag v1.3.1.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 5, 2026

🐳 Docker Image Built Successfully

Image: ghcr.io/pladisdev/chat-yapper:pr-5b4c76f
Tag: pr-5b4c76f

Test this PR with Docker:

docker pull ghcr.io/pladisdev/chat-yapper:pr-5b4c76f

docker run -d \
  --name chat-yapper-pr \
  -p 8069:8008 \
  -e TWITCH_CLIENT_ID=your_id \
  -e TWITCH_CLIENT_SECRET=your_secret \
  ghcr.io/pladisdev/chat-yapper:pr-5b4c76f

Access at: http://localhost:8069

The Docker image will be published to the GitHub Container Registry when merged to main.

@pladisdev pladisdev merged commit 2f15868 into main Apr 5, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants