From 213ef476d5aa27e3590754da52ff1e8c0b48cc38 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sat, 23 Mar 2019 12:05:04 -0300 Subject: Upload/delete tracks --- native-app/index.js | 154 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 131 insertions(+), 23 deletions(-) (limited to 'native-app') diff --git a/native-app/index.js b/native-app/index.js index 500eebf..f6936e3 100755 --- a/native-app/index.js +++ b/native-app/index.js @@ -1,18 +1,31 @@ #!/usr/bin/env node +const { promisify } = require('util'); const { spawn } = require('child_process'); const EventEmitter = require('events'); const FIFO = require('fifo-js'); const http = require('http'); const path = require('path'); const fs = require('fs'); +const os = require('os'); +const sanitizeFilename = require('sanitize-filename'); + +const readdir = promisify(fs.readdir); const basePath = path.resolve(__dirname, '..'); const logFile = basePath + '/native-app/log'; const log = msg => fs.appendFileSync(logFile, msg + '\n'); +// const log = () => {}; log('Started ' + Date()); +const emptyStream = () => { + const stream = new EventEmitter(); + stream.write = () => {}; + stream.end = () => {}; + return stream; +}; + class TrackPlayer { constructor(file) { this.file = file; @@ -31,10 +44,7 @@ class TrackPlayer { this.file, ]); - const stream = new EventEmitter(); - stream.write = () => {}; - stream.end = () => {}; - this.process.stderr.pipe(stream); + this.process.stderr.pipe(emptyStream()); } sendCommand(command) { @@ -64,23 +74,39 @@ class TrackPlayer { } } -const tracks = { - mantis: new TrackPlayer(basePath + '/track1.wav'), - bass: new TrackPlayer(basePath + '/track2.wav'), - main: new TrackPlayer(basePath + '/track3.wav') -}; +const musicDir = basePath + '/native-app/music'; -for (const track of Object.values(tracks)) { - track.loadProcess(); - track.pause(); -} +let tracks; + +const loadTracks = () => new Promise(async resolve => { + tracks = {}; + + for (const trackName of await readdir(musicDir)) { + tracks[trackName] = new TrackPlayer(musicDir + '/' + trackName); + } + + for (const track of Object.values(tracks)) { + track.loadProcess(); + track.pause(); + } + + setTimeout(() => { + for (const track of Object.values(tracks)) { + track.seekToStart(); + track.play(); + } + + resolve(); + }, 250); +}); -setTimeout(() => { +const killTracks = () => { for (const track of Object.values(tracks)) { - track.seekToStart(); - track.play(); + track.process.kill(); } +}; +loadTracks().then(async () => { let targetMode = [ {track: 'main', volume: 100} ]; @@ -96,22 +122,104 @@ setTimeout(() => { } }, 20); - process.stdin.on('data', data => { - const probablyJSON = data.toString().slice(data.indexOf('[')).trim(); + const uploadData = {}; + + const handleCommand = text => { + log('COMMAND: ' + text); + + const probablyJSON = text.slice(Math.min(...[ + text.indexOf('['), text.indexOf('{') + ].filter(x => x >= 0))).trim(); + + let data; try { - targetMode = JSON.parse(probablyJSON); + data = JSON.parse(probablyJSON); } catch (error) { + return; + } + + if (Array.isArray(data)) { + targetMode = data; + } else if (data.type === 'uploadTrack') { + const buffer = Buffer.from(data.base64, 'base64'); + log(`Write file: ${data.trackName} -- ${buffer.length} bytes`); + fs.writeFileSync(musicDir + '/' + sanitizeFilename(data.trackName), buffer); + killTracks(); + loadTracks(); + writeMessage(JSON.stringify({type: 'uploadFinished', trackName: data.trackName})); + } else if (data.type === 'deleteTrack') { + try { + fs.unlinkSync(musicDir + '/' + sanitizeFilename(data.trackName)); + } catch (error) { + log('Error deleting track: ' + error); + } + + if (tracks[data.trackName]) { + tracks[data.trackName].process.kill(); + delete tracks[data.trackName]; + } + + writeMessage(JSON.stringify({type: 'deleteFinished', trackName: data.trackName})); + } + }; + + let readBytesPromise = null; + let stdinBuffer = Buffer.from([]); + + process.stdin.on('data', data => { + stdinBuffer = Buffer.concat([stdinBuffer, data]); + if (readBytesPromise) { + readBytesPromise(); } }); -}, 250); + + const readBytes = numBytes => { + if (stdinBuffer.length >= numBytes) { + const bytes = stdinBuffer.slice(0, numBytes); + stdinBuffer = stdinBuffer.slice(numBytes); // Splice + return Promise.resolve(bytes); + } else { + return new Promise(resolve => { + readBytesCount = numBytes; + readBytesPromise = resolve; + }).then(() => readBytes(numBytes)); + } + }; + + const writeMessage = message => { + log(message); + + const size = message.length; + const rawSize = Buffer.alloc(4); + if (os.endianness() === 'BE') { + rawSize.writeUInt32BE(size, 0); + } else { + rawSize.writeUInt32LE(size, 0); + } + + process.stdout.write(rawSize); + process.stdout.write(Buffer.from(message, 'utf8')); + }; + + while (true) { + const rawSize = await readBytes(4); + let size; + if (os.endianness() === 'BE') { + size = rawSize.readUInt32BE(); + } else { + size = rawSize.readUInt32LE(); + } + const data = await readBytes(size); + const command = data.toString('utf8'); + handleCommand(command); + } +}); log('Go!'); process.on('SIGTERM', () => { log('Exiting'); - for (const track of Object.values(tracks)) { - track.process.kill(); - } + killTracks(); log('Cleaned up'); }); -- cgit 1.3.0-6-gf8a5