File size: 4,127 Bytes
c1f12bf
2068834
c1f12bf
 
 
 
 
 
 
 
7fd5ad1
c1f12bf
 
 
2068834
 
 
c1f12bf
001cba6
c1f12bf
 
 
2068834
c1f12bf
001cba6
 
c1f12bf
 
001cba6
 
c1f12bf
 
 
 
 
 
001cba6
 
 
 
 
 
 
 
 
 
c1f12bf
001cba6
 
2068834
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c1f12bf
2068834
c1f12bf
 
 
2068834
 
 
 
7fd5ad1
2068834
 
7fd5ad1
 
c1f12bf
7fd5ad1
2068834
 
c1f12bf
2068834
 
 
c1f12bf
 
2068834
 
 
7fd5ad1
2068834
 
 
 
 
7fd5ad1
 
 
 
 
 
 
2068834
c1f12bf
7fd5ad1
 
c1f12bf
7fd5ad1
2068834
 
c1f12bf
 
2068834
 
 
a9f8c5a
 
 
2068834
a9f8c5a
 
6915e3a
2068834
6915e3a
 
 
2068834
89b09eb
2068834
 
 
 
89b09eb
 
 
2068834
 
 
a9f8c5a
 
 
 
2068834
 
 
7fd5ad1
 
 
001cba6
 
 
c1f12bf
001cba6
 
77030e1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
'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)