perf: trim live-status batch IPC payload + skip empty broadcasts
The live-status batch poller (60s cadence, every streamer in the watch list) was sending two things on every tick: - `changes` — the diff vs. the previous tick, used by the renderer - `snapshot` — the full Map<login, boolean> serialized as a record Renderer destructures only `changes` (renderer-streamers.ts line 20). The snapshot field was wire-noise. For a typical 30-50 streamer watch list, that snapshot is ~1.5KB of JSON every minute, never read on the other side. Dropped from the broadcast payload. Initial-state sync still works: the renderer's initLiveStatusSubscription calls window.api.getLiveStatusSnapshot() once at boot to pre-fill its map. The broadcast is only for diffs. Also added a short-circuit on the main side: if changes.length === 0 (every streamer's live status matched the cached value this tick), don't broadcast at all. The renderer would just iterate an empty array and trigger a no-op render; saves the wakeup entirely. Type signature updates ride through preload.ts + renderer-globals.d.ts so the API contract stays accurate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
336fc77c85
commit
dd08f33dc6
11
src/main.ts
11
src/main.ts
@ -3770,10 +3770,13 @@ async function runLiveStatusBatchPoll(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (mainWindow) {
|
||||
const snapshot: Record<string, boolean> = {};
|
||||
for (const [k, v] of liveStatusByLogin.entries()) snapshot[k] = v;
|
||||
mainWindow.webContents.send('live-status-batch-update', { changes, snapshot });
|
||||
if (mainWindow && changes.length > 0) {
|
||||
// Renderer only consumes `changes` — initial state comes via
|
||||
// the get-live-status-snapshot IPC at boot. Don't ship the
|
||||
// full map on every tick (was ~1.5KB JSON per 60s with zero
|
||||
// consumer-side use). Also skip the broadcast entirely when
|
||||
// nothing actually changed.
|
||||
mainWindow.webContents.send('live-status-batch-update', { changes });
|
||||
}
|
||||
} catch (e) {
|
||||
appendDebugLog('live-status-poll-failed', String(e));
|
||||
|
||||
@ -94,7 +94,7 @@ contextBridge.exposeInMainWorld('api', {
|
||||
getStreamerProfile: (login: string, forceRefresh?: boolean) => ipcRenderer.invoke('get-streamer-profile', login, forceRefresh),
|
||||
getVodStoryboard: (vodId: string) => ipcRenderer.invoke('get-vod-storyboard', vodId),
|
||||
getLiveStatusSnapshot: () => ipcRenderer.invoke('get-live-status-snapshot'),
|
||||
onLiveStatusBatchUpdate: (callback: (info: { changes: Array<{ login: string; isLive: boolean }>; snapshot: Record<string, boolean> }) => void) => {
|
||||
onLiveStatusBatchUpdate: (callback: (info: { changes: Array<{ login: string; isLive: boolean }> }) => void) => {
|
||||
ipcRenderer.on('live-status-batch-update', (_, info) => callback(info));
|
||||
},
|
||||
searchArchive: (filter: Record<string, unknown>) => ipcRenderer.invoke('search-archive', filter),
|
||||
|
||||
2
src/renderer-globals.d.ts
vendored
2
src/renderer-globals.d.ts
vendored
@ -347,7 +347,7 @@ interface ApiBridge {
|
||||
getStreamerProfile(login: string, forceRefresh?: boolean): Promise<StreamerProfile | null>;
|
||||
getVodStoryboard(vodId: string): Promise<VodStoryboard | null>;
|
||||
getLiveStatusSnapshot(): Promise<Record<string, boolean>>;
|
||||
onLiveStatusBatchUpdate(callback: (info: { changes: Array<{ login: string; isLive: boolean }>; snapshot: Record<string, boolean> }) => void): void;
|
||||
onLiveStatusBatchUpdate(callback: (info: { changes: Array<{ login: string; isLive: boolean }> }) => void): void;
|
||||
searchArchive(filter: {
|
||||
query?: string;
|
||||
type?: 'all' | 'live' | 'vod' | 'chat' | 'events';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user