module.exports = Player var h = require('virtual-dom/h') var hyperx = require('hyperx') var hx = hyperx(h) function Player (state, dispatch) { // Unfortunately, play/pause can't be done just by modifying HTML. // Instead, grab the DOM node and play/pause it if necessary var videoElement = document.querySelector('video') if (videoElement !== null) { if (state.video.isPaused && !videoElement.paused) { videoElement.pause() } else if (!state.video.isPaused && videoElement.paused) { videoElement.play() } // When the user clicks or drags on the progress bar, jump to that position if (state.video.jumpToTime) { videoElement.currentTime = state.video.jumpToTime state.video.jumpToTime = null } state.video.currentTime = videoElement.currentTime state.video.duration = videoElement.duration } // When in fullscreen, hide player controls if the mouse stays still for a while var hideControls = state.isFullScreen && state.video.mouseStationarySince !== 0 && new Date().getTime() - state.video.mouseStationarySince > 2000 // Show the video as large as will fit in the window, play immediately return hx`
${renderPlayerControls(state, dispatch)}
` function onMouseMove () { if (state.isFullScreen) dispatch('fullscreenVideoMouseMoved') } // As soon as the video loads far enough to know the dimensions, resize the // window to match the video resolution function onLoadedMetadata (e) { var video = e.target var dimensions = { width: video.videoWidth, height: video.videoHeight } dispatch('setDimensions', dimensions) } } // Renders all video controls: play/pause, scrub, loading bar // TODO: cast buttons function renderPlayerControls (state, dispatch) { var positionPercent = 100 * state.video.currentTime / state.video.duration var playbackCursorStyle = { left: 'calc(' + positionPercent + '% - 8px)' } var torrent = state.torrentPlaying._torrent var elements = [ hx`
${renderLoadingBar(state)}
`, hx` dispatch('toggleFullScreen')}> fullscreen ` ] // If we've detected a Chromecast or AppleTV, the user can play video there if (state.devices.chromecast) { elements.push(hx` dispatch('openChromecast', torrent)}> cast `) } if (state.devices.airplay) { elements.push(hx` dispatch('openAirplay', torrent)}> airplay `) } // On OSX, the back button is in the title bar of the window; see app.js // On other platforms, we render one over the video on mouseover if (process.platform !== 'darwin') { elements.push(hx` dispatch('back')}> chevron_left `) } elements.push(hx` dispatch('playPause')}> ${state.video.isPaused ? 'play_arrow' : 'pause'} `) return hx`
${elements}
` // Handles a click or drag to scrub (jump to another position in the video) function handleScrub (e) { var windowWidth = document.querySelector('body').clientWidth var fraction = e.clientX / windowWidth var position = fraction * state.video.duration /* seconds */ dispatch('playbackJump', position) } } // Renders the loading bar. Shows which parts of the torrent are loaded, which // can be "spongey" / non-contiguous function renderLoadingBar (state) { var torrent = state.torrentPlaying._torrent if (torrent === null) { return [] } // Find all contiguous parts of the torrent which are loaded var parts = [] var lastPartPresent = false var numParts = torrent.pieces.length for (var i = 0; i < numParts; i++) { var partPresent = torrent.bitfield.get(i) if (partPresent && !lastPartPresent) { parts.push({start: i, count: 1}) } else if (partPresent) { parts[parts.length - 1].count++ } lastPartPresent = partPresent } // Output an list of rectangles to show loading progress return hx`
${parts.map(function (part) { var style = { left: (100 * part.start / numParts) + '%', width: (100 * part.count / numParts) + '%' } return hx`
` })}
` }