1- """废弃免费 YUI 预设音色的自动平移(PR: voice-tone-R6NtLH3Hk0 → 现役 yui_cn )。
1+ """废弃免费 YUI 预设音色的自动平移(PR: voice-tone-R6NtLH3Hk0 → 现役 YUI 音色 )。
22
33回归点:YUI 默认音色 ID 更替后,存量用户 characters.json 里残留的旧 tone ID
44已不在 free_voices 白名单,cleanup_invalid_voice_ids 会判 invalid 清空 → 空
55voice 落到 free/step 的 default_voice(qingchunshaonv),导致默认 YUI 用户无声
6- 掉档到通用女声。cleanup 在判 invalid 前先把旧值平移到现役 yui_cn 兜住。
6+ 掉档到通用女声。cleanup 在判 invalid 前先把旧值按线路平移:国内 free → 现役
7+ yui_cn;海外 free → 品牌 sentinel "yui"(free_intl native),与
8+ ensure_default_yui_voice_for_free_api 对偶。
79"""
810from __future__ import annotations
911
1315OLD_YUI_VOICE_ID = "voice-tone-R6NtLH3Hk0"
1416NEW_YUI_VOICE_ID = "voice-tone-RcH2svtsrw"
1517
18+ _DOMESTIC_FREE = {"CORE_API_TYPE" : "free" , "CORE_URL" : "wss://www.lanlan.tech/core" }
19+ _OVERSEAS_FREE = {"CORE_API_TYPE" : "free" , "CORE_URL" : "wss://www.lanlan.app/core" }
20+ _NON_FREE = {"CORE_API_TYPE" : "qwen" , "CORE_URL" : "" }
1621
17- def _make_manager (character_data : dict ) -> ConfigManager :
22+
23+ def _make_manager (character_data : dict , core_config : dict | None = None ,
24+ non_mainland : bool = False ) -> ConfigManager :
1825 mgr = object .__new__ (ConfigManager )
1926 mgr ._saved = {}
2027 mgr .load_characters = lambda : character_data
@@ -23,6 +30,8 @@ def _save(data):
2330 mgr ._saved ["data" ] = data
2431
2532 mgr .save_characters = _save
33+ mgr .get_core_config = lambda : dict (core_config if core_config is not None else _DOMESTIC_FREE )
34+ mgr ._check_non_mainland = lambda : non_mainland
2635 return mgr
2736
2837
@@ -62,6 +71,38 @@ def test_cleanup_migrates_whitespace_padded_deprecated_voice(monkeypatch):
6271 assert mgr ._saved .get ("data" ) is character_data
6372
6473
74+ def test_cleanup_overseas_free_remaps_to_yui_sentinel (monkeypatch ):
75+ """海外免费(lanlan.app)下废弃 StepFun tone 应绑品牌 sentinel "yui",
76+ 而非国内 voice-tone preset——否则非空 voice_id 会落进 external TTS。"""
77+ _patch_free_voices (monkeypatch , {"yui_cn" : NEW_YUI_VOICE_ID })
78+ character_data = _yui (OLD_YUI_VOICE_ID )
79+ mgr = _make_manager (character_data , core_config = _OVERSEAS_FREE )
80+
81+ cleaned , legacy = mgr .cleanup_invalid_voice_ids ()
82+
83+ assert cleaned == 0
84+ assert get_reserved (character_data ["猫娘" ]["YUI" ], "voice_id" , default = "" ) == "yui"
85+
86+
87+ def test_cleanup_overseas_by_geo_remaps_to_yui_sentinel (monkeypatch ):
88+ """URL 仍是 lanlan.tech 但地理判海外时,靠 _check_non_mainland 兜底绑 "yui"。"""
89+ _patch_free_voices (monkeypatch , {"yui_cn" : NEW_YUI_VOICE_ID })
90+ character_data = _yui (OLD_YUI_VOICE_ID )
91+ mgr = _make_manager (character_data , core_config = _DOMESTIC_FREE , non_mainland = True )
92+
93+ mgr .cleanup_invalid_voice_ids ()
94+
95+ assert get_reserved (character_data ["猫娘" ]["YUI" ], "voice_id" , default = "" ) == "yui"
96+
97+
98+ def test_remap_non_free_route_keeps_value (monkeypatch ):
99+ """非 free 路由(如 qwen)下废弃 StepFun preset 用不上,不迁移、交清空兜底。"""
100+ _patch_free_voices (monkeypatch , {"yui_cn" : NEW_YUI_VOICE_ID })
101+ mgr = _make_manager ({}, core_config = _NON_FREE )
102+
103+ assert mgr .remap_deprecated_free_yui_voice_id (OLD_YUI_VOICE_ID ) == OLD_YUI_VOICE_ID
104+
105+
65106def test_cleanup_keeps_current_yui_voice_untouched (monkeypatch ):
66107 _patch_free_voices (monkeypatch , {"yui_cn" : NEW_YUI_VOICE_ID })
67108 character_data = _yui (NEW_YUI_VOICE_ID )
@@ -104,18 +145,18 @@ def test_cleanup_clears_whitespace_padded_invalid_voice(monkeypatch):
104145
105146
106147def test_remap_requires_yui_cn_not_other_preset (monkeypatch ):
107- """回归(Codex):free_voices 缺 yui_cn、只有别的 preset 时不得借 cuteGirl
108- 等当替身把废弃 YUI 串成别的音色——原样返回,交清空兜底。"""
148+ """回归(Codex):国内 free 但 free_voices 缺 yui_cn、只有别的 preset 时不得
149+ 借 cuteGirl 等当替身把废弃 YUI 串成别的音色——原样返回,交清空兜底。"""
109150 _patch_free_voices (monkeypatch , {"cuteGirl" : "voice-tone-PGLiyZt65w" })
110- mgr = object . __new__ ( ConfigManager )
151+ mgr = _make_manager ({} )
111152
112153 assert mgr .remap_deprecated_free_yui_voice_id (OLD_YUI_VOICE_ID ) == OLD_YUI_VOICE_ID
113154
114155
115156def test_remap_keeps_deprecated_when_current_unresolvable (monkeypatch ):
116- """现役 yui_cn 解析不出(free_voices 为空)时不乱换。"""
157+ """国内 free 但现役 yui_cn 解析不出(free_voices 为空)时不乱换。"""
117158 _patch_free_voices (monkeypatch , {})
118- mgr = object . __new__ ( ConfigManager )
159+ mgr = _make_manager ({} )
119160
120161 assert mgr .remap_deprecated_free_yui_voice_id (OLD_YUI_VOICE_ID ) == OLD_YUI_VOICE_ID
121162 assert mgr .remap_deprecated_free_yui_voice_id (NEW_YUI_VOICE_ID ) == NEW_YUI_VOICE_ID
0 commit comments