node-mpv-2
Version:
A Node module for MPV
229 lines (216 loc) • 6.68 kB
JavaScript
'use strict';
let net = require('net');
// This file contains the event handlers for the mpv module
// These function should not be called on their own, they are just bound
// to the respective events when the module is initialized
//
// Since they need access to some member variables they have to included to
// the module itself
//
// These events are bound in the _startStop.js submodule
const events = {
// When the MPV is closed (either quit by the user or has crashed),
// this handler is called
//
// If quit by the user, the quit event is emitted, does not occur
// when the quit() method was used
// If crashed the crashed event is emitted and if set to auto_restart, mpv
// is restarted right away
//
// Event: close
closeHandler: function (error_code) {
// Clear all the listeners of this module
this.mpvPlayer.removeAllListeners('close');
this.mpvPlayer.removeAllListeners('error');
this.mpvPlayer.removeAllListeners('message');
clearInterval(this.timepositionListenerId);
// destroy the socket because a new one will be created
this.socket.socket.destroy();
// unset the running flag
this.running = false;
// check the error code
switch (error_code) {
// quit by the user on purpose
case 0:
this.emit('quit');
if(this.options.debug || this.options.verbose){
console.log('[Node-MPV]: MPV was quit by the user.');
}
break;
// crashed
case 4:
// restart if auto restart enabled
if(this.options.auto_restart){
if(this.options.debug || this.options.verbose){
console.log('[Node-MPV]: MPV Player has crashed, tying to restart');
}
// restart mpv
this.start()
.then(() => {
// emit crashed event
this.emit('crashed');
if(this.options.debug || this.options.verbose){
console.log('[Node-MPV]: Restarted MPV Player');
}
})
// report the error if one occurs
.catch((error) => {
console.log(error);
});
}
// disabled auto restart
else{
// emit crashed event
this.emit('crashed');
if(this.options.debug || this.options.verbose){
console.log('[Node-MPV]: MPV Player has crashed');
}
}
break;
default:
if(this.options.debug || this.options.verbose){
console.log('[Node-MPV]: MPV player was terminated with an unknown error code: ' + error_code);
}
}
},
// When ever an error is called from the MPV child process it is reported here
//
// @param error {Object}
// Error object sent by MPV
//
// Event: error
errorHandler: function (error) {
if(this.options.debug){
console.log(error);
}
},
// Parses the messages emittet from the ipcInterface. They are all JSON objects
// sent directly by MPV
//
// The different events
// idle: MPV stopped playing
// playback-restart: MPV started playing
// pause: MPV has paused
// resume: MPV has resumed
// property-change One (or more) of the properties have changed
// are handled. They are then turned into events of this module
//
// This handler also handles the properties requested via the getProperty methpd
//
// @param message {Object}
// JSON message from MPV
//
// Event: message
messageHandler: function (message) {
// handle MPV event messages
if('event' in message){
// Handle the different event types
switch(message.event) {
case 'idle':
if(this.options.verbose){console.log('Event: stopped')};
// emit stopped event
this.emit('stopped');
break;
case 'playback-restart':
if(this.options.verbose){console.log('Event: start')};
// emit play event
this.emit('started');
break;
case 'pause':
if(this.options.verbose){console.log('Event: pause')};
// emit paused event
this.emit('paused');
break;
case 'unpause':
if(this.options.verbose){console.log('Event: unpause')};
// emit unpaused event
this.emit('resumed');
break;
case 'seek':
if(this.options.verbose){console.log('Event: seek')};
// socket to watch for the change after a seek has happened
const observeSocket = new net.Socket();
// startseek position
let seekstarttimepos = this.currentTimePos;
// timeout tracker
let timeout = 0;
// promise to watch the socket output
new Promise((resolve, reject) => {
// connect a tempoary socket to the mpv player
observeSocket.connect({path: this.options.socket}, () =>{
// receive the data
observeSocket.on('data', (data) => {
// increase timeout
timeout += 1;
// console.log(data.toJSON());
let messages = data.toString('utf-8').split('\n');
// check every message
messages.forEach((message) => {
// ignore empty messages
if(message.length > 0){
message = JSON.parse(message);
if('event' in message){
// after the seek is finished the playback-restart event is emitted
if(message.event === 'playback-restart'){
// resolve the promise
resolve({
start: seekstarttimepos,
end: this.currentTimePos
});
}
// when the track has changed we don't need a seek event
else if (message.event === 'tracks-changed'){
reject('Track changed after seek');
}
}
}
});
// reject the promise if it took to long until the playback-restart happens
// to prevent having sockets listening forever
if(timeout > 10){
reject('Seek event timeout');
}
});
});
})
// socket destruction and event emittion
.then((times) => {
observeSocket.destroy();
this.emit('seek', (times));
})
// handle any rejection of the promise
.catch((status) => {
observeSocket.destroy();
if(this.options.debug){
console.log(status);
}
});
break;
// observed properties
case 'property-change':
// time position events are handled seperately
if(message.name === 'time-pos'){
// set the current time position
this.currentTimePos = message.data;
}
else{
// emit a status event
this.emit('status', {
'property': message.name,
'value': message.data
});
// output if verbose
if(this.options.verbose){
console.log('[Node-MPV]: Event: status');
console.log('[Node-MPV]: Property change: ' + message.name + ' - ' + message.data);
}
}
break;
// Default
default:
break;
}
}
}
}
module.exports = events;