123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- 'use strict'
- import os from 'node:os'
- import * as ansi from 'tui-lib/util/ansi'
- import telc from 'tui-lib/util/telchars'
- import {flattenGrouplike, parentSymbol, searchForItem} from './playlist-utils.js'
- import processSmartPlaylist from './smart-playlist.js'
- import Backend from './backend.js'
- 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)
- }
- const QP = await backend.addQueuePlayer()
- // TODO: nah
- QP.setVolume(60)
- process.stdin.setRawMode(true)
- process.stdin.on('data', async data => {
- if (data[0] === 0x03) {
- await QP.stopPlaying()
- process.exit(0)
- }
- })
- const sourcePath = process.argv[2] || os.homedir() + '/Music'
- let grouplike = {source: ['crawl-local', sourcePath]}
- grouplike = await processSmartPlaylist(grouplike)
- 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)]
- QP.setPause(false)
- const promise = untilEvent(QP, 'playing')
- QP.play(track)
- await promise
- 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 fmtTime = () => {
- let t = (playTime + Date.now() - startTime) / 1000
- t = Math.floor(t * 10) / 10
- if (t % 1 === 0) {
- t = t + '.0'
- }
- return t + 's'
- }
- const echoFn = () => {
- write(resetLine + fmtTime())
- }
- while (true) {
- let echo
- if (!QP.player.isPaused) {
- echo = setInterval(echoFn, 50)
- }
- const key = await untilEvent(process.stdin, 'data')
- clearInterval(echo)
- if (key[0] === 0x10 || (key[0] === 0x20 && !QP.player.isPaused)) {
- if (QP.player.isPaused) {
- startTime = Date.now()
- console.log(resetLine + dim + '<Unpaused.>')
- write(ansi.resetAttributes())
- } else {
- console.log(resetLine + dim + `<Paused @ ${fmtTime()}. Type the track's name below! ^P to resume.>`)
- playTime += Date.now() - startTime
- write(ansi.resetAttributes())
- echoFn()
- displayInput()
- }
- QP.togglePause()
- /*
- } else if (key[0] === 0x3f && (!key.length || !QP.player.isPaused)) {
- QP.setPause(false)
- gaveUp = true
- break
- */
- } else if (QP.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) {
- QP.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))
|