Skip to content

Commit

Permalink
v0.3.0 (Merge branch 'indev' after messing up)
Browse files Browse the repository at this point in the history
  • Loading branch information
liddack committed Jul 7, 2018
2 parents b05f591 + b70640a commit ed01560
Show file tree
Hide file tree
Showing 7 changed files with 1,121 additions and 1,091 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Please note that this only works with [Discord desktop client](https://discordap
![Enable the option "Listen on port"](https://cdn.discordapp.com/attachments/416273308540207116/428748994307424256/unknown.png)

2. Install [`Node.JS`](https://nodejs.org/en/download/current/) (we recommend using the latest version). Optional but better, also install [`Yarn`](https://yarnpkg.com/docs/install).
3. Download this project as a .zip file, extract it and open a terminal window in the project directory. Otherwise, if you have [Git](https://git-scm.com/) installed, run:

3. [Download this project as a .zip file](https://github.com/angeloanan/MPC-DiscordRPC/archive/master.zip), extract it and open a terminal window in the project directory. Otherwise, if you have [Git](https://git-scm.com/) installed, run:

```sh
git clone https://github.com/angeloanan/MPC-DiscordRPC.git && cd MPC-DiscordRPC
Expand Down
64 changes: 31 additions & 33 deletions core.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
'use strict'

const log = require('fancy-log'),
jsdom = require('jsdom'),
{ JSDOM } = jsdom
jsdom = require('jsdom'),
{ JSDOM } = jsdom;

// Discord Rich Presence has a string length limit of 128 characters.
// This little plugin (based on https://stackoverflow.com/a/43006978/7090367)
// helps by trimming strings up to a given length.
String.prototype.trimStr = function (length) {
return this.length > length ? this.substring(0, length - 3) + "..." : this
}
return this.length > length ? this.substring(0, length - 3) + "..." : this;
};

// Defines playback data fetched from MPC.
let playback = {
Expand All @@ -20,7 +18,7 @@ let playback = {
state: '',
prevState: '',
prevPosition: '',
}
};

// Defines strings and image keys according to the 'state' string
// provided by MPC.
Expand All @@ -41,7 +39,7 @@ const states = {
string: 'Playing',
stateKey: 'play_small'
}
}
};

/**
* Sends Rich Presence updates to Discord client.
Expand All @@ -50,16 +48,16 @@ const states = {
*/
const updatePresence = (res, rpc) => {
// Identifies which MPC fork is running.
let mpcFork = res.headers.server.replace(' WebServer', '')
const mpcFork = res.headers.server.replace(' WebServer', '');

// Gets a DOM object based on MPC Web Interface variables page.
let { document } = new JSDOM(res.body).window
const { document } = new JSDOM(res.body).window;

// Gets relevant info from the DOM object.
playback.filename = document.getElementById('filepath').textContent.split("\\").pop().trimStr(128)
playback.state = document.getElementById('state').textContent
playback.duration = sanitizeTime(document.getElementById('durationstring').textContent)
playback.position = sanitizeTime(document.getElementById('positionstring').textContent)
playback.filename = document.getElementById('filepath').textContent.split("\\").pop().trimStr(128);
playback.state = document.getElementById('state').textContent;
playback.duration = sanitizeTime(document.getElementById('durationstring').textContent);
playback.position = sanitizeTime(document.getElementById('positionstring').textContent);

// Prepares playback data for Discord Rich Presence.
let payload = {
Expand All @@ -70,19 +68,19 @@ const updatePresence = (res, rpc) => {
largeImageText: mpcFork,
smallImageKey: states[playback.state].stateKey,
smallImageText: states[playback.state].string
}
};

// Makes changes to payload data according to playback state.
switch (playback.state) {
case '-1': // Idling
payload.state = states[playback.state].string
payload.details = undefined
payload.state = states[playback.state].string;
payload.details = undefined;
break;
case '1': // Paused
payload.state = playback.position + ' / ' + playback.duration
payload.state = playback.position + ' / ' + playback.duration;
break;
case '2': // Playing
payload.startTimestamp = (Date.now() / 1000) - convert(playback.position)
payload.startTimestamp = (Date.now() / 1000) - convert(playback.position);
break;
}

Expand All @@ -94,18 +92,18 @@ const updatePresence = (res, rpc) => {
)) {
rpc.setActivity(payload)
.catch((err) => {
log.error('ERROR: ' + err)
})
log.error('ERROR: ' + err);
});
log.info('INFO: Presence update sent: ' +
`${states[playback.state].string} - ${playback.position} / ${playback.duration} - ${playback.filename}`
)
);
}

// Replaces previous playback state and position for later comparison.
playback.prevState = playback.state
playback.prevPosition = playback.position
return true
}
playback.prevState = playback.state;
playback.prevPosition = playback.position;
return true;
};

/**
* Simple and quick utility to convert time from 'hh:mm:ss' format to seconds.
Expand All @@ -116,9 +114,9 @@ const convert = time => {
let parts = time.split(':'),
seconds = parseInt(parts[parts.length - 1]),
minutes = parseInt(parts[parts.length - 2]),
hours = (parts.length > 2) ? parseInt(parts[0]) : 0
return ((hours * 60 * 60) + (minutes * 60) + seconds)
}
hours = (parts.length > 2) ? parseInt(parts[0]) : 0;
return ((hours * 60 * 60) + (minutes * 60) + seconds);
};

/**
* In case the given 'hh:mm:ss' formatted time string is less than 1 hour,
Expand All @@ -128,9 +126,9 @@ const convert = time => {
*/
const sanitizeTime = time => {
if (time.split(':')[0] === '00') {
return time.substr(3, time.length - 1)
return time.substr(3, time.length - 1);
}
return time
}
return time;
};

module.exports = updatePresence
module.exports = updatePresence;
76 changes: 37 additions & 39 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,104 @@
'use strict'
const log = require('fancy-log');

const log = require('fancy-log')

log.info('INFO: Loading...')
log.info('INFO: Loading...');

const snekfetch = require('snekfetch'),
{ Client } = require('discord-rpc'),
updatePresence = require('./core'),
events = require('events'),
config = require('./config'),
clientID = '427863248734388224'
clientID = '427863248734388224';

let mediaEmitter = new events.EventEmitter(),
active = false,
discordRPCLoop,
mpcServerLoop,
rpc
rpc;

// Checks if port set in config.js is valid.
if (isNaN(config.port)) {
throw new Error('Port is empty or invalid! Please set a valid port number in \'config.js\' file.')
throw new Error('Port is empty or invalid! Please set a valid port number in \'config.js\' file.');
}

const uri = `http://localhost:${config.port}/variables.html`
const uri = `http://localhost:${config.port}/variables.html`;

log.info('INFO: Fully ready. Trying to connect to Discord client...')
log.info('INFO: Fully ready. Trying to connect to Discord client...');

// When it succesfully connects to MPC Web Interface, it begins checking MPC
// every 5 seconds, getting its playback data and sending it to Discord Rich Presence
// through updatePresence() function from core.js.
mediaEmitter.on('CONNECTED', res => {
clearInterval(mpcServerLoop)
mpcServerLoop = setInterval(checkMPCEndpoint, 5000)
clearInterval(mpcServerLoop);
mpcServerLoop = setInterval(checkMPCEndpoint, 5000);
if (!active) {
log.info(`INFO: Connected to ${res.headers.server}`)
log.info(`INFO: Connected to ${res.headers.server}`);
}
active = updatePresence(res, rpc)
})
active = updatePresence(res, rpc);
});

// When connection to MPC fails it attempts to connect
// to MPC again every 15 seconds.
mediaEmitter.on('CONN_ERROR', code => {
log.error(`ERROR: Unable to connect to Media Player Classic on port ${config.port}. ` +
`Make sure MPC is running, Web Interface is enabled and the port set in 'config.js' file is correct.\n` + code)
`Make sure MPC is running, Web Interface is enabled and the port set in 'config.js' file is correct.\n` + code);
// If MPC was previously connected (ie. MPC gets closed while script is running)
// the whole process is killed and restarted by Forever in order to clean MPC Rich Presence
// from user's profile, as destroyRPC() apparently can't do so.
if (active) {
log.warn('WARN: Killing process to clean Rich Presence from your profile...')
process.exit(0)
log.warn('WARN: Killing process to clean Rich Presence from your profile...');
process.exit(0);
}
if (mpcServerLoop._onTimeout !== checkMPCEndpoint) {
clearInterval(mpcServerLoop)
mpcServerLoop = setInterval(checkMPCEndpoint, 15000)
clearInterval(mpcServerLoop);
mpcServerLoop = setInterval(checkMPCEndpoint, 15000);
}
})
});

// If RPC successfully connects to Discord client,
// it will attempt to connect to MPC Web Interface every 15 seconds.
mediaEmitter.on('discordConnected', () => {
clearInterval(discordRPCLoop)
log.info('INFO: Connected to Discord. Listening MPC on ' + uri)
checkMPCEndpoint()
mpcServerLoop = setInterval(checkMPCEndpoint, 15000)
})
clearInterval(discordRPCLoop);
log.info('INFO: Connected to Discord. Listening MPC on ' + uri);
checkMPCEndpoint();
mpcServerLoop = setInterval(checkMPCEndpoint, 15000);
});

// If RPC gets disconnected from Discord Client,
// it will stop checking MPC playback data.
mediaEmitter.on('discordDisconnected', () => {
clearInterval(mpcServerLoop)
})
clearInterval(mpcServerLoop);
});

// Tries to connect to MPC Web Interface and,
// if connected, fetches its data.
function checkMPCEndpoint() {
snekfetch.get(uri)
.then(function (res) {
mediaEmitter.emit('CONNECTED', res)
})
.catch(function (err) {
mediaEmitter.emit('CONN_ERROR', err)
.then(res => {
mediaEmitter.emit('CONNECTED', res);
})
.catch(err => {
mediaEmitter.emit('CONN_ERROR', err);
});
}

// Initiates a new RPC connection to Discord client.
function initRPC(clientID) {
rpc = new Client({ transport: 'ipc' })
rpc = new Client({ transport: 'ipc' });
rpc.on('ready', () => {
clearInterval(discordRPCLoop)
mediaEmitter.emit('discordConnected')
clearInterval(discordRPCLoop);
mediaEmitter.emit('discordConnected');
rpc.transport.once('close', async () => {
await destroyRPC();
log.error('ERROR: Connection to Discord client was closed. Trying again in 10 seconds...');
mediaEmitter.emit('discordDisconnected')
mediaEmitter.emit('discordDisconnected');
discordRPCLoop = setInterval(initRPC, 10000, clientID);
});
})
});

// Log in to the RPC server on Discord client, and check whether or not it errors.
rpc.login(clientID).catch(error => {
rpc.login(clientID).catch(() => {
log.warn('WARN: Connection to Discord has failed. Trying again in 10 seconds...');
})
});
}

// Destroys any active RPC connection.
Expand Down
Loading

0 comments on commit ed01560

Please sign in to comment.