1
1
import VideoPlayer from "./VideoPlayer" ;
2
- import React , {
3
- useCallback ,
4
- useEffect ,
5
- useMemo ,
6
- useRef ,
7
- useState ,
8
- } from "react" ;
2
+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
9
3
import { useApiHost } from "@/api" ;
10
4
import Player from "video.js/dist/types/player" ;
11
- import { formatUnixTimestampToDateTime } from "@/utils/dateUtil" ;
5
+ import { formatUnixTimestampToDateTime , isCurrentHour } from "@/utils/dateUtil" ;
12
6
import { ReviewSegment } from "@/types/review" ;
13
7
import { Slider } from "../ui/slider" ;
14
8
import { getIconForLabel , getIconForSubLabel } from "@/utils/iconUtil" ;
@@ -43,16 +37,12 @@ export default function PreviewThumbnailPlayer({
43
37
} : PreviewPlayerProps ) {
44
38
const apiHost = useApiHost ( ) ;
45
39
const { data : config } = useSWR < FrigateConfig > ( "config" ) ;
46
- const playerRef = useRef < Player | null > ( null ) ;
47
40
48
41
const [ hoverTimeout , setHoverTimeout ] = useState < NodeJS . Timeout | null > ( ) ;
49
42
const [ playback , setPlayback ] = useState ( false ) ;
50
43
const [ progress , setProgress ] = useState ( 0 ) ;
51
44
52
- const playingBack = useMemo (
53
- ( ) => relevantPreview && playback ,
54
- [ playback , autoPlayback , relevantPreview ]
55
- ) ;
45
+ const playingBack = useMemo ( ( ) => playback , [ playback , autoPlayback ] ) ;
56
46
57
47
useEffect ( ( ) => {
58
48
if ( ! autoPlayback ) {
@@ -76,10 +66,6 @@ export default function PreviewThumbnailPlayer({
76
66
77
67
const onPlayback = useCallback (
78
68
( isHovered : Boolean ) => {
79
- if ( ! relevantPreview ) {
80
- return ;
81
- }
82
-
83
69
if ( isHovered ) {
84
70
setHoverTimeout (
85
71
setTimeout ( ( ) => {
@@ -94,16 +80,9 @@ export default function PreviewThumbnailPlayer({
94
80
95
81
setPlayback ( false ) ;
96
82
setProgress ( 0 ) ;
97
-
98
- if ( playerRef . current ) {
99
- playerRef . current . pause ( ) ;
100
- playerRef . current . currentTime (
101
- review . start_time - relevantPreview . start
102
- ) ;
103
- }
104
83
}
105
84
} ,
106
- [ hoverTimeout , relevantPreview , review , playerRef ]
85
+ [ hoverTimeout , review ]
107
86
) ;
108
87
109
88
return (
@@ -115,10 +94,8 @@ export default function PreviewThumbnailPlayer({
115
94
>
116
95
{ playingBack ? (
117
96
< PreviewContent
118
- playerRef = { playerRef }
119
97
review = { review }
120
98
relevantPreview = { relevantPreview }
121
- playback = { playingBack }
122
99
setProgress = { setProgress }
123
100
setReviewed = { setReviewed }
124
101
/>
@@ -173,21 +150,18 @@ export default function PreviewThumbnailPlayer({
173
150
}
174
151
175
152
type PreviewContentProps = {
176
- playerRef : React . MutableRefObject < Player | null > ;
177
153
review : ReviewSegment ;
178
154
relevantPreview : Preview | undefined ;
179
- playback : boolean ;
180
155
setProgress ?: ( progress : number ) => void ;
181
156
setReviewed ?: ( ) => void ;
182
157
} ;
183
158
function PreviewContent ( {
184
- playerRef,
185
159
review,
186
160
relevantPreview,
187
- playback,
188
161
setProgress,
189
162
setReviewed,
190
163
} : PreviewContentProps ) {
164
+ const playerRef = useRef < Player | null > ( null ) ;
191
165
const playerStartTime = useMemo ( ( ) => {
192
166
if ( ! relevantPreview ) {
193
167
return 0 ;
@@ -219,7 +193,7 @@ function PreviewContent({
219
193
220
194
// preview
221
195
222
- if ( relevantPreview && playback ) {
196
+ if ( relevantPreview ) {
223
197
return (
224
198
< VideoPlayer
225
199
options = { {
@@ -293,5 +267,79 @@ function PreviewContent({
293
267
} }
294
268
/>
295
269
) ;
270
+ } else if ( isCurrentHour ( review . start_time ) ) {
271
+ return (
272
+ < InProgressPreview
273
+ review = { review }
274
+ setProgress = { setProgress }
275
+ setReviewed = { setReviewed }
276
+ />
277
+ ) ;
278
+ }
279
+ }
280
+
281
+ const MIN_LOAD_TIMEOUT_MS = 200 ;
282
+ type InProgressPreviewProps = {
283
+ review : ReviewSegment ;
284
+ setProgress ?: ( progress : number ) => void ;
285
+ setReviewed ?: ( ) => void ;
286
+ } ;
287
+ function InProgressPreview ( {
288
+ review,
289
+ setProgress,
290
+ setReviewed,
291
+ } : InProgressPreviewProps ) {
292
+ const apiHost = useApiHost ( ) ;
293
+ const { data : previewFrames } = useSWR < string [ ] > (
294
+ `preview/${ review . camera } /start/${ Math . floor (
295
+ review . start_time
296
+ ) - 4 } /end/${ Math . ceil ( review . end_time ) + 4 } /frames`
297
+ ) ;
298
+ const [ key , setKey ] = useState ( 0 ) ;
299
+
300
+ const handleLoad = useCallback ( ( ) => {
301
+ if ( ! previewFrames ) {
302
+ return ;
303
+ }
304
+
305
+ if ( key == previewFrames . length - 1 ) {
306
+ if ( setProgress ) {
307
+ setProgress ( 100 ) ;
308
+ }
309
+
310
+ return ;
311
+ }
312
+
313
+ setTimeout ( ( ) => {
314
+ if ( setProgress ) {
315
+ setProgress ( ( key / ( previewFrames . length - 1 ) ) * 100 ) ;
316
+ }
317
+
318
+ if ( setReviewed && key == previewFrames . length / 2 ) {
319
+ setReviewed ( ) ;
320
+ }
321
+
322
+ setKey ( key + 1 ) ;
323
+ } , MIN_LOAD_TIMEOUT_MS ) ;
324
+ } , [ key , previewFrames ] ) ;
325
+
326
+ if ( ! previewFrames || previewFrames . length == 0 ) {
327
+ return (
328
+ < img
329
+ className = "h-full w-full"
330
+ loading = "lazy"
331
+ src = { `${ apiHost } ${ review . thumb_path . replace ( "/media/frigate/" , "" ) } ` }
332
+ />
333
+ ) ;
296
334
}
335
+
336
+ return (
337
+ < div className = "w-full h-full flex items-center bg-black" >
338
+ < img
339
+ className = "w-full"
340
+ src = { `${ apiHost } api/preview/${ previewFrames [ key ] } /thumbnail.jpg` }
341
+ onLoad = { handleLoad }
342
+ />
343
+ </ div >
344
+ ) ;
297
345
}
0 commit comments