@warren-bank/airtube
Advanced tools
Comparing version 1.0.2 to 2.0.0
172
index.js
@@ -14,2 +14,3 @@ #!/usr/bin/env node | ||
const bonjour = require('bonjour')(); | ||
const prompt = require('./lib/AirPlay_devices').prompt; | ||
@@ -19,5 +20,6 @@ program | ||
.usage('<url> [options]') | ||
// .option('-v, --verbose', 'enable verbose mode') // TODO verbose output | ||
.option('-v, --verbose', 'enable verbose mode') | ||
.option('-t, --timeout <seconds>', 'timeout for bonjour discovery', parseInt, 0) | ||
.option('-d, --device <device>', 'hostname or IP') | ||
.option('-p, --port <port>', 'port number', 7000) | ||
.option('-p, --port <port>', 'port number', parseInt, 7000) | ||
.parse(process.argv); | ||
@@ -33,6 +35,13 @@ | ||
spinner: 'dots2', | ||
color: 'blue', | ||
color: 'yellow', | ||
interval: 100 | ||
}); | ||
let logDiscovering = ora({ | ||
text: 'Discovering AirPlay devices...', | ||
spinner: 'dots2', | ||
color: 'yellow', | ||
interval: 100 | ||
}); | ||
let logConnecting = ora({ | ||
@@ -46,7 +55,33 @@ text: 'Connecting to AirPlay device...', | ||
let logPlay = ora({ | ||
spinner: 'dots', | ||
color: 'green', | ||
interval: 200 | ||
text: 'Playing YouTube video...', | ||
spinner: 'dots2', | ||
color: 'yellow', | ||
interval: 100 | ||
}); | ||
const logVerboseCommand = (text) => { | ||
if (program.verbose && text) { | ||
console.log(chalk.green(text)) | ||
} | ||
} | ||
const logVerboseOutput = (text) => { | ||
if (program.verbose && text) { | ||
if (typeof text === 'object') { | ||
text = JSON.stringify(text, null, 4) | ||
} | ||
console.log(chalk.yellow(text)) | ||
} | ||
} | ||
const logSuccess = (logger, text) => { | ||
logger.succeed(text) | ||
logVerboseOutput('Success: ' + text) | ||
} | ||
const logFailure = (logger, text) => { | ||
logger.fail(text) | ||
logVerboseOutput('Failure: ' + text) | ||
} | ||
logGetInfo.start(); | ||
@@ -63,6 +98,7 @@ | ||
console.error('\n' + chalk.red('Error'), err.message); | ||
// TODO Show stack in verbose | ||
logVerboseOutput(err.stack) | ||
} | ||
process.exit(-1); | ||
}); | ||
return(-1); | ||
}) | ||
.then(process.exit); | ||
@@ -72,27 +108,50 @@ | ||
return new Promise((resolve, reject) => { | ||
if (!info || !info.formats) { | ||
logGetInfo.fail('Cannot get video info.'); | ||
reject(); | ||
return; | ||
} | ||
if (!info || !info.formats) { | ||
logFailure(logGetInfo, 'Cannot get video info.'); | ||
reject(); | ||
return; | ||
} | ||
const format = ytdl.chooseFormat(info.formats, { | ||
filter: 'video' // TODO different qualities | ||
}); | ||
const format = ytdl.chooseFormat(info.formats, { | ||
filter: 'video' // TODO different qualities | ||
}); | ||
if (!format || !format.url) { | ||
logGetInfo.fail('Cannot find proper source.'); | ||
reject(); | ||
return; | ||
} | ||
if (!format || !format.url) { | ||
logFailure(logGetInfo, 'Cannot find proper source.'); | ||
reject(); | ||
return; | ||
} | ||
const url = format.url; | ||
const title = info.title; | ||
logGetInfo.succeed(`Video info loaded. Using ${format.resolution}.`); | ||
resolve({url, title}); | ||
logSuccess(logGetInfo, `Video info loaded. Using ${chalk.blue(format.qualityLabel)}.`); | ||
resolve(format); | ||
}) | ||
.then(format => { | ||
if (program.verbose) { | ||
logVerboseCommand(`ytdl.getInfo("${url}")`) | ||
logVerboseOutput(info) | ||
logVerboseCommand(`ytdl.chooseFormat(formats, {filter: 'video'})`) | ||
logVerboseOutput(format) | ||
} | ||
); | ||
return format; | ||
}) | ||
.then(format => { | ||
const hash = '#video.' + ( | ||
(format.isHLS) | ||
? 'm3u8' | ||
: (format.isDashMPD) | ||
? 'mpd' | ||
: (format.container) | ||
? format.container | ||
: 'mp4' | ||
) | ||
const videoInfo = { | ||
title: info.videoDetails.title, | ||
url: (format.url + hash) | ||
} | ||
return videoInfo | ||
}); | ||
} | ||
// TODO logging for device discovering | ||
function findDevice(videoInfo) { | ||
@@ -103,6 +162,43 @@ return new Promise((resolve, reject) => { | ||
} else { | ||
const browser = bonjour.find({type: 'airplay'}, devices => { | ||
browser.stop(); | ||
resolve({deviceHost: devices.host, devicePort: devices.port, videoInfo}); | ||
const devices = [] | ||
let timer = 0 | ||
logDiscovering.start(); | ||
const browser = bonjour.find({type: 'airplay'}, device => { | ||
if (program.verbose) { | ||
logVerboseCommand(`bonjour.find({type: 'airplay'})`) | ||
logVerboseOutput(device) | ||
} | ||
if (timer === 0) { | ||
browser.stop(); | ||
resolve({deviceHost: device.host, devicePort: device.port, videoInfo}); | ||
} | ||
else { | ||
devices.push(device) | ||
} | ||
}); | ||
if (program.timeout > 0) { | ||
timer = setTimeout( | ||
() => { | ||
browser.stop(); | ||
if (devices.length === 0) { | ||
logFailure(logDiscovering, 'Unable to discover any AirPlay devices.'); | ||
reject(); | ||
return; | ||
} | ||
logSuccess(logDiscovering, `Discovered ${chalk.blue(devices.length)} AirPlay device${(devices.length === 1) ? '' : 's'}.`); | ||
// prompt user to choose 1 of several AirPlay devices found on the LAN | ||
const cb = (device) => { | ||
resolve({deviceHost: device.host, devicePort: device.port, videoInfo}); | ||
} | ||
prompt(devices, cb) | ||
}, | ||
(program.timeout * 1000) | ||
) | ||
} | ||
} | ||
@@ -119,3 +215,3 @@ }); | ||
if (!airplayDevice) { | ||
logConnecting.fail('AirPlay device connection error.'); | ||
logFailure(logConnecting, 'AirPlay device connection error.'); | ||
reject(); | ||
@@ -128,3 +224,3 @@ return; | ||
if (err) { | ||
logConnecting.fail('AirPlay playback error.'); | ||
logFailure(logConnecting, 'AirPlay playback error.'); | ||
reject(err); | ||
@@ -134,7 +230,11 @@ return; | ||
logConnecting.succeed(`Connected to ${chalk.blue(deviceHost + ':' + devicePort)}`); | ||
logPlay.text = `Playing "${chalk.green(videoInfo.title)}". Press ${chalk.yellow('Ctrl+C')} to stop.`; | ||
logSuccess(logConnecting, `Connected to ${chalk.blue(deviceHost + ':' + devicePort)}`); | ||
if (videoInfo.title) { | ||
logPlay.text = `Playing "${chalk.blue(videoInfo.title)}"...`; | ||
} | ||
logPlay.start(); | ||
logSuccess(logPlay, logPlay.text); | ||
resolve(0); | ||
}) | ||
}); | ||
} | ||
} |
{ | ||
"name": "@warren-bank/airtube", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "Command line tool to play YouTube on Apple TV (AirPlay v1)", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -23,6 +23,8 @@ ## Command line tool to play YouTube videos on your Apple TV | ||
``` | ||
-h, --help output usage information | ||
-V, --version output the version number | ||
-d, --device <device> hostname or IP | ||
-p, --port <port> port number | ||
-h, --help output usage information | ||
-V, --version output the version number | ||
-v, --verbose enable verbose mode | ||
-t, --timeout <seconds> timeout for bonjour discovery | ||
-d, --device <device> hostname or IP | ||
-p, --port <port> port number | ||
``` |
9987
5
230
30