Compare commits

...

49 commits

Author SHA1 Message Date
Todd Treece
53f718d7cb 1.6.1 2016-07-11 16:50:46 -04:00
Todd Treece
8e8b712a2c add link to tutorial 2016-07-11 14:28:27 -04:00
Todd Treece
5f6758a0c4 Merge pull request #3 from BenjaminHCCarr/patch-1
Update Readme.md to reflect Node 6.x
2016-07-11 13:47:42 -04:00
Ben
779a33c275 Update Readme.md to reflect Node 6.x
Update re: https://github.com/adafruit/nprone-cli/issues/1
2016-07-11 13:42:25 -04:00
Todd Treece
7ab918401b 1.6.0 2016-06-13 17:06:07 -04:00
Todd Treece
f9c8cbc8b2 add touch and release threshold config 2016-06-13 17:05:49 -04:00
Todd Treece
504ed539a0 1.5.0 2016-06-08 09:44:09 -04:00
Todd Treece
006253dcbb remove service install 2016-06-08 09:43:44 -04:00
Todd Treece
732f77a0b7 1.4.6 2016-06-08 09:28:26 -04:00
Todd Treece
1a2de1b9b8 cd to the package folder on cli launch 2016-06-08 09:27:50 -04:00
Todd Treece
15347a0fac 1.4.5 2016-06-08 08:17:45 -04:00
Todd Treece
a28bf3233c move auto update to bash script 2016-06-08 08:17:17 -04:00
Todd Treece
c9ec1e1c6a 1.4.4 2016-06-07 20:03:16 -04:00
Todd Treece
e578c9d7bc point mpr121 package at npm 2016-06-07 20:02:09 -04:00
Todd Treece
a677c110dc 1.4.3 2016-06-07 17:16:54 -04:00
Todd Treece
90f5e4a87a check if service _doesn't_ exist in install-service 2016-06-07 17:16:14 -04:00
Todd Treece
ac2ba5bce5 1.4.2 2016-06-07 17:04:02 -04:00
Todd Treece
981f5d136c bump autoupdate version 2016-06-07 17:03:44 -04:00
Todd Treece
ecf942a3b9 1.4.1 2016-06-07 16:45:52 -04:00
Todd Treece
f485556bc8 bump autoupdate version 2016-06-07 16:45:37 -04:00
Todd Treece
1dcfbe36c1 1.4.0 2016-06-07 16:09:02 -04:00
Todd Treece
03ae7ab0dd add pm2 service install 2016-06-07 16:08:49 -04:00
Todd Treece
d5a6d0c9d3 1.3.1 2016-06-07 15:22:34 -04:00
Todd Treece
0116703708 fix bin link 2016-06-07 15:22:23 -04:00
Todd Treece
0963d73936 1.3.0 2016-06-07 15:06:24 -04:00
Todd Treece
78b223430b add auto update 2016-06-07 15:01:48 -04:00
Todd Treece
fa42d5acb5 1.2.5 2016-06-07 12:43:08 -04:00
Todd Treece
59ca024174 increase mpr121 touch threshold default to 24 2016-06-07 12:42:49 -04:00
Todd Treece
069307c18c 1.2.4 2016-06-07 12:31:18 -04:00
Todd Treece
8f2878aade set mpr121 thresholds 2016-06-07 12:30:24 -04:00
Todd Treece
1323f7ea75 1.2.3 2016-06-03 12:31:02 -04:00
Todd Treece
761d22482d Update README.md 2016-06-03 11:05:56 -04:00
Todd Treece
96d1e7fbf9 add node version info to readme 2016-06-02 08:51:34 -04:00
Todd Treece
26d512d56d 1.2.2 2016-05-27 13:55:43 -04:00
Todd Treece
d18f56c9fc bump to version 2.x of npr-api 2016-05-27 13:54:45 -04:00
Todd Treece
1d2f816d98 1.2.1 2016-05-27 11:58:15 -04:00
Todd Treece
674ad2a4fc add notice about npr logo 2016-05-27 11:15:32 -04:00
Todd Treece
14444c1709 1.2.0 2016-05-19 11:35:46 -04:00
Todd Treece
b57a15918f add new mpr lib to package 2016-05-19 11:35:17 -04:00
Todd Treece
15a7b1ba68 1.1.0 2016-05-17 20:25:39 -04:00
Todd Treece
252a9ab148 enable mpr121 on arm platforms 2016-05-17 20:23:31 -04:00
Todd Treece
bc177d90a2 1.0.8 2016-05-17 17:47:23 -04:00
Todd Treece
5896c49d43 add license 2016-05-17 17:47:09 -04:00
Todd Treece
08e6afa682 update readme & package.json to reflect new commands 2016-05-17 17:39:33 -04:00
Todd Treece
5a44ba00ca hook in new modules 2016-05-17 17:39:09 -04:00
Todd Treece
6ffd098a74 tweak auth storage location 2016-05-17 17:38:18 -04:00
Todd Treece
dc891451d9 add ui module 2016-05-17 17:37:44 -04:00
Todd Treece
900ef29ee0 add story module 2016-05-17 17:37:21 -04:00
Todd Treece
a8a53b8900 add player module 2016-05-17 17:37:06 -04:00
12 changed files with 703 additions and 130 deletions

View file

@ -1,9 +0,0 @@
language: node_js
node_js:
- '0.12'
before_script:
- npm install -g gulp
notifications:
email:
on_success: change
on_failure: change

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016 Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,9 +1,77 @@
# NPR One Raspberry Pi Radio [![Build Status](https://travis-ci.org/adafruit/nprone_raspi.svg?branch=master)](https://travis-ci.org/adafruit/nprone_raspi)
# NPR One CLI
This project uses the NPR One API to create a standalone NPR One streaming radio using a Raspberry Pi.
This is a simple command line based NPR One client for OS X and Linux. A full tutorial with setup instructions can be found [in the Adafruit Learning System](https://learn.adafruit.com/raspberry-pi-zero-npr-one-radio).
## Installation
This package requires the latest stable version of [Node.js](https://nodejs.org) (v6.0 or higher due to the use of es6).
```sh
$ node -v
v6.2.0
```
npm install ¯\_(ツ)_/¯
Install `mplayer` on OS X using [homebrew](http://brew.sh/):
```
$ brew install mplayer
```
Install `mplayer` on Linux:
```
$ sudo apt-get install -y mplayer
```
Make sure you have the latest stable [node.js](https://nodejs.org/en/) installed (6.0 or higher), and then run:
```
npm install -g npr-one
```
## Usage
Sign into the [NPR Dev Console](http://dev.npr.org/), create a new app, and use your App ID & Secret to authorize the CLI. The audio player will save your authorization and begin playing.
```
$ npr-one
\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>> \\\\\\\\\\\\\\\\\\\\\\\\\\
\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>> \\\\\\\\\\\\\\\\\\\\\\\\\\
\\\\\\\\\\\\\>>\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>> \\\\\\\\\\\\\\>>>\\\\\\\\\
\\\\\\\ >\\\\\\\\>>>>>>> \>>>>>>>> \\\\\\\\ (\\\\\\\\
\\\\\\\ .>>>>= \\\\\\\\>>>>>>> =>>>> >>>>>>> \\\\\\\\ (>>>>\\\\\\\\\
\\\\\\\ )\\\\\ (\\\\\\\>>>>>>> >>>>>> )>>>>>> \\\\\\\\ .\\\\\\\\\\\\\\
\\\\\\\ )\\\\\ (\\\\\\\>>>>>>> >>>>>> )>>>>>> \\\\\\\\ )\\\\\\\\\\\\\\
\\\\\\\ )\\\\\ (\\\\\\\>>>>>>> >>>>>\ >>>>>>> \\\\\\\\ )\\\\\\\\\\\\\\
\\\\\\\ )\\\\\ (\\\\\\\>>>>>>> ->>>>>>>> \\\\\\\\ )\\\\\\\\\\\\\\
\\\\\\\>>>>\\\\\>>>>\\\\\\\>>>>>>> >>>>(>>>>>>>>>>> \\\\\\\\>>>>\\\\\\\\\\\\\\
\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>> >>>>>>>>>>>>>>>> \\\\\\\\\\\\\\\\\\\\\\\\\\
\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>===>>>>>>>>>>>>>>>> \\\\\\\\\\\\\\\\\\\\\\\\\\
[downloaded] WYPR FM
[downloaded] NPR thanks our sponsors
[playing] WYPR FM
[downloaded] Welcome To Czechia: Czech Republic Looks To Adopt Shorter Name
[downloaded] Belgian Transport Minister Resigns Over Airport Security Debate
[downloaded] Tax Season
[downloaded] Adapting To A More Extreme Climate, Coastal Cities Get Creative
[downloaded] NPR thanks our sponsors
```
### Keyboard Controls
```
space play/pause
↑ volume up
↓ volume down
← rewind 15 seconds
→ skip to the next story
i mark as interesting
```
## License
Copyright (c) 2016 Adafruit Industries. Licensed under the MIT license.
The NPR logo is a registered trademark of NPR used with permission from NPR. All rights reserved.

34
cli Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd $DIR
cat logo.txt
_LATEST=$(npm view npr-one version)
_CURRENT=$(node version.js)
_RELEASE='/etc/os-release'
if [ -f $_RELEASE ]; then
source $_RELEASE
if [ $ID == 'raspbian' ]; then
sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
fi
fi
if [ $_CURRENT != $_LATEST ]; then
echo "updating..."
npm install -g npr-one
fi
node index.js

View file

@ -1,48 +0,0 @@
require('dotenv').load();
var gulp = require('gulp'),
jshint = require('gulp-jshint'),
mocha = require('gulp-mocha');
gulp.task('lint', function() {
var lint = jshint({
"curly": false,
"eqeqeq": true,
"immed": true,
"latedef": "nofunc",
"newcap": false,
"noarg": true,
"sub": true,
"undef": false,
"unused": "var",
"boss": true,
"eqnull": true,
"node": true,
"-W086": true
});
return gulp.src([
'index.js',
'lib/*.js',
'test/*.js'
]).pipe(lint)
.pipe(jshint.reporter('jshint-stylish'));
});
gulp.task('test', function() {
return gulp.src('test/*.js', {read: false})
.pipe(mocha())
.once('error', function(err) {
console.error(err);
process.exit(1);
})
.once('end', function() {
process.exit();
});
});
gulp.task('default', ['lint']);

View file

@ -1,13 +1,30 @@
var npr = require('npr-api')(),
fs = require('fs'),
chalk = require('chalk'),
auth = require('./lib/auth'),
omx = require('node-omx')();
wget = require('wget-improved');
'use strict';
var logo = fs.readFileSync('./logo.txt', 'utf8');
process.title = 'npr-one';
console.log(logo);
if(process.platform != 'linux' && process.platform != 'darwin') {
console.error('Your platform is not currently supported');
process.exit(1);
}
const NPR = require('npr-api'),
chalk = require('chalk'),
auth = require('./lib/auth'),
fs = require('fs'),
path = require('path'),
config = path.join(process.env['HOME'], '.npr-one'),
dotenv = require('dotenv').load({silent: true, path: config}),
Player = require('./lib/player'),
Story = require('./lib/story'),
UI = require('./lib/ui');
const logo = fs.readFileSync(path.join(__dirname,'logo.txt'), 'utf8');
const npr = new NPR(),
story = new Story(npr),
player = new Player();
console.log('connecting to npr one...');
// silence swagger log output
process.env.NODE_ENV = 'test';
@ -15,22 +32,29 @@ process.env.NODE_ENV = 'test';
npr.one
.init()
.then(auth.getToken.bind(auth, npr))
.then(function(token) {
.then((token) => {
process.stdout.write('\x1B[2J');
process.stdout.write('\x1B[0f');
console.log(logo);
return npr.one.setAccessToken(token);
})
.then(function() {
return npr.one.listening.getRecommendations({ channel: 'npr' });
})
.then(function(recommendations) {
// print out the first two recommendations to the console
var tmp = '/tmp/test.mp4 ';
wget.download(recommendations.items[0].links.audio[0].href, tmp)
.on('end', function() {
omx.play(tmp);
omx.on('play', function() {console.log('play');});
omx.on('stop', function() {console.log('stop');});
});
.then(story.getRecommendations.bind(story))
.then(player.load.bind(player))
.then(() => {
const ui = new UI({
touchThreshold: process.env.MPR121_TOUCH,
releaseThreshold: process.env.MPR121_RELEASE
});
ui.on('skip', player.skip.bind(player));
ui.on('pause', player.pause.bind(player));
ui.on('rewind', player.rewind.bind(player));
ui.on('interesting', player.interesting.bind(player));
ui.on('volumeup', player.increaseVolume.bind(player));
ui.on('volumedown', player.decreaseVolume.bind(player));
})
.catch(function(err) {
console.error(err,err.stack);
console.error(err, err.stack);
});

View file

@ -1,46 +1,49 @@
var inquirer = require('inquirer'),
dotenv = require('dotenv').load(),
fs = require('fs'),
npr;
'use strict';
var client_creds = [
const inquirer = require('inquirer'),
path = require('path'),
config = path.join(process.env['HOME'], '.npr-one'),
fs = require('fs');
let npr;
const client_creds = [
{
type: 'input',
name: 'CLIENT_ID',
message: 'NPR OAuth Client ID',
message: 'NPR Application ID',
},
{
type: 'input',
name: 'CLIENT_SECRET',
message: 'NPR OAuth Client Secret',
message: 'NPR Application Secret',
}
];
var device_code = [
const device_code = [
{
type: 'list',
name: 'device',
message: 'Authorize this Pi @ ',
message: 'Authorize the NPR One CLI @ ',
choices: ['Complete', 'Exit']
}
];
var requestDeviceCode = function() {
const requestDeviceCode = () => {
return new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
npr.one.authorization
.generateDeviceCode({
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'listening.write identity.readonly'
scope: 'listening.readonly listening.write identity.readonly'
})
.then(function(res) {
.then((res) => {
device_code[0].message += res.verification_uri;
device_code[0].message += ' using code: ' + res.user_code;
device_code[0].message += `${res.verification_uri} using code: ${res.user_code}`;
inquirer.prompt(device_code, function(answers) {
inquirer.prompt(device_code, (answers) => {
if(answers.device === 'Exit')
process.exit();
@ -56,9 +59,9 @@ var requestDeviceCode = function() {
};
var requestToken = function(code) {
const requestToken = (code) => {
return new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
npr.one.authorization
.createToken({
@ -67,7 +70,7 @@ var requestToken = function(code) {
client_secret: process.env.CLIENT_SECRET,
code: code
})
.then(function(res) {
.then((res) => {
process.env.NPR_ACCESS_TOKEN = res.access_token;
resolve(res.access_token);
})
@ -77,14 +80,14 @@ var requestToken = function(code) {
};
var getClientCreds = function() {
const getClientCreds = () => {
return new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
if(process.env.CLIENT_ID && process.env.CLIENT_SECRET)
return resolve();
inquirer.prompt(client_creds, function(answers) {
inquirer.prompt(client_creds, (answers) => {
process.env.CLIENT_ID = answers.CLIENT_ID;
process.env.CLIENT_SECRET = answers.CLIENT_SECRET;
@ -97,11 +100,11 @@ var getClientCreds = function() {
};
exports.getToken = function(api) {
exports.getToken = (api) => {
npr = api;
return new Promise(function(resolve, reject) {
return new Promise((resolve, reject) => {
if(process.env.NPR_ACCESS_TOKEN)
return resolve(process.env.NPR_ACCESS_TOKEN);
@ -109,8 +112,8 @@ exports.getToken = function(api) {
getClientCreds()
.then(requestDeviceCode.bind(this))
.then(requestToken.bind(this))
.then(function(token) {
fs.writeFileSync('.env', 'NPR_ACCESS_TOKEN=' + process.env.NPR_ACCESS_TOKEN + '\n');
.then((token) => {
fs.writeFileSync(config, `NPR_ACCESS_TOKEN=${process.env.NPR_ACCESS_TOKEN}\n`);
resolve(token);
})
.catch(reject);

161
lib/player.js Normal file
View file

@ -0,0 +1,161 @@
'use strict';
const Mplayer = require('mplayer'),
chalk = require('chalk'),
log = require('npmlog'),
EventEmitter = require('events');
const resetLine = function() {
process.stdout.clearLine();
process.stdout.cursorTo(0)
};
class Player extends EventEmitter {
constructor() {
super();
log.addLevel('playing', 2001, {bg: 'black', fg: 'green'}, '[playing]');
log.addLevel('skipped', 2002, {bg: 'black', fg: 'red'}, '[skipped]');
log.addLevel('finished', 2003, {bg: 'black', fg: 'red'}, '[finished]');
log.addLevel('volume', 2004, {bg: 'black', fg: 'red'}, '[volume]');
log.addLevel('interesting', 2005, {bg: 'black', fg: 'red'}, '[interesting]');
this.story = null;
this.volume = 100;
this.time = 0;
this.playing = false;
this.player = new Mplayer();
this.player.on('stop', this.done.bind(this));
this.player.on('time', (time) => this.time = time);
this.player.on('error', console.error);
}
load(story) {
this.story = story;
return this.play();
}
play() {
if(! this.story) return;
this.story.start().then((file) => {
this.player.openFile(file);
resetLine();
log.playing(this.story.title);
this.player.play();
this.time = 0;
this.playing = true;
});
}
increaseVolume() {
if(! this.player) return;
this.volume += 10;
if(this.volume > 100)
this.volume = 100;
this.player.volume(this.volume);
resetLine();
log.volume(this.volume);
}
decreaseVolume() {
if(! this.player)
return;
this.volume -= 10;
if(this.volume < 10)
this.volume = 10;
this.player.volume(this.volume);
resetLine();
log.volume(this.volume);
}
pause() {
if(! this.player) return;
if(this.playing) {
this.player.pause();
this.playing = false;
} else {
this.player.play();
this.playing = true;
}
}
rewind() {
if(! this.player) return;
if(this.time < 15)
this.time = 15;
this.player.seek(this.time-15);
}
skip() {
if(! this.player) return;
if(! this.story) return;
if(! this.story.canSkip) return;
resetLine();
log.skipped(this.story.title);
this.story.next(this.time);
this.player.stop();
}
interesting() {
if(! this.player) return;
if(! this.story) return;
if(this.story.interesting) return;
resetLine();
log.interesting(this.story.title);
this.story.markInteresting(this.time);
}
done() {
if(! this.story.skipped) {
resetLine();
log.finished(this.story.title);
}
this.story.finished();
this.time = 0;
this.playing = false;
this.play();
}
}
exports = module.exports = Player;

235
lib/story.js Normal file
View file

@ -0,0 +1,235 @@
'use strict';
const rimraf = require('rimraf'),
touch = require('touch'),
fs = require('fs'),
url = require('url'),
S = require('string'),
Gauge = require('gauge'),
chalk = require('chalk'),
wget = require('wget-improved');
class Story {
constructor(npr) {
this.npr = npr;
this.recommendations = [];
this.completed = [];
this.ratings = [];
this.current = null;
}
download(rec) {
return new Promise((resolve, reject) => {
const filename = `/tmp/npr-${rec.attributes.uid}`;
try {
fs.accessSync(filename);
rec.file = filename;
rec.downloaded = true;
rec.downloading = false;
rec.download = Promise.resolve(rec);
console.log(`${chalk.red.bgBlack('[downloaded]')} ${rec.attributes.title}`);
return resolve(rec);
} catch(e) {}
touch.sync(`/tmp/npr-${rec.attributes.uid}`);
rec.file = filename;
rec.downloading = true;
const bar = new Gauge(process.stderr, {
cleanupOnExit: false,
template: [
{value: chalk.green.bgBlack('[download]'), kerning: 1},
{type: 'section', kerning: 1, length: 20},
{type: 'progressbar' }
]
});
wget.download(rec.links.audio[0].href, rec.file)
.on('error', reject)
.on('progress', (progress) => {
bar.show(rec.attributes.title, progress);
})
.on('end', () => {
bar.disable();
console.log(`${chalk.red.bgBlack('[downloaded]')} ${rec.attributes.title}`);
rec.downloaded = true;
rec.downloading = false;
resolve(rec);
});
});
}
fetchNew() {
var next = this.recommendations.find((rec) => {
return !rec.file && !rec.downloaded && !rec.downloading;
});
if(! next) return;
next.download = this.download(next);
next.download.then(() => this.fetchNew());
return next.download;
}
getRecommendations() {
return this.npr.one.listening.getRecommendations({ channel: 'npr' })
.then((rec) => {
this.recommendations = rec.items;
return this.fetchNew();
})
.then(() => this.recommendations[1].download)
.then(() => {
this.current = this.recommendations.shift();
return this;
});
}
sendRatings() {
let args = url.parse(this.current.links.recommendations[0].href, true).query;
args.body = this.ratings;
return this.npr.one.listening.postRating(args)
.then((res) => {
res.items.forEach((rec) => {
if(this.checkExisting(rec)) return;
this.recommendations.push(rec);
});
this.ratings = [];
return this.fetchNew();
})
.catch(() => {});
}
checkExisting(rec) {
if(rec.attributes.uid == this.id) return true;
if(rec.attributes.type == 'stationId') return true;
const exists = this.recommendations.find((existing) => {
return rec.attributes.uid == existing.attributes.uid;
});
const completed = this.completed.find((existing) => {
return rec.attributes.uid == existing.attributes.uid;
});
if(exists || completed)
return true;
return false;
}
get id() {
return this.current.attributes.uid;
}
get file() {
return this.current.file;
}
get skipped() {
return this.current.skipped;
}
get canSkip() {
return this.current.attributes.skippable;
}
get interesting() {
return this.current.interesting;
}
get title() {
return this.current.attributes.title;
}
get rating() {
return Object.assign({}, this.current.attributes.rating);
}
start() {
const rating = this.rating;
rating.timestamp = (new Date()).toISOString();
this.ratings.push(rating);
this.sendRatings();
if(this.current.downloaded)
return Promise.resolve(this.file);
if(! this.current.downloading)
this.current.download = this.download(this.current);
return this.current.download.then(rec => rec.file);
}
markInteresting(sec) {
if(this.interesting) return;
const rating = this.rating;
rating.rating = 'THUMBSUP';
rating.elapsed = sec;
rating.timestamp = (new Date()).toISOString();
this.ratings.push(rating);
this.current.interesting = true;
}
next(sec) {
const rating = this.rating;
rating.rating = 'SKIP';
rating.elapsed = Math.floor(sec);
rating.timestamp = (new Date()).toISOString();
this.ratings.push(rating);
this.current.skipped = true;
}
finished() {
const rating = this.rating;
if(! this.skipped) {
rating.rating = 'COMPLETED';
rating.elapsed = rating.duration;
rating.timestamp = (new Date()).toISOString();
this.ratings.push(rating);
}
rimraf(this.file, ()=>{});
this.completed.push(this.current);
this.current = this.recommendations.shift();
}
}
exports = module.exports = Story;

85
lib/ui.js Normal file
View file

@ -0,0 +1,85 @@
'use strict';
const EventEmitter = require('events'),
keypress = require('keypress');
class UI extends EventEmitter {
constructor(config) {
super();
this.touchThreshold = config.touchThreshold || 24;
this.releaseThreshold = config.releaseThreshold || 12;
if(process.platform == 'linux' && process.arch == 'arm')
this.mprInit();
this.keyboardInit();
}
keyboardInit() {
keypress(process.stdin);
process.stdin.on('keypress', (ch, key) => {
if(key && key.name == 'right') this.skip();
if(key && key.name == 'left') this.rewind();
if(key && key.name == 'up') this.volumeup();
if(key && key.name == 'down') this.volumedown();
if(key && key.name == 'space') this.pause();
if(key && key.name == 'i') this.interesting();
if(key && key.ctrl && key.name == 'c') process.exit();
});
process.stdin.setRawMode(true);
process.stdin.resume();
}
mprInit() {
const MPR121 = require('adafruit-mpr121'),
mpr121 = new MPR121(0x5A, 1);
mpr121.setThresholds(this.touchThreshold, this.releaseThreshold);
mpr121.on('touch', (pin) => {
if(pin === 0) this.skip();
if(pin === 1) this.pause();;
if(pin === 2) this.rewind();
if(pin === 3) this.interesting();
if(pin === 4) this.volumeup();
if(pin === 5) this.volumedown();
});
}
skip(pressed) {
this.emit('skip');
}
pause(pressed) {
this.emit('pause');
}
rewind(pressed) {
this.emit('rewind');
}
interesting(pressed) {
this.emit('interesting');
}
volumeup(pressed) {
this.emit('volumeup');
}
volumedown(pressed) {
this.emit('volumedown');
}
}
exports = module.exports = UI;

View file

@ -1,11 +1,9 @@
{
"name": "nprone-raspi",
"version": "1.0.1",
"description": "A NPR One client for the Raspberry Pi",
"name": "npr-one",
"version": "1.6.1",
"description": "A NPR One command line client",
"main": "index.js",
"scripts": {
"test": "gulp"
},
"bin": "./cli",
"repository": {
"type": "git",
"url": "git+https://github.com/adafruit/nprone_raspi.git"
@ -14,6 +12,7 @@
"npr",
"one",
"radio",
"cli",
"raspberry",
"pi",
"raspi",
@ -21,24 +20,21 @@
],
"author": "Todd Treece <todd@uniontownlabs.org>",
"license": "MIT",
"bugs": {
"url": "https://github.com/adafruit/nprone_raspi/issues"
},
"homepage": "https://github.com/adafruit/nprone_raspi#readme",
"devDependencies": {
"gulp": "^3.9.0",
"gulp-jshint": "^1.11.2",
"gulp-mocha": "^2.1.3",
"jshint-stylish": "^2.0.1"
},
"dependencies": {
"bunyan": "^1.4.0",
"chalk": "^1.1.0",
"chalk": "^1.1.3",
"dotenv": "^1.2.0",
"es6-shim": "^0.32.2",
"gauge": "^2.2.1",
"inquirer": "^0.9.0",
"node-omx": "^0.2.1",
"swagger-client": "^2.1.2",
"wget-improved": "^1.1.1"
"keypress": "^0.2.1",
"mplayer": "^2.0.1",
"npmlog": "^2.0.3",
"npr-api": "^2.0.0",
"rimraf": "^2.5.2",
"string": "^3.3.1",
"touch": "^1.0.0",
"wget-improved": "^1.3.0"
},
"optionalDependencies": {
"adafruit-mpr121": "^1.0.0"
}
}

2
version.js Normal file
View file

@ -0,0 +1,2 @@
const pkg = require('./package.json');
console.log(pkg.version);