Initial commit: Polish AI dubbing pipeline (ComfyUI + Higgs Audio V3)
Converts SRT subtitles into a Polish lektor-style voiceover using a local
ComfyUI instance running Higgs Audio V3, then muxes the result back into the
source video as a second audio track. Video and original audio are always
stream-copied; only the new voiceover track is encoded.
Contents:
lektor_ai_comfy.py main pipeline (standard library only, needs ffmpeg)
lektor_ai_comfy_v2.py batched-context experiment (timing unresolved)
lektor_ai.py earlier version using the hosted Boson AI API
install_comfyui.sh reproduces the ComfyUI environment, versions pinned
docs/ requirements, installation, usage, architecture,
troubleshooting
gudowski/ voice references (.wav) with their transcripts (.txt)
Source media and generated video are gitignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Inspect voice picker and Text mode on AI Studio TTS page."""
|
||||
import json, time
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
profile_dir = Path.home() / ".config" / "lektor_ai" / "browser_profile"
|
||||
|
||||
def dump_elements(page, label):
|
||||
elements = page.evaluate("""() => {
|
||||
const sel = 'input, textarea, select, [role="combobox"], [role="listbox"], [role="textbox"], [role="option"], button, [contenteditable]';
|
||||
return [...document.querySelectorAll(sel)].map(el => ({
|
||||
tag: el.tagName,
|
||||
role: el.getAttribute('role') || '',
|
||||
ariaLabel: el.getAttribute('aria-label') || '',
|
||||
placeholder: el.getAttribute('placeholder') || '',
|
||||
class: el.className?.toString().slice(0, 100) || '',
|
||||
text: (el.innerText || el.value || '').slice(0, 80),
|
||||
}));
|
||||
}""")
|
||||
path = f"output/elements_{label}.json"
|
||||
Path(path).write_text(json.dumps(elements, indent=2, ensure_ascii=False))
|
||||
print(f"[{label}] {len(elements)} elements → {path}")
|
||||
|
||||
with sync_playwright() as pw:
|
||||
ctx = pw.chromium.launch_persistent_context(
|
||||
str(profile_dir),
|
||||
channel="chrome",
|
||||
headless=False,
|
||||
viewport={"width": 1400, "height": 900},
|
||||
ignore_default_args=["--enable-automation"],
|
||||
args=["--disable-blink-features=AutomationControlled", "--start-maximized"],
|
||||
)
|
||||
page = ctx.new_page()
|
||||
page.goto("https://aistudio.google.com/generate-speech", wait_until="networkidle", timeout=60_000)
|
||||
time.sleep(3)
|
||||
|
||||
# Open editor
|
||||
page.locator('.text-input-container').first.click()
|
||||
time.sleep(2)
|
||||
|
||||
# Step 1: Click Text mode (the other toggle)
|
||||
page.locator('button[role="radio"]').filter(has_text="Text").click()
|
||||
time.sleep(2)
|
||||
page.screenshot(path="output/shot_3_text_mode.png", full_page=True)
|
||||
dump_elements(page, "3_text_mode")
|
||||
|
||||
# Step 2: Back to Composer, click voice chip
|
||||
page.locator('button[role="radio"]').filter(has_text="Composer").click()
|
||||
time.sleep(2)
|
||||
page.locator('button.voice-chip').first.click()
|
||||
time.sleep(2)
|
||||
page.screenshot(path="output/shot_4_voice_picker.png", full_page=True)
|
||||
dump_elements(page, "4_voice_picker")
|
||||
|
||||
input("Press Enter to close...")
|
||||
ctx.close()
|
||||
Reference in New Issue
Block a user