1
- import { useEffect , useRef } from "react" ;
1
+ import { useEffect , useRef , useState } from "react" ;
2
+ import Webcam from "react-webcam" ;
3
+ import LockedModal from "../../../components/locked_modal" ;
2
4
import { useBoothSocket } from "../../../hooks/boothSocket" ;
3
5
6
+ import '../../../assets/css/photobooth.scss' ;
4
7
import '../../../assets/css/karaoke.scss' ;
8
+ import { b64ImageToBlob } from "../../../utils/files" ;
5
9
import CDGPlayer from "./cdgplayer" ;
6
- import Webcam from "react-webcam " ;
10
+ import VideoPlayer from "./videoplayer " ;
7
11
import { Stack , Typography } from "@mui/material" ;
8
- import { songTitle } from "../../../utils/songs" ;
9
12
import OsdSong from "./osd_song" ;
10
- import VideoPlayer from "./videoplayer" ;
11
- import { b64ImageToBlob } from "../../../utils/files" ;
12
13
import { useTranslation } from "react-i18next" ;
14
+ import { songTitle } from "../../../utils/songs" ;
15
+
16
+ type LastPicture = {
17
+ url : string ;
18
+ loaded : boolean ;
19
+ } ;
13
20
14
- export default function Karaoke ( ) {
21
+ export default function Photobooth ( ) {
22
+ const webcamRef = useRef < Webcam > ( null ) ;
15
23
const { t} = useTranslation ( ) ;
16
24
const { appState, lastMessage, sendMessage } = useBoothSocket ( ) ;
17
- const webcamRef = useRef < Webcam > ( null ) ;
18
- const module = appState . modules . karaoke ;
25
+ const [ timer , setTimer ] = useState ( - 1 ) ;
26
+ const [ flash , setFlash ] = useState < boolean > ( false ) ;
27
+ const [ lastPicture , setLastPicture ] = useState < LastPicture | null > ( null ) ;
19
28
20
- const takePicture = async ( ) => {
29
+ const module = appState . modules . photobooth ;
30
+ const modulek = appState . modules . karaoke ;
31
+ const resolution = module . webcam_resolution ;
32
+
33
+ const takePicture = async ( unattended : boolean ) => {
21
34
if ( ! webcamRef || ! webcamRef . current ) {
22
35
return ;
23
36
}
@@ -27,15 +40,26 @@ export default function Karaoke() {
27
40
let form = new FormData ( ) ;
28
41
29
42
form . append ( 'image' , b64ImageToBlob ( imageSrc ) ) ;
30
- form . append ( 'unattended' , 'true' )
43
+ form . append ( 'unattended' , unattended ? 'true' : 'false ')
31
44
form . append ( 'event' , '' + appState ?. app_state ?. current_event ?. id )
32
45
33
46
try {
34
- await fetch ( '/api/picture' , {
47
+ const resp = await fetch ( '/api/picture' , {
35
48
method : 'POST' ,
36
49
body : form ,
37
50
} ) ;
51
+
52
+ setTimer ( - 1 ) ;
53
+
54
+ if ( ! unattended ) {
55
+ const image = await resp . blob ( ) ;
56
+ const url = URL . createObjectURL ( image ) ;
57
+
58
+ setLastPicture ( { url, loaded : false } ) ;
59
+ setTimeout ( ( ) => setLastPicture ( null ) , 3500 ) ;
60
+ }
38
61
} catch {
62
+ setTimer ( - 1 ) ;
39
63
}
40
64
}
41
65
} ;
@@ -45,33 +69,60 @@ export default function Karaoke() {
45
69
return ;
46
70
}
47
71
72
+ if ( lastMessage . type == 'TAKE_PICTURE' && timer === - 1 ) {
73
+ if ( appState . current_mode === 'DISABLED' ) {
74
+ return ;
75
+ }
76
+
77
+ setTimer ( module . default_timer )
78
+ return
79
+ }
80
+
48
81
if ( lastMessage . type == 'UNATTENDED_PICTURE' ) {
49
- takePicture ( ) ;
82
+ takePicture ( true ) ;
50
83
}
51
84
} , [ lastMessage ] ) ;
52
85
86
+ useEffect ( ( ) => {
87
+ if ( timer > 0 ) {
88
+ setTimeout ( ( ) => {
89
+ setTimer ( timer - 1 ) ;
90
+ } , 1000 ) ;
91
+ }
92
+
93
+ if ( timer == 0 ) {
94
+ setFlash ( true ) ;
95
+ setTimeout ( ( ) => {
96
+ takePicture ( false ) ;
97
+ setFlash ( false ) ;
98
+ setTimer ( - 1 ) ;
99
+ } , 500 ) ;
100
+ }
101
+ } , [ timer ] ) ;
102
+
53
103
return < div className = "karaoke" >
54
104
< Webcam
55
105
forceScreenshotSourceSize
56
106
ref = { webcamRef }
57
- width = { appState . modules . photobooth . webcam_resolution . width }
58
- height = { appState . modules . photobooth . webcam_resolution . height }
107
+ width = { resolution . width }
108
+ height = { resolution . height }
109
+ onClick = { ( ) => appState . current_mode !== 'DISABLED' && sendMessage ( 'photobooth/TAKE_PICTURE' ) }
59
110
screenshotFormat = "image/jpeg"
60
- videoConstraints = { { facingMode : 'user' , ...appState . modules . photobooth . webcam_resolution } }
61
- className = 'karaoke__webcam'
111
+ videoConstraints = { { facingMode : 'user' , ...resolution } }
62
112
/>
113
+
63
114
{
64
- module . currentSong && module . preplayTimer == 0 &&
115
+ modulek . currentSong && modulek . preplayTimer == 0 &&
65
116
< >
66
117
{
67
- module . currentSong . format . toLowerCase ( ) === 'cdg' &&
118
+ modulek . currentSong . format . toLowerCase ( ) === 'cdg' &&
68
119
< CDGPlayer
69
120
cdgAlpha = { .8 }
70
121
cdgSize = { window . innerHeight / 2 }
71
122
width = { window . innerWidth / 2 }
72
123
height = { window . innerHeight / 2 }
73
- isPlaying = { module . started }
74
- song = { module . currentSong }
124
+ isPlaying = { modulek . started }
125
+ song = { modulek . currentSong }
75
126
onEnd = { ( ) => sendMessage ( 'karaoke/PLAYING_ENDED' ) }
76
127
onError = { ( ) => { } }
77
128
onLoad = { ( ) => { } }
@@ -80,39 +131,43 @@ export default function Karaoke() {
80
131
/>
81
132
}
82
133
{
83
- module . currentSong . format . toLowerCase ( ) !== 'cdg' && < VideoPlayer
84
- isPlaying = { module . started }
85
- song = { module . currentSong }
134
+ modulek . currentSong . format . toLowerCase ( ) !== 'cdg' && < VideoPlayer
135
+ isPlaying = { modulek . started }
136
+ song = { modulek . currentSong }
86
137
onEnd = { ( ) => sendMessage ( 'karaoke/PLAYING_ENDED' ) }
87
138
onStatus = { ( x : any ) => sendMessage ( 'karaoke/PLAYING_STATUS' , { 'current' : x . position , 'total' : x . total } ) }
88
139
/>
89
140
}
90
141
</ >
91
142
}
92
143
{
93
- module . currentSong && module . preplayTimer > 0 &&
144
+ modulek . currentSong && modulek . preplayTimer > 0 &&
94
145
< Stack display = "column" className = "karaoke__no_song" >
95
146
< Typography variant = "h1" > { t ( 'karaoke.now_playing' ) } :</ Typography >
96
- < Typography variant = "h2" > { songTitle ( module . currentSong ) } </ Typography >
97
- < Typography variant = "h3" > { module . preplayTimer } </ Typography >
147
+ < Typography variant = "h2" > { songTitle ( modulek . currentSong ) } </ Typography >
148
+ < Typography variant = "h3" > { modulek . preplayTimer } </ Typography >
98
149
{
99
- module . currentSong . sung_by && module . currentSong . sung_by . length > 0 &&
100
- < Typography variant = "h2" > { t ( 'karaoke.sung_by' ) } { module . currentSong . sung_by } </ Typography >
150
+ modulek . currentSong . sung_by && modulek . currentSong . sung_by . length > 0 &&
151
+ < Typography variant = "h2" > { t ( 'karaoke.sung_by' ) } { modulek . currentSong . sung_by } </ Typography >
101
152
}
102
153
</ Stack >
103
154
}
104
155
{
105
- ! module . currentSong &&
106
- < Stack display = "column" className = "karaoke__no_song" >
107
- < Typography variant = "h1" > { t ( 'karaoke.no_song_playing' ) } </ Typography >
108
- </ Stack >
109
- }
110
- {
111
- module . queue . length > 0 &&
156
+ modulek . queue . length > 0 &&
112
157
< Stack className = "karaoke__next_song" gap = { 1 } >
113
158
< Typography variant = "h3" > { t ( 'karaoke.next_up' ) } :</ Typography >
114
- < OsdSong song = { module . queue [ 0 ] } />
159
+ < OsdSong song = { modulek . queue [ 0 ] } />
115
160
</ Stack >
116
161
}
117
- </ div > ;
162
+
163
+ { timer >= 0 && < div className = { `timer` } > { timer > 0 && timer } </ div > }
164
+ { flash && < div className = "timer flash" > </ div > }
165
+ { appState . current_mode === 'DISABLED' && < LockedModal /> }
166
+
167
+ {
168
+ lastPicture && < div className = "picture_frame" style = { lastPicture . loaded ? { } : { display : 'none' } } >
169
+ < img src = { lastPicture . url } onLoad = { ( ) => setLastPicture ( { ...lastPicture , loaded : true } ) } alt = "Last picture" />
170
+ </ div >
171
+ }
172
+ </ div >
118
173
}
0 commit comments