You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
8.4 KiB

4 months ago
import { useState, useEffect, useRef } from "react";
import {Howl, Howler} from 'howler';
4 months ago
import { invoke } from "@tauri-apps/api/core";
4 months ago
import { listen } from '@tauri-apps/api/event';
4 months ago
import "./App.css";
4 months ago
import { OSC_ADDRESS } from "./utils/constant";
4 months ago
const DefaultFadeDuration = 3; // 1 second
4 months ago
const CLIENT_COUNT=12;
4 months ago
const CueType={
Bg: 'bg',
Announce: 'announce',
Light: 'light'
}
const EmojiType={
bg: '🎵',
announce: '📢',
light: '💡'
}
function App() {
const [cuelist, setCuelist] = useState([]);
const [currentCue, setCurrentCue] = useState(null);
const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds
const [timestamp, setTimestamp] = useState();
4 months ago
const [clientStatus, setClientStatus] = useState({});
4 months ago
const refCue = useRef(null);
const refNextCue = useRef(null);
4 months ago
4 months ago
function sendOsc(addr, message, id){
4 months ago
invoke('send_osc_message', {
key: addr,
4 months ago
message: `${id}#${message}`,
4 months ago
host:'0.0.0.0:0',
target:'192.168.234.255:8000',
});
}
4 months ago
4 months ago
function sendOscToSound(addr, message){
4 months ago
4 months ago
invoke('send_osc_message', {
key: addr,
message,
host:'0.0.0.0:0',
target:'192.168.234.255:58100',
});
4 months ago
}
4 months ago
function onOsc(message){
const [id, status, name]= message.args[0]?.split('#');
if(clientStatus[id]){
setClientStatus(prev=>({
...prev,
[id]:[
{
status,
name,
timestamp: new Date().toLocaleTimeString(),
},
...prev[id],
]
}));
}else{
setClientStatus(prev=>({
...prev,
[id]:[{
status,
name,
timestamp: new Date().toLocaleTimeString(),
}]
}));
4 months ago
}
4 months ago
4 months ago
}
4 months ago
4 months ago
4 months ago
4 months ago
function playCue({id, name, description, type, auto, audioFile, ...props}) {
console.log('Playing cue:', {id, name, description, type, auto, audioFile, ...props});
// Handle other cue types and properties here
4 months ago
if(props.audioCue){
sendOscToSound(OSC_ADDRESS.SCS_GO_CUE, props.audioCue);
}
4 months ago
if(props.clientCue){
4 months ago
sendOsc(OSC_ADDRESS.PLAYCUE, props.clientCue,'all');
4 months ago
}
4 months ago
if(props.lightCue){
// sendOsc(OSC_ADDRESS.CLIENT_INPUT, props.lightCue);
}
4 months ago
4 months ago
}
function stop() {
4 months ago
console.log('Stop all');
4 months ago
4 months ago
sendOsc(OSC_ADDRESS.STOPCUE,'','all');
4 months ago
sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, '');
4 months ago
}
function secondToTime(seconds) {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs < 10 ? '0' : ''}${secs}`;
}
function getAudioDuration(){
const type= refCue.current?.type;
// console.log('getAudioDuration', type, refCue.current);
// switch(type) {
// case CueType.Bg:
if(refAudioBg.current) {
return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`;
} else {
return 'N/A';
}
// case CueType.Announce:
// return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A';
// default:
// return 'N/A';
// }
}
useEffect(()=>{
if(currentCue) {
refCue.current = currentCue;
// console.log('Current Cue:', currentCue);
}
},[currentCue]);
useEffect(()=>{
fetch('/cuelist.json')
.then(response => response.json())
.then(data => {
console.log('Cuelist data:', data);
setCuelist(data.cuelist);
})
.catch(error => {
console.error('Error fetching cuelist:', error);
});
4 months ago
listen('osc_message', (event) => {
console.log(`Received OSC message: ${event.payload}`);
onOsc(event.payload);
});
4 months ago
},[]);
return (
4 months ago
<main className="overflow-y-auto flex flex-row gap-8 p-4 min-h-screen">
<section className="flex flex-col gap-4">
<section className="flex flex-row justify-between items-center">
<div className="text-6xl p-4 bg-pink-300">{currentCue ? `${currentCue.name}` : 'None'}</div>
<p>
{timestamp}
</p>
<button onClick={stop}>stop all</button>
<span className="flex flex-col gap-2 items-stretch">
<label htmlFor="fade_duration">Fade Duration</label>
<input type="range" id="fade_duration" min="0" max="30" step="0.1" defaultValue={DefaultFadeDuration} className="slider"
onChange={(e) => {
const value = parseFloat(e.target.value);
setFadeDuration(value);
}}></input>
<span className="text-2xl">{fadeDuration}s</span>
</span>
</section>
<table className="border-collapse w-full **:p-1 border-green-500 border-4">
<thead className="bg-green-500">
<tr className="text-left lowercase font-[900]">
{/* <th>ID</th> */}
<th></th>
<th>Name</th>
<th>Description</th>
<th>Type</th>
<th>Auto</th>
<th>Due | Light | Audio</th>
<th>clientCue</th>
</tr>
</thead>
<tbody>
{cuelist?.map(({id, name, description, type, auto,...props}, index) => (
<tr key={id} className={currentCue?.id === id ? 'bg-green-200' : ''}>
<td className="flex flex-row gap-2">
<button
onClick={()=>{
playCue({id, name, description, type, auto, ...props});
}}>go</button>
{type==CueType.Announce && <button
onClick={()=>{
if(props.audioCue){
sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, '');
}
}}>stop</button>}
</td>
<td>{name}</td>
<td>{description}</td>
<td>{EmojiType[type]}</td>
<td>{auto ? '⤵' : ''}</td>
<td>{props.duration} {props.lightCue && `L${props.lightCue}`} {props.audioCue && `S${props.audioCue}`}</td>
<td className={`${props.clientCue&& 'bg-green-300 rounded-full text-center font-[900]'}`}>{props.clientCue || ''}</td>
</tr>
))}
</tbody>
</table>
4 months ago
</section>
4 months ago
<table>
<thead className="bg-blue-500">
<tr className="text-left lowercase font-[900]">
<th>id</th>
<th>status</th>
<th>cue</th>
<th>timestamp</th>
<th>control</th>
</tr>
</thead>
<tbody>
4 months ago
{Array.from(Array(CLIENT_COUNT).keys()).map((i) => {
const id=i+1;
const log = clientStatus[id];
return (
<tr key={id} className="text-left lowercase font-[900]">
<td className="font-[900]">{id}</td>
{<>
<td>{log?.length>0 &&log[0]?.status}</td>
<td>{log?.length>0 && log[0]?.name}</td>
<td>{log?.length>0 &&log[0]?.timestamp}</td>
</>}
<td className="flex flex-row gap-1 p-1">
<button onClick={()=>sendOsc(OSC_ADDRESS.STOPCUE,'',id)}>stop</button>
{cuelist?.filter(c=>c.clientCue).map(c=>(
<button key={c.clientCue} onClick={()=>{
sendOsc(OSC_ADDRESS.PLAYCUE, c.clientCue, id);
}}>{c.clientCue}</button>
))}
4 months ago
</td>
</tr>
4 months ago
);
})}
4 months ago
</tbody>
</table>
4 months ago
</main>
);
}
export default App;