implemented / fixed start, resume, pause and stop functionality

This commit is contained in:
Daniel Sommer 2022-05-02 14:19:30 +02:00
parent 7dc3fb6ece
commit ac1fca8368
3 changed files with 55 additions and 24 deletions

View file

@ -40,17 +40,19 @@ class Api {
} }
global.audioserver = new AudioServer('/home/velvettear/mounts/kingston/public/test.pcm'); global.audioserver = new AudioServer('/home/velvettear/mounts/kingston/public/test.pcm');
}); });
this.#registerEndpoint(constants.API_RESUME, constants.REQUEST_METHOD_POST, async () => {
if (global.audioserver === undefined) {
return;
}
await global.audioserver.destroy();
global.audioserver = new AudioServer('/home/velvettear/mounts/kingston/public/test.pcm', global.audioserver.progress);
});
this.#registerEndpoint(constants.API_PAUSE, constants.REQUEST_METHOD_POST, async () => { this.#registerEndpoint(constants.API_PAUSE, constants.REQUEST_METHOD_POST, async () => {
if (global.audioserver === undefined) { if (global.audioserver === undefined) {
return; return;
} }
global.audioserver.pausePlayback(); global.audioserver.pausePlayback();
}); });
this.#registerEndpoint(constants.API_RESUME, constants.REQUEST_METHOD_POST, async () => {
if (global.audioserver === undefined) {
return;
}
});
this.#registerEndpoint(constants.API_STOP, constants.REQUEST_METHOD_POST, async () => { this.#registerEndpoint(constants.API_STOP, constants.REQUEST_METHOD_POST, async () => {
if (global.audioserver === undefined) { if (global.audioserver === undefined) {
return; return;

View file

@ -1,24 +1,21 @@
const sleep = require('../libs/util.js').sleep;
const net = require('net'); const net = require('net');
const fs = require('fs'); const { stat, open } = require('fs/promises');
const stat = require('fs/promises').stat;
const Message = require('./Message.js'); const Message = require('./Message.js');
const constants = require('../libs/constants.js');
class AudioServer { class AudioServer {
constructor(file) { constructor(file, progress) {
this.listen = config?.server?.listen || '0.0.0.0'; this.listen = config?.server?.listen || '0.0.0.0';
this.port = 0; this.port = 0;
this.buffer = { this.buffer = {
file: file, file: file
stream: fs.createReadStream(file)
}; };
this.clients = []; this.clients = [];
this.sockets = []; this.sockets = [];
this.broadcasts = {}; this.broadcasts = {};
this.progress = 0; this.progress = progress || 0;
this.server = net.createServer(); this.server = net.createServer();
this.#prepare(); this.#prepare();
} }
@ -41,17 +38,35 @@ class AudioServer {
}); });
this.server.on('error', (err) => { this.server.on('error', (err) => {
logger.error('ERROR IN AUDIOSERVER ' + err); logger.error('ERROR IN AUDIOSERVER ' + err);
reject('an error occured preparing the audio server for file \'' + this.file + '\' > ' + err); reject('audio server encountered an error: ' + err);
});
this.server.on('close', () => {
logger.info('audio server closed');
}); });
}); });
await this.#prepareBuffer();
this.#announceAudioServer();
}
async #prepareBuffer() {
if (this.buffer.fd !== undefined) {
this.buffer.fd.close();
}
if (this.buffer.stream?.destroyed === false) {
this.buffer.stream.close();
this.buffer.stream.destroy();
}
this.buffer.fd = await open(this.buffer.file);
this.buffer.stream = this.buffer.fd.createReadStream({
start: this.progress
});
const stats = await stat(this.buffer.file); const stats = await stat(this.buffer.file);
this.buffer.size = stats.size; this.buffer.size = stats.size - this.progress;
let percentage = 30; let percentage = 30;
if (!(isNaN(config.audio?.threshold))) { if (!(isNaN(config.audio?.threshold))) {
percentage = config.audio.threshold; percentage = config.audio.threshold;
} }
this.buffer.threshold = (this.buffer.size / 100) * percentage; this.buffer.threshold = (this.buffer.size / 100) * percentage;
this.#announceAudioServer();
} }
#handleEvents() { #handleEvents() {
@ -145,6 +160,9 @@ class AudioServer {
if (client === undefined) { if (client === undefined) {
return; return;
} }
if (this.progress < data?.progress) {
this.progress = data.progress;
}
switch (client.state) { switch (client.state) {
case constants.CLIENT_STATE_REGISTERED: case constants.CLIENT_STATE_REGISTERED:
return this.#handleStateRegistered(client); return this.#handleStateRegistered(client);
@ -174,7 +192,7 @@ class AudioServer {
if (!this.#allClientsInState(constants.CLIENT_STATE_READY)) { if (!this.#allClientsInState(constants.CLIENT_STATE_READY)) {
return; return;
} }
this.#startPlayback(); this.startPlayback();
} }
async #handleStatePlaying(client) { async #handleStatePlaying(client) {
@ -196,6 +214,10 @@ class AudioServer {
async #handleStateStopped(client, data) { async #handleStateStopped(client, data) {
logger.debug(client.getTag() + ' stopped playback, progress: ' + data.progress + '/' + this.buffer.size + '...'); logger.debug(client.getTag() + ' stopped playback, progress: ' + data.progress + '/' + this.buffer.size + '...');
if (!this.#allClientsInState(constants.CLIENT_STATE_STOPPED)) {
return;
}
this.destroy();
} }
async #handleStateError(client, data) { async #handleStateError(client, data) {
@ -232,7 +254,7 @@ class AudioServer {
logger.debug('sent broadcast for audio server to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_REGISTERED] + '\'...'); logger.debug('sent broadcast for audio server to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_REGISTERED] + '\'...');
} }
async #startPlayback() { async startPlayback() {
this.broadcasts[constants.CLIENT_STATE_PLAYING] = await new Message('audio:play').broadcast(); this.broadcasts[constants.CLIENT_STATE_PLAYING] = await new Message('audio:play').broadcast();
logger.debug('sent broadcast to start playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_PLAYING] + '\'...'); logger.debug('sent broadcast to start playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_PLAYING] + '\'...');
} }
@ -242,7 +264,7 @@ class AudioServer {
logger.debug('sent broadcast to pause playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_PAUSED] + '\'...'); logger.debug('sent broadcast to pause playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_PAUSED] + '\'...');
} }
async stopPlayback() { async stopPlayback() {
this.broadcasts[constants.CLIENT_STATE_STOPPED] = await new Message('audio:stop').broadcast(); this.broadcasts[constants.CLIENT_STATE_STOPPED] = await new Message('audio:stop').broadcast();
logger.debug('sent broadcast to stop playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_STOPPED] + '\'...'); logger.debug('sent broadcast to stop playback to client(s) \'' + this.broadcasts[constants.CLIENT_STATE_STOPPED] + '\'...');
} }
@ -289,16 +311,22 @@ class AudioServer {
} }
audiosocket.destroy(); audiosocket.destroy();
} }
this.buffer.fd.close();
this.buffer.stream.close();
this.buffer.stream.destroy();
if (this.server?.listening !== true) {
return;
}
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
this.server.close((err) => { this.server.close((error) => {
if (err !== undefined) { if (error !== undefined) {
logger.error('ERROR CLOSING AUDIOSERVER ' + err); logger.error('an error occured closing the audio server: ' + error);
reject(err); // TODO: reject and try/catch later?
// reject(error);
} }
resolve(); resolve();
}); });
}); });
} }
} }

View file

@ -9,6 +9,7 @@ module.exports = {
CLIENT_STATE_READY: 'ready', CLIENT_STATE_READY: 'ready',
CLIENT_STATE_PLAYING: 'playing', CLIENT_STATE_PLAYING: 'playing',
CLIENT_STATE_PAUSED: 'paused', CLIENT_STATE_PAUSED: 'paused',
CLIENT_STATE_RESUMING: 'resuming',
CLIENT_STATE_STOPPED: 'stopped', CLIENT_STATE_STOPPED: 'stopped',
CLIENT_STATE_ERROR: 'error', CLIENT_STATE_ERROR: 'error',