# HG changeset patch # User Emmanuel Gil Peyrot # Date 1329937076 -3600 # Node ID 156c2fd7c62654c8b6f210be4de01d91d605e16a First commit. diff --git a/config.js b/config.js new file mode 100644 --- /dev/null +++ b/config.js @@ -0,0 +1,4 @@ +exports.jid = 'you@example.org'; +exports.password = 'pass'; +exports.muc = 'isshouni@muc.linkmauve.fr'; +exports.nick = 'You'; diff --git a/isshouni.js b/isshouni.js new file mode 100755 --- /dev/null +++ b/isshouni.js @@ -0,0 +1,142 @@ +#!/usr/bin/env node +'use strict'; + +/** + Copyright (c) 2012, Emmanuel Gil Peyrot + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + +var config = require('./config'); +var args = ['-slave'].concat(process.argv.slice(2)); + +var tty = require('tty'); +var xmpp = require('node-xmpp'); +var JID = require('./jid'); +var spawn = require('child_process').spawn; + +var cl = new xmpp.Client(config); +var mplayer = spawn('mplayer', args); + +var connect = function() { + cl.send(new xmpp.Element('presence', {to: config.muc + '/' + config.nick}) + .c('x', {xmlns: 'http://jabber.org/protocol/muc'}) + .c('history', {maxchars: '0'}) + .up()); + //TODO: handle join errors. +}; +cl.on('online', connect); + +var disconnect = function() { + cl.send(new xmpp.Element('presence', {to: config.muc + '/' + config.nick, type: 'unavailable'})); + process.stdin.end(); + cl.end(); +}; + +var send_message = function(name, arg) { + //console.log('SENT: ' + name + ' ' + arg); + var message = new xmpp.Element('message', {to: config.muc, type: 'groupchat'}) + .c(name, {xmlns: 'urn:linkmauve:player'}); + + if (arg) + message.t(arg); + + cl.send(message.up()); +}; + +var send_command = function(text) { + //console.log(text); + mplayer.stdin.write(text + '\n'); +}; + +cl.on('stanza', function(stanza) { + //console.log(stanza.toString()); + if (!stanza.is('message') || stanza.attrs['type'] !== 'groupchat') + return; + + var from = new JID(stanza.attrs['from']); + if (from.bare !== config.muc) + return; + if (from.resource === config.nick) + return; + + var message = stanza.getChild('body'); + if (message) + send_command('osd_show_text "' + from.resource + ': ' + message.getText() + '"'); + + var payload = stanza.getChild(null, 'urn:linkmauve:player'); + if (!payload) + return; + + //send_command('osd_show_text "' + from.resource + ' did ' + payload.name + '"'); + + if (payload.name === 'pause') + send_command('pause'); + else if (payload.name === 'seek') + send_command('seek ' + payload.getText()); + else if (payload.name === 'synchro') + send_command('seek ' + payload.getText() + ' 2'); +}); + +cl.on('error', function(e) { + console.log(e); +}); + +var commands = { + '+': 'volume 1', + '-': 'volume -1', + 'm': 'mute', + ' ': function() { + send_message('pause'); + send_command('get_time_pos'); + return 'pause'; + }, + 'q': function() { + tty.setRawMode(false); + disconnect(); + return 'quit'; + }, +}; + +process.stdin.resume(); +tty.setRawMode(true); +process.stdin.on('keypress', function(char, key) { + if (!char) { + if (key.name == 'right') + send_command('seek 10'); + else if (key.name == 'left') + send_command('seek -10'); + else if (key.name == 'up') + send_command('seek 60'); + else if (key.name == 'down') + send_command('seek -60'); + } else if (char in commands) { + var command = commands[char]; + if (typeof command === 'function') + command = command(); + send_command(command); + } +}); + +mplayer.stdout.on('data', function(data) { + //console.log('DATA: '+data); + data = /(ANS_TIME_POSITION)=([0-9\.]+)/.exec(data); + if (!data) + return; + + var cmd = data[1]; + var arg = data[2]; + if (cmd === 'ANS_TIME_POSITION') + send_message('synchro', arg); +}); diff --git a/jid.js b/jid.js new file mode 100644 --- /dev/null +++ b/jid.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + Copyright (c) 2012, Emmanuel Gil Peyrot + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + +/** + * @constructor Creates a new JID object. + * @param {String} [aJID] The host, bare or full JID. + */ +var JID = function(aJID) { + this.node = null; + this.domain = null; + this.resource = null; + + if (aJID) + this.full = aJID; + + //TODO: use a stringprep library to validate the input. +}; + +JID.prototype = { + toString: function() { + return this.full; + }, + + equals: function(aJID) { + if (!(aJID instanceof JID)) + aJID = new JID(aJID); + + return (this.node === aJID.node && + this.domain === aJID.domain && + this.resource === aJID.resource) + }, + + get bare() { + if (!this.domain) + return null; + + if (this.node) + return this.node + '@' + this.domain; + + return this.domain; + }, + + set bare(aJID) { + if (!aJID) + return; + + var s = aJID.indexOf('/'); + if (s != -1) + aJID = aJID.substring(0, s); + + s = aJID.indexOf('@'); + if (s == -1) { + this.node = null; + this.domain = aJID; + } else { + this.node = aJID.substring(0, s); + this.domain = aJID.substring(s+1); + } + }, + + get full() { + if (!this.domain) + return null; + + var full = this.domain; + + if (this.node) + full = this.node + '@' + full; + + if (this.resource) + full = full + '/' + this.resource; + + return full; + }, + + set full(aJID) { + if (!aJID) + return; + + var s = aJID.indexOf('/'); + if (s == -1) + this.resource = null; + else { + this.resource = aJID.substring(s+1); + aJID = aJID.substring(0, s); + } + + s = aJID.indexOf('@'); + if (s == -1) { + this.node = null; + this.domain = aJID; + } else { + this.node = aJID.substring(0, s); + this.domain = aJID.substring(s+1); + } + } +}; + +module.exports = JID;