From fa4830d698f6a193b01fe48b29f1e6ffecb10f71 Mon Sep 17 00:00:00 2001 From: erkstruwe Date: Sun, 13 Mar 2016 06:08:49 +0100 Subject: [PATCH] v0.0.1 --- .gitignore | 3 + chromecast-cli.js | 264 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 35 ++++++ 3 files changed, 302 insertions(+) create mode 100644 chromecast-cli.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index e920c16..760a25c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ node_modules # Optional REPL history .node_repl_history + +# IDE +.idea \ No newline at end of file diff --git a/chromecast-cli.js b/chromecast-cli.js new file mode 100644 index 0000000..383a22c --- /dev/null +++ b/chromecast-cli.js @@ -0,0 +1,264 @@ +#!/usr/bin/env node + +var app = require('commander'); +var async = require('async'); +var lodash = require('lodash'); +var Client = require('castv2-client').Client; +var Receiver = require('castv2-client').DefaultMediaReceiver; + +var connect = function(host, cb) { + var client = new Client(); + client.on('error', function(e) { + console.error('Client error', e); + client.close(); + }); + client.on('message', function(message) { + console.log('Client message', message); + }); + client.on('close', function() { + console.error('Client closed'); + }); + + return client.connect(host, function() { + return cb(null, client); + }); +}; + +app + .version('0.0.1') + .option('-H, --host ', 'IP address or hostname of Chromecast (required)'); + +app + .command('play ') + .description('Play file at ') + // TODO .option('-r, --repeat-mode ', 'Set repeat mode (REPEAT_OFF, REPEAT_ONE or REPEAT_ALL)', /^(REPEAT_OFF|REPEAT_ONE|REPEAT_ALL)$/i, 'REPEAT_OFF') + .option('-f, --force', 'Force play even if Chromecast is already casting') + .action(function(src, options) { + if (!app.host) + throw new Error('--host option is required'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + status: ['client', function(cb, r) { + return r.client.receiver.getStatus(cb); + }], + receiver: ['client', 'status', function(cb, r) { + if (!options.force && lodash.get(r.status, 'applications.0')) + return cb(new Error('Already casting. Use --force option to override.')); + return r.client.launch(Receiver, cb); + }], + media: ['receiver', function(cb, r) { + var media = { + contentId: src + }; + r.receiver.load(media, {autoplay: true, repeatMode: options.repeatMode}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.media.playerState, r.media.media, r.media.repeatMode); + return process.exit(); + }); + }); + +app + .command('volume ') + .description('Set the volume to ') + .action(function(volume) { + if (!app.host) + throw new Error('--host option is required'); + + volume = parseFloat(volume); + if (!volume) + throw new Error('Invalid volume parameter. Has to be float between 0.0 and 1.0.'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + volume: ['client', function(cb, r) { + r.client.receiver.setVolume({level: volume, muted: false}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.volume); + return process.exit(); + }); + }); + +app + .command('volumeStepUp ') + .description('Set the volume higher') + .action(function(volumeStep) { + if (!app.host) + throw new Error('--host option is required'); + + volumeStep = parseFloat(volumeStep); + if (!volumeStep) + throw new Error('Invalid volumeStep parameter. Has to be float between 0.0 and 1.0.'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + oldVolume: ['client', function(cb, r) { + r.client.receiver.getVolume(cb); + }], + newVolume: ['client', 'oldVolume', function(cb, r) { + var volume = r.oldVolume.level; + volume += volumeStep; + volume = volume > 1 ? 1 : volume; + r.client.receiver.setVolume({level: volume, muted: false}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.newVolume); + return process.exit(); + }); + }); + +app + .command('volumeStepDown ') + .description('Set the volume lower') + .action(function(volumeStep) { + if (!app.host) + throw new Error('--host option is required'); + + volumeStep = parseFloat(volumeStep); + if (!volumeStep) + throw new Error('Invalid volumeStep parameter. Has to be float between 0.0 and 1.0.'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + oldVolume: ['client', function(cb, r) { + r.client.receiver.getVolume(cb); + }], + newVolume: ['client', 'oldVolume', function(cb, r) { + var volume = r.oldVolume.level; + volume -= volumeStep; + volume = volume < 0 ? 0 : volume; + r.client.receiver.setVolume({level: volume, muted: false}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.newVolume); + return process.exit(); + }); + }); + +app + .command('mute') + .description('Mute') + .action(function() { + if (!app.host) + throw new Error('--host option is required'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + volume: ['client', function(cb, r) { + r.client.receiver.setVolume({muted: true}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.volume); + return process.exit(); + }); + }); + +app + .command('unmute') + .description('Unmute') + .action(function() { + if (!app.host) + throw new Error('--host option is required'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + volume: ['client', function(cb, r) { + r.client.receiver.setVolume({muted: false}, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.volume); + return process.exit(); + }); + }); + +app + .command('stop') + .description('Stop playback') + .action(function() { + if (!app.host) + throw new Error('--host option is required'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + stop: ['client', function(cb, r) { + return r.client.receiver.stop(null, cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + return process.exit(); + }); + }); + +app + .command('status') + .description('Get Chromecast status') + .action(function() { + if (!app.host) + throw new Error('--host option is required'); + + return async.auto({ + client: function(cb) { + return connect(app.host, cb); + }, + status: ['client', function(cb, r) { + return r.client.receiver.getStatus(cb); + }] + }, function(e, r) { + if (e) { + console.error(e); + return process.exit(1); + } + console.log(r.status); + return process.exit(); + }); + }); + +app + .parse(process.argv); + +if (!process.argv.slice(2).length) { + app.help(); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e8c5189 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "chromecast-cli", + "version": "0.0.1", + "description": "Command line interface for Google Chromecast", + "author": "Erk Struwe ", + "license": "GPL-3.0", + "main": "chromecast-cli.js", + "engines": { + "node": "5.8.0" + }, + "homepage": "https://github.com/erkstruwe/chromecast-cli#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/erkstruwe/chromecast-cli.git" + }, + "bugs": { + "url": "https://github.com/erkstruwe/chromecast-cli/issues" + }, + "scripts": { + "test": "jshint chromecast-cli.js", + "start": "node chromecast-cli.js --help" + }, + "jshintConfig": { + "esversion": 6 + }, + "dependencies": { + "async": "^1.5.2", + "castv2-client": "~1.1.1", + "commander": "^2.9.0", + "lodash": "^4.6.1" + }, + "devDependencies": { + "jshint": "~2.9.1" + } +}