1 sortSources = (sources) ->
3 console.error('sortSources() called with null source list')
6 qualities = ['2160', '1440', '1080', '720', '540', '480', '360', '240']
7 pref = String(USEROPTS.default_quality)
8 if USEROPTS.default_quality == 'best'
10 idx = qualities.indexOf(pref)
14 qualityOrder = qualities.slice(idx).concat(qualities.slice(0, idx).reverse())
15 qualityOrder.unshift('auto')
18 for quality in qualityOrder
22 sources[quality].forEach((source) ->
23 source.quality = quality
24 if source.contentType == 'video/flv'
29 sourceOrder = sourceOrder.concat(nonflv)
30 flvOrder = flvOrder.concat(flv)
32 return sourceOrder.concat(flvOrder).map((source) ->
33 type: source.contentType
36 label: getSourceLabel(source)
39 getSourceLabel = (source) ->
40 if source.res is 'auto'
43 return "#{source.quality}p #{source.contentType.split('/')[1]}"
45 waitUntilDefined(window, 'videojs', =>
46 videojs.options.flash.swf = '/video-js.swf'
49 hasAnyTextTracks = (data) ->
50 ntracks = data?.meta?.textTracks?.length ? 0
53 window.VideoJSPlayer = class VideoJSPlayer extends Player
54 constructor: (data) ->
55 if not (this instanceof VideoJSPlayer)
56 return new VideoJSPlayer(data)
61 waitUntilDefined(window, 'videojs', =>
66 if @mediaType == 'cm' and hasAnyTextTracks(data)
67 attrs.crossorigin = 'anonymous'
70 .addClass('video-js vjs-default-skin embed-responsive-item')
74 @sources = sortSources(data.meta.direct)
75 if @sources.length == 0
76 console.error('VideoJSPlayer::constructor(): data.meta.direct
83 # TODO: Refactor VideoJSPlayer to use a preLoad()/load()/postLoad() pattern
84 # VideoJSPlayer should provide the core functionality and logic for specific
85 # dependent player types (gdrive) should be an extension
86 if data.meta.gdrive_subtitles
87 data.meta.gdrive_subtitles.available.forEach((subt) ->
88 label = subt.lang_original
90 label += " (#{subt.name})"
92 src: "/gdvtt/#{data.id}/#{subt.lang}/#{subt.name}.vtt?\
93 vid=#{data.meta.gdrive_subtitles.vid}"
100 if data.meta.textTracks
101 data.meta.textTracks.forEach((track) ->
109 if track.default? and track.default
112 $('<track/>').attr(attrs).appendTo(video)
115 @player = videojs(video[0],
116 # https://github.com/Dash-Industry-Forum/dash.js/issues/2184
117 autoplay: @sources[0].type != 'application/dash+xml',
120 videoJsResolutionSwitcher:
121 default: @sources[0].res
124 # Have to use updateSrc instead of <source> tags
125 # see: https://github.com/videojs/video.js/issues/3428
126 @player.updateSrc(@sources)
127 @player.on('error', =>
128 err = @player.error()
129 if err and err.code == 4
130 console.error('Caught error, trying next source')
131 # Does this really need to be done manually?
133 if @sourceIdx < @sources.length
134 @player.src(@sources[@sourceIdx])
136 console.error('Out of sources, video will not play')
137 if @mediaType is 'gd'
138 if not window.hasDriveUserscript
139 window.promptToInstallDriveUserscript()
141 window.tellUserNotToContactMeAboutThingsThatAreNotSupported()
144 @player.on('ended', ->
146 socket.emit('playNext')
149 @player.on('pause', =>
155 @player.on('play', =>
161 # Workaround for IE-- even after seeking completes, the loading
163 @player.on('seeked', =>
164 $('.vjs-waiting').removeClass('vjs-waiting')
167 # Workaround for Chrome-- it seems that the click bindings for
168 # the subtitle menu aren't quite set up until after the ready
169 # event finishes, so set a timeout for 1ms to force this code
170 # not to run until the ready() function returns.
172 $('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each((i, elem) ->
173 textNode = elem.childNodes[0]
174 if textNode.textContent == localStorage.lastSubtitle
178 if elem.attributes['aria-checked'].value == 'true'
179 localStorage.lastSubtitle = textNode.textContent
186 @setMediaProperties(data)
187 # Note: VideoJS does have facilities for loading new videos into the
188 # existing player object, however it appears to be pretty glitchy when
189 # a video can't be played (either previous or next video). It's safer
190 # to just reset the entire thing.
196 if @player and @player.readyState() > 0
201 if @player and @player.readyState() > 0
205 if @player and @player.readyState() > 0
206 @player.currentTime(time)
208 setVolume: (volume) ->
210 @player.volume(volume)
213 if @player and @player.readyState() > 0
214 cb(@player.currentTime())
219 if @player and @player.readyState() > 0