Files
navidrome/ui/src/eventStream.js
Deluan 4f83987840
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push Docker manifest (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
fix(ui): keep the NowPlayingPanel badge in sync.
Introduced a new event, EVENT_STREAM_RECONNECTED, to track the last
timestamp of stream reconnections. This change updates the activity
reducer to handle the new event and modifies the NowPlayingPanel to
refresh data based on server and stream status.

Signed-off-by: Deluan <deluan@navidrome.org>
2025-06-29 11:35:10 -04:00

115 lines
3.3 KiB
JavaScript

import { baseUrl } from './utils'
import throttle from 'lodash.throttle'
import { processEvent, serverDown, streamReconnected } from './actions'
import { REST_URL } from './consts'
import config from './config'
const newEventStream = async () => {
let url = baseUrl(`${REST_URL}/events`)
if (localStorage.getItem('token')) {
url = url + `?jwt=${localStorage.getItem('token')}`
}
return new EventSource(url)
}
let eventStream
let reconnectTimer
const RECONNECT_DELAY = 5000
const setupHandlers = (stream, dispatchFn) => {
stream.addEventListener('serverStart', eventHandler(dispatchFn))
stream.addEventListener('scanStatus', throttledEventHandler(dispatchFn))
stream.addEventListener('refreshResource', eventHandler(dispatchFn))
if (config.enableNowPlaying) {
stream.addEventListener('nowPlayingCount', eventHandler(dispatchFn))
}
stream.addEventListener('keepAlive', eventHandler(dispatchFn))
stream.onerror = (e) => {
// eslint-disable-next-line no-console
console.log('EventStream error', e)
dispatchFn(serverDown())
if (stream) stream.close()
scheduleReconnect(dispatchFn)
}
}
const scheduleReconnect = (dispatchFn) => {
if (!reconnectTimer) {
reconnectTimer = setTimeout(() => {
reconnectTimer = null
connect(dispatchFn)
}, RECONNECT_DELAY)
}
}
const connect = async (dispatchFn) => {
try {
const stream = await newEventStream()
eventStream = stream
setupHandlers(stream, dispatchFn)
// Dispatch reconnection event to refresh critical data
dispatchFn(streamReconnected())
return stream
} catch (e) {
// eslint-disable-next-line no-console
console.log(`Error connecting to server:`, e)
scheduleReconnect(dispatchFn)
}
}
const eventHandler = (dispatchFn) => (event) => {
const data = JSON.parse(event.data)
if (event.type !== 'keepAlive') {
dispatchFn(processEvent(event.type, data))
}
}
const throttledEventHandler = (dispatchFn) =>
throttle(eventHandler(dispatchFn), 100, { trailing: true })
const startEventStreamLegacy = async (dispatchFn) => {
return newEventStream()
.then((newStream) => {
newStream.addEventListener('serverStart', eventHandler(dispatchFn))
newStream.addEventListener(
'scanStatus',
throttledEventHandler(dispatchFn),
)
newStream.addEventListener('refreshResource', eventHandler(dispatchFn))
if (config.enableNowPlaying) {
newStream.addEventListener('nowPlayingCount', eventHandler(dispatchFn))
}
newStream.addEventListener('keepAlive', eventHandler(dispatchFn))
newStream.onerror = (e) => {
// eslint-disable-next-line no-console
console.log('EventStream error', e)
dispatchFn(serverDown())
}
return newStream
})
.catch((e) => {
// eslint-disable-next-line no-console
console.log(`Error connecting to server:`, e)
})
}
const startEventStreamNew = async (dispatchFn) => {
if (eventStream) {
eventStream.close()
eventStream = null
}
return connect(dispatchFn)
}
const startEventStream = async (dispatchFn) => {
if (!localStorage.getItem('is-authenticated')) {
return Promise.resolve()
}
if (config.devNewEventStream) {
return startEventStreamNew(dispatchFn)
}
return startEventStreamLegacy(dispatchFn)
}
export { startEventStream }