Airplay support. Nicer looking cast screen
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
`
|
`
|
||||||
|
|||||||
Reference in New Issue
Block a user