clapper / src /services /monitor /useMonitor.ts
jbilcke-hf's picture
jbilcke-hf HF staff
add linter and prettifier
c1f12bf
raw
history blame
4.13 kB
'use client'
import { create } from 'zustand'
import { ClapSegment } from '@aitube/clap'
import { TimelineStore, useTimeline } from '@aitube/timeline'
import {
MonitoringMode,
MonitorStore,
RendererStore,
} from '@aitube/clapper-services'
import { useAudio } from '../audio/useAudio'
import { getDefaultMonitorState } from './getDefaultMonitorState'
import { useRenderer } from '../renderer'
export const useMonitor = create<MonitorStore>((set, get) => ({
...getDefaultMonitorState(),
bindShortcuts: () => {
if (get().shortcutsAreBound) {
return
}
document.addEventListener('keydown', (event) => {
const element = event.target as unknown as HTMLElement
if (
event.code === 'Space' &&
// those exception are important, otherwise we won't be able to add spaces
// in the search boxes, edit fields, or even the script editor
element.nodeName !== 'INPUT' &&
element.nodeName !== 'TEXTAREA'
) {
console.log(
'[SHORTCUT DETECTED] User pressed space key outside a text input: toggling video playback'
)
// prevent the default behavior, which is strange (automatic scroll to the buttom)
// https://www.jankollars.com/posts/preventing-space-scrolling/
event.preventDefault()
get().togglePlayback()
}
})
set({
shortcutsAreBound: true,
})
},
setMonitoringMode: (mode: MonitoringMode) => {
set({ mode })
},
setStaticVideoRef: (staticVideoRef: HTMLVideoElement) => {
set({
mode: MonitoringMode.STATIC,
staticVideoRef,
})
},
checkIfPlaying: (): boolean => {
return get().isPlaying
},
/**
* Used to play/pause the project timeline (video and audio)
* @param forceValue
* @returns
*/
togglePlayback: (
forcePlaying?: boolean
): {
wasPlaying: boolean
isPlaying: boolean
} => {
const { isPlaying: wasPlaying, mode, staticVideoRef } = get()
const { play, stop } = useAudio.getState()
if (mode === MonitoringMode.NONE) {
set({
isPlaying: false,
lastTimelineUpdateAtInMs: performance.now(),
})
return {
wasPlaying: false,
isPlaying: false,
}
}
const isPlaying =
typeof forcePlaying === 'boolean' ? forcePlaying : !wasPlaying
if (mode === MonitoringMode.STATIC && staticVideoRef) {
if (isPlaying) {
// console.log(`previous value = ` + staticVideoRef.currentTime)
staticVideoRef.play()
} else {
staticVideoRef.pause()
}
} else if (mode === MonitoringMode.DYNAMIC) {
// console.log(`TODO Julian: implement dynamic mode`)
if (isPlaying) {
// restart audio
play()
} else {
stop()
}
}
set({
isPlaying,
lastTimelineUpdateAtInMs: performance.now(),
})
return {
wasPlaying,
isPlaying,
}
},
jumpAt: (timeInMs: number = 0) => {
const monitor: MonitorStore = get()
const renderer: RendererStore = useRenderer.getState()
const timeline: TimelineStore = useTimeline.getState()
const { isPlaying, mode, staticVideoRef } = monitor
const { renderLoop } = renderer
const { setCursorTimestampAtInMs, cursorTimestampAtInMs } = timeline
if (cursorTimestampAtInMs !== timeInMs) {
setCursorTimestampAtInMs(timeInMs)
}
if (mode === MonitoringMode.NONE) {
return
}
if (mode === MonitoringMode.STATIC) {
if (!staticVideoRef) {
return
}
// console.log("resetting static video current time")
staticVideoRef.currentTime = timeInMs / 1000
} else if (mode === MonitoringMode.DYNAMIC) {
// we force a full state recompute
// and we also pass jumpedSomewhere=true to indicate that we
// need a buffer transition
renderLoop(true)
}
},
setLastTimelineUpdateAtInMs: (lastTimelineUpdateAtInMs: number) => {
set({ lastTimelineUpdateAtInMs })
},
}))
setTimeout(() => {
if (typeof document !== 'undefined') {
useMonitor.getState().bindShortcuts()
}
}, 0)