diff --git a/src/backend/common/AbstractComponent.ts b/src/backend/common/AbstractComponent.ts index a0a124c1..2dd60da6 100644 --- a/src/backend/common/AbstractComponent.ts +++ b/src/backend/common/AbstractComponent.ts @@ -36,7 +36,7 @@ import { DrizzlePlayRepository } from "./database/drizzle/repositories/PlayRepos import type {ClientType, MonitoringStatus} from "../../core/Atomic.ts"; import type {SourceType} from "../../core/Atomic.ts"; import { DrizzleComponentRepository } from "./database/drizzle/repositories/ComponentRepository.ts"; -import dayjs from "dayjs"; +import dayjs, { type Dayjs } from "dayjs"; import { COMPONENT_STATE, type ComponentCommonApi, type ComponentCommonApiJson, type ComponentState, type PlayApiCommonDetailed } from "../../core/Api.ts"; import type {WebhookPayload} from "./infrastructure/config/health/webhooks.ts"; import type { MarkRequired } from "ts-essentials"; @@ -67,6 +67,10 @@ export default abstract class AbstractComponent extends AbstractInitializable { type: ClientType | SourceType; name: string; + lastActiveAt?: Dayjs + lastReadyAt?: Dayjs; + protected lastUpdatedComponentDatesAt?: Dayjs + protected constructor(config: AbstractComponentConfig) { super(config); this.transformManager = config.transformManager ?? getRoot().items.transformerManager; @@ -112,6 +116,8 @@ export default abstract class AbstractComponent extends AbstractInitializable { name: this.config?.name ?? this.name }); this.componentId = this.dbComponent.id; + this.lastActiveAt = this.dbComponent.lastActiveAt; + this.lastReadyAt = this.dbComponent.lastReadyAt; return true; } @@ -545,8 +551,8 @@ export default abstract class AbstractComponent extends AbstractInitializable { monitoringStatus: this.getMonitoringStatus(), countNonLive: this.dbComponent.countNonLive, createdAt: this.dbComponent.createdAt?.toISOString(), - lastReadyAt: this.dbComponent.lastReadyAt?.toISOString(), - lastActiveAt: this.dbComponent.lastActiveAt?.toISOString(), + lastReadyAt: this.lastReadyAt?.toISOString(), + lastActiveAt: this.lastActiveAt?.toISOString(), error: this.error !== undefined && this.error instanceof Error ? serializeError(this.error) : this.error, warning: this.warning !== undefined && this.warning instanceof Error ? serializeError(this.warning) : this.warning, ...this.additionalApiData() @@ -598,5 +604,34 @@ export default abstract class AbstractComponent extends AbstractInitializable { public getMonitoringStatus = (): MonitoringStatus => ({ monitoring: this.isMonitoring(), origin: this.monitoringActivity !== undefined ? MONITORING_ORIGIN_USER : MONITORING_ORIGIN_SYSTEM - }) + }); + + protected async updateDates(data: {lastActiveAt?: Dayjs, lastReadyAt?: Dayjs, force?: boolean}) { + const { + lastActiveAt, + lastReadyAt, + force = false + } = data; + let updated = false; + if(lastActiveAt !== undefined && lastActiveAt !== this.lastActiveAt) { + this.lastActiveAt = lastActiveAt; + updated = true; + } + if(lastReadyAt !== undefined && lastReadyAt !== this.lastReadyAt) { + this.lastReadyAt = lastReadyAt; + updated = true; + } + if(updated && ( + force + || this.lastUpdatedComponentDatesAt === undefined + || Math.abs(this.lastUpdatedComponentDatesAt.diff(dayjs(), 's')) >= 60 + ) + ) { + await this.componentRepo.updateById(this.dbComponent.id, { + lastActiveAt: this.lastActiveAt, + lastReadyAt: this.lastReadyAt + }); + this.lastUpdatedComponentDatesAt = dayjs(); + } + } } diff --git a/src/backend/scrobblers/AbstractScrobbleClient.ts b/src/backend/scrobblers/AbstractScrobbleClient.ts index f06a41ae..bd36ede5 100644 --- a/src/backend/scrobblers/AbstractScrobbleClient.ts +++ b/src/backend/scrobblers/AbstractScrobbleClient.ts @@ -1103,7 +1103,7 @@ export default abstract class AbstractScrobbleClient extends AbstractComponent i this.emitEvent('queueEmptied', {}); this.setStatus('Waiting for Plays from Sources'); } - this.componentRepo.updateById(this.dbComponent.id, {lastActiveAt: dayjs(), lastReadyAt: dayjs()}); + this.updateDates({lastActiveAt: dayjs(), lastReadyAt: dayjs()}); await delay(signal, this.scrobbleSleep); } } catch (e) { diff --git a/src/backend/sources/AbstractSource.ts b/src/backend/sources/AbstractSource.ts index 669c0ca0..9bcb84d8 100644 --- a/src/backend/sources/AbstractSource.ts +++ b/src/backend/sources/AbstractSource.ts @@ -198,7 +198,7 @@ export default abstract class AbstractSource extends AbstractComponent implement } return true; } else if(!this.canPoll) { - this.componentRepo.updateById(this.dbComponent.id, {lastReadyAt: dayjs()}); + this.updateDates({lastReadyAt: dayjs(), force: true}); } } return true; @@ -751,7 +751,7 @@ export default abstract class AbstractSource extends AbstractComponent implement this.setIsSleeping(true); this.emitComponentUpdate>({sleeping: true, wakeAt: this.getWakeAt().toISOString()}) // set last active before we sleep - this.componentRepo.updateById(this.dbComponent.id, {lastActiveAt: dayjs(), lastReadyAt: dayjs()}); + this.updateDates({lastActiveAt: dayjs(), lastReadyAt: dayjs()}); while(dayjs().isBefore(this.getWakeAt())) { // check for polling status every half second and wait till wake up time await delay(signal, 500);