diff options
-rw-r--r-- | guess.js | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/guess.js b/guess.js new file mode 100644 index 0000000..deceb01 --- /dev/null +++ b/guess.js @@ -0,0 +1,192 @@ +'use strict' + +const Backend = require('./backend') +const processSmartPlaylist = require('./smart-playlist') + +const { + flattenGrouplike, + parentSymbol, + searchForItem +} = require('./playlist-utils') + +const { + util: { + ansi, + telchars: telc + } +} = require('./tui-lib') + +function untilEvent(object, event) { + return new Promise(resolve => { + object.once(event, resolve) + }) +} + +const resetLine = '\r\x1b[0J' +const dim = ansi.resetAttributes() + ansi.setAttributes([ansi.A_DIM]) +const write = data => process.stdout.write(data) + +async function game() { + const backend = new Backend() + + const result = await backend.setup() + if (result.error) { + console.error(result.error) + process.exit(1) + } + + // TODO: nah + backend.setVolume(60) + + process.stdin.setRawMode(true) + process.stdin.on('data', async data => { + if (data[0] === 0x03) { + await backend.stopPlaying() + process.exit(0) + } + }) + + const sourcePath = process.argv[2] || process.env.HOME + '/Music' + let grouplike = {source: ['crawl-local', sourcePath]} + grouplike = await processSmartPlaylist(grouplike) + + // TODO: Actually let the user choose this..! + const group = grouplike.items.find(item => item.name === 'library') + const allTracks = flattenGrouplike(grouplike).items + + const displayTrack = (track, shouldLimit) => { + const parent = track[parentSymbol] + const limit = (shouldLimit + ? text => text.length > 30 ? '…' + text.slice(-30) : text + : text => text) + process.stdout.write(limit(track.name)) + if (parent.name) { + process.stdout.write(' (From ' + limit(parent.name) + ')') + } + } + + while (allTracks.length) { + const track = allTracks[Math.floor(Math.random() * allTracks.length)] + backend.play(track) + await untilEvent(backend, 'playing') + console.log('-- Listen! Then press space to pause and make a guess. --') + + let startTime = Date.now() + let playTime = 0 + let gaveUp = false + let giveUpNext = false + + let trackMatch = null + let input = '' + const displayInput = () => { + write(resetLine) + write(' >> ' + ansi.setAttributes([ansi.A_BRIGHT])) + write(input) + write(dim + ' :: ' + ansi.resetAttributes()) + if (trackMatch) { + displayTrack(trackMatch, true) + } else { + write(ansi.setForeground(ansi.C_RED)) + write('(None)') + write(ansi.resetAttributes()) + } + write(`\r\x1b[${4 + input.length}C`) + } + + const echoFn = () => { + let t = (playTime + Date.now() - startTime) / 1000 + t = Math.floor(t * 10) / 10 + if (t % 1 === 0) { + t = t + '.0' + } + write(resetLine + t + 's') + } + + while (true) { + let echo + if (!backend.player.isPaused) { + echo = setInterval(echoFn, 50) + } + const key = await untilEvent(process.stdin, 'data') + clearInterval(echo) + if (key[0] === 0x10 || (key[0] === 0x20 && !backend.player.isPaused)) { + if (backend.player.isPaused) { + startTime = Date.now() + console.log(resetLine + dim + '<Unpaused.>') + write(ansi.resetAttributes()) + } else { + playTime += Date.now() - startTime + console.log(resetLine + dim + '<Paused. Type the track\'s name below! ^P to resume.>') + write(ansi.resetAttributes()) + echoFn() + displayInput() + } + backend.togglePause() + /* + } else if (key[0] === 0x3f && (!key.length || !backend.player.isPaused)) { + backend.setPause(false) + gaveUp = true + break + */ + } else if (backend.player.isPaused) { + if (telc.isBackspace(key)) { + input = input.slice(0, -1) + giveUpNext = false + } else if (telc.isEnter(key)) { + if (trackMatch) { + write('\n') + try { + if (trackMatch === track) { + write(ansi.setAttributes([ansi.A_BRIGHT, ansi.C_GREEN])) + write('-- You got it! --\n') + displayTrack(trackMatch, false) + break + } else { + write(ansi.setAttributes([ansi.A_BRIGHT, ansi.C_RED])) + write('-- Not this one! --\n') + displayTrack(trackMatch, false) + } + } finally { + write(ansi.resetAttributes()) + write('\n') + } + } else { + if (giveUpNext) { + backend.setPause(false) + gaveUp = true + break + } else { + write(resetLine + '(Press enter twice in a row to reveal the answer.)\n') + giveUpNext = true + } + } + } else { + input += key.toString() + giveUpNext = false + } + trackMatch = searchForItem({items: allTracks}, input) + displayInput() + } + } + + process.stdout.write(resetLine) + + if (gaveUp) { + console.log('-- You chose to reveal the track that played. --') + displayTrack(track, false) + console.log('') + } + + console.log('-- Press space to continue! ^C to quit. --') + while (true) { + const key = await untilEvent(process.stdin, 'data') + if (key[0] === 0x20) { + break + } + } + console.log('') + } +} + +game().catch(err => console.error(err)) + |