|
|
|
@ -1,5 +1,5 @@ |
|
|
|
import { useState, useEffect, useRef } from "react"; |
|
|
|
import { useState, useEffect, useRef } from "react"; |
|
|
|
import {Howl, Howler} from 'howler'; |
|
|
|
import { Howl, Howler } from 'howler'; |
|
|
|
import { invoke } from "@tauri-apps/api/core"; |
|
|
|
import { invoke } from "@tauri-apps/api/core"; |
|
|
|
import { listen } from '@tauri-apps/api/event'; |
|
|
|
import { listen } from '@tauri-apps/api/event'; |
|
|
|
import "./App.css"; |
|
|
|
import "./App.css"; |
|
|
|
@ -8,165 +8,206 @@ import { Light } from "./utils/light"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DefaultFadeDuration = 3; // 1 second |
|
|
|
const DefaultFadeDuration = 3; // 1 second |
|
|
|
const CLIENT_COUNT=13; |
|
|
|
const CLIENT_COUNT = 13; |
|
|
|
const CUE_FILE='cuelist_0924.json'; |
|
|
|
const CUE_FILE = 'cuelist_0924.json'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const CueType={ |
|
|
|
const CueType = { |
|
|
|
Bg: 'bg', |
|
|
|
Bg: 'bg', |
|
|
|
Announce: 'announce', |
|
|
|
Announce: 'announce', |
|
|
|
Light: 'light' |
|
|
|
Light: 'light' |
|
|
|
} |
|
|
|
} |
|
|
|
const EmojiType={ |
|
|
|
const EmojiType = { |
|
|
|
bg: '🎵', |
|
|
|
bg: '🎵', |
|
|
|
announce: '📢', |
|
|
|
announce: '📢', |
|
|
|
light: '💡' |
|
|
|
light: '💡' |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function App() { |
|
|
|
function App() { |
|
|
|
|
|
|
|
|
|
|
|
const [cuelist, setCuelist] = useState([]); |
|
|
|
const [cuelist, setCuelist] = useState([]); |
|
|
|
const [currentCue, setCurrentCue] = useState(null); |
|
|
|
const [currentCue, setCurrentCue] = useState(null); |
|
|
|
const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds |
|
|
|
const [fadeDuration, setFadeDuration] = useState(DefaultFadeDuration); // Default fade duration in seconds |
|
|
|
const [timestamp, setTimestamp] = useState(); |
|
|
|
|
|
|
|
const [clientStatus, setClientStatus] = useState({}); |
|
|
|
const [clientStatus, setClientStatus] = useState({}); |
|
|
|
|
|
|
|
|
|
|
|
const refCue = useRef(null); |
|
|
|
const refCue = useRef(null); |
|
|
|
// const refNextCue = useRef(null); |
|
|
|
// const refNextCue = useRef(null); |
|
|
|
const refLight= useRef(); |
|
|
|
const refLight = useRef(); |
|
|
|
const refDuration= useRef(); |
|
|
|
const refDuration = useRef(); |
|
|
|
const refTimer= useRef(); |
|
|
|
const refTotalTime=useRef(); |
|
|
|
|
|
|
|
const refTimer = useRef(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function sendOsc(addr, message, id){ |
|
|
|
function sendOsc(addr, message, id) { |
|
|
|
|
|
|
|
|
|
|
|
invoke('send_osc_message', { |
|
|
|
invoke('send_osc_message', { |
|
|
|
key: addr, |
|
|
|
key: addr, |
|
|
|
message: `${id}#${message}`, |
|
|
|
message: `${id}#${message}`, |
|
|
|
host:'0.0.0.0:0', |
|
|
|
host: '0.0.0.0:0', |
|
|
|
target:'192.168.51.255:8000', |
|
|
|
target: '192.168.51.255:8000', |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function sendOscToSound(addr, message){ |
|
|
|
function sendOscToSound(addr, message) { |
|
|
|
|
|
|
|
|
|
|
|
invoke('send_osc_message', { |
|
|
|
invoke('send_osc_message', { |
|
|
|
key: addr, |
|
|
|
key: addr, |
|
|
|
message, |
|
|
|
message, |
|
|
|
host:'0.0.0.0:0', |
|
|
|
host: '0.0.0.0:0', |
|
|
|
target:'192.168.51.100:58200', |
|
|
|
target: '192.168.51.100:58200', |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onOsc(message){ |
|
|
|
function onOsc(message) { |
|
|
|
|
|
|
|
|
|
|
|
const addr= message.addr; |
|
|
|
const addr = message.addr; |
|
|
|
const [id, status, name]= message.args[0]?.split('#'); |
|
|
|
const [id, status, name] = message.args[0]?.split('#'); |
|
|
|
// console.log('receive osc:', id, status,name); |
|
|
|
// console.log('receive osc:', id, status,name); |
|
|
|
|
|
|
|
|
|
|
|
switch(addr){ |
|
|
|
switch (addr) { |
|
|
|
case OSC_ADDRESS.CLIENT_STATUS: |
|
|
|
case OSC_ADDRESS.CLIENT_STATUS: |
|
|
|
|
|
|
|
|
|
|
|
setClientStatus(prev=>({ |
|
|
|
setClientStatus(prev => ({ |
|
|
|
...prev, |
|
|
|
...prev, |
|
|
|
[id]:{ |
|
|
|
[id]: { |
|
|
|
status, |
|
|
|
status, |
|
|
|
name, |
|
|
|
name, |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
} |
|
|
|
} |
|
|
|
})); |
|
|
|
})); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case OSC_ADDRESS.CLIENT_DURATION: |
|
|
|
case OSC_ADDRESS.CLIENT_DURATION: |
|
|
|
setClientStatus(prev=>({ |
|
|
|
setClientStatus(prev => ({ |
|
|
|
...prev, |
|
|
|
...prev, |
|
|
|
[id]:{ |
|
|
|
[id]: { |
|
|
|
...prev[id], |
|
|
|
...prev[id], |
|
|
|
duration: status, |
|
|
|
duration: status, |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
} |
|
|
|
} |
|
|
|
})); |
|
|
|
})); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case OSC_ADDRESS.CLIENT_INPUT: |
|
|
|
case OSC_ADDRESS.CLIENT_INPUT: |
|
|
|
setClientStatus(prev=>({ |
|
|
|
setClientStatus(prev => ({ |
|
|
|
...prev, |
|
|
|
...prev, |
|
|
|
[id]:{ |
|
|
|
[id]: { |
|
|
|
...prev[id], |
|
|
|
...prev[id], |
|
|
|
input: status, |
|
|
|
input: status, |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
timestamp: new Date().toLocaleTimeString(), |
|
|
|
} |
|
|
|
} |
|
|
|
})); |
|
|
|
})); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getTotalLeftTime(id){ |
|
|
|
|
|
|
|
const cue=cuelist.find(el=>el.id==id); |
|
|
|
|
|
|
|
const index=cuelist.indexOf(cue); |
|
|
|
|
|
|
|
if(index==-1) return; |
|
|
|
|
|
|
|
|
|
|
|
function playCue({id, name, description, type, auto, audioFile, ...props}) { |
|
|
|
let sum=0; |
|
|
|
|
|
|
|
for(var i=index;i<cuelist.length;++i){ |
|
|
|
console.log('Playing cue:', {id, name, description, type, auto, audioFile, ...props}); |
|
|
|
if(cuelist[i].duration) sum+=cuelist[i].duration; |
|
|
|
setCurrentCue({id, name, description, type, auto, audioFile, ...props}); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Handle other cue types and properties here |
|
|
|
|
|
|
|
if(props.audioCue){ |
|
|
|
|
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_GO_CUE, props.audioCue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(props.clientCue){ |
|
|
|
return sum; |
|
|
|
sendOsc(OSC_ADDRESS.PLAYCUE, props.clientCue,'all'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(props.lightCue){ |
|
|
|
} |
|
|
|
// sendOsc(OSC_ADDRESS.CLIENT_INPUT, props.lightCue); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(props.lightCue=='fade_in_light') refLight.current.fadeIn(); // Fade in light for conversation start |
|
|
|
|
|
|
|
if(props.lightCue=='fade_out_light') refLight.current.fadeOut(); // Fade out light for conversation end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
function playCue({ id, name, description, type, auto, audioFile, ...props }) { |
|
|
|
if(props.reset){ |
|
|
|
|
|
|
|
//sendOsc(OSC_ADDRESS.RESETCUE,'','all'); |
|
|
|
|
|
|
|
refLight.current.set(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
console.log('Playing cue:', { id, name, description, type, auto, audioFile, ...props }); |
|
|
|
|
|
|
|
setCurrentCue({ id, name, description, type, auto, audioFile, ...props }); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handle other cue types and properties here |
|
|
|
|
|
|
|
if (props.audioCue) { |
|
|
|
|
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_GO_CUE, props.audioCue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (props.clientCue) { |
|
|
|
|
|
|
|
sendOsc(OSC_ADDRESS.PLAYCUE, props.clientCue, 'all'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(props.duration){ |
|
|
|
if (props.lightCue) { |
|
|
|
if(refTimer.current) clearInterval(refTimer.current); |
|
|
|
// sendOsc(OSC_ADDRESS.CLIENT_INPUT, props.lightCue); |
|
|
|
let due= props.duration; |
|
|
|
|
|
|
|
|
|
|
|
if (props.lightCue == 'fade_in_light') refLight.current.fadeIn(); // Fade in light for conversation start |
|
|
|
console.log('Start timer:', due); |
|
|
|
if (props.lightCue == 'fade_out_light') refLight.current.fadeOut(); // Fade out light for conversation end |
|
|
|
const next=cuelist.find(c=>c.id==id+1); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
refTimer.current= setInterval(()=>{ |
|
|
|
if (props.reset) { |
|
|
|
due--; |
|
|
|
//sendOsc(OSC_ADDRESS.RESETCUE,'','all'); |
|
|
|
if(due<=0){ |
|
|
|
refLight.current.set(1); |
|
|
|
clearInterval(refTimer.current); |
|
|
|
|
|
|
|
refTimer.current=null; |
|
|
|
} |
|
|
|
if(auto){ |
|
|
|
|
|
|
|
console.log('Auto play next cue:', next); |
|
|
|
if (refTimer.current) { |
|
|
|
playCue(next); |
|
|
|
clearTimeout(refTimer.current); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
refDuration.current.innerText= secondToTime(due); |
|
|
|
|
|
|
|
},1000); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (props.duration) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// calculate last time |
|
|
|
|
|
|
|
let totalLefTime=getTotalLeftTime(id); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clear any existing timer |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Store the end time of the timer |
|
|
|
|
|
|
|
const totalEndTime=new Date().getTime()+totalLefTime*1000; |
|
|
|
|
|
|
|
const endTime = new Date().getTime() + (props.duration * 1000); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Function to update the timer |
|
|
|
|
|
|
|
const tick = () => { |
|
|
|
|
|
|
|
const now = new Date().getTime(); |
|
|
|
|
|
|
|
const timeLeft = endTime - now; |
|
|
|
|
|
|
|
const totalTimeLeft=totalEndTime-now; |
|
|
|
|
|
|
|
//console.log(totalTimeLeft); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (timeLeft <= 0) { |
|
|
|
|
|
|
|
// Timer has finished |
|
|
|
|
|
|
|
clearTimeout(refTimer.current); |
|
|
|
|
|
|
|
refTimer.current = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (auto) { |
|
|
|
|
|
|
|
console.log('Auto play next cue:', next); |
|
|
|
|
|
|
|
const next = cuelist.find(c => c.id === id + 1); |
|
|
|
|
|
|
|
playCue(next); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update the displayed duration |
|
|
|
|
|
|
|
refDuration.current.innerText = secondToTime(timeLeft / 1000); |
|
|
|
|
|
|
|
refTotalTime.current.innerText=`${secondToTime(totalTimeLeft/1000)} / ${secondToTime(totalLefTime)}`; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Call the next tick |
|
|
|
|
|
|
|
refTimer.current = setTimeout(tick, 100); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Start the timer |
|
|
|
|
|
|
|
console.log('Start timer:', props.duration); |
|
|
|
|
|
|
|
refTimer.current = setTimeout(tick, 100); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
function stop() { |
|
|
|
function stop() { |
|
|
|
console.log('Stop all'); |
|
|
|
console.log('Stop all'); |
|
|
|
|
|
|
|
|
|
|
|
sendOsc(OSC_ADDRESS.STOPCUE,'','all'); |
|
|
|
sendOsc(OSC_ADDRESS.STOPCUE, '', 'all'); |
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_FADE_ALL, ''); |
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_FADE_ALL, ''); |
|
|
|
|
|
|
|
|
|
|
|
// cear timer |
|
|
|
// cear timer |
|
|
|
if(refTimer.current) clearInterval(refTimer.current); |
|
|
|
if (refTimer.current) clearInterval(refTimer.current); |
|
|
|
} |
|
|
|
} |
|
|
|
function reset() { |
|
|
|
function reset() { |
|
|
|
console.log('Reset all'); |
|
|
|
console.log('Reset all'); |
|
|
|
|
|
|
|
|
|
|
|
sendOsc(OSC_ADDRESS.RESETCUE,'','all'); |
|
|
|
sendOsc(OSC_ADDRESS.RESETCUE, '', 'all'); |
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, ''); |
|
|
|
sendOscToSound(OSC_ADDRESS.SCS_STOP_ALL, ''); |
|
|
|
refLight.current.set(1); |
|
|
|
refLight.current.set(1); |
|
|
|
|
|
|
|
|
|
|
|
@ -174,18 +215,18 @@ function App() { |
|
|
|
function secondToTime(seconds) { |
|
|
|
function secondToTime(seconds) { |
|
|
|
const minutes = Math.floor(seconds / 60); |
|
|
|
const minutes = Math.floor(seconds / 60); |
|
|
|
const secs = Math.floor(seconds % 60); |
|
|
|
const secs = Math.floor(seconds % 60); |
|
|
|
return `${minutes}:${secs < 10 ? '0' : ''}${secs}`; |
|
|
|
return `${minutes.toString().padStart(2,'0')}:${secs < 10 ? '0' : ''}${secs}`; |
|
|
|
} |
|
|
|
} |
|
|
|
function getAudioDuration(){ |
|
|
|
function getAudioDuration() { |
|
|
|
|
|
|
|
|
|
|
|
const type= refCue.current?.type; |
|
|
|
const type = refCue.current?.type; |
|
|
|
// console.log('getAudioDuration', type, refCue.current); |
|
|
|
// console.log('getAudioDuration', type, refCue.current); |
|
|
|
// switch(type) { |
|
|
|
// switch(type) { |
|
|
|
// case CueType.Bg: |
|
|
|
// case CueType.Bg: |
|
|
|
if(refAudioBg.current) { |
|
|
|
if (refAudioBg.current) { |
|
|
|
return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`; |
|
|
|
return `${secondToTime(refAudioBg.current.seek())} / ${secondToTime(refAudioBg.current.duration())} ${refAudioBg.current?.volume()}`; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return 'N/A'; |
|
|
|
return 'N/A'; |
|
|
|
} |
|
|
|
} |
|
|
|
// case CueType.Announce: |
|
|
|
// case CueType.Announce: |
|
|
|
// return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A'; |
|
|
|
// return refAudioAnnounce.current ? `${secondToTime(refAudioAnnounce.current.seek())} / ${secondToTime(refAudioAnnounce.current.duration())}` : 'N/A'; |
|
|
|
@ -195,138 +236,137 @@ function App() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
useEffect(()=>{ |
|
|
|
useEffect(() => { |
|
|
|
if(currentCue) { |
|
|
|
if (currentCue) { |
|
|
|
refCue.current = currentCue; |
|
|
|
refCue.current = currentCue; |
|
|
|
// console.log('Current Cue:', currentCue); |
|
|
|
// console.log('Current Cue:', currentCue); |
|
|
|
} |
|
|
|
} |
|
|
|
},[currentCue]); |
|
|
|
}, [currentCue]); |
|
|
|
useEffect(()=>{ |
|
|
|
useEffect(() => { |
|
|
|
fetch(`/${CUE_FILE}`) |
|
|
|
fetch(`/${CUE_FILE}`) |
|
|
|
.then(response => response.json()) |
|
|
|
.then(response => response.json()) |
|
|
|
.then(data => { |
|
|
|
.then(data => { |
|
|
|
console.log('Cuelist data:', data); |
|
|
|
console.log('Cuelist data:', data); |
|
|
|
setCuelist(data.cuelist); |
|
|
|
setCuelist(data.cuelist); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch(error => { |
|
|
|
.catch(error => { |
|
|
|
console.error('Error fetching cuelist:', error); |
|
|
|
console.error('Error fetching cuelist:', error); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
listen('osc_message', (event) => { |
|
|
|
|
|
|
|
// console.log(`Received OSC message: ${JSON.stringify(event.payload)}`); |
|
|
|
|
|
|
|
onOsc(event.payload); |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
listen('osc_message', (event) => { |
|
|
|
|
|
|
|
// console.log(`Received OSC message: ${JSON.stringify(event.payload)}`); |
|
|
|
|
|
|
|
onOsc(event.payload); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},[]); |
|
|
|
|
|
|
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<main className="overflow-y-auto flex flex-row gap-8 p-4 min-h-screen"> |
|
|
|
<main className="overflow-y-auto flex flex-row gap-8 p-4 min-h-screen"> |
|
|
|
<section className="flex-1 flex flex-col gap-4"> |
|
|
|
<section className="flex-1 flex flex-col gap-4"> |
|
|
|
<section className="flex flex-row justify-between items-center gap-2"> |
|
|
|
<section className="grid grid-cols-4 items-center gap-2"> |
|
|
|
<div className="flex flex-row font-bold items-stretch justify-center w-1/3 bg-pink-300 gap-4 p-2"> |
|
|
|
<div className="flex flex-row font-bold items-stretch justify-bewteen bg-pink-300 gap-4 p-2"> |
|
|
|
<div className="text-4xl">{currentCue ? `${currentCue.name}` : 'None'}</div> |
|
|
|
<div className="text-4xl">{currentCue ? `${currentCue.name}` : 'None'}</div> |
|
|
|
<div className="text-4xl" ref={refDuration}></div> |
|
|
|
<div className="text-4xl" ref={refDuration}></div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<p> |
|
|
|
<button className="text-4xl self-stretch" onClick={stop}>stop all</button> |
|
|
|
{timestamp} |
|
|
|
<button className="text-4xl self-stretch" onClick={reset}>reset all</button> |
|
|
|
</p> |
|
|
|
<Light ref={refLight} /> |
|
|
|
<button className="text-4xl self-stretch" onClick={stop}>stop all</button> |
|
|
|
<span className="flex flex-col gap-2 items-stretch hidden"> |
|
|
|
<button className="text-4xl self-stretch" onClick={reset}>reset all</button> |
|
|
|
<label htmlFor="fade_duration">Fade Duration</label> |
|
|
|
<Light ref={refLight} /> |
|
|
|
<input type="range" id="fade_duration" min="0" max="30" step="0.1" defaultValue={DefaultFadeDuration} className="slider" |
|
|
|
<span className="flex flex-col gap-2 items-stretch hidden"> |
|
|
|
onChange={(e) => { |
|
|
|
<label htmlFor="fade_duration">Fade Duration</label> |
|
|
|
const value = parseFloat(e.target.value); |
|
|
|
<input type="range" id="fade_duration" min="0" max="30" step="0.1" defaultValue={DefaultFadeDuration} className="slider" |
|
|
|
setFadeDuration(value); |
|
|
|
onChange={(e) => { |
|
|
|
}}></input> |
|
|
|
const value = parseFloat(e.target.value); |
|
|
|
<span className="text-2xl">{fadeDuration}s</span> |
|
|
|
setFadeDuration(value); |
|
|
|
</span> |
|
|
|
}}></input> |
|
|
|
</section> |
|
|
|
<span className="text-2xl">{fadeDuration}s</span> |
|
|
|
<table className="border-collapse w-full **:p-1 border-green-500 border-4"> |
|
|
|
</span> |
|
|
|
<thead className="bg-green-500"> |
|
|
|
</section> |
|
|
|
<tr className="text-left lowercase font-[900]"> |
|
|
|
<table className="border-collapse w-full **:p-1 border-green-500 border-4"> |
|
|
|
{/* <th>ID</th> */} |
|
|
|
<thead className="bg-green-500"> |
|
|
|
<th></th> |
|
|
|
<tr className="text-left lowercase font-[900]"> |
|
|
|
<th>Name</th> |
|
|
|
{/* <th>ID</th> */} |
|
|
|
<th>Description</th> |
|
|
|
<th></th> |
|
|
|
<th>Type</th> |
|
|
|
<th>Name</th> |
|
|
|
<th>Auto</th> |
|
|
|
<th>Description</th> |
|
|
|
<th>Due</th> |
|
|
|
<th>Type</th> |
|
|
|
<th>Light</th> |
|
|
|
<th>Auto</th> |
|
|
|
<th>Audio</th> |
|
|
|
<th>Due</th> |
|
|
|
<th>Client</th> |
|
|
|
<th>Light</th> |
|
|
|
</tr> |
|
|
|
<th>Audio</th> |
|
|
|
</thead> |
|
|
|
<th>Client</th> |
|
|
|
<tbody> |
|
|
|
</tr> |
|
|
|
{cuelist?.map(({ id, name, description, type, auto, ...props }, index) => ( |
|
|
|
</thead> |
|
|
|
<tr key={id} className={currentCue?.id === id ? 'bg-green-200' : `${props.debug && 'text-red-500'}`}> |
|
|
|
<tbody> |
|
|
|
<td className="flex flex-row gap-2"> |
|
|
|
{cuelist?.map(({id, name, description, type, auto,...props}, index) => ( |
|
|
|
<button |
|
|
|
<tr key={id} className={currentCue?.id === id ? 'bg-green-200' : `${props.debug && 'text-red-500'}`}> |
|
|
|
onClick={() => { |
|
|
|
<td className="flex flex-row gap-2"> |
|
|
|
playCue({ id, name, description, type, auto, ...props }); |
|
|
|
<button |
|
|
|
}}>go</button> |
|
|
|
onClick={()=>{ |
|
|
|
</td> |
|
|
|
playCue({id, name, description, type, auto, ...props}); |
|
|
|
<td>{name}</td> |
|
|
|
}}>go</button> |
|
|
|
<td className="text-sm">{description}</td> |
|
|
|
</td> |
|
|
|
<td>{EmojiType[type]}</td> |
|
|
|
<td>{name}</td> |
|
|
|
<td>{auto ? '⤵️' : ''}</td> |
|
|
|
<td className="text-sm">{description}</td> |
|
|
|
<td>{props.duration}</td> |
|
|
|
<td>{EmojiType[type]}</td> |
|
|
|
<td className="text-sm">{props.lightCue && `L${props.lightCue}`}</td> |
|
|
|
<td>{auto ? '⤵️' : ''}</td> |
|
|
|
<td className="text-sm">{props.audioCue && `S${props.audioCue}`}</td> |
|
|
|
<td>{props.duration}</td> |
|
|
|
<td className={`text-sm ${props.clientCue && 'bg-green-300 rounded-full text-center font-[900]'}`}>{props.clientCue || ''}</td> |
|
|
|
<td className="text-sm">{props.lightCue && `L${props.lightCue}`}</td> |
|
|
|
|
|
|
|
<td className="text-sm">{props.audioCue && `S${props.audioCue}`}</td> |
|
|
|
|
|
|
|
<td className={`text-sm ${props.clientCue&& 'bg-green-300 rounded-full text-center font-[900]'}`}>{props.clientCue || ''}</td> |
|
|
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
</tbody> |
|
|
|
|
|
|
|
</table> |
|
|
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
<table className="flex-1 text-sm"> |
|
|
|
|
|
|
|
<thead className="bg-blue-500"> |
|
|
|
|
|
|
|
<tr className="text-center lowercase font-[900]"> |
|
|
|
|
|
|
|
<th>id</th> |
|
|
|
|
|
|
|
<th>status</th> |
|
|
|
|
|
|
|
<th>cue</th> |
|
|
|
|
|
|
|
<th>input</th> |
|
|
|
|
|
|
|
<th>due</th> |
|
|
|
|
|
|
|
<th>timestamp</th> |
|
|
|
|
|
|
|
<th></th> |
|
|
|
|
|
|
|
</tr> |
|
|
|
</tr> |
|
|
|
</thead> |
|
|
|
))} |
|
|
|
<tbody> |
|
|
|
</tbody> |
|
|
|
{Array.from(Array(CLIENT_COUNT).keys()).map((i) => { |
|
|
|
</table> |
|
|
|
const id=(i+1).toString();//.padStart(2,'0'); |
|
|
|
<div className="text-6xl text-center font-bold bg-blue-500 text-white p-4" ref={refTotalTime}></div> |
|
|
|
const log = clientStatus[id.toString()]; |
|
|
|
|
|
|
|
return ( |
|
|
|
</section> |
|
|
|
<tr key={id} className="text-left lowercase"> |
|
|
|
<table className="flex-1 text-sm"> |
|
|
|
<td className="font-[900]">{id}</td> |
|
|
|
<thead className="bg-blue-500"> |
|
|
|
{<> |
|
|
|
<tr className="text-center lowercase font-[900]"> |
|
|
|
<td>{log?.status}</td> |
|
|
|
<th>id</th> |
|
|
|
<td> |
|
|
|
<th>status</th> |
|
|
|
<span className="bg-green-200 rounded-full p-2">{log?.name}</span> |
|
|
|
<th>cue</th> |
|
|
|
</td> |
|
|
|
<th>input</th> |
|
|
|
<td className="flex-1 text-[9px] min-w-[80px]"> |
|
|
|
<th>due</th> |
|
|
|
{log?.input?.substr(log.input.length - 20)?.split('').map((ch,i)=>i%3<1?ch:'*').join('')} |
|
|
|
<th>timestamp</th> |
|
|
|
</td> |
|
|
|
<th></th> |
|
|
|
<td>{log?.duration}</td> |
|
|
|
</tr> |
|
|
|
<td className="text-[12px]">{log?.timestamp}</td> |
|
|
|
</thead> |
|
|
|
</>} |
|
|
|
<tbody> |
|
|
|
<td className="flex flex-row justify-end gap-1 p-1"> |
|
|
|
{Array.from(Array(CLIENT_COUNT).keys()).map((i) => { |
|
|
|
<button onClick={()=>sendOsc(OSC_ADDRESS.STOPCUE,'',id)}>stop</button> |
|
|
|
const id = (i + 1).toString();//.padStart(2,'0'); |
|
|
|
<button onClick={()=>sendOsc(OSC_ADDRESS.RESETCUE,'',id)}>reset</button> |
|
|
|
const log = clientStatus[id.toString()]; |
|
|
|
{cuelist?.filter(c=>c.clientCue && !c.debug).map((c, index)=>( |
|
|
|
return ( |
|
|
|
<button key={`${id}${c.clientCue}${index}`} onClick={()=>{ |
|
|
|
<tr key={id} className="text-left lowercase"> |
|
|
|
sendOsc(OSC_ADDRESS.PLAYCUE, c.clientCue, id); |
|
|
|
<td className="font-[900]">{id}</td> |
|
|
|
}}>{c.clientCue}</button> |
|
|
|
{<> |
|
|
|
))} |
|
|
|
<td>{log?.status}</td> |
|
|
|
|
|
|
|
<td> |
|
|
|
|
|
|
|
<span className="bg-green-200 rounded-full p-2">{log?.name}</span> |
|
|
|
|
|
|
|
</td> |
|
|
|
|
|
|
|
<td className="flex-1 text-[9px] min-w-[80px]"> |
|
|
|
|
|
|
|
{log?.input?.substr(log.input.length - 20)?.split('').map((ch, i) => i % 3 < 1 ? ch : '*').join('')} |
|
|
|
</td> |
|
|
|
</td> |
|
|
|
</tr> |
|
|
|
<td>{log?.duration}</td> |
|
|
|
); |
|
|
|
<td className="text-[12px]">{log?.timestamp}</td> |
|
|
|
})} |
|
|
|
</>} |
|
|
|
</tbody> |
|
|
|
<td className="flex flex-row justify-end gap-1 p-1"> |
|
|
|
</table> |
|
|
|
<button onClick={() => sendOsc(OSC_ADDRESS.STOPCUE, '', id)}>stop</button> |
|
|
|
|
|
|
|
<button onClick={() => sendOsc(OSC_ADDRESS.RESETCUE, '', id)}>reset</button> |
|
|
|
|
|
|
|
{cuelist?.filter(c => c.clientCue && !c.debug).map((c, index) => ( |
|
|
|
|
|
|
|
<button key={`${id}${c.clientCue}${index}`} onClick={() => { |
|
|
|
|
|
|
|
sendOsc(OSC_ADDRESS.PLAYCUE, c.clientCue, id); |
|
|
|
|
|
|
|
}}>{c.clientCue}</button> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
</td> |
|
|
|
|
|
|
|
</tr> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
</tbody> |
|
|
|
|
|
|
|
</table> |
|
|
|
</main> |
|
|
|
</main> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|