mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-06 16:03:52 +00:00
f965db148c
* refactor: setup global timer state --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me>
296 lines
8.1 KiB
TypeScript
296 lines
8.1 KiB
TypeScript
import { LogOrigin, OntimeEvent, Playback } from 'ontime-types';
|
|
import { validatePlayback } from 'ontime-utils';
|
|
|
|
import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js';
|
|
import { eventStore } from '../stores/EventStore.js';
|
|
import { eventTimer } from './TimerService.js';
|
|
import { clock } from './Clock.js';
|
|
import { logger } from '../classes/Logger.js';
|
|
import { RestorePoint } from './RestoreService.js';
|
|
import { state } from '../state.js';
|
|
|
|
/**
|
|
* Service manages playback status of app
|
|
* Coordinating with necessary services
|
|
*/
|
|
export class PlaybackService {
|
|
/**
|
|
* makes calls for loading and starting given event
|
|
* @param {OntimeEvent} event
|
|
* @return {boolean} success
|
|
*/
|
|
static loadEvent(event: OntimeEvent): boolean {
|
|
let success = false;
|
|
if (!event) {
|
|
logger.error(LogOrigin.Playback, 'No event found');
|
|
} else if (event.skip) {
|
|
logger.warning(LogOrigin.Playback, `Refused playback of skipped event ID ${event.id}`);
|
|
} else {
|
|
eventLoader.loadEvent(event);
|
|
eventTimer.load(event);
|
|
success = true;
|
|
}
|
|
eventStore.broadcast();
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* starts event matching given ID
|
|
* @param {string} eventId
|
|
* @return {boolean} success
|
|
*/
|
|
static startById(eventId: string): boolean {
|
|
const event = EventLoader.getEventWithId(eventId);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
PlaybackService.start();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* starts an event at index
|
|
* @param {number} eventIndex
|
|
* @return {boolean} success
|
|
*/
|
|
static startByIndex(eventIndex: number): boolean {
|
|
const event = EventLoader.getEventAtIndex(eventIndex);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
PlaybackService.start();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* starts first event matching given cue
|
|
* @param {string} cue
|
|
* @return {boolean} success
|
|
*/
|
|
static startByCue(cue: string): boolean {
|
|
const event = EventLoader.getEventWithCue(cue);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
PlaybackService.start();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* loads event matching given ID
|
|
* @param {string} eventId
|
|
* @return {boolean} success
|
|
*/
|
|
static loadById(eventId: string): boolean {
|
|
const event = EventLoader.getEventWithId(eventId);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* loads event matching given ID
|
|
* @param {number} eventIndex
|
|
* @return {boolean} success
|
|
*/
|
|
static loadByIndex(eventIndex: number): boolean {
|
|
const event = EventLoader.getEventAtIndex(eventIndex);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* loads first event matching given cue
|
|
* @param {string} cue
|
|
* @return {boolean} success
|
|
*/
|
|
static loadByCue(cue: string): boolean {
|
|
const event = EventLoader.getEventWithCue(cue);
|
|
const success = PlaybackService.loadEvent(event);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Loads event before currently selected
|
|
*/
|
|
static loadPrevious() {
|
|
const previousEvent = eventLoader.findPrevious();
|
|
if (previousEvent) {
|
|
const success = PlaybackService.loadEvent(previousEvent);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${previousEvent.id}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads event after currently selected
|
|
* @param {string} [fallbackAction] - 'stop', 'pause'
|
|
* @return {boolean} success
|
|
*/
|
|
static loadNext(fallbackAction?: 'stop' | 'pause'): boolean {
|
|
const nextEvent = eventLoader.findNext();
|
|
if (nextEvent) {
|
|
const success = PlaybackService.loadEvent(nextEvent);
|
|
if (success) {
|
|
logger.info(LogOrigin.Playback, `Loaded event with ID ${nextEvent.id}`);
|
|
return true;
|
|
}
|
|
} else if (fallbackAction === 'stop') {
|
|
logger.info(LogOrigin.Playback, 'No next event found! Stopping playback');
|
|
PlaybackService.stop();
|
|
return false;
|
|
} else if (fallbackAction === 'pause') {
|
|
logger.info(LogOrigin.Playback, 'No next event found! Pausing playback');
|
|
PlaybackService.pause();
|
|
return false;
|
|
} else {
|
|
logger.info(LogOrigin.Playback, 'No next event found! Continuing playback');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts playback on selected event
|
|
*/
|
|
static start() {
|
|
if (validatePlayback(state.playback).start) {
|
|
eventTimer.start();
|
|
const newState = state.playback;
|
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts playback on next event
|
|
* @param {string} [fallbackAction] - 'stop', 'pause'
|
|
*/
|
|
static startNext(fallbackAction?: 'stop' | 'pause') {
|
|
const success = PlaybackService.loadNext(fallbackAction);
|
|
if (success) {
|
|
PlaybackService.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pauses playback on selected event
|
|
*/
|
|
static pause() {
|
|
if (validatePlayback(state.playback).pause) {
|
|
eventTimer.pause();
|
|
const newState = state.playback;
|
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops timer and unloads any events
|
|
*/
|
|
static stop() {
|
|
if (validatePlayback(state.playback).stop) {
|
|
eventLoader.reset();
|
|
eventTimer.stop();
|
|
const newState = state.playback;
|
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reloads current event
|
|
*/
|
|
static reload() {
|
|
if (state.timer.selectedEventId) {
|
|
this.loadById(state.timer.selectedEventId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets playback to roll
|
|
*/
|
|
static roll() {
|
|
if (EventLoader.getPlayableEvents()) {
|
|
const rollTimers = eventLoader.findRoll(clock.timeNow());
|
|
|
|
// nothing to play
|
|
if (rollTimers === null) {
|
|
logger.warning(LogOrigin.Server, 'Roll: no events found');
|
|
PlaybackService.stop();
|
|
return;
|
|
}
|
|
|
|
const { currentEvent, nextEvent } = rollTimers;
|
|
if (!currentEvent && !nextEvent) {
|
|
logger.warning(LogOrigin.Server, 'Roll: no events found');
|
|
PlaybackService.stop();
|
|
return;
|
|
}
|
|
|
|
eventTimer.roll(currentEvent, nextEvent);
|
|
|
|
const newState = state.playback;
|
|
logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description resume playback state given a restore point
|
|
* @param restorePoint
|
|
*/
|
|
static resume(restorePoint: RestorePoint) {
|
|
const willResume = () => logger.info(LogOrigin.Server, 'Resuming playback');
|
|
|
|
if (restorePoint.playback === Playback.Roll) {
|
|
willResume();
|
|
PlaybackService.roll();
|
|
}
|
|
|
|
if (restorePoint.selectedEventId) {
|
|
const event = EventLoader.getEventWithId(restorePoint.selectedEventId);
|
|
// the db would have to change for the event not to exist
|
|
// we do not kow the reason for the crash, so we check anyway
|
|
if (!event) {
|
|
return;
|
|
}
|
|
|
|
eventLoader.loadEvent(event);
|
|
eventTimer.resume(event, restorePoint);
|
|
eventStore.broadcast();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds time to current event
|
|
* @param {number} time - time to add in seconds
|
|
*/
|
|
static addTime(time: number) {
|
|
if (state.timer.selectedEventId) {
|
|
const timeInMs = time * 1000;
|
|
eventTimer.addTime(timeInMs);
|
|
timeInMs > 0
|
|
? logger.info(LogOrigin.Playback, `Added ${time} sec`)
|
|
: logger.info(LogOrigin.Playback, `Removed ${time} sec`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds delay to current event
|
|
* @deprecated Use addTime
|
|
* @param {number} delayTime time in minutes
|
|
*/
|
|
static setDelay(delayTime: number) {
|
|
this.addTime(delayTime * 60);
|
|
}
|
|
}
|