dsh-boot-suite 2.15.15 → 2.15.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [2.15.17] — 2026-11-02
|
|
2
|
+
|
|
3
|
+
- **字幕不再提前消失**:每句配音的中文台词与波形改为跟随该句音频的真实播放时钟(Audio 元素的 currentTime / ended),不再按动画时间轴推算;即使主线程卡顿导致音频晚开始,字幕也会和语音同时出现、同时结束。
|
|
4
|
+
- **掉线彩蛋字幕同步**:彩蛋里「警告:特工已离线。」同样跟随 voice-line4 的播放时钟,不会在语音结束前消失。
|
|
5
|
+
- 版本号 2.15.17,构建号 cw-65。
|
|
6
|
+
|
|
7
|
+
## [2.15.16] — 2026-11-02
|
|
8
|
+
|
|
9
|
+
- **掉线彩蛋不再硬切配音**:如果掉线正好触发在某句配音播放期间,先等这句播完(含中文字幕)再弹出彩蛋,不会出现「语音没说完、字幕先消失」;有 0.9s 兜底超时与动画结束保护。
|
|
10
|
+
- 版本号 2.15.16,构建号 cw-64。
|
|
11
|
+
|
|
1
12
|
## [2.15.15] — 2026-11-02
|
|
2
13
|
|
|
3
14
|
- **重连提示音接入插件配音效果**:`voice-reconnect` 现在与其余配音一样走「配音效果」(AI 机械音/机器人/无线电/老电视/低沉),不再是无效果的原声。
|
package/lib/client.js
CHANGED
|
@@ -269,7 +269,7 @@ window.__ModuleLoader__.load({
|
|
|
269
269
|
—— 桌面端走浏览器半侧自举时没有 1,就靠这一条
|
|
270
270
|
3) 下面这个常量:两份都没拿到时的兜底,别让面板显示 undefined
|
|
271
271
|
版本号本身只在 lib/index.js 里读一次,这里不重复维护。 */
|
|
272
|
-
var FALLBACK_VERSION = '2.15.
|
|
272
|
+
var FALLBACK_VERSION = '2.15.17'
|
|
273
273
|
/* 手动选包成功后置 true:让还在后台跑的“浏览器下载监听”不要再弹第二次更新器 */
|
|
274
274
|
var __manualUpdateApplied = false
|
|
275
275
|
function readInjectedVersion() {
|
|
@@ -2022,7 +2022,7 @@ window.__ModuleLoader__.load({
|
|
|
2022
2022
|
/* 每次改客户端就把这个号 +1。
|
|
2023
2023
|
有了它在控制台一眼就能判断"浏览器里跑的是不是最新那份" ——
|
|
2024
2024
|
排查问题时,分不清是代码问题还是旧包,会白猜一轮。 */
|
|
2025
|
-
var BUILD = 'cw-
|
|
2025
|
+
var BUILD = 'cw-65'
|
|
2026
2026
|
|
|
2027
2027
|
function log() {
|
|
2028
2028
|
try {
|
package/package.json
CHANGED
|
@@ -77,7 +77,19 @@ function render(){
|
|
|
77
77
|
vOn=true;
|
|
78
78
|
}else{
|
|
79
79
|
const vsched=(window.__VSCHED__&&window.__VSCHED__.length)?window.__VSCHED__:(window.VOICE_SCHED||[]).map(function(s){var atV=(s.at<=9.2)?s.at-3.4:5.8+(s.at-9.2)/1.5;return{key:s.key,at:atV,dur:s.dur};});
|
|
80
|
-
|
|
80
|
+
/* v2.15.17:有配音时,字幕/波形只跟「真正在播的那句」走(音频时钟),
|
|
81
|
+
避免时间轴窗口结束得比语音早、字幕提前消失。静音模式仍按时间轴显示。 */
|
|
82
|
+
const audioOn=!!window.__DSH_AUDIO_ON__, avNow=window.__DSH_ACTIVE_VOICE__||'', aNow=+window.__DSH_AUDIO_T__||0, aPlaying=!!window.__DSH_AUDIO_PLAYING__;
|
|
83
|
+
for(const s of vsched){
|
|
84
|
+
let vt=null;
|
|
85
|
+
if(audioOn){if(avNow===s.key&&aPlaying)vt=Math.max(0,aNow);}
|
|
86
|
+
else if(VT>=s.at-0.02&&VT<s.at+s.dur)vt=VT-s.at;
|
|
87
|
+
if(vt==null)continue;
|
|
88
|
+
sub=(cfg.voice&&cfg.voice.subs&&cfg.voice.subs[s.key])||"";
|
|
89
|
+
const e=(window.VOICE_ENV||{})[s.key];
|
|
90
|
+
if(e&&e.length){vh=Array.from({length:20},(_,k)=>{const st=Math.max(0,vt-(19-k)*0.055);const fi=st/0.008;const ai=Math.min(e.length-1,Math.floor(fi));const af=fi-Math.floor(fi);const a=((e[ai]||0)*(1-af)+(e[Math.min(e.length-1,ai+1)]||0)*af);return 4+Math.min(360,Math.pow(Math.max(0,a),0.9)*360)});vOn=true;}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
81
93
|
}
|
|
82
94
|
const h=vh||Array(20).fill(4);const voiceVisible=vOn?1:0;renderVoiceRow($('voice'),$('subtitle'),h,sub,voiceVisible);$('seek').value=t;$('time').textContent=(t-(+cfg.startAt||0)).toFixed(2)+' / '+(cfg.duration-(+cfg.startAt||0)).toFixed(2)+' s';
|
|
83
95
|
}
|
|
@@ -118,14 +130,14 @@ $('prevFrame').onclick=()=>{pause();seek(t-1/60)};$('nextFrame').onclick=()=>{pa
|
|
|
118
130
|
function bgmSig(on){try{if(parent&&parent!==window)parent.postMessage({type:'dsh-startup-bgm',on:!!on},'*')}catch(e){}}
|
|
119
131
|
var sched=vc.sched||[[4.6,'orig1',2.65],[8.0,'line1',3.375],[12.8,'line2',3.232],[20.0,'line3',3.300],[25.45,'orig3',2.77]];
|
|
120
132
|
var eggLineAt=+(vc.eggLineAt!=null?vc.eggLineAt:2.0), eggReplayWait=+(vc.eggReplayWait!=null?vc.eggReplayWait:2.0), line4Dur=+(vc.line4Dur||1.911);
|
|
121
|
-
var aud={},played={},activeVoice=null,cueGate=false,cueFailTimer=null;window.__DSH_AUDIO_ON__=!mute;window.__DSH_ACTIVE_VOICE__='';
|
|
122
|
-
sched.forEach(function(e){var k=e[1];if(!mute&&vc[k]){var a=new Audio(vc[k]);a.preload='auto';a.volume=voiceVol;a.addEventListener('ended',function(){if(activeVoice===k){activeVoice=null;window.__DSH_ACTIVE_VOICE__='';}voiceDuck(false)});aud[k]=a;played[k]=false;}});
|
|
133
|
+
var aud={},played={},activeVoice=null,cueGate=false,cueFailTimer=null;window.__DSH_AUDIO_ON__=!mute;window.__DSH_ACTIVE_VOICE__='';window.__DSH_AUDIO_T__=-1;window.__DSH_AUDIO_PLAYING__=false;
|
|
134
|
+
sched.forEach(function(e){var k=e[1];if(!mute&&vc[k]){var a=new Audio(vc[k]);a.preload='auto';a.volume=voiceVol;a.addEventListener('ended',function(){if(activeVoice===k){activeVoice=null;window.__DSH_ACTIVE_VOICE__='';}window.__DSH_AUDIO_PLAYING__=false;window.__DSH_AUDIO_T__=-1;voiceDuck(false)});aud[k]=a;played[k]=false;}});
|
|
123
135
|
if(!mute&&vc.reconnect){var rc=new Audio(vc.reconnect);rc.preload='auto';rc.volume=voiceVol;rc.addEventListener('ended',function(){if(activeVoice==='reconnect'){activeVoice=null;window.__DSH_ACTIVE_VOICE__=''}window.__DSH_CUE__='';window.__DSH_CUE_UNTIL__=0;cueGate=false;voiceDuck(false)});aud.reconnect=rc;played.reconnect=true;}
|
|
124
136
|
/* v2.15.14:重连提示音只播自己这一行;播完(ended)才解除 cueGate,后续 line2/line3/orig3 才允许开始 */
|
|
125
137
|
function playReconnectCue(){if(mute||!aud.reconnect)return;cueGate=true;setTimeout(function(){try{attachVoiceFx('reconnect');aud.reconnect.currentTime=0;aud.reconnect.play().catch(function(){})}catch(e){}activeVoice='reconnect';window.__DSH_ACTIVE_VOICE__='';var txt=(cfg.voice&&cfg.voice.subs&&cfg.voice.subs.reconnect)||'';var d=isFinite(aud.reconnect.duration)&&aud.reconnect.duration>0?aud.reconnect.duration:2.82;window.__DSH_CUE__=txt;window.__DSH_CUE_UNTIL__=performance.now()+d*1000+150;voiceDuck(true);if(cueFailTimer)clearTimeout(cueFailTimer);
|
|
126
138
|
/* 兜底:音频若因自动播放策略被拒,ended 不会触发;到时强制解除门控,避免后续配音全被卡住 */
|
|
127
139
|
cueFailTimer=setTimeout(function(){if(activeVoice==='reconnect'){activeVoice=null;window.__DSH_ACTIVE_VOICE__='';window.__DSH_CUE__='';window.__DSH_CUE_UNTIL__=0;cueGate=false;voiceDuck(false)}},d*1000+1500)},500)}
|
|
128
|
-
if(!mute&&vc.line4){var l4=new Audio(vc.line4);l4.preload='auto';l4.volume=voiceVol;l4.addEventListener('ended',function(){if(activeVoice==='line4'){activeVoice=null;window.__DSH_ACTIVE_VOICE__='';}voiceDuck(false)});aud.line4=l4;played.line4=false;}
|
|
140
|
+
if(!mute&&vc.line4){var l4=new Audio(vc.line4);l4.preload='auto';l4.volume=voiceVol;l4.addEventListener('ended',function(){if(activeVoice==='line4'){activeVoice=null;window.__DSH_ACTIVE_VOICE__='';}window.__DSH_AUDIO_PLAYING__=false;window.__DSH_AUDIO_T__=-1;voiceDuck(false)});aud.line4=l4;played.line4=false;}
|
|
129
141
|
/* v2.15.5:模板内配音效果(与全局 5 个声音效果同参数);只在宿主注入 voiceFx 时生效。 */
|
|
130
142
|
var VOICE_FX_PRESETS={'ai-mech':{hp:130,lp:4200,peak:{f:260,g:4,q:1.0},drive:0.15,ring:0},'robot':{hp:180,lp:3200,peak:{f:1500,g:3,q:0.9},drive:0.35,ring:42},'radio':{hp:420,lp:2800,peak:{f:1800,g:5,q:1.2},drive:0.6,ring:0},'oldtv':{hp:220,lp:2200,peak:null,drive:0.45,ring:0},'dark':{hp:90,lp:1200,peak:{f:180,g:5,q:0.8},drive:0.1,ring:0}};
|
|
131
143
|
var voiceFxMode=String((cfg&&cfg.voiceFx)||'none');
|
|
@@ -167,21 +179,25 @@ $('prevFrame').onclick=()=>{pause();seek(t-1/60)};$('nextFrame').onclick=()=>{pa
|
|
|
167
179
|
eggSub.setAttribute('font-family',"'DIN Alternate','Bahnschrift','Microsoft YaHei',sans-serif");
|
|
168
180
|
eggSvg.appendChild(eggRow);eggSvg.appendChild(eggSub);eggEl.appendChild(eggSvg);
|
|
169
181
|
}
|
|
170
|
-
|
|
182
|
+
/* v2.15.17:彩蛋里的中文台词改为跟随 voice-line4 的真实播放时钟,不再用墙钟推算。
|
|
183
|
+
这样即使彩蛋的 setTimeout 被主线程拖慢,字幕也会和语音同时出现、同时结束。 */
|
|
184
|
+
function eggVoice(){
|
|
171
185
|
ensureEggVoice();
|
|
186
|
+
var a=aud.line4, live=!!(window.__DSH_AUDIO_ON__&&a&&!a.paused&&!a.ended&&window.__DSH_ACTIVE_VOICE__==='line4');
|
|
187
|
+
var vt=live?Math.max(0,+a.currentTime||0):-1;
|
|
172
188
|
var e=(window.VOICE_ENV||{}).line4||[], vh=Array(20).fill(4);
|
|
173
|
-
if(
|
|
189
|
+
if(live&&vt<line4Dur+0.25&&e.length){
|
|
174
190
|
var fi=vt/0.008, ai=Math.min(e.length-1,Math.floor(fi)), af=fi-Math.floor(fi);
|
|
175
191
|
var amp=((e[ai]||0)*(1-af)+(e[Math.min(e.length-1,ai+1)]||0)*af);
|
|
176
192
|
vh=[];for(var k=0;k<20;k++){var tv2=vt-(19-k)*0.06;var f2=tv2/0.008,i2=Math.min(e.length-1,Math.floor(f2));var a2=((e[i2]||0)*(1-(f2-Math.floor(f2)))+(e[Math.min(e.length-1,i2+1)]||0)*(f2-Math.floor(f2)));vh.push(4+Math.min(440,Math.pow(Math.max(0,a2),0.85)*440))}
|
|
177
193
|
}
|
|
178
|
-
var show=
|
|
194
|
+
var show=live?1:0;
|
|
179
195
|
var st=show?((cfg.voice&&cfg.voice.subs&&cfg.voice.subs.line4)||''):'';
|
|
180
196
|
renderVoiceRow(eggRow,eggSub,vh,st,show);
|
|
181
197
|
}
|
|
182
|
-
var shown=false,done=false,rolled=false,v3Ended=false,waitStart=0,v4Timer=null,replayTimer=null,eggShownAt=0;
|
|
198
|
+
var shown=false,done=false,rolled=false,v3Ended=false,waitStart=0,v4Timer=null,replayTimer=null,eggShownAt=0,eggWait=false,eggWaitTimer=null;
|
|
183
199
|
var hit=q.get('egg')==='1'||Math.random()<EGG_CHANCE;
|
|
184
|
-
function stopVoice(){voiceDuck(false);activeVoice=null;window.__DSH_ACTIVE_VOICE__='';window.__DSH_CUE__='';window.__DSH_CUE_UNTIL__=0;cueGate=false;if(cueFailTimer){clearTimeout(cueFailTimer);cueFailTimer=null}Object.keys(aud).forEach(function(k){var a=aud[k];try{a.pause();a.currentTime=0}catch(e){}});if(v4Timer){clearTimeout(v4Timer);v4Timer=null}if(replayTimer){clearTimeout(replayTimer);replayTimer=null}}
|
|
200
|
+
function stopVoice(){voiceDuck(false);activeVoice=null;window.__DSH_ACTIVE_VOICE__='';window.__DSH_AUDIO_PLAYING__=false;window.__DSH_AUDIO_T__=-1;window.__DSH_CUE__='';window.__DSH_CUE_UNTIL__=0;cueGate=false;if(cueFailTimer){clearTimeout(cueFailTimer);cueFailTimer=null}Object.keys(aud).forEach(function(k){var a=aud[k];try{a.pause();a.currentTime=0}catch(e){}});if(v4Timer){clearTimeout(v4Timer);v4Timer=null}if(replayTimer){clearTimeout(replayTimer);replayTimer=null}}
|
|
185
201
|
function playKey(k,offset){var a=aud[k];if(a&&!played[k]){played[k]=true;attachVoiceFx(k);try{a.currentTime=Math.max(0,offset||0);a.play().catch(function(){});activeVoice=k;window.__DSH_ACTIVE_VOICE__=k}catch(e){}voiceDuck(true);}}
|
|
186
202
|
function resetPlayedAfter(t0){
|
|
187
203
|
Object.keys(played).forEach(function(k){played[k]=false});v3Ended=false;
|
|
@@ -193,16 +209,16 @@ $('prevFrame').onclick=()=>{pause();seek(t-1/60)};$('nextFrame').onclick=()=>{pa
|
|
|
193
209
|
});
|
|
194
210
|
}
|
|
195
211
|
function showEgg(){
|
|
196
|
-
if(shown||done)return; shown=true; rolled=true; playing=false;
|
|
212
|
+
if(shown||done)return; shown=true; rolled=true; playing=false;eggWait=false;if(eggWaitTimer){clearTimeout(eggWaitTimer);eggWaitTimer=null}
|
|
197
213
|
stopVoice();
|
|
198
214
|
bgmSig(false);
|
|
199
215
|
eggEl.classList.add('on'); eggEl.setAttribute('aria-hidden','false'); try{document.body.classList.add('egg-on')}catch(e){}
|
|
200
|
-
eggShownAt=performance.now(); eggVoice(
|
|
216
|
+
eggShownAt=performance.now(); eggVoice();
|
|
201
217
|
v4Timer=setTimeout(function(){playKey('line4')},eggLineAt*1000);
|
|
202
218
|
/* v2.15.6:不再自动 reconnect——彩蛋必须等用户点【重新连接】,否则动画会自己往下走、声音叠在一起。 */
|
|
203
219
|
render();
|
|
204
220
|
}
|
|
205
|
-
function eggFrame(sec){showEgg();stopVoice();window.__EGG_T=Math.max(0,sec);eggShownAt=performance.now()-window.__EGG_T*1000;if(sec>=eggLineAt)playKey('line4');eggVoice(
|
|
221
|
+
function eggFrame(sec){showEgg();stopVoice();window.__EGG_T=Math.max(0,sec);eggShownAt=performance.now()-window.__EGG_T*1000;if(sec>=eggLineAt)playKey('line4');eggVoice();}
|
|
206
222
|
function hideEgg(){eggEl.classList.remove('on');eggEl.setAttribute('aria-hidden','true');try{document.body.classList.remove('egg-on')}catch(e){}shown=false;}
|
|
207
223
|
/* v2.15.14:重连后不再续播断句(原来会把 line1 从半句接着放,和 reconnect 叠在一起)。
|
|
208
224
|
顺序:等待 0.5s → 只播 reconnect(独占一行)→ 播完解除 cueGate → line2/line3/orig3 按时间线继续。 */
|
|
@@ -220,9 +236,27 @@ $('prevFrame').onclick=()=>{pause();seek(t-1/60)};$('nextFrame').onclick=()=>{pa
|
|
|
220
236
|
if(aud.orig3)aud.orig3.addEventListener('ended',function(){v3Ended=true});
|
|
221
237
|
(function watch(){
|
|
222
238
|
if(!done){
|
|
239
|
+
/* v2.15.17:把当前配音的真实播放状态镜像到 window,供 render()/eggVoice() 使用,
|
|
240
|
+
让字幕与波形以音频时钟为准,而不是按时间轴推算。 */
|
|
241
|
+
var __av=activeVoice?aud[activeVoice]:null;
|
|
242
|
+
if(__av){window.__DSH_AUDIO_T__=+__av.currentTime||0;window.__DSH_AUDIO_DUR__=+__av.duration||0;window.__DSH_AUDIO_PLAYING__=!__av.paused&&!__av.ended;}
|
|
223
243
|
if(playing&&!cueGate){sched.forEach(function(e){if(t>=e[0])playKey(e[1]);});}
|
|
224
|
-
|
|
225
|
-
if(shown){
|
|
244
|
+
/* v2.15.16:掉线彩蛋若正好赶上某句配音,先等这句播完再弹出,避免语音和字幕被硬切断(有兜底超时)。 */
|
|
245
|
+
if(!rolled&&playing&&!shown&&t>=eggAt){
|
|
246
|
+
if(!hit){rolled=true;}
|
|
247
|
+
else if(!eggWait&&activeVoice&&aud[activeVoice]&&!aud[activeVoice].paused&&!aud[activeVoice].ended){
|
|
248
|
+
var eggA=aud[activeVoice], eggLeft=(+eggA.duration||0)-(+eggA.currentTime||0);
|
|
249
|
+
if(isFinite(eggLeft)&&eggLeft>0.05&&t+eggLeft<=cfg.duration-0.25){
|
|
250
|
+
eggWait=true;
|
|
251
|
+
(function(a){
|
|
252
|
+
var fired=false,fin=function(){if(fired)return;fired=true;eggWait=false;eggWaitTimer=null;rolled=true;showEgg();};
|
|
253
|
+
a.addEventListener('ended',fin,{once:true});
|
|
254
|
+
eggWaitTimer=setTimeout(fin,eggLeft*1000+900);
|
|
255
|
+
})(eggA);
|
|
256
|
+
} else {rolled=true;showEgg();}
|
|
257
|
+
} else if(!eggWait){rolled=true;showEgg();}
|
|
258
|
+
}
|
|
259
|
+
if(shown){eggVoice();}
|
|
226
260
|
if(!shown&&t>=cfg.duration-0.02){if(!waitStart){waitStart=performance.now();window.__WAIT_START__=waitStart;}var waitVoice=(!mute&&aud.orig3&&!v3Ended&&(performance.now()-waitStart<4000));if(!waitVoice)flashDone();}
|
|
227
261
|
}
|
|
228
262
|
requestAnimationFrame(watch);
|
package/test/smoke-package.mjs
CHANGED
|
@@ -115,4 +115,10 @@ if (!bigfishApp.includes('cueFailTimer')) throw new Error('重连提示音缺少
|
|
|
115
115
|
if (!bigfishApp.includes("attachVoiceFx('reconnect')")) throw new Error('重连提示音未接入配音效果')
|
|
116
116
|
if (!bigfishApp.includes("[20.0,'line3',3.300]")) throw new Error('app.js 默认时间线未把 line3 顺延到 20.0s')
|
|
117
117
|
if (!bigfishCfg.includes("[20.0,'line3',3.300]")) throw new Error('config.js 时间线未把 line3 顺延到 20.0s')
|
|
118
|
+
/* v2.15.16:掉线彩蛋等当前配音播完再弹出,不硬切语音/字幕 */
|
|
119
|
+
if (!bigfishApp.includes('掉线彩蛋若正好赶上某句配音')) throw new Error('app.js 缺少彩蛋等待当前配音播完的逻辑')
|
|
120
|
+
if (!bigfishApp.includes('eggWait=true')) throw new Error('app.js 缺少 eggWait 等待状态')
|
|
121
|
+
/* v2.15.17:字幕/波形跟随配音真实播放时钟 */
|
|
122
|
+
if (!bigfishApp.includes('__DSH_AUDIO_T__')) throw new Error('app.js 缺少配音真实时钟镜像 __DSH_AUDIO_T__')
|
|
123
|
+
if (!bigfishApp.includes('function eggVoice(){')) throw new Error('app.js 彩蛋字幕未改为音频时钟驱动')
|
|
118
124
|
console.log('ok package.json version=' + pkg.version + ',无 BOM,关键文件齐全(含更新前回滚 checkpoint)');
|
|
@@ -463,7 +463,7 @@ ok(intervals.every((t) => t.cleared) || intervals.length === 0, '旧的 interval
|
|
|
463
463
|
intervals.map((t) => (t.cleared ? 'cleared' : 'live')).join(','))
|
|
464
464
|
|
|
465
465
|
console.log('\n=== ⑦ v2.13.0 测试钩子 / BUILD ===')
|
|
466
|
-
ok(mod.__BUILD === 'cw-
|
|
466
|
+
ok(mod.__BUILD === 'cw-65', '__BUILD = cw-65(排查浏览器跑的是不是新包)', String(mod.__BUILD))
|
|
467
467
|
ok(mod.__VIDEO_ID === 'dsh-cw-video', '__VIDEO_ID = dsh-cw-video', String(mod.__VIDEO_ID))
|
|
468
468
|
ok(typeof mod.__startShuffle === 'function', '__startShuffle 导出')
|
|
469
469
|
ok(typeof mod.__stopShuffle === 'function', '__stopShuffle 导出')
|