|
|
|
|
@@ -4,6 +4,7 @@ var h = require('virtual-dom/h')
|
|
|
|
|
var hyperx = require('hyperx')
|
|
|
|
|
var hx = hyperx(h)
|
|
|
|
|
|
|
|
|
|
var WebChimeraPlayer = require('wcjs-player')
|
|
|
|
|
var prettyBytes = require('prettier-bytes')
|
|
|
|
|
var Bitfield = require('bitfield')
|
|
|
|
|
|
|
|
|
|
@@ -27,7 +28,14 @@ function Player (state) {
|
|
|
|
|
|
|
|
|
|
function renderMedia (state) {
|
|
|
|
|
if (!state.server) return
|
|
|
|
|
if (false) return renderMediaTag(state)
|
|
|
|
|
else return renderMediaVLC(state)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Renders using a <video> or <audio> tag
|
|
|
|
|
// Handles only a subset of codecs, but it's cleaner and more efficient
|
|
|
|
|
// See renderMediaVLC()
|
|
|
|
|
function renderMediaTag (state) {
|
|
|
|
|
// Unfortunately, play/pause can't be done just by modifying HTML.
|
|
|
|
|
// Instead, grab the DOM node and play/pause it if necessary
|
|
|
|
|
var mediaElement = document.querySelector(state.playing.type) /* get the <video> or <audio> tag */
|
|
|
|
|
@@ -65,7 +73,6 @@ function renderMedia (state) {
|
|
|
|
|
|
|
|
|
|
// Add subtitles to the <video> tag
|
|
|
|
|
var trackTags = []
|
|
|
|
|
|
|
|
|
|
if (state.playing.subtitles.enabled && state.playing.subtitles.tracks.length > 0) {
|
|
|
|
|
for (var i = 0; i < state.playing.subtitles.tracks.length; i++) {
|
|
|
|
|
var track = state.playing.subtitles.tracks[i]
|
|
|
|
|
@@ -122,6 +129,102 @@ function renderMedia (state) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Renders using WebChimera.js to render using VLC
|
|
|
|
|
// That lets us play media that the <video> tag can't play
|
|
|
|
|
function renderMediaVLC (state) {
|
|
|
|
|
// Unfortunately, WebChimera can't be done just by modifying HTML.
|
|
|
|
|
// Instead, grab the DOM node
|
|
|
|
|
if (document.querySelector('#media-player')) {
|
|
|
|
|
if (!state.playing.chimera) {
|
|
|
|
|
state.playing.chimera = new WebChimeraPlayer('#media-player')
|
|
|
|
|
.addPlayer({
|
|
|
|
|
autoplay: true,
|
|
|
|
|
vlcArgs: ['-vvv'],
|
|
|
|
|
wcjsRendererOptions: {'fallbackRenderer': true}
|
|
|
|
|
})
|
|
|
|
|
.onPlaying(dispatcher('mediaPlaying'))
|
|
|
|
|
.onPaused(dispatcher('mediaPaused'))
|
|
|
|
|
.onBuffering(dispatcher('mediaStalled'))
|
|
|
|
|
.onTime(dispatcher('mediaTimeUpdate'))
|
|
|
|
|
.onEnded(onEnded)
|
|
|
|
|
.onFrameSetup(onLoadedMetadata)
|
|
|
|
|
.addPlaylist(state.server.localURL)
|
|
|
|
|
state.playing.chimera.ui(false)
|
|
|
|
|
} else {
|
|
|
|
|
var player = state.playing.chimera
|
|
|
|
|
if (state.playing.isPaused && player.playing()) {
|
|
|
|
|
player.pause()
|
|
|
|
|
} else if (!state.playing.isPaused && !player.playing()) {
|
|
|
|
|
player.play()
|
|
|
|
|
}
|
|
|
|
|
// When the user clicks or drags on the progress bar, jump to that position
|
|
|
|
|
if (state.playing.jumpToTime) {
|
|
|
|
|
player.time(state.playing.jumpToTime * 1000) // WebChimera expects milliseconds
|
|
|
|
|
state.playing.jumpToTime = null
|
|
|
|
|
}
|
|
|
|
|
// Set volume
|
|
|
|
|
if (state.playing.setVolume !== null && isFinite(state.playing.setVolume)) {
|
|
|
|
|
player.volume(Math.round(state.playing.setVolume * 100)) // WebChimera expects integer percent
|
|
|
|
|
state.playing.setVolume = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.playing.currentTime = player.time() / 1000
|
|
|
|
|
state.playing.duration = player.length() / 1000
|
|
|
|
|
state.playing.volume = player.volume() / 100
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
state.playing.chimera = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add subtitles to the <video> tag
|
|
|
|
|
var trackTags = []
|
|
|
|
|
if (state.playing.subtitles.enabled && state.playing.subtitles.tracks.length > 0) {
|
|
|
|
|
for (var i = 0; i < state.playing.subtitles.tracks.length; i++) {
|
|
|
|
|
var track = state.playing.subtitles.tracks[i]
|
|
|
|
|
trackTags.push(hx`
|
|
|
|
|
<track
|
|
|
|
|
default=${track.selected ? 'default' : ''}
|
|
|
|
|
label=${track.language}
|
|
|
|
|
type='subtitles'
|
|
|
|
|
src=${track.buffer}>
|
|
|
|
|
`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the <audio> or <video> tag
|
|
|
|
|
var mediaType = state.playing.type /* 'video' or 'audio' */
|
|
|
|
|
var mediaTag = hx`
|
|
|
|
|
<div id='media-player' class='${mediaType}-player'>
|
|
|
|
|
${trackTags}
|
|
|
|
|
</div>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// Show the media.
|
|
|
|
|
return hx`
|
|
|
|
|
<div
|
|
|
|
|
class='letterbox'
|
|
|
|
|
onmousemove=${dispatcher('mediaMouseMoved')}>
|
|
|
|
|
${mediaTag}
|
|
|
|
|
${renderOverlay(state)}
|
|
|
|
|
</div>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
// As soon as the video loads enough to know the video dimensions, resize the window
|
|
|
|
|
function onLoadedMetadata (e) {
|
|
|
|
|
if (mediaType !== 'video') return
|
|
|
|
|
var dimensions = {
|
|
|
|
|
width: player.width(),
|
|
|
|
|
height: player.height()
|
|
|
|
|
}
|
|
|
|
|
dispatch('setDimensions', dimensions)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When the video completes, pause the video instead of looping
|
|
|
|
|
function onEnded (e) {
|
|
|
|
|
state.playing.isPaused = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderOverlay (state) {
|
|
|
|
|
var elems = []
|
|
|
|
|
var audioMetadataElem = renderAudioMetadata(state)
|
|
|
|
|
|