2015-05-15 05:03:05 +00:00
|
|
|
sortSources = (sources) ->
|
|
|
|
if not sources
|
|
|
|
console.error('sortSources() called with null source list')
|
|
|
|
return []
|
|
|
|
|
|
|
|
qualities = ['1080', '720', '480', '360', '240']
|
|
|
|
pref = String(USEROPTS.default_quality)
|
|
|
|
idx = qualities.indexOf(pref)
|
|
|
|
if idx < 0
|
2015-08-04 02:02:56 +00:00
|
|
|
idx = 2
|
2015-05-15 05:03:05 +00:00
|
|
|
|
2015-08-04 02:02:56 +00:00
|
|
|
qualityOrder = qualities.slice(idx).concat(qualities.slice(0, idx).reverse())
|
2015-05-15 05:03:05 +00:00
|
|
|
sourceOrder = []
|
|
|
|
flvOrder = []
|
|
|
|
for quality in qualityOrder
|
|
|
|
if quality of sources
|
|
|
|
flv = []
|
|
|
|
nonflv = []
|
|
|
|
sources[quality].forEach((source) ->
|
2015-06-28 16:42:21 +00:00
|
|
|
source.quality = quality
|
2015-07-03 18:24:21 +00:00
|
|
|
if source.contentType == 'video/flv'
|
2015-05-15 05:03:05 +00:00
|
|
|
flv.push(source)
|
|
|
|
else
|
|
|
|
nonflv.push(source)
|
|
|
|
)
|
|
|
|
sourceOrder = sourceOrder.concat(nonflv)
|
|
|
|
flvOrder = flvOrder.concat(flv)
|
|
|
|
|
|
|
|
return sourceOrder.concat(flvOrder).map((source) ->
|
2015-07-03 18:24:21 +00:00
|
|
|
type: source.contentType
|
2015-05-15 05:03:05 +00:00
|
|
|
src: source.link
|
2015-06-28 16:42:21 +00:00
|
|
|
quality: source.quality
|
2015-05-15 05:03:05 +00:00
|
|
|
)
|
|
|
|
|
2015-07-01 16:38:01 +00:00
|
|
|
waitUntilDefined(window, 'videojs', =>
|
|
|
|
videojs.options.flash.swf = '/video-js.swf'
|
|
|
|
)
|
|
|
|
|
2015-05-15 05:03:05 +00:00
|
|
|
window.VideoJSPlayer = class VideoJSPlayer extends Player
|
2015-04-24 02:40:08 +00:00
|
|
|
constructor: (data) ->
|
2015-05-15 05:03:05 +00:00
|
|
|
if not (this instanceof VideoJSPlayer)
|
|
|
|
return new VideoJSPlayer(data)
|
|
|
|
|
|
|
|
@setMediaProperties(data)
|
2015-07-03 18:24:21 +00:00
|
|
|
@loadPlayer(data)
|
2015-05-15 05:03:05 +00:00
|
|
|
|
2015-07-03 18:24:21 +00:00
|
|
|
loadPlayer: (data) ->
|
2015-05-15 05:03:05 +00:00
|
|
|
waitUntilDefined(window, 'videojs', =>
|
|
|
|
video = $('<video/>')
|
|
|
|
.addClass('video-js vjs-default-skin embed-responsive-item')
|
|
|
|
.attr(width: '100%', height: '100%')
|
|
|
|
removeOld(video)
|
|
|
|
|
|
|
|
sources = sortSources(data.meta.direct)
|
|
|
|
if sources.length == 0
|
2015-06-28 16:42:21 +00:00
|
|
|
console.error('VideoJSPlayer::constructor(): data.meta.direct
|
|
|
|
has no sources!')
|
2015-05-15 05:03:05 +00:00
|
|
|
@mediaType = null
|
|
|
|
return
|
|
|
|
|
|
|
|
sources.forEach((source) ->
|
2015-06-28 16:42:21 +00:00
|
|
|
$('<source/>').attr(
|
|
|
|
src: source.src
|
|
|
|
type: source.type
|
|
|
|
'data-quality': source.quality
|
|
|
|
).appendTo(video)
|
2015-05-15 05:03:05 +00:00
|
|
|
)
|
|
|
|
|
2015-07-25 08:19:32 +00:00
|
|
|
if data.meta.gdrive_subtitles
|
|
|
|
data.meta.gdrive_subtitles.available.forEach((subt) ->
|
2015-07-26 19:28:43 +00:00
|
|
|
label = subt.lang_original
|
|
|
|
if subt.name
|
|
|
|
label += " (#{subt.name})"
|
2015-07-25 08:19:32 +00:00
|
|
|
$('<track/>').attr(
|
|
|
|
src: "/gdvtt/#{data.id}/#{subt.lang}/#{subt.name}.vtt?\
|
|
|
|
vid=#{data.meta.gdrive_subtitles.vid}"
|
|
|
|
kind: 'subtitles'
|
|
|
|
srclang: subt.lang
|
2015-07-26 19:28:43 +00:00
|
|
|
label: label
|
2015-07-25 08:19:32 +00:00
|
|
|
).appendTo(video)
|
|
|
|
)
|
|
|
|
|
2015-05-15 05:03:05 +00:00
|
|
|
@player = videojs(video[0], autoplay: true, controls: true)
|
|
|
|
@player.ready(=>
|
2015-07-05 20:50:34 +00:00
|
|
|
@setVolume(VOLUME)
|
2015-05-15 05:03:05 +00:00
|
|
|
@player.on('ended', ->
|
|
|
|
if CLIENT.leader
|
|
|
|
socket.emit('playNext')
|
|
|
|
)
|
|
|
|
|
|
|
|
@player.on('pause', =>
|
|
|
|
@paused = true
|
|
|
|
if CLIENT.leader
|
|
|
|
sendVideoUpdate()
|
|
|
|
)
|
|
|
|
|
|
|
|
@player.on('play', =>
|
|
|
|
@paused = false
|
|
|
|
if CLIENT.leader
|
|
|
|
sendVideoUpdate()
|
|
|
|
)
|
2015-07-02 06:59:21 +00:00
|
|
|
|
|
|
|
# Workaround for IE-- even after seeking completes, the loading
|
|
|
|
# spinner remains.
|
|
|
|
@player.on('seeked', =>
|
|
|
|
$('.vjs-waiting').removeClass('vjs-waiting')
|
|
|
|
)
|
2015-07-26 19:28:43 +00:00
|
|
|
|
2015-07-26 20:29:06 +00:00
|
|
|
# Workaround for Chrome-- it seems that the click bindings for
|
|
|
|
# the subtitle menu aren't quite set up until after the ready
|
|
|
|
# event finishes, so set a timeout for 1ms to force this code
|
|
|
|
# not to run until the ready() function returns.
|
|
|
|
setTimeout(->
|
|
|
|
$('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each((i, elem) ->
|
|
|
|
if elem.textContent == localStorage.lastSubtitle
|
|
|
|
elem.click()
|
|
|
|
|
|
|
|
elem.onclick = ->
|
|
|
|
if elem.attributes['aria-selected'].value == 'true'
|
|
|
|
localStorage.lastSubtitle = elem.textContent
|
|
|
|
)
|
|
|
|
, 1)
|
2015-05-15 05:03:05 +00:00
|
|
|
)
|
|
|
|
)
|
2015-04-24 02:40:08 +00:00
|
|
|
|
|
|
|
load: (data) ->
|
2015-05-15 05:03:05 +00:00
|
|
|
@setMediaProperties(data)
|
2015-07-03 18:24:21 +00:00
|
|
|
# Note: VideoJS does have facilities for loading new videos into the
|
|
|
|
# existing player object, however it appears to be pretty glitchy when
|
|
|
|
# a video can't be played (either previous or next video). It's safer
|
|
|
|
# to just reset the entire thing.
|
|
|
|
@loadPlayer(data)
|
2015-05-15 05:03:05 +00:00
|
|
|
|
|
|
|
play: ->
|
|
|
|
@paused = false
|
|
|
|
if @player and @player.readyState() > 0
|
|
|
|
@player.play()
|
|
|
|
|
|
|
|
pause: ->
|
|
|
|
@paused = true
|
|
|
|
if @player and @player.readyState() > 0
|
|
|
|
@player.pause()
|
|
|
|
|
|
|
|
seekTo: (time) ->
|
|
|
|
if @player and @player.readyState() > 0
|
|
|
|
@player.currentTime(time)
|
|
|
|
|
|
|
|
setVolume: (volume) ->
|
2015-07-05 20:50:34 +00:00
|
|
|
if @player
|
2015-05-15 05:03:05 +00:00
|
|
|
@player.volume(volume)
|
|
|
|
|
|
|
|
getTime: (cb) ->
|
|
|
|
if @player and @player.readyState() > 0
|
|
|
|
cb(@player.currentTime())
|
|
|
|
else
|
|
|
|
cb(0)
|
|
|
|
|
|
|
|
getVolume: (cb) ->
|
|
|
|
if @player and @player.readyState() > 0
|
|
|
|
if @player.muted()
|
|
|
|
cb(0)
|
|
|
|
else
|
|
|
|
cb(@player.volume())
|
|
|
|
else
|
|
|
|
cb(VOLUME)
|