« get me outta code hell

Upload/delete tracks - interactive-bgm - Browser extension that adds background music based on the site you're browsing
about summary refs log tree commit diff
path: root/native-app
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2019-03-23 12:05:04 -0300
committerFlorrie <towerofnix@gmail.com>2019-03-23 12:05:04 -0300
commit213ef476d5aa27e3590754da52ff1e8c0b48cc38 (patch)
treef87f5ad7e20a9b6d7ba4c5ea38b6e4639f92b57e /native-app
parent29e9b65926ee3062f7490e247dd7971e9de23a52 (diff)
Upload/delete tracks
Diffstat (limited to 'native-app')
-rwxr-xr-xnative-app/index.js154
1 files changed, 131 insertions, 23 deletions
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');
 });