Add WebUi (Adopted from https://github.com/kigner/audio.cpp-webui, add support for Linux) - #87
Conversation
|
Thank you @patrickjchen. The changes to model manager in this PR may break existing CI/Nix flows. Could you tentatively create a dedicated model_manager.py for the UI so the existing one stays stays lightweight and focused on the framework installer/catalog? The manager for UI can be in the webui dir, which will make the UI self-contained. |
The WebUI comes from kigner's fork and is the bulk of the logic here: https://github.com/kigner/audio.cpp-webui.git (merged at 8924917). It is a Gradio UI over audiocpp_server that loads one model at a time on demand, with model management, background downloads, per-family request profiles, VRAM budgeting and adaptive chunking for TTS/ASR/VC, and a dialogue mode that chains diarization into ASR. All of that is kigner's work and is carried over as-is. That fork targets Windows only and its interface is Chinese only. What this commit adds is the cross-platform and localization work on top: Cross-platform: - Resolve the server binary by OS convention (.exe only on Windows) instead of assuming audiocpp_server.exe, and name run_server.sh/.bat per platform in the messages that mention it. - Find from-source builds in either layout: build/<os>-<backend>-<type>/bin, where the backend is in the directory name, and a plain `cmake -B build` -> build/bin, which says nothing about the backend and is classified from its CMakeCache (GGML_CUDA:BOOL=ON). Without the second, a stock Linux dev tree found no binary at all and SERVER_EXE pointed at a path that did not exist. - Add run_webui.sh as the POSIX counterpart of run_webui.bat. Localization, English first: - Strings move to webui/locales/<lang>.json, chosen by AUDIOCPP_LANG (default en) or the language picker in the UI. en.json is the per-key fallback, so a partial translation degrades to English rather than showing raw keys, and an unknown or malformed locale warns and falls back instead of taking the UI down. Adding a language is dropping in a file. zh.json keeps the fork's original Chinese. - Catalog entries hold English display_name/input_hint; a locale overrides any of them via catalog.<id>.<field>, which is how zh.json restores the Chinese model names. Without this the catalog's own strings leaked into the English UI. - t() applies str.format only when given arguments, so a translation can reorder placeholders, and a bad one falls back rather than raising. The picker switches language in place. Gradio bakes each label in when it builds the Blocks tree and cannot rebuild it in another language, so switching means re-labelling every component: a registry maps each component property back to the locale key that produced it, and the handler turns that into one update each. The registry is derived rather than declared. Every localized string already goes through t(), so a component's English text identifies its key by reverse lookup, keeping ~120 call sites free of registration boilerplate. Two cases reverse lookup cannot see, and how they are handled: - Interpolated labels never match their template ("Model list (task=tts)" vs "Model list (task={task})"), so they register explicitly via i18n_register(). - Several keys sharing one English string are ambiguous; _english_key_index() checks whether any locale translates them differently and drops those, leaving the component on its current label rather than risking the wrong one. Switching is process-wide, not per session: Gradio serves one Blocks object to all connections, which suits a local single-user tool. Values the server parses stay English while their labels localize, and the TTS/Voice Design sample texts are only re-localized while untouched, so a switch cannot discard typed input. Gradio's own widget text ("Click to Upload", ...) is compiled into its frontend bundle and picked from the browser locale, so it follows the browser rather than this picker and is left alone; the README says so. Fixed while localizing: - Two loops named their variable `t`, shadowing the translator for the rest of their scope. - The ASR language list built labels as "<localized> <English>", which read as a stutter once the localized name was itself English ("Chinese Chinese"). Comments and docstrings in webui.py are still Chinese and are left for a later pass. model_manager: fetch HF files via huggingface_hub, kigner's fix. Xet-backed repos redirect to a CDN that rejects plain GETs against the resolve URL, so only the hub client can pull their weights. Co-Authored-By: kigner <aidiscovery2045@proton.me>
The upstream author (kigner/audio.cpp-webui) rewrote the i18n layer and added
~1200 lines of features since our fork point. Their scheme is inline
_t(zh, en) calls with Traditional Chinese generated from the Simplified source
by OpenCC, which is mutually exclusive with our locales/*.json key lookup.
Take theirs as the base and re-apply our Linux work on top, rather than hand-
porting every new string into a locale file.
i18n:
- Adopt webui/ui_i18n.py; drop webui/locales/{en,zh}.json and the t()/registry
code they fed. English/Simplified/Traditional all now come from one source.
- Default to English (was Chinese) and reorder the picker to match.
- AUDIOCPP_LANG supplies the default only when nothing has been picked in the
UI yet; a saved pick wins, so a stale env var can't defeat the picker.
Accepts the spellings people actually write (zh_TW, zh-CN, english, ...).
Linux:
- Re-apply EXE_SUFFIX / SERVER_EXE_NAME / SERVER_LAUNCHER and the from-source
build discovery (_cmake_cache_backend, _discover_dev_bin_dirs), which also
fixes _find_gguf_exe: upstream hardcoded build/windows-*-release, so it
could never find a Linux converter build.
- ffmpeg lookup and the "gguf converter not found" messages no longer name
.exe unconditionally.
- requirements.txt: mark pywin32 and the pythonnet/pywebview stack
sys_platform == "win32" so the file installs on Linux.
model_manager: the two branches changed the same download path for different
reasons, so keep both. Upstream's resumable .part/Range downloads and Windows
rename-retry stay; snapshot fetches route through hf_hub_download again, which
is the only way to pull Xet-backed repos (upstream already ships hf-xet but
never wired it in). Added prune_hf_local_dir_cache so the hub client's
.cache/huggingface bookkeeping isn't promoted into installed model dirs.
Tests: 20 pass (test_ui_i18n updated for the English default, plus new cases
for the env-var precedence rule and alias normalization).
test_realtime_pipeline.py is untouched and still needs torch to run.
The try/except that falls back to `webui.ui_i18n` for the test harness also swallowed ImportErrors raised *inside* ui_i18n — most notably a missing opencc — and reported them as "No module named 'webui.ui_i18n'; 'webui' is not a package", which points at the wrong problem. Only fall back when the unresolved name is ui_i18n/webui itself; re-raise anything else so a missing dependency names itself.
The WebUI's on-demand audiocpp_server defaulted to 8080, which on this setup collides with a Windows-side listener (5KPlayer's Airplay.exe). Set the catalog port to 8088. Code fallbacks and the standalone run_server default stay at 8080 by design; override lives in the config.
Adopting upstream's streaming support routed the offline TTS button through the generator do_tts_or_stream, whose immediate first yield replaces Gradio's native "processing X.Xs" indicator with a static "⏳ Generating…" message — so the seconds that used to tick during synthesis showed nothing until the final "用时 X.Xs" status. Run the blocking do_tts in a daemon thread and have the generator yield "⏳ 生成中…已用时 Xs" every ~0.5s until it finishes, restoring the live count (and now also covering the model-reload wait). do_tts already returns (out, msg) and never raises, so no extra error handling is needed.
The 53 _ui_log(...) event lines (model load, TTS/ASR/music start+done, downloads, GGUF conversion, ...) were hardcoded Chinese f-strings, so the console and webui log stayed Chinese even with the English UI default. Route each through _t(zh, en, ...), converting inline f-string expressions into named format kwargs. Also localize the helper fragments they interpolate (resample note, threads note, per-segment note, audio-duration hint) and the remaining non-_ui_log log paths: the _pump_server_output duplicate-line collapser and the required_files read-failure print. Now logs default to English and switch to Chinese with the picker, matching the rest of the UI. Verified: all 258 _t() calls have matching zh/en placeholder sets with every placeholder covered by a kwarg; 20 tests pass.
0c96c49 to
82da724
Compare
done, a new file model_manager_webui.py is created, and the original one is restored |
|
@patrickjchen Merged! This is a big step forward for the project and thank you for the great work! |
|
@patrickjchen I played around with it, and the UI is pretty cool! I organized it a bit to make the webui fully self-contained. |
|
@0xShug0,
That's great! Glad it's merged.
About the next project: have you thought about making it a MCP server? I
think it's necessary to have that functionality, and you are the best
person to define the schema. I can help implement the functionality.
Best regards,
Patrick
…On Tue, Jul 21, 2026 at 2:02 PM 0xShug0 ***@***.***> wrote:
*0xShug0* left a comment (0xShug0/audio.cpp#87)
<#87 (comment)>
@patrickjchen <https://github.com/patrickjchen> I played around with it,
and the UI is pretty cool!
I organized it a bit to make the webui fully self-contained.
—
Reply to this email directly, view it on GitHub
<#87?email_source=notifications&email_token=AE6EWUSB4JQBTUCCCMPFXQL5F6V2HA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBTG4ZTMOBWGIY2M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5037368621>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AE6EWUR5QWY3SHBL44K6JKD5F6V2HAVCNFSNUABGKJSXA33TNF2G64TZHMYTENZYGIZTKMZQGI5US43TOVSTWNBZGM4TSOBSGE2TFILWAI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
@patrickjchen MCP sounds interesting, but I’m currently considering a redesign of the public interface to make UI development, external integrations, and community contributions easier. I'd prefer to stabilize that interface first so MCP can be implemented as a thin adapter instead of becoming coupled to the current framework internals. BTW, would you like to add an English README for the WebUI? The current one is only available in Chinese. |
|
@patrickjchen Would you like to update the Web UI based on the release 0.4? |
…d support for Linux) (0xShug0#87) * webui: add the Gradio WebUI, cross-platform and English-first The WebUI comes from kigner's fork and is the bulk of the logic here: https://github.com/kigner/audio.cpp-webui.git (merged at 8924917). It is a Gradio UI over audiocpp_server that loads one model at a time on demand, with model management, background downloads, per-family request profiles, VRAM budgeting and adaptive chunking for TTS/ASR/VC, and a dialogue mode that chains diarization into ASR. All of that is kigner's work and is carried over as-is. That fork targets Windows only and its interface is Chinese only. What this commit adds is the cross-platform and localization work on top: Cross-platform: - Resolve the server binary by OS convention (.exe only on Windows) instead of assuming audiocpp_server.exe, and name run_server.sh/.bat per platform in the messages that mention it. - Find from-source builds in either layout: build/<os>-<backend>-<type>/bin, where the backend is in the directory name, and a plain `cmake -B build` -> build/bin, which says nothing about the backend and is classified from its CMakeCache (GGML_CUDA:BOOL=ON). Without the second, a stock Linux dev tree found no binary at all and SERVER_EXE pointed at a path that did not exist. - Add run_webui.sh as the POSIX counterpart of run_webui.bat. Localization, English first: - Strings move to webui/locales/<lang>.json, chosen by AUDIOCPP_LANG (default en) or the language picker in the UI. en.json is the per-key fallback, so a partial translation degrades to English rather than showing raw keys, and an unknown or malformed locale warns and falls back instead of taking the UI down. Adding a language is dropping in a file. zh.json keeps the fork's original Chinese. - Catalog entries hold English display_name/input_hint; a locale overrides any of them via catalog.<id>.<field>, which is how zh.json restores the Chinese model names. Without this the catalog's own strings leaked into the English UI. - t() applies str.format only when given arguments, so a translation can reorder placeholders, and a bad one falls back rather than raising. The picker switches language in place. Gradio bakes each label in when it builds the Blocks tree and cannot rebuild it in another language, so switching means re-labelling every component: a registry maps each component property back to the locale key that produced it, and the handler turns that into one update each. The registry is derived rather than declared. Every localized string already goes through t(), so a component's English text identifies its key by reverse lookup, keeping ~120 call sites free of registration boilerplate. Two cases reverse lookup cannot see, and how they are handled: - Interpolated labels never match their template ("Model list (task=tts)" vs "Model list (task={task})"), so they register explicitly via i18n_register(). - Several keys sharing one English string are ambiguous; _english_key_index() checks whether any locale translates them differently and drops those, leaving the component on its current label rather than risking the wrong one. Switching is process-wide, not per session: Gradio serves one Blocks object to all connections, which suits a local single-user tool. Values the server parses stay English while their labels localize, and the TTS/Voice Design sample texts are only re-localized while untouched, so a switch cannot discard typed input. Gradio's own widget text ("Click to Upload", ...) is compiled into its frontend bundle and picked from the browser locale, so it follows the browser rather than this picker and is left alone; the README says so. Fixed while localizing: - Two loops named their variable `t`, shadowing the translator for the rest of their scope. - The ASR language list built labels as "<localized> <English>", which read as a stutter once the localized name was itself English ("Chinese Chinese"). Comments and docstrings in webui.py are still Chinese and are left for a later pass. model_manager: fetch HF files via huggingface_hub, kigner's fix. Xet-backed repos redirect to a CDN that rejects plain GETs against the resolve URL, so only the hub client can pull their weights. Co-Authored-By: kigner <aidiscovery2045@proton.me> * webui: rebase on upstream 3-language UI, keep Linux support The upstream author (kigner/audio.cpp-webui) rewrote the i18n layer and added ~1200 lines of features since our fork point. Their scheme is inline _t(zh, en) calls with Traditional Chinese generated from the Simplified source by OpenCC, which is mutually exclusive with our locales/*.json key lookup. Take theirs as the base and re-apply our Linux work on top, rather than hand- porting every new string into a locale file. i18n: - Adopt webui/ui_i18n.py; drop webui/locales/{en,zh}.json and the t()/registry code they fed. English/Simplified/Traditional all now come from one source. - Default to English (was Chinese) and reorder the picker to match. - AUDIOCPP_LANG supplies the default only when nothing has been picked in the UI yet; a saved pick wins, so a stale env var can't defeat the picker. Accepts the spellings people actually write (zh_TW, zh-CN, english, ...). Linux: - Re-apply EXE_SUFFIX / SERVER_EXE_NAME / SERVER_LAUNCHER and the from-source build discovery (_cmake_cache_backend, _discover_dev_bin_dirs), which also fixes _find_gguf_exe: upstream hardcoded build/windows-*-release, so it could never find a Linux converter build. - ffmpeg lookup and the "gguf converter not found" messages no longer name .exe unconditionally. - requirements.txt: mark pywin32 and the pythonnet/pywebview stack sys_platform == "win32" so the file installs on Linux. model_manager: the two branches changed the same download path for different reasons, so keep both. Upstream's resumable .part/Range downloads and Windows rename-retry stay; snapshot fetches route through hf_hub_download again, which is the only way to pull Xet-backed repos (upstream already ships hf-xet but never wired it in). Added prune_hf_local_dir_cache so the hub client's .cache/huggingface bookkeeping isn't promoted into installed model dirs. Tests: 20 pass (test_ui_i18n updated for the English default, plus new cases for the env-var precedence rule and alias normalization). test_realtime_pipeline.py is untouched and still needs torch to run. * webui: don't mask a missing opencc behind the ui_i18n import fallback The try/except that falls back to `webui.ui_i18n` for the test harness also swallowed ImportErrors raised *inside* ui_i18n — most notably a missing opencc — and reported them as "No module named 'webui.ui_i18n'; 'webui' is not a package", which points at the wrong problem. Only fall back when the unresolved name is ui_i18n/webui itself; re-raise anything else so a missing dependency names itself. * webui: move on-demand server to port 8088 to avoid an 8080 clash The WebUI's on-demand audiocpp_server defaulted to 8080, which on this setup collides with a Windows-side listener (5KPlayer's Airplay.exe). Set the catalog port to 8088. Code fallbacks and the standalone run_server default stay at 8080 by design; override lives in the config. * webui: restore the live elapsed-seconds counter during offline TTS Adopting upstream's streaming support routed the offline TTS button through the generator do_tts_or_stream, whose immediate first yield replaces Gradio's native "processing X.Xs" indicator with a static "⏳ Generating…" message — so the seconds that used to tick during synthesis showed nothing until the final "用时 X.Xs" status. Run the blocking do_tts in a daemon thread and have the generator yield "⏳ 生成中…已用时 Xs" every ~0.5s until it finishes, restoring the live count (and now also covering the model-reload wait). do_tts already returns (out, msg) and never raises, so no extra error handling is needed. * webui: localize console/log messages so they follow the UI language The 53 _ui_log(...) event lines (model load, TTS/ASR/music start+done, downloads, GGUF conversion, ...) were hardcoded Chinese f-strings, so the console and webui log stayed Chinese even with the English UI default. Route each through _t(zh, en, ...), converting inline f-string expressions into named format kwargs. Also localize the helper fragments they interpolate (resample note, threads note, per-segment note, audio-duration hint) and the remaining non-_ui_log log paths: the _pump_server_output duplicate-line collapser and the required_files read-failure print. Now logs default to English and switch to Chinese with the picker, matching the rest of the UI. Verified: all 258 _t() calls have matching zh/en placeholder sets with every placeholder covered by a kwarg; 20 tests pass. * make it work on Windows * restore model_manager.py, and introduce a new one model_manager_webui.py --------- Co-authored-by: Jilong Chen <jilong.chen@gmail.com> Co-authored-by: kigner <aidiscovery2045@proton.me>
…d support for Linux) (0xShug0#87) * webui: add the Gradio WebUI, cross-platform and English-first The WebUI comes from kigner's fork and is the bulk of the logic here: https://github.com/kigner/audio.cpp-webui.git (merged at 8924917). It is a Gradio UI over audiocpp_server that loads one model at a time on demand, with model management, background downloads, per-family request profiles, VRAM budgeting and adaptive chunking for TTS/ASR/VC, and a dialogue mode that chains diarization into ASR. All of that is kigner's work and is carried over as-is. That fork targets Windows only and its interface is Chinese only. What this commit adds is the cross-platform and localization work on top: Cross-platform: - Resolve the server binary by OS convention (.exe only on Windows) instead of assuming audiocpp_server.exe, and name run_server.sh/.bat per platform in the messages that mention it. - Find from-source builds in either layout: build/<os>-<backend>-<type>/bin, where the backend is in the directory name, and a plain `cmake -B build` -> build/bin, which says nothing about the backend and is classified from its CMakeCache (GGML_CUDA:BOOL=ON). Without the second, a stock Linux dev tree found no binary at all and SERVER_EXE pointed at a path that did not exist. - Add run_webui.sh as the POSIX counterpart of run_webui.bat. Localization, English first: - Strings move to webui/locales/<lang>.json, chosen by AUDIOCPP_LANG (default en) or the language picker in the UI. en.json is the per-key fallback, so a partial translation degrades to English rather than showing raw keys, and an unknown or malformed locale warns and falls back instead of taking the UI down. Adding a language is dropping in a file. zh.json keeps the fork's original Chinese. - Catalog entries hold English display_name/input_hint; a locale overrides any of them via catalog.<id>.<field>, which is how zh.json restores the Chinese model names. Without this the catalog's own strings leaked into the English UI. - t() applies str.format only when given arguments, so a translation can reorder placeholders, and a bad one falls back rather than raising. The picker switches language in place. Gradio bakes each label in when it builds the Blocks tree and cannot rebuild it in another language, so switching means re-labelling every component: a registry maps each component property back to the locale key that produced it, and the handler turns that into one update each. The registry is derived rather than declared. Every localized string already goes through t(), so a component's English text identifies its key by reverse lookup, keeping ~120 call sites free of registration boilerplate. Two cases reverse lookup cannot see, and how they are handled: - Interpolated labels never match their template ("Model list (task=tts)" vs "Model list (task={task})"), so they register explicitly via i18n_register(). - Several keys sharing one English string are ambiguous; _english_key_index() checks whether any locale translates them differently and drops those, leaving the component on its current label rather than risking the wrong one. Switching is process-wide, not per session: Gradio serves one Blocks object to all connections, which suits a local single-user tool. Values the server parses stay English while their labels localize, and the TTS/Voice Design sample texts are only re-localized while untouched, so a switch cannot discard typed input. Gradio's own widget text ("Click to Upload", ...) is compiled into its frontend bundle and picked from the browser locale, so it follows the browser rather than this picker and is left alone; the README says so. Fixed while localizing: - Two loops named their variable `t`, shadowing the translator for the rest of their scope. - The ASR language list built labels as "<localized> <English>", which read as a stutter once the localized name was itself English ("Chinese Chinese"). Comments and docstrings in webui.py are still Chinese and are left for a later pass. model_manager: fetch HF files via huggingface_hub, kigner's fix. Xet-backed repos redirect to a CDN that rejects plain GETs against the resolve URL, so only the hub client can pull their weights. Co-Authored-By: kigner <aidiscovery2045@proton.me> * webui: rebase on upstream 3-language UI, keep Linux support The upstream author (kigner/audio.cpp-webui) rewrote the i18n layer and added ~1200 lines of features since our fork point. Their scheme is inline _t(zh, en) calls with Traditional Chinese generated from the Simplified source by OpenCC, which is mutually exclusive with our locales/*.json key lookup. Take theirs as the base and re-apply our Linux work on top, rather than hand- porting every new string into a locale file. i18n: - Adopt webui/ui_i18n.py; drop webui/locales/{en,zh}.json and the t()/registry code they fed. English/Simplified/Traditional all now come from one source. - Default to English (was Chinese) and reorder the picker to match. - AUDIOCPP_LANG supplies the default only when nothing has been picked in the UI yet; a saved pick wins, so a stale env var can't defeat the picker. Accepts the spellings people actually write (zh_TW, zh-CN, english, ...). Linux: - Re-apply EXE_SUFFIX / SERVER_EXE_NAME / SERVER_LAUNCHER and the from-source build discovery (_cmake_cache_backend, _discover_dev_bin_dirs), which also fixes _find_gguf_exe: upstream hardcoded build/windows-*-release, so it could never find a Linux converter build. - ffmpeg lookup and the "gguf converter not found" messages no longer name .exe unconditionally. - requirements.txt: mark pywin32 and the pythonnet/pywebview stack sys_platform == "win32" so the file installs on Linux. model_manager: the two branches changed the same download path for different reasons, so keep both. Upstream's resumable .part/Range downloads and Windows rename-retry stay; snapshot fetches route through hf_hub_download again, which is the only way to pull Xet-backed repos (upstream already ships hf-xet but never wired it in). Added prune_hf_local_dir_cache so the hub client's .cache/huggingface bookkeeping isn't promoted into installed model dirs. Tests: 20 pass (test_ui_i18n updated for the English default, plus new cases for the env-var precedence rule and alias normalization). test_realtime_pipeline.py is untouched and still needs torch to run. * webui: don't mask a missing opencc behind the ui_i18n import fallback The try/except that falls back to `webui.ui_i18n` for the test harness also swallowed ImportErrors raised *inside* ui_i18n — most notably a missing opencc — and reported them as "No module named 'webui.ui_i18n'; 'webui' is not a package", which points at the wrong problem. Only fall back when the unresolved name is ui_i18n/webui itself; re-raise anything else so a missing dependency names itself. * webui: move on-demand server to port 8088 to avoid an 8080 clash The WebUI's on-demand audiocpp_server defaulted to 8080, which on this setup collides with a Windows-side listener (5KPlayer's Airplay.exe). Set the catalog port to 8088. Code fallbacks and the standalone run_server default stay at 8080 by design; override lives in the config. * webui: restore the live elapsed-seconds counter during offline TTS Adopting upstream's streaming support routed the offline TTS button through the generator do_tts_or_stream, whose immediate first yield replaces Gradio's native "processing X.Xs" indicator with a static "⏳ Generating…" message — so the seconds that used to tick during synthesis showed nothing until the final "用时 X.Xs" status. Run the blocking do_tts in a daemon thread and have the generator yield "⏳ 生成中…已用时 Xs" every ~0.5s until it finishes, restoring the live count (and now also covering the model-reload wait). do_tts already returns (out, msg) and never raises, so no extra error handling is needed. * webui: localize console/log messages so they follow the UI language The 53 _ui_log(...) event lines (model load, TTS/ASR/music start+done, downloads, GGUF conversion, ...) were hardcoded Chinese f-strings, so the console and webui log stayed Chinese even with the English UI default. Route each through _t(zh, en, ...), converting inline f-string expressions into named format kwargs. Also localize the helper fragments they interpolate (resample note, threads note, per-segment note, audio-duration hint) and the remaining non-_ui_log log paths: the _pump_server_output duplicate-line collapser and the required_files read-failure print. Now logs default to English and switch to Chinese with the picker, matching the rest of the UI. Verified: all 258 _t() calls have matching zh/en placeholder sets with every placeholder covered by a kwarg; 20 tests pass. * make it work on Windows * restore model_manager.py, and introduce a new one model_manager_webui.py --------- Co-authored-by: Jilong Chen <jilong.chen@gmail.com> Co-authored-by: kigner <aidiscovery2045@proton.me>
i18n:
- Adopt webui/ui_i18n.py:English/Simplified/Traditional all now come from one source.
- Default to English (was Chinese) and reorder the picker to match.
- AUDIOCPP_LANG supplies the default only when nothing has been picked in the
UI yet; a saved pick wins, so a stale env var can't defeat the picker.
Accepts the spellings people actually write (zh_TW, zh-CN, english, ...).
Linux:
- Re-apply EXE_SUFFIX / SERVER_EXE_NAME / SERVER_LAUNCHER and the from-source
build discovery (_cmake_cache_backend, _discover_dev_bin_dirs), which also
fixes _find_gguf_exe: upstream hardcoded build/windows-*-release, so it
could never find a Linux converter build.
- ffmpeg lookup and the "gguf converter not found" messages no longer name
.exe unconditionally.
- requirements.txt: mark pywin32 and the pythonnet/pywebview stack
sys_platform == "win32" so the file installs on Linux.