160 lines
4.6 KiB
JavaScript
160 lines
4.6 KiB
JavaScript
var chromecasts = require('chromecasts')()
|
|
var airplay = require('airplay-js')
|
|
|
|
var config = require('../../config')
|
|
var state = require('../state')
|
|
|
|
// The Cast module talks to Airplay and Chromecast
|
|
// * Modifies state when things change
|
|
// * Starts and stops casting, provides remote video controls
|
|
module.exports = {
|
|
init,
|
|
openChromecast,
|
|
openAirplay,
|
|
stopCasting,
|
|
playPause,
|
|
seek,
|
|
isCasting
|
|
}
|
|
|
|
// Callback to notify module users when state has changed
|
|
var update
|
|
|
|
function init (callback) {
|
|
update = callback
|
|
|
|
// Start polling Chromecast or Airplay, whenever we're connected
|
|
setInterval(() => pollCastStatus(state), 1000)
|
|
|
|
// Listen for devices: Chromecast and Airplay
|
|
chromecasts.on('update', function (player) {
|
|
state.devices.chromecast = player
|
|
addChromecastEvents()
|
|
})
|
|
|
|
var browser = airplay.createBrowser()
|
|
browser.on('deviceOn', function (player) {
|
|
state.devices.airplay = player
|
|
addAirplayEvents()
|
|
}).start()
|
|
}
|
|
|
|
function addChromecastEvents () {
|
|
state.devices.chromecast.on('error', function (err) {
|
|
state.devices.chromecast.errorMessage = err.message
|
|
update()
|
|
})
|
|
state.devices.chromecast.on('disconnect', function () {
|
|
state.playing.location = 'local'
|
|
update()
|
|
})
|
|
}
|
|
|
|
function addAirplayEvents () {}
|
|
|
|
// Update our state from the remote TV
|
|
function pollCastStatus (state) {
|
|
if (state.playing.location === 'chromecast') {
|
|
state.devices.chromecast.status(function (err, status) {
|
|
if (err) return console.log('Error getting %s status: %o', state.playing.location, err)
|
|
state.video.isPaused = status.playerState === 'PAUSED'
|
|
state.video.currentTime = status.currentTime
|
|
update()
|
|
})
|
|
} else if (state.playing.location === 'airplay') {
|
|
state.devices.airplay.status(function (status) {
|
|
state.video.isPaused = status.rate === 0
|
|
state.video.currentTime = status.position
|
|
update()
|
|
})
|
|
}
|
|
}
|
|
|
|
function openChromecast () {
|
|
if (state.playing.location !== 'local') {
|
|
throw new Error('You can\'t connect to Chromecast when already connected to another device')
|
|
}
|
|
|
|
state.playing.location = 'chromecast-pending'
|
|
var torrentSummary = state.saved.torrents.find((x) => x.infoHash === state.playing.infoHash)
|
|
state.devices.chromecast.play(state.server.networkURL, {
|
|
type: 'video/mp4',
|
|
title: config.APP_NAME + ' — ' + torrentSummary.name
|
|
}, function (err) {
|
|
state.playing.location = err ? 'local' : 'chromecast'
|
|
update()
|
|
})
|
|
update()
|
|
}
|
|
|
|
function openAirplay () {
|
|
if (state.playing.location !== 'local') {
|
|
throw new Error('You can\'t connect to Airplay when already connected to another device')
|
|
}
|
|
|
|
state.playing.location = 'airplay-pending'
|
|
state.devices.airplay.play(state.server.networkURL, 0, function (res) {
|
|
if (res.statusCode !== 200) {
|
|
state.playing.location = 'local'
|
|
state.errors.push({
|
|
time: new Date().getTime(),
|
|
message: 'Couldn\'t connect to Airplay'
|
|
})
|
|
} else {
|
|
state.playing.location = 'airplay'
|
|
}
|
|
update()
|
|
})
|
|
update()
|
|
}
|
|
|
|
// Stops Chromecast or Airplay, move video back to local screen
|
|
function stopCasting () {
|
|
if (state.playing.location === 'chromecast') {
|
|
state.devices.chromecast.stop(stoppedCasting)
|
|
} else if (state.playing.location === 'airplay') {
|
|
state.devices.airplay.stop(stoppedCasting)
|
|
} else if (state.playing.location.endsWith('-pending')) {
|
|
// Connecting to Chromecast took too long or errored out. Let the user cancel
|
|
stoppedCasting()
|
|
}
|
|
}
|
|
|
|
function stoppedCasting () {
|
|
state.playing.location = 'local'
|
|
state.video.jumpToTime = state.video.currentTime
|
|
update()
|
|
}
|
|
|
|
// Checks whether we are connected and already casting
|
|
// Returns false if we not casting (state.playing.location === 'local')
|
|
// or if we're trying to connect but haven't yet ('chromecast-pending', etc)
|
|
function isCasting () {
|
|
return state.playing.location === 'chromecast' || state.playing.location === 'airplay'
|
|
}
|
|
|
|
function playPause () {
|
|
var device
|
|
if (state.playing.location === 'chromecast') {
|
|
device = state.devices.chromecast
|
|
if (!state.video.isPaused) device.pause(castCallback)
|
|
else device.play(null, null, castCallback)
|
|
} else if (state.playing.location === 'airplay') {
|
|
device = state.devices.airplay
|
|
if (!state.video.isPaused) device.rate(0, castCallback)
|
|
else device.rate(1, castCallback)
|
|
}
|
|
}
|
|
|
|
function seek (time) {
|
|
if (state.playing.location === 'chromecast') {
|
|
state.devices.chromecast.seek(time, castCallback)
|
|
} else if (state.playing.location === 'airplay') {
|
|
state.devices.airplay.scrub(time, castCallback)
|
|
}
|
|
}
|
|
|
|
function castCallback () {
|
|
console.log(state.playing.location + ' callback: %o', arguments)
|
|
}
|