npr-one
Version:
A NPR One command line client
236 lines (165 loc) • 5.01 kB
JavaScript
'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;