fix(ncm): 歌单详情LoadFailed——track/all端点不存在(404)回退detail+分批song_detail已验证链路; 列表误触改按下锁定行+拖动抑制 (agent-227, 真机验证256首出曲) + perf(meowpanel): 屏幕帧整数定点DCT(无FPU软浮点3.96s→0.19s/帧,实测4-5fps)+前端链式轮询防堆积+实测fps显示 (agent-228)
ncm→73422166 已部署; meowpanel→f310a00e 已部署
This commit is contained in:
1 parent
d394c73a7c
commit
c041facd89
13 files changed
+447
-65
No files matched your search
@@ -805,42 +805,59 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------------- 10. 实时屏幕 / 点击遥控 ---------------- */
|
||||
/* ---------------- 10. 实时屏幕 / 点击遥控 ----------------
|
||||
* 轮询改「上一帧到了再取下一帧」(链式 setTimeout,绝不堆积/重叠):
|
||||
* 每帧下载完成后按目标周期自校正 delay(周期 = max(0, interval - 请求耗时)),
|
||||
* 故名义 fps 被尊重,且请求变慢时自动退化为 1/请求耗时。 */
|
||||
let screenTimer = null, screenBusy = false, screenOn = false;
|
||||
let screenObjUrl = null, screenFps = 1;
|
||||
let screenLastMs = 0, screenMeasFps = 0; // 实测帧率(帧间间隔 EMA)
|
||||
function stopScreenPoll() {
|
||||
if (screenTimer) { clearInterval(screenTimer); screenTimer = null; }
|
||||
screenOn = false;
|
||||
if (screenTimer) { clearTimeout(screenTimer); screenTimer = null; }
|
||||
}
|
||||
function restartScreenPoll() {
|
||||
if (screenTimer) { clearInterval(screenTimer); screenTimer = null; }
|
||||
const img = $('#screen-img'); if (!img) return;
|
||||
screenOn = true;
|
||||
const ms = Math.max(500, Math.round(1000 / screenFps));
|
||||
screenTimer = setInterval(fetchScreen, ms);
|
||||
if (screenTimer) { clearTimeout(screenTimer); screenTimer = null; }
|
||||
// 请求在途时不重复启动(避免并发请求);在途帧结束后会按最新 screenFps 自续
|
||||
if (!screenBusy) screenTimer = setTimeout(fetchScreen, 0);
|
||||
}
|
||||
async function fetchScreen() {
|
||||
if (screenBusy) return;
|
||||
screenBusy = true;
|
||||
const t0 = performance.now();
|
||||
try {
|
||||
const headers = {};
|
||||
const token = storageGet('meowpanel.token');
|
||||
if (token) headers['X-MeowToken'] = token;
|
||||
const res = await fetch('/api/screen.jpeg?t=' + Date.now() + '&q=70',
|
||||
Object.keys(headers).length ? { headers: headers } : undefined);
|
||||
if (!res.ok) { $('#screen-status').textContent = '获取失败(HTTP ' + res.status + ')'; screenBusy = false; return; }
|
||||
if (!res.ok) { $('#screen-status').textContent = '获取失败(HTTP ' + res.status + ')'; return; }
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const img = $('#screen-img');
|
||||
if (!img) { URL.revokeObjectURL(url); screenBusy = false; return; }
|
||||
if (!img) { URL.revokeObjectURL(url); return; }
|
||||
img.src = url;
|
||||
if (screenObjUrl) URL.revokeObjectURL(screenObjUrl);
|
||||
screenObjUrl = url;
|
||||
$('#screen-status').textContent = '已更新 · ' + new Date().toLocaleTimeString();
|
||||
// 实测帧率:上一帧到本帧间隔的 EMA(首帧无间隔)
|
||||
const now = performance.now();
|
||||
if (screenLastMs) {
|
||||
const dt = (now - screenLastMs) / 1000;
|
||||
screenMeasFps = screenMeasFps ? screenMeasFps * 0.7 + (1 / dt) * 0.3 : 1 / dt;
|
||||
}
|
||||
screenLastMs = now;
|
||||
$('#screen-status').textContent = '已更新' + (screenMeasFps ? ' · ' + screenMeasFps.toFixed(1) + ' fps' : '') + ' · ' + new Date().toLocaleTimeString();
|
||||
} catch (e) {
|
||||
$('#screen-status').textContent = '后端未连接';
|
||||
}
|
||||
screenBusy = false;
|
||||
// 自校正续链(本轮耗时计入周期,保证名义 fps)
|
||||
if (screenOn) {
|
||||
const intervalMs = Math.max(500, Math.round(1000 / screenFps));
|
||||
const wait = Math.max(0, intervalMs - (performance.now() - t0));
|
||||
screenTimer = setTimeout(fetchScreen, wait);
|
||||
}
|
||||
}
|
||||
async function screenTap(e) {
|
||||
const img = $('#screen-img'); if (!img) return;
|
||||
@@ -882,8 +899,8 @@
|
||||
restartScreenPoll();
|
||||
});
|
||||
$('#screen-img').addEventListener('click', screenTap);
|
||||
// restartScreenPoll 已排程首帧(上一帧到了再取下一帧),勿再显式 fetch 以免双发
|
||||
restartScreenPoll();
|
||||
fetchScreen();
|
||||
}
|
||||
function bindScreen() {
|
||||
// 页面隐藏(切到其它标签页)时停刷;回到本页继续
|
||||
|
||||
Reference in new issue
Block a user