Skip to content

Commit dee5dc7

Browse files
authored
Merge pull request #22 from PartyHall/feat-pb-kara
feat(karaoke): Add standard photobooth capabilities
2 parents d3caeef + ccd8bef commit dee5dc7

File tree

13 files changed

+251
-53
lines changed

13 files changed

+251
-53
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ tmp/
55
/partyhall
66
config.yaml
77
.cache
8+
9+
ansible/inventories/host_vars/partyhall.yml
10+
11+
# Temporary while I make the docs
12+
docs/

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ reset-db:
1313
sqlite3 0_DATA/partyhall.db < sql/fixtures.sql
1414

1515
take-picture:
16-
docker compose exec mosquitto mosquitto_pub -h 127.0.0.1 -t partyhall/button_press -m "TAKE_PICTURE"
16+
docker compose exec mosquitto mosquitto_pub -h 127.0.0.1 -t partyhall/button_press -m "partyhall/photobooth/take_picture"
1717

1818
show-debug:
1919
docker compose exec mosquitto mosquitto_pub -h 127.0.0.1 -t partyhall/button_press -m "DISPLAY_DEBUG"

ansible/inventories/group_vars/all.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ partyhall_debug_mode: false
33
partyhall_guests_allowed: true
44

55
# Admin account
6-
partyhall_admin_username: 'username' # Your admin username
7-
partyhall_admin_fullname: 'user fullname' # The name that is displayed when you choose a song
6+
partyhall_admin_username: 'admin' # Your admin username
7+
partyhall_admin_fullname: 'Admin' # The name that is displayed when you choose a song
88

99
partyhall_language: 'en' # Either en or fr, lowercase only!
1010

@@ -23,14 +23,14 @@ partyhall_karaoke_pre_play_timer: 5
2323
ntp_enabled: true
2424
ntp_timezone: Europe/Paris
2525

26-
ethernet_interface: ens33
26+
ethernet_interface: enp2s0
2727
grub_resolution: 1920x1080 # Any grub-supported resolution, it might be 1280x720 if you use an older screen
2828

2929
spotify_name: 'PartyHall'
3030

3131
hotspot_enabled: false
32-
hotspot_interface: wlx9ca2f4bcc3da
33-
hotspot_driver: 'rtl8192eu'
32+
hotspot_interface: wlan0
33+
# hotspot_driver: 'rtl8192eu'
3434
hotspot_ssid: "PartyHall"
3535
hotspot_dns_name: "partyhall.local"
3636
hotspot_wifi_channel: 11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
partyhall_admin_password: '' # Your admin password
3+
partyhall_spotify_client_id: ''
4+
partyhall_spotify_client_secret: ''
5+
hotspot_password: ''

ansible/inventories/host_vars/partyhall.yml

-5
This file was deleted.

gui/src/assets/css/photobooth.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.photobooth{
1+
.photobooth, .karaoke{
22
position: relative;
33

44
width: 100%;

gui/src/components/admin/volume.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,17 @@ export default function VolumeAdmin() {
4141
}
4242
}, [debouncedSetVolume]);
4343

44+
if (!appState.pulseaudio_devices || appState.pulseaudio_devices.length === 0) {
45+
return <>
46+
<Typography variant="h2" fontSize={18}>{t('admin_main.volume')}</Typography>
47+
<Typography variant="body1" color="GrayText">{t('admin_main.no_devices')}</Typography>
48+
</>
49+
}
50+
4451
return <>
4552
<Typography variant="h2" fontSize={18}>{t('admin_main.volume')}</Typography>
4653
{
47-
hasRole('ADMIN') &&
54+
hasRole('ADMIN') && appState.pulseaudio_devices && appState.pulseaudio_devices.length > 0 &&
4855
<>
4956
<InputLabel id="volume-device-label">{t('admin_main.device')}</InputLabel>
5057
<Select value={appState.pulseaudio_selected?.index} labelId="volume-device-label" onChange={x => sendMessage('SET_SOUND_CARD', x.target.value)}>
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { useEffect, useRef } from "react";
2+
import { useBoothSocket } from "../../../hooks/boothSocket";
3+
4+
import '../../../assets/css/karaoke.scss';
5+
import CDGPlayer from "./cdgplayer";
6+
import Webcam from "react-webcam";
7+
import { Stack, Typography } from "@mui/material";
8+
import { songTitle } from "../../../utils/songs";
9+
import OsdSong from "./osd_song";
10+
import VideoPlayer from "./videoplayer";
11+
import { b64ImageToBlob } from "../../../utils/files";
12+
import { useTranslation } from "react-i18next";
13+
14+
/**
15+
* This is a temp file
16+
* Currently, the karaoke module in index.tsx is a clone of the photobooth
17+
* With the capability to play songs
18+
* Later this probably will be merged as only one
19+
* Or something else, IDK yet
20+
*
21+
* But the idea is that the photobooth thing should still work when in Karaoke
22+
*/
23+
export default function Karaoke() {
24+
const {t} = useTranslation();
25+
const { appState, lastMessage, sendMessage } = useBoothSocket();
26+
const webcamRef = useRef<Webcam>(null);
27+
const module = appState.modules.karaoke;
28+
29+
const takePicture = async () => {
30+
if (!webcamRef || !webcamRef.current) {
31+
return;
32+
}
33+
34+
const imageSrc = webcamRef.current.getScreenshot();
35+
if (imageSrc) {
36+
let form = new FormData();
37+
38+
form.append('image', b64ImageToBlob(imageSrc));
39+
form.append('unattended', 'true')
40+
form.append('event', ''+appState?.app_state?.current_event?.id)
41+
42+
try {
43+
await fetch('/api/picture', {
44+
method: 'POST',
45+
body: form,
46+
});
47+
} catch {
48+
}
49+
}
50+
};
51+
52+
useEffect(() => {
53+
if (!lastMessage) {
54+
return;
55+
}
56+
57+
if (lastMessage.type == 'UNATTENDED_PICTURE') {
58+
takePicture();
59+
}
60+
}, [lastMessage]);
61+
62+
return <div className="karaoke">
63+
<Webcam
64+
forceScreenshotSourceSize
65+
ref={webcamRef}
66+
width={appState.modules.photobooth.webcam_resolution.width}
67+
height={appState.modules.photobooth.webcam_resolution.height}
68+
screenshotFormat="image/jpeg"
69+
videoConstraints={{ facingMode: 'user', ...appState.modules.photobooth.webcam_resolution }}
70+
className='karaoke__webcam'
71+
/>
72+
{
73+
module.currentSong && module.preplayTimer == 0 &&
74+
<>
75+
{
76+
module.currentSong.format.toLowerCase() === 'cdg' &&
77+
<CDGPlayer
78+
cdgAlpha={.8}
79+
cdgSize={window.innerHeight / 2}
80+
width={window.innerWidth/2}
81+
height={window.innerHeight / 2}
82+
isPlaying={module.started}
83+
song={module.currentSong}
84+
onEnd={() => sendMessage('karaoke/PLAYING_ENDED')}
85+
onError={() => {}}
86+
onLoad={() => {}}
87+
onPlay={() => {}}
88+
onStatus={(x: any) => sendMessage('karaoke/PLAYING_STATUS', {'current': x.position, 'total': x.total})}
89+
/>
90+
}
91+
{
92+
module.currentSong.format.toLowerCase() !== 'cdg' && <VideoPlayer
93+
isPlaying={module.started}
94+
song={module.currentSong}
95+
onEnd={() => sendMessage('karaoke/PLAYING_ENDED')}
96+
onStatus={(x: any) => sendMessage('karaoke/PLAYING_STATUS', {'current': x.position, 'total': x.total})}
97+
/>
98+
}
99+
</>
100+
}
101+
{
102+
module.currentSong && module.preplayTimer > 0 &&
103+
<Stack display="column" className="karaoke__no_song">
104+
<Typography variant="h1">{t('karaoke.now_playing')}:</Typography>
105+
<Typography variant="h2">{songTitle(module.currentSong)}</Typography>
106+
<Typography variant="h3">{module.preplayTimer}</Typography>
107+
{
108+
module.currentSong.sung_by && module.currentSong.sung_by.length > 0 &&
109+
<Typography variant="h2">{t('karaoke.sung_by')} {module.currentSong.sung_by}</Typography>
110+
}
111+
</Stack>
112+
}
113+
{
114+
!module.currentSong &&
115+
<Stack display="column" className="karaoke__no_song">
116+
<Typography variant="h1">{t('karaoke.no_song_playing')}</Typography>
117+
</Stack>
118+
}
119+
{
120+
module.queue.length > 0 &&
121+
<Stack className="karaoke__next_song" gap={1}>
122+
<Typography variant="h3">{t('karaoke.next_up')}:</Typography>
123+
<OsdSong song={module.queue[0]} />
124+
</Stack>
125+
}
126+
</div>;
127+
}

0 commit comments

Comments
 (0)