Airplay support. Nicer looking cast screen

This commit is contained in:
DC
2015-03-15 23:02:04 -07:00
parent 66648186f4
commit 26dc1e82fd
3 changed files with 73 additions and 64 deletions

View File

@@ -356,6 +356,8 @@ input {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
background-size: cover;
background-position: center center;
} }
.player video { .player video {
@@ -701,28 +703,27 @@ body.drag .torrent-placeholder span {
/* /*
* CHROMECAST / AIRPLAY CONTROLS * CHROMECAST / AIRPLAY CONTROLS
*/ */
.cast-screen { .cast-screen {
width: 400px; width: 100%;
margin: auto; height: 200px;
color: #eee; color: #eee;
line-height: normal; text-align: center;
line-height: 2;
align-self: center;
} }
.cast-screen h1 { .cast-screen .icon {
font-size: 3em; font-size: 50px;
} }
.cast-screen .cast-type,
.cast-screen .cast-status { .cast-screen .cast-status {
margin: 40px 0; font-size: 32px;
font-size: 2em;
} }
.cast-screen .stop-casting { .cast-screen .cast-type {
cursor: pointer; font-weight: bold;
}
.cast-screen .stop-casting:hover {
color: #9af;
} }
/* /*

View File

@@ -32,7 +32,10 @@ function init (callback) {
addChromecastEvents() addChromecastEvents()
}) })
airplay.createBrowser().on('deviceOn', function (player) { var browser = airplay.createBrowser()
var devices = browser.getDevices(true)
console.log('TODO GET DEVICES RET %o', devices)
browser.on('deviceOn', function (player) {
state.devices.airplay = player state.devices.airplay = player
addAirplayEvents() addAirplayEvents()
}).start() }).start()
@@ -47,27 +50,26 @@ function addChromecastEvents () {
state.playing.location = 'local' state.playing.location = 'local'
update() update()
}) })
state.devices.chromecast.on('status', handleStatus)
} }
function addAirplayEvents () {} function addAirplayEvents () {}
// Update our state from the remote TV // Update our state from the remote TV
function pollCastStatus (state) { function pollCastStatus (state) {
var device if (state.playing.location === 'chromecast') {
if (state.playing.location === 'chromecast') device = state.devices.chromecast state.devices.chromecast.status(function (err, status) {
else if (state.playing.location === 'airplay') device = state.devices.airplay if (err) return console.log('Error getting %s status: %o', state.playing.location, err)
else return state.video.isPaused = status.playerState === 'PAUSED'
state.video.currentTime = status.currentTime
device.status(function (err, status) { update()
if (err) return console.log('Error getting %s status: %o', state.playing.location, err) })
handleStatus(status) } else if (state.playing.location === 'airplay') {
}) state.devices.airplay.status(function (status) {
} state.video.isPaused = status.rate === 0
state.video.currentTime = status.position
function handleStatus (status) { update()
state.video.isCastPaused = status.playerState === 'PAUSED' })
state.video.currentTime = status.currentTime }
} }
function openChromecast () { function openChromecast () {
@@ -93,9 +95,16 @@ function openAirplay () {
} }
state.playing.location = 'airplay-pending' state.playing.location = 'airplay-pending'
state.devices.airplay.play(state.server.networkURL, 0, function () { state.devices.airplay.play(state.server.networkURL, 0, function (res) {
console.log('Airplay', arguments) // TODO: handle airplay errors if (res.statusCode !== 200) {
state.playing.location = 'airplay' state.playing.location = 'local'
state.errors.push({
time: new Date().getTime(),
message: 'Couldn\'t connect to Airplay'
})
} else {
state.playing.location = 'airplay'
}
update() update()
}) })
update() update()
@@ -106,7 +115,7 @@ function stopCasting () {
if (state.playing.location === 'chromecast') { if (state.playing.location === 'chromecast') {
state.devices.chromecast.stop(stoppedCasting) state.devices.chromecast.stop(stoppedCasting)
} else if (state.playing.location === 'airplay') { } else if (state.playing.location === 'airplay') {
throw new Error('Unimplemented') // TODO stop airplay state.devices.airplay.stop(stoppedCasting)
} else if (state.playing.location.endsWith('-pending')) { } else if (state.playing.location.endsWith('-pending')) {
// Connecting to Chromecast took too long or errored out. Let the user cancel // Connecting to Chromecast took too long or errored out. Let the user cancel
stoppedCasting() stoppedCasting()
@@ -127,22 +136,26 @@ function isCasting () {
} }
function playPause () { function playPause () {
var device = getActiveDevice() var device
if (!state.video.isPaused) device.pause(castCallback) if (state.playing.location === 'chromecast') {
else device.play(null, null, castCallback) 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) { function seek (time) {
var device = getActiveDevice() if (state.playing.location === 'chromecast') {
device.seek(time, castCallback) state.devices.chromecast.seek(time, castCallback)
} } else if (state.playing.location === 'airplay') {
state.devices.airplay.scrub(time, castCallback)
function getActiveDevice () { }
if (state.playing.location === 'chromecast') return state.devices.chromecast
else if (state.playing.location === 'airplay') return state.devices.airplay
else throw new Error('getActiveDevice() called, but we\'re not casting')
} }
function castCallback () { function castCallback () {
console.log('Cast callback: %o', arguments) console.log(state.playing.location + ' callback: %o', arguments)
} }

View File

@@ -76,28 +76,23 @@ function renderCastScreen (state, dispatch) {
var isStarting = state.playing.location.endsWith('-pending') var isStarting = state.playing.location.endsWith('-pending')
if (!isChromecast && !isAirplay) throw new Error('Unimplemented cast type') if (!isChromecast && !isAirplay) throw new Error('Unimplemented cast type')
// Finally, show a static title screen and the cast status // Show a nice title image, if possible
var header = isChromecast ? 'Chromecast' : 'AirPlay' var style = {}
var content var infoHash = state.playing.infoHash
if (isStarting) { var torrentSummary = state.saved.torrents.find((x) => x.infoHash === infoHash)
content = hx` if (torrentSummary && torrentSummary.posterURL) {
<div class='cast-status'>Connecting...</div> var cleanURL = torrentSummary.posterURL.replace(/\\/g, '/')
` style.backgroundImage = `radial-gradient(circle at center, rgba(0,0,0,0.4) 0%,rgba(0,0,0,1) 100%), url(${cleanURL})`
} else {
content = hx`
<div class='cast-status'>
<div class='button stop-casting'
onclick=${() => dispatch('stopCasting')}>
Stop Casting
</div>
</div>
`
} }
// Show whether we're connected to Chromecast / Airplay
var castStatus = isStarting ? 'Connecting...' : 'Connected'
return hx` return hx`
<div class='letterbox'> <div class='letterbox' style=${style}>
<div class='cast-screen'> <div class='cast-screen'>
<h1>${header}</h1> <i class='icon'>${isAirplay ? 'airplay' : 'cast'}</i>
${content} <div class='cast-type'>${isAirplay ? 'AirPlay' : 'Chromecast'}</div>
<div class='cast-status'>${castStatus}</div>
</div> </div>
</div> </div>
` `