diff --git a/Makefile b/Makefile index fb52343..7c3ec79 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: deploy clean start default frontend +.PHONY: deploy clean start debug frontend install-service server/server: frontend rm -rf server/dist @@ -8,19 +8,23 @@ server/server: frontend frontend: npm run build -deploy: server/server - scp server/server kiosk: +deploy: server/server install-service + ssh kiosk "systemctl --user stop digital-turntable.service" || true + scp server/server kiosk:~/server + ssh kiosk "chmod +x ~/server && systemctl --user start digital-turntable.service" + +install-service: + ssh kiosk "mkdir -p ~/.config/systemd/user" + scp server/digital-turntable.service kiosk:~/.config/systemd/user/digital-turntable.service + ssh kiosk "systemctl --user daemon-reload && systemctl --user enable digital-turntable.service" clean: rm -rf dist server/dist server/server start: deploy - # TODO: This doesn't kill weston nicely; I should handle that? Or background or something - ssh kiosk "pgrep -f './server' || ./server &" ssh kiosk "pkill -f 'weston --shell=kiosk-shell.so'" || true ssh kiosk weston --shell=kiosk-shell.so -- firefox --kiosk localhost:8000 --remote-debugging-port=9222 debug: deploy - ssh kiosk "pgrep -f './server' || ./server &" ssh kiosk "pkill -f 'weston --shell=kiosk-shell.so'" || true ssh -L 6000:localhost:6000 kiosk weston --shell=kiosk-shell.so -- firefox --kiosk localhost:8000 --start-debugger-server 6000 diff --git a/server/digital-turntable.service b/server/digital-turntable.service new file mode 100644 index 0000000..57ed601 --- /dev/null +++ b/server/digital-turntable.service @@ -0,0 +1,13 @@ +[Unit] +Description=Digital Turntable kiosk server +After=network.target + +[Service] +Type=simple +WorkingDirectory=%h +ExecStart=%h/server +Restart=on-failure +RestartSec=2 + +[Install] +WantedBy=default.target diff --git a/src/App.tsx b/src/App.tsx index dc97a78..37db27c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,20 +52,40 @@ function navidromeURL(path: string, params: Record): string { }).toString(); } +const WELCOME_TEXT = "Scan an album to begin."; function App() { const [album, setAlbum] = useState(null); const [buffer, setBuffer] = useState([]); const [nowPlaying, play] = useState(0); - const [searching, setSearching] = useState(false); + const [displayText, setDisplayText] = useState(WELCOME_TEXT); const [paused, setPaused] = useState(true); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const audioRef = useRef(null); + const [lastClicked, setLastClicked] = useState(new Date); + + const click = () => setLastClicked(new Date); + + useEffect(() => { + const IDLE_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes + window.setTimeout(() => { + setDisplayText("Asleep. Tap screen to scan."); + fetch('/api/reader/auto', { method: 'POST' }); + }, lastClicked.getTime() + IDLE_TIMEOUT_MS - new Date().getTime()); + + return () => { + setDisplayText(WELCOME_TEXT); + fetch('/api/reader/on', { method: 'POST' }); + } + }, [lastClicked]); const handleEnded = () => { if ((album?.tracks?.length ?? 0) > nowPlaying + 1) { // if there's a next track play(nowPlaying + 1); setPaused(false); + } else { + setAlbum(null); + setDisplayText(WELCOME_TEXT); // Either I could always be diligent about resetting it elsewhere…or I can just do it here! } }; @@ -129,7 +149,7 @@ function App() { console.log("empty barcode; ignoring"); return; } - setSearching(true); + setDisplayText("Searching…"); console.log("Scanned barcode:", barcode); var mbRelease: IRelease | IReleaseMatch; if (barcode.startsWith("mbid:")) { @@ -145,8 +165,8 @@ function App() { inc: ['artist-credits', 'release-groups', 'genres'], // TODO }); if (searchResponse.count === 0) { - console.log("No album found for barcode:", barcode); // TODO: some kind of toast? - setSearching(false); + setDisplayText("No album found for barcode: " + barcode); + setTimeout(() => setDisplayText(WELCOME_TEXT), 3000); return; } mbRelease = searchResponse.releases[0]; @@ -179,7 +199,6 @@ function App() { // # …and play! play(0); setPaused(false); - setSearching(false); } else { // Not found in Navidrome; populate with MusicBrainz data instead const fullMbRelease = await mbApi.lookup('release', mbRelease.id, ['recordings', 'artist-credits', 'release-groups']); const tracks: Track[] = fullMbRelease.media[0].tracks.map((t: ITrack) => ({ // TODO: handle multi-disc albums @@ -196,7 +215,6 @@ function App() { coverArtLink: await getCoverArtSrcURL(fullMbRelease.id, fullMbRelease['release-group']?.id || '') || '', }) console.log(album); - setSearching(false); } } @@ -214,7 +232,7 @@ function App() {

This album cannot be played, as Chandler doesn't own a copy.

); return ( -
+