Compare commits

..

No commits in common. "52ad6cc8041a1dd95cbee8866e079c04e8574ef0" and "538f7dc5a7358280b8ff57937e7f1dba5c15ea9a" have entirely different histories.

3 changed files with 16 additions and 49 deletions

View file

@ -1,4 +1,4 @@
.PHONY: deploy clean start debug frontend install-service .PHONY: deploy clean start default frontend
server/server: frontend server/server: frontend
rm -rf server/dist rm -rf server/dist
@ -8,23 +8,19 @@ server/server: frontend
frontend: frontend:
npm run build npm run build
deploy: server/server install-service deploy: server/server
ssh kiosk "systemctl --user stop digital-turntable.service" || true scp server/server kiosk:
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: clean:
rm -rf dist server/dist server/server rm -rf dist server/dist server/server
start: deploy 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 "pkill -f 'weston --shell=kiosk-shell.so'" || true
ssh kiosk weston --shell=kiosk-shell.so -- firefox --kiosk localhost:8000 --remote-debugging-port=9222 ssh kiosk weston --shell=kiosk-shell.so -- firefox --kiosk localhost:8000 --remote-debugging-port=9222
debug: deploy debug: deploy
ssh kiosk "pgrep -f './server' || ./server &"
ssh kiosk "pkill -f 'weston --shell=kiosk-shell.so'" || true 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 ssh -L 6000:localhost:6000 kiosk weston --shell=kiosk-shell.so -- firefox --kiosk localhost:8000 --start-debugger-server 6000

View file

@ -1,13 +0,0 @@
[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

View file

@ -52,40 +52,20 @@ function navidromeURL(path: string, params: Record<string, string>): string {
}).toString(); }).toString();
} }
const WELCOME_TEXT = "Scan an album to begin.";
function App() { function App() {
const [album, setAlbum] = useState<AlbumType | null>(null); const [album, setAlbum] = useState<AlbumType | null>(null);
const [buffer, setBuffer] = useState<string[]>([]); const [buffer, setBuffer] = useState<string[]>([]);
const [nowPlaying, play] = useState<number>(0); const [nowPlaying, play] = useState<number>(0);
const [displayText, setDisplayText] = useState(WELCOME_TEXT); const [searching, setSearching] = useState(false);
const [paused, setPaused] = useState(true); const [paused, setPaused] = useState(true);
const [currentTime, setCurrentTime] = useState(0); const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0); const [duration, setDuration] = useState(0);
const audioRef = useRef<HTMLAudioElement | null>(null); const audioRef = useRef<HTMLAudioElement | null>(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 = () => { const handleEnded = () => {
if ((album?.tracks?.length ?? 0) > nowPlaying + 1) { // if there's a next track if ((album?.tracks?.length ?? 0) > nowPlaying + 1) { // if there's a next track
play(nowPlaying + 1); play(nowPlaying + 1);
setPaused(false); 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!
} }
}; };
@ -149,7 +129,7 @@ function App() {
console.log("empty barcode; ignoring"); console.log("empty barcode; ignoring");
return; return;
} }
setDisplayText("Searching…"); setSearching(true);
console.log("Scanned barcode:", barcode); console.log("Scanned barcode:", barcode);
var mbRelease: IRelease | IReleaseMatch; var mbRelease: IRelease | IReleaseMatch;
if (barcode.startsWith("mbid:")) { if (barcode.startsWith("mbid:")) {
@ -165,8 +145,8 @@ function App() {
inc: ['artist-credits', 'release-groups', 'genres'], // TODO inc: ['artist-credits', 'release-groups', 'genres'], // TODO
}); });
if (searchResponse.count === 0) { if (searchResponse.count === 0) {
setDisplayText("No album found for barcode: " + barcode); console.log("No album found for barcode:", barcode); // TODO: some kind of toast?
setTimeout(() => setDisplayText(WELCOME_TEXT), 3000); setSearching(false);
return; return;
} }
mbRelease = searchResponse.releases[0]; mbRelease = searchResponse.releases[0];
@ -199,6 +179,7 @@ function App() {
// # …and play! // # …and play!
play(0); play(0);
setPaused(false); setPaused(false);
setSearching(false);
} else { // Not found in Navidrome; populate with MusicBrainz data instead } else { // Not found in Navidrome; populate with MusicBrainz data instead
const fullMbRelease = await mbApi.lookup('release', mbRelease.id, ['recordings', 'artist-credits', 'release-groups']); 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 const tracks: Track[] = fullMbRelease.media[0].tracks.map((t: ITrack) => ({ // TODO: handle multi-disc albums
@ -215,6 +196,7 @@ function App() {
coverArtLink: await getCoverArtSrcURL(fullMbRelease.id, fullMbRelease['release-group']?.id || '') || '', coverArtLink: await getCoverArtSrcURL(fullMbRelease.id, fullMbRelease['release-group']?.id || '') || '',
}) })
console.log(album); console.log(album);
setSearching(false);
} }
} }
@ -232,7 +214,7 @@ function App() {
<p className='welcome' style={{ gridColumn: 'span 2' }}>This album cannot be played, as Chandler doesn't own a copy.</p> <p className='welcome' style={{ gridColumn: 'span 2' }}>This album cannot be played, as Chandler doesn't own a copy.</p>
); );
return ( return (
<div className="app" onPointerDown={click}> <div className="app">
<audio <audio
ref={audioRef} ref={audioRef}
src={album.tracks[nowPlaying].source} src={album.tracks[nowPlaying].source}
@ -249,8 +231,10 @@ function App() {
{player} {player}
</div> </div>
) )
} else if (searching) {
return (<div className="welcome">Searching</div>);
} else { } else {
return (<div className="welcome" onPointerDown={click}>{displayText}</div>); return (<div className="welcome">Scan an album to begin.</div>);
} }
} }