support audio and subtitle tracks (multiple languages etc), first round

This commit is contained in:
rolux 2014-07-23 15:55:09 +02:00
parent 0f6d70766f
commit 07fdb094dd
5 changed files with 328 additions and 124 deletions

View file

@ -43,6 +43,7 @@ Ox.AnnotationFolder = function(options, self) {
item: '', item: '',
items: [], items: [],
keyboard: '', keyboard: '',
languages: 'all',
out: 0, out: 0,
position: 0, position: 0,
range: 'all', range: 'all',
@ -413,6 +414,9 @@ Ox.AnnotationFolder = function(options, self) {
&& item['in'] <= self.options.position && item['in'] <= self.options.position
&& item.out >= self.options.position && item.out >= self.options.position
) )
) && (
self.options.languages == 'all'
|| self.options.languages.indexOf(item.language) > -1
) && ( ) && (
self.options.users == 'all' self.options.users == 'all'
|| self.options.users.indexOf(item.user) > -1 || self.options.users.indexOf(item.user) > -1

View file

@ -91,6 +91,11 @@ Ox.AnnotationPanel = function(options, self) {
self.editing = false; self.editing = false;
self.languages = getLanguages();
self.enabledLanguages = self.languages.map(function(language) {
return language.code;
});
if (self.options.showUsers) { if (self.options.showUsers) {
self.users = getUsers(); self.users = getUsers();
self.enabledUsers = self.users; self.enabledUsers = self.users;
@ -170,6 +175,21 @@ Ox.AnnotationPanel = function(options, self) {
return folder; return folder;
} }
function getLanguages() {
return Ox.sortBy(Ox.map(Ox.unique(Ox.flatten(
self.options.layers.map(function(layer) {
return layer.items.map(function(item) {
return item.languages;
});
})
)), function(language) {
return {
code: language,
name: Ox.getLanguageNameByCode(language)
};
}), 'name');
}
function getUsers() { function getUsers() {
return Ox.sort(Ox.unique(Ox.flatten( return Ox.sort(Ox.unique(Ox.flatten(
self.options.layers.map(function(layer) { self.options.layers.map(function(layer) {
@ -419,6 +439,15 @@ Ox.AnnotationPanel = function(options, self) {
{id: 'text', title: Ox._('By Text'), checked: self.options.sort == 'text'} {id: 'text', title: Ox._('By Text'), checked: self.options.sort == 'text'}
]} ]}
], ],
self.languages.length > 1 ? [
{},
{id: 'languages', title: Ox._('Show Languages'), disabled: true},
{group: 'languages', min: 1, max: -1, items: self.languages.map(function(language) {
return {id: language.code, title: Ox._(language.name), checked:
self.enabledLanguages.indexOf(language.code) > -1
};
})}
] : [],
self.options.showUsers && self.users.length ? [ self.options.showUsers && self.users.length ? [
{}, {},
{id: 'users', title: Ox._('Show Users'), disabled: true}, {id: 'users', title: Ox._('Show Users'), disabled: true},
@ -438,7 +467,14 @@ Ox.AnnotationPanel = function(options, self) {
.bindEvent({ .bindEvent({
change: function(data) { change: function(data) {
var set = {}; var set = {};
if (data.id == 'users') { if (data.id == 'languages') {
self.enabledLanguages = data.checked.map(function(checked) {
return checked.id;
});
self.$folder.forEach(function($folder) {
$folder.options({languages: self.enabledLanguages});
});
} else if (data.id == 'users') {
self.enabledUsers = data.checked.map(function(checked) { self.enabledUsers = data.checked.map(function(checked) {
return checked.id; return checked.id;
}); });
@ -529,6 +565,7 @@ Ox.AnnotationPanel = function(options, self) {
// called from addannotation callback // called from addannotation callback
var i = Ox.getIndexById(self.options.layers, layer); var i = Ox.getIndexById(self.options.layers, layer);
self.$folder[i].addItem(item); self.$folder[i].addItem(item);
self.languages = getLanguages();
self.users = getUsers(); self.users = getUsers();
if (self.enabledUsers != 'all' && self.enabledUsers.indexOf(item.user) == -1) { if (self.enabledUsers != 'all' && self.enabledUsers.indexOf(item.user) == -1) {
self.enabledUsers.push(item.user); self.enabledUsers.push(item.user);
@ -584,6 +621,7 @@ Ox.AnnotationPanel = function(options, self) {
} else { } else {
// called from removeannotation callback // called from removeannotation callback
self.options.selected = ''; self.options.selected = '';
self.languages = getLanguages();
self.users = getUsers(); self.users = getUsers();
renderOptionsMenu(); renderOptionsMenu();
renderEditMenu(); renderEditMenu();

View file

@ -86,6 +86,7 @@ Ox.VideoAnnotationPanel = function(options, self) {
showLayers: {}, showLayers: {},
showUsers: false, showUsers: false,
subtitles: [], subtitles: [],
subtitlesDefaultTrack: 'en',
subtitlesLayer: null, subtitlesLayer: null,
timeline: '', timeline: '',
timelines: [], timelines: [],
@ -282,6 +283,31 @@ Ox.VideoAnnotationPanel = function(options, self) {
self.options.subtitles = self.options.subtitles || getSubtitles(); self.options.subtitles = self.options.subtitles || getSubtitles();
if (Ox.isObject(self.options.video[0])) {
self.resolutions = Ox.sort(Ox.unique(
self.options.video.map(function(video) {
return video.resolution;
})
)).map(function(resolution) {
return {
id: resolution + '',
title: resolution + 'p',
checked: self.options.resolution == resolution
};
});
self.audioTracks = Ox.sort(Ox.unique(
self.options.video.map(function(video) {
return video.track;
})
)).map(function(track) {
return {
id: track,
title: Ox._(Ox.getLanguageNameByCode(video.track)),
checked: self.options.audioTrack == track
};
});
}
self.options.layers.forEach(function(layer, i) { self.options.layers.forEach(function(layer, i) {
that.bindEvent('key_' + (i + 1), function() { that.bindEvent('key_' + (i + 1), function() {
layer.editable layer.editable
@ -350,6 +376,7 @@ Ox.VideoAnnotationPanel = function(options, self) {
showMilliseconds: 3, showMilliseconds: 3,
sizeIsLarge: self.options.videoSize == 'large', sizeIsLarge: self.options.videoSize == 'large',
subtitles: Ox.clone(self.options.subtitles, true), subtitles: Ox.clone(self.options.subtitles, true),
subtitlesDefaultTrack: self.options.subtitlesDefaultTrack,
type: type, type: type,
video: type == 'play' ? self.options.video : self.options.getFrameURL, video: type == 'play' ? self.options.video : self.options.getFrameURL,
volume: self.options.volume, volume: self.options.volume,
@ -487,16 +514,6 @@ Ox.VideoAnnotationPanel = function(options, self) {
} }
}); });
self.resolutions = [];
Ox.forEach(self.options.video, function(url, resolution) {
Ox.Log('Video', url, resolution);
self.resolutions.push({
id: resolution + '',
title: resolution + 'p',
checked: self.options.resolution == resolution
});
});
self.$keyboardShortcuts = $('<div>').css({margin: '16px'}); self.$keyboardShortcuts = $('<div>').css({margin: '16px'});
[ [
{key: Ox.UI.symbols.space, action: Ox._('Play/Pause')}, {key: Ox.UI.symbols.space, action: Ox._('Play/Pause')},
@ -564,16 +581,21 @@ Ox.VideoAnnotationPanel = function(options, self) {
{id: 'size', title: Ox._('Large Player'), checked: self.options.videoSize == 'large'}, {id: 'size', title: Ox._('Large Player'), checked: self.options.videoSize == 'large'},
{id: 'loop', title: Ox._('Loop'), checked: self.options.loop, keyboard: 'l'}, {id: 'loop', title: Ox._('Loop'), checked: self.options.loop, keyboard: 'l'},
{}, {},
{id: 'resolutions', title: Ox._('Resolution'), disabled: true}, {id: 'resolutions', title: Ox._('Resolution'), items: [
{group: 'resolution', min: 1, max: 1, items: self.resolutions} {group: 'resolution', min: 1, max: 1, items: self.resolutions}
]}
], ],
self.audioTracks.length > 1 ? [
{id: 'audioTracks', title: Ox._('Audio'), items: [
{group: 'audioTrack', min: 1, max: 1, items: self.audioTracks}
]}
] : [],
self.options.subtitles.length ? [ self.options.subtitles.length ? [
{}, // TODO...
{id: 'subtitles', title: Ox._('Show Subtitles'), checked: self.options.enableSubtitles} {id: 'subtitles', title: Ox._('Show Subtitles'), checked: self.options.enableSubtitles}
] : [], ] : [],
[ [
{}, {id: 'timelines', title: Ox._('Timeline'), items: [
{id: 'timelines', title: Ox._('Timeline'), disabled: true},
{group: 'timeline', min: 1, max: 1, items: Ox.map( {group: 'timeline', min: 1, max: 1, items: Ox.map(
self.options.timelines, self.options.timelines,
function(timeline) { function(timeline) {
@ -581,7 +603,8 @@ Ox.VideoAnnotationPanel = function(options, self) {
checked: timeline.id == self.options.timeline checked: timeline.id == self.options.timeline
}, timeline); }, timeline);
} }
)}, )}
]},
{}, {},
{id: 'gotoPosterFrame', title: Ox._('Go to Poster Frame'), keyboard: 'shift p'}, {id: 'gotoPosterFrame', title: Ox._('Go to Poster Frame'), keyboard: 'shift p'},
{id: 'setPosterFrame', title: Ox._('Set Poster Frame'), disabled: !self.options.enableSetPosterFrame}, {id: 'setPosterFrame', title: Ox._('Set Poster Frame'), disabled: !self.options.enableSetPosterFrame},
@ -650,7 +673,10 @@ Ox.VideoAnnotationPanel = function(options, self) {
} else if (id == 'resolution') { } else if (id == 'resolution') {
self.options.resolution = parseInt(data.checked[0].id, 10); self.options.resolution = parseInt(data.checked[0].id, 10);
self.$player[0].options({resolution: self.options.resolution}); self.$player[0].options({resolution: self.options.resolution});
} else if (id == 'audioTrack') {
// ...
} else if (id == 'subtitles') { } else if (id == 'subtitles') {
// TODO...
toggleSubtitles(); toggleSubtitles();
} else if (id == 'timeline') { } else if (id == 'timeline') {
self.options.timeline = data.checked[0].id; self.options.timeline = data.checked[0].id;
@ -1132,7 +1158,14 @@ Ox.VideoAnnotationPanel = function(options, self) {
id: subtitle.id, id: subtitle.id,
'in': subtitle['in'], 'in': subtitle['in'],
out: subtitle.out, out: subtitle.out,
text: subtitle.value.replace(/\n/g, ' ').replace(/<br\/?>/g, '\n') text: subtitle.value.replace(/\n/g, ' ').replace(/<br\/?>/g, '\n'),
tracks: (
subtitle.languages
? subtitle.languages
: [self.options.subtitleDefaultTrack]
).map(function(language) {
return Ox.getLanguageNameByCode(language);
})
}; };
}) : []; }) : [];
} }

View file

@ -8,6 +8,8 @@ Ox.VideoPlayer <f> Generic Video Player
in <n> In point (sec) in <n> In point (sec)
out <n> Out point (sec) out <n> Out point (sec)
text <s> Text text <s> Text
tracks <[s]> Track names, like "English" or "Director's Commentary"
audioTrack <s|''> Two-letter ISO 639-1 language code or track name
censored <a|[]> Array of censored ranges censored <a|[]> Array of censored ranges
censoredIcon <s|''> 'Censored' icon censoredIcon <s|''> 'Censored' icon
censoredTooltip <s|''> Tooltip for 'censored' icon censoredTooltip <s|''> Tooltip for 'censored' icon
@ -68,14 +70,17 @@ Ox.VideoPlayer <f> Generic Video Player
in <n> In point (sec) in <n> In point (sec)
out <n> Out point (sec) out <n> Out point (sec)
text <s> Text text <s> Text
tracks <[s]> Track names, like "English" or "Director's Commentary"
subtitlesDefaultTrack <s|'en'> Default subtitle language (ISO 639-1) or track name
subtitlesTrack <s|'en'> Subtitle language (ISO 639-1) or track name
timeline <s> Timeline image URL timeline <s> Timeline image URL
timelineType <s|''> Current timeline type id timelineType <s|''> Current timeline type id
timelineTypes <[o]|[]> Array of timeline type objects (id and title) timelineTypes <[o]|[]> Array of timeline type objects (id and title)
title <s|''> Video title title <s|''> Video title
type <s|'play'> 'play', 'in' or 'out' type <s|'play'> 'play', 'in' or 'out'
video <s|[s]|o|''> Video URL video <s|[s]|[o]|''> Video URL
String or array of strings ([part1, part2, ...]) or object String or array of strings ([part1, part2, ...]) or array of objects
({resolution: url, ...} or {resolution: [part1, part2, ...], ...}) ({duration, index, resolution, src, track})
volume <n|1> Volume (0-1) volume <n|1> Volume (0-1)
width <n|256> Width in px width <n|256> Width in px
self <o> shared private variable self <o> shared private variable
@ -111,6 +116,7 @@ Ox.VideoPlayer = function(options, self) {
var that = Ox.Element({}, self) var that = Ox.Element({}, self)
.defaults({ .defaults({
annotations: [], annotations: [],
audioTrack: '',
brightness: 1, brightness: 1,
censored: [], censored: [],
censoredIcon: '', censoredIcon: '',
@ -160,6 +166,8 @@ Ox.VideoPlayer = function(options, self) {
showProgress: false, showProgress: false,
sizeIsLarge: false, sizeIsLarge: false,
subtitles: [], subtitles: [],
subtitlesDefaultTrack: 'en',
subtitlesTrack: 'en',
timeline: '', timeline: '',
timelineType: '', timelineType: '',
timelineTypes: [], timelineTypes: [],
@ -171,10 +179,8 @@ Ox.VideoPlayer = function(options, self) {
}) })
.options(options || {}) .options(options || {})
.update({ .update({
enableSubtitles: function() { audioTrack: setAudioTrack,
self.options.enableSubtitles = !self.options.enableSubtitles; enableSubtitles: setSubtitleTrack,
toggleSubtitles();
},
find: setSubtitleText, find: setSubtitleText,
fullscreen: function() { fullscreen: function() {
self.options.fullscreen = !self.options.fullscreen; self.options.fullscreen = !self.options.fullscreen;
@ -215,6 +221,7 @@ Ox.VideoPlayer = function(options, self) {
loadSubtitles(); loadSubtitles();
self.$settings && self.$settings.replaceWith(self.$settings = renderSettings()); self.$settings && self.$settings.replaceWith(self.$settings = renderSettings());
}, },
subtitlesTrack: setSubtitlesTrack,
timeline: function() { timeline: function() {
self.$timeline.options({imageURL: self.options.timeline}); self.$timeline.options({imageURL: self.options.timeline});
}, },
@ -231,8 +238,6 @@ Ox.VideoPlayer = function(options, self) {
}) })
.addClass('OxVideoPlayer'); .addClass('OxVideoPlayer');
Ox.Log('Video', 'VIDEO PLAYER OPTIONS', self.options)
Ox.UI.$window.on({ Ox.UI.$window.on({
resize: function() { resize: function() {
self.options.fullscreen && setSizes(); self.options.fullscreen && setSizes();
@ -250,6 +255,10 @@ Ox.VideoPlayer = function(options, self) {
self.options.annotations = self.options.subtitles; self.options.annotations = self.options.subtitles;
} }
if (Ox.isObject(self.options.video[0]) && 'index' in self.options.video[0]) {
self.options.video = Ox.sortBy(self.options.video, 'index');
}
setVideo(); setVideo();
if (self.options.playInToOut) { if (self.options.playInToOut) {
@ -369,20 +378,20 @@ Ox.VideoPlayer = function(options, self) {
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
*/ */
if ( if ((
(!self.options.externalControls && !self.options.externalControls && (
(self.options.controlsTop.length || self.options.controlsBottom.length)) self.options.controlsTop.length
) { || self.options.controlsBottom.length
)
)) {
that.on({ that.on({
mouseenter: function() { mouseenter: function() {
showControls(); showControls();
self.mouseHasLeft = false; self.mouseHasLeft = false;
//Ox.Log('Video', 'MOUSE HAS ENTERED')
}, },
mouseleave: function() { mouseleave: function() {
hideControls(); hideControls();
self.mouseHasLeft = true; self.mouseHasLeft = true;
//Ox.Log('Video', 'MOUSE HAS LEFT')
} }
}); });
} }
@ -396,7 +405,9 @@ Ox.VideoPlayer = function(options, self) {
self.$videoContainer = Ox.Element() self.$videoContainer = Ox.Element()
.addClass('OxVideoContainer') .addClass('OxVideoContainer')
.css({ .css({
top: self.options.externalControls && self.options.controlsTop.length ? '16px' : 0 top: self.options.externalControls
&& self.options.controlsTop.length
? '16px' : 0
}) })
.appendTo(that) .appendTo(that)
@ -528,8 +539,8 @@ Ox.VideoPlayer = function(options, self) {
.hide() .hide()
.appendTo(self.$videoContainer); .appendTo(self.$videoContainer);
if (!Ox.isEmpty( if (!Ox.isEmpty(
Ox.isObject(self.options.video) Ox.isObject(self.options.video[0])
? self.options.video[self.options.resolution] ? getVideo()
: self.options.video : self.options.video
)) { )) {
showLoadingIcon(); showLoadingIcon();
@ -631,7 +642,9 @@ Ox.VideoPlayer = function(options, self) {
self['$controls' + titleCase] = Ox.Bar({ self['$controls' + titleCase] = Ox.Bar({
size: self.barHeight size: self.barHeight
}) })
.addClass('OxControls' + (self.options.externalControls ? '' : ' OxOnScreen')) .addClass('OxControls' + (
self.options.externalControls ? '' : ' OxOnScreen'
))
.css({ .css({
opacity: self.options.externalControls ? 1 : 0 opacity: self.options.externalControls ? 1 : 0
}) })
@ -679,7 +692,9 @@ Ox.VideoPlayer = function(options, self) {
self.$fullscreenButton = Ox.Button({ self.$fullscreenButton = Ox.Button({
style: 'video', style: 'video',
tooltip: [Ox._('Enter Fullscreen'), Ox._('Exit Fullscreen')], tooltip: [
Ox._('Enter Fullscreen'), Ox._('Exit Fullscreen')
],
type: 'image', type: 'image',
value: self.options.fullscreen ? 'shrink' : 'grow', value: self.options.fullscreen ? 'shrink' : 'grow',
values: ['grow', 'shrink'] values: ['grow', 'shrink']
@ -696,7 +711,10 @@ Ox.VideoPlayer = function(options, self) {
self.$setButton = Ox.Button({ self.$setButton = Ox.Button({
style: 'video', style: 'video',
title: 'goTo' + Ox.toTitleCase(self.options.type), title: 'goTo' + Ox.toTitleCase(self.options.type),
tooltip: Ox._('Go to ' + Ox.toTitleCase(self.options.type) + ' Point'), tooltip: Ox._(
'Go to ' + Ox.toTitleCase(self.options.type)
+ ' Point'
),
type: 'image' type: 'image'
}) })
.bindEvent({ .bindEvent({
@ -801,9 +819,11 @@ Ox.VideoPlayer = function(options, self) {
self.positionWidth = getPositionWidth(); self.positionWidth = getPositionWidth();
self.$position = Ox.Element({ self.$position = Ox.Element({
tooltip: Ox._(self.options.type == 'play' ? 'Position' tooltip: Ox._(
self.options.type == 'play' ? 'Position'
: self.options.type == 'in' ? 'In Point' : self.options.type == 'in' ? 'In Point'
: 'Out Point') : 'Out Point'
)
}) })
.addClass('OxPosition') .addClass('OxPosition')
.css({ .css({
@ -893,7 +913,10 @@ Ox.VideoPlayer = function(options, self) {
self.$setButton = Ox.Button({ self.$setButton = Ox.Button({
style: 'video', style: 'video',
title: 'set' + Ox.toTitleCase(self.options.type), title: 'set' + Ox.toTitleCase(self.options.type),
tooltip: Ox._('Set ' + Ox.toTitleCase(self.options.type) + ' Point'), tooltip: Ox._(
'Set ' + Ox.toTitleCase(self.options.type)
+ ' Point'
),
type: 'image' type: 'image'
}) })
.bindEvent({ .bindEvent({
@ -1276,9 +1299,12 @@ Ox.VideoPlayer = function(options, self) {
self.posterMarkerCSS = getPosterMarkerCSS(); self.posterMarkerCSS = getPosterMarkerCSS();
self.$video.css(self.videoCSS); self.$video.css(self.videoCSS);
self.$poster && self.$poster.css(self.videoCSS); self.$poster && self.$poster.css(self.videoCSS);
self.$posterMarker && Ox.forEach(self.$posterMarker, function(marker, position) { self.$posterMarker && Ox.forEach(
self.$posterMarker,
function(marker, position) {
marker.css(self.posterMarkerCSS[position]); marker.css(self.posterMarkerCSS[position]);
}); }
);
self.out = self.options.playInToOut && self.out < self.$video.duration() self.out = self.options.playInToOut && self.out < self.$video.duration()
? self.out : self.$video.duration(); ? self.out : self.$video.duration();
self.options.duration = self.out - self['in']; self.options.duration = self.out - self['in'];
@ -1482,7 +1508,8 @@ Ox.VideoPlayer = function(options, self) {
}; };
} else if (element == 'subtitle') { } else if (element == 'subtitle') {
css = { css = {
bottom: (Math.floor(self.height / 16) + !!self.controlsBottomAreVisible * 16) + 'px', bottom: (Math.floor(self.height / 16)
+ !!self.controlsBottomAreVisible * 16) + 'px',
width: self.width + 'px', width: self.width + 'px',
fontSize: Math.floor(self.height / 20) + 'px', fontSize: Math.floor(self.height / 20) + 'px',
WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)' WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)'
@ -1508,19 +1535,19 @@ Ox.VideoPlayer = function(options, self) {
return css; return css;
} }
function getMenuSection(str) {
var match = str.match(/<span class="(.+?)">/);
return match ? match[1] : null;
}
function getPosition(e) { function getPosition(e) {
// fixme: no offsetX in firefox??? // fixme: no offsetX in firefox???
if ($.browser.mozilla) { if ($.browser.mozilla) {
//Ox.Log('Video', e, e.layerX - 56)
return Ox.limit( return Ox.limit(
(e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(), (e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(),
0, self.$video.duration() 0, self.$video.duration()
); );
} else { } else {
/*Ox.Log('Video', e.offsetX, Ox.limit(
(e.offsetX - self.barHeight / 2) / self.timelineImageWidth * self.video.duration,
0, self.video.duration
))*/
return Ox.limit( return Ox.limit(
(e.offsetX - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(), (e.offsetX - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(),
0, self.$video.duration() 0, self.$video.duration()
@ -1560,7 +1587,6 @@ Ox.VideoPlayer = function(options, self) {
} }
function getProgressImageURL() { function getProgressImageURL() {
//Ox.Log('Video', '---', self.timelineImageWidth)
if (!self.timelineImageWidth) return; if (!self.timelineImageWidth) return;
var width = self.timelineImageWidth, var width = self.timelineImageWidth,
height = self.barHeight, height = self.barHeight,
@ -1595,6 +1621,7 @@ Ox.VideoPlayer = function(options, self) {
if ( if (
v['in'] <= self.options.position v['in'] <= self.options.position
&& v.out >= self.options.position && v.out >= self.options.position
&& v.track == self.options.subtitlesTrack
) { ) {
subtitle = v.text; subtitle = v.text;
return false; // break return false; // break
@ -1603,6 +1630,14 @@ Ox.VideoPlayer = function(options, self) {
return subtitle; return subtitle;
} }
function getSubtitles() {
return self.options.enableSubtitles
? self.options.subtitles.map(function(v) {
return v.track == self.options.subtitlesTrack;
})
: [];
}
function getTimeline() { function getTimeline() {
var $timeline = Ox.SmallVideoTimeline({ var $timeline = Ox.SmallVideoTimeline({
//_offset: getTimelineLeft(), //_offset: getTimelineLeft(),
@ -1634,7 +1669,6 @@ Ox.VideoPlayer = function(options, self) {
}); });
} }
}); });
//Ox.Log('Video', '??', $timeline.find('.OxInterface'))
$timeline.children().css({ $timeline.children().css({
marginLeft: getTimelineLeft() + 'px' marginLeft: getTimelineLeft() + 'px'
}); });
@ -1668,8 +1702,8 @@ Ox.VideoPlayer = function(options, self) {
} }
function getTitleWidth() { function getTitleWidth() {
return (self.options.fullscreen ? window.innerWidth : self.options.width) - return (self.options.fullscreen ? window.innerWidth : self.options.width)
self.options.controlsTop.reduce(function(prev, curr) { - self.options.controlsTop.reduce(function(prev, curr) {
return prev + ( return prev + (
curr == 'title' || curr == 'chapterTitle' || curr == 'space' ? 0 curr == 'title' || curr == 'chapterTitle' || curr == 'space' ? 0
: Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5)) : Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5))
@ -1678,6 +1712,13 @@ Ox.VideoPlayer = function(options, self) {
}, 0); }, 0);
} }
function getVideo() {
return self.options.video.filter(function(video) {
return video.audioTrack == self.options.audioTrack
&& video.resolution == self.options.resolution;
});
}
function getVideoCSS(videoWidth, videoHeight) { function getVideoCSS(videoWidth, videoHeight) {
// optional arguments allow for this function to be used for poster CSS // optional arguments allow for this function to be used for poster CSS
var playerWidth = self.width, var playerWidth = self.width,
@ -1758,7 +1799,11 @@ Ox.VideoPlayer = function(options, self) {
//Ox.Log('Video', 'hideControls'); //Ox.Log('Video', 'hideControls');
clearTimeout(self.interfaceTimeout); clearTimeout(self.interfaceTimeout);
self.interfaceTimeout = setTimeout(function() { self.interfaceTimeout = setTimeout(function() {
if (!self.exitFullscreen && !self.inputHasFocus && !self.mouseIsInControls) { if (
!self.exitFullscreen
&& !self.inputHasFocus
&& !self.mouseIsInControls
) {
self.interfaceIsVisible = false; self.interfaceIsVisible = false;
self.controlsTopAreVisible = false; self.controlsTopAreVisible = false;
self.controlsBottomAreVisible = false; self.controlsBottomAreVisible = false;
@ -1812,10 +1857,6 @@ Ox.VideoPlayer = function(options, self) {
return Math.abs(a - b) < 0.001; return Math.abs(a - b) < 0.001;
} }
function isResolution(str) {
return str.slice(0, -1).match(/^\d+$/) && str.slice(-1) == 'p';
}
function itemchange(data) { function itemchange(data) {
var item = self.$video.options('items')[data.item]; var item = self.$video.options('items')[data.item];
Ox.Log('Video', 'ITEMCHANGE', item); Ox.Log('Video', 'ITEMCHANGE', item);
@ -1856,9 +1897,12 @@ Ox.VideoPlayer = function(options, self) {
self.posterMarkerCSS = getPosterMarkerCSS(); self.posterMarkerCSS = getPosterMarkerCSS();
self.$video.css(self.videoCSS); self.$video.css(self.videoCSS);
self.$poster && self.$poster.css(self.videoCSS); self.$poster && self.$poster.css(self.videoCSS);
self.$posterMarker && Ox.forEach(self.$posterMarker, function(marker, position) { self.$posterMarker && Ox.forEach(
self.$posterMarker,
function(marker, position) {
marker.css(self.posterMarkerCSS[position]); marker.css(self.posterMarkerCSS[position]);
}); }
);
self.out = self.options.playInToOut && self.out < self.$video.duration() self.out = self.options.playInToOut && self.out < self.$video.duration()
? self.out : self.$video.duration(); ? self.out : self.$video.duration();
@ -1899,6 +1943,15 @@ Ox.VideoPlayer = function(options, self) {
} }
function loadedsubtitles() { function loadedsubtitles() {
if (!self.subtitlesTracks || Ox.isEmpty(self.subtitlesTracks)) {
self.subtitlesTracks = [{
code: self.options.subtitlesDefaultTrack,
name: Ox.getLanguageNameByCode(
self.options.subtitlesDefaultTrack
)
}];
}
self.subtitlesTracks.push({code: '', name: 'None'});
if (self.options.find) { if (self.options.find) {
submitFindInput(self.options.find); submitFindInput(self.options.find);
if (self.options.duration) { if (self.options.duration) {
@ -1917,6 +1970,21 @@ Ox.VideoPlayer = function(options, self) {
function loadSubtitles() { function loadSubtitles() {
if (self.options.subtitles) { if (self.options.subtitles) {
if (Ox.isArray(self.options.subtitles)) { if (Ox.isArray(self.options.subtitles)) {
self.subtitlesTracks = Ox.sortBy(
Ox.unique(
self.options.subtitles.map(function(subtitle) {
return subtitle.track;
}).filter(function(track) {
return !!track;
})
).map(function(track) {
return {
code: track,
name: Ox.getLanguageNameByCode(track)
};
}),
'name'
);
loadedsubtitles(); loadedsubtitles();
} else { } else {
if (self.options.subtitles.indexOf('\n') > -1) { if (self.options.subtitles.indexOf('\n') > -1) {
@ -1982,24 +2050,32 @@ Ox.VideoPlayer = function(options, self) {
} }
function renderSettings() { function renderSettings() {
// fixme: use proper ids (as class of span)
var $settings = $('<div>') var $settings = $('<div>')
.addClass('OxControls OxSettings') .addClass('OxControls OxSettings')
.on({ .on({
click: function(e) { click: function(e) {
var $target = $(e.target), resolution, title, type; var $target = $(e.target), resolution, title, track, type;
self.$settings.hide(); self.$settings.hide();
if (!$target.is('.OxLine') && !$target.is('.OxSpace')) { if (!$target.is('.OxLine') && !$target.is('.OxSpace')) {
title = $(e.target).parent().children()[0].innerHTML; title = $(e.target).parent().children()[0].innerHTML;
if (title == Ox._('Download')) { if (title == Ox._('Download')) {
that.triggerEvent('download'); that.triggerEvent('download');
} else if (title == Ox._('Subtitles')) { } else if (getMenuSection(title) == 'resolution') {
toggleSubtitles(); resolution = parseInt(Ox.stripTags(title), 10);
} else if (isResolution(title)) {
resolution = parseInt(title, 10);
if (resolution != self.options.resolution) { if (resolution != self.options.resolution) {
self.options.resolution = resolution; self.options.resolution = resolution;
setResolution(); setResolution();
} }
} else if (getMenuSection(title) == 'audioTrack') {
track = Ox.stripTags(title);
self.options.audioTrack = Ox.getLanguageCodeByName(track);
setAudioTrack();
} else if (getMenuSection(title) == 'subtitlesTrack') {
track = Ox.stripTags(title);
self.options.subtitlesTrack = track == 'None'
? '' : Ox.getLanguageCodeByName(track);
setSubtitleTrack();
} else { } else {
type = self.options.timelineTypes[ type = self.options.timelineTypes[
Ox.indexOf(self.options.timelineTypes, function(type) { Ox.indexOf(self.options.timelineTypes, function(type) {
@ -2043,27 +2119,43 @@ Ox.VideoPlayer = function(options, self) {
self.resolutions.map(function(resolution) { self.resolutions.map(function(resolution) {
return { return {
checked: resolution == self.options.resolution, checked: resolution == self.options.resolution,
title: resolution + 'p' title: '<span class="resolution">'
+ resolution + 'p</span>'
}; };
}), }),
self.options.subtitles.length self.audioTracks.length > 1
? [{}, { ? [{}, {disabled: true, title: Ox._('Audio')}].concat(
checked: self.options.enableSubtitles, self.audioTracks.map(function(track) {
title: Ox._('Subtitles')
}]
: [],
self.options.timelineTypes.length
? [{}, {
disabled: true,
title: Ox._('Timeline')
}] : [],
self.options.timelineTypes.length
? self.options.timelineTypes.map(function(type) {
return { return {
checked: type.id == self.options.timelineType, checked: track.code == self.options.audioTrack,
title: type.title title: '<span class="audioTrack">'
+ Ox._(track.name) + '</span>'
}; };
}) })
)
: [],
self.options.subtitles.length
? [{}, {disabled: true, title: Ox._('Subtitles')}].concat(
self.subtitlesTracks.map(function(track) {
return {
checked: track.code == self.options.enableSubtitles
? self.options.subtitlesTrack : '',
title: '<span class="subtitlesTrack">'
+ Ox._(track.name) + '</span>'
};
})
)
: [],
self.options.timelineTypes.length
? [{}, {disabled: true, title: Ox._('Timeline')}].concat(
self.options.timelineTypes.map(function(type) {
return {
checked: type.id == self.options.timelineType,
title: '<span class="timeline">'
+ type.title + '</span>'
};
})
)
: [], : [],
self.options.enableDownload self.options.enableDownload
? [{}, {title: Ox._('Download')}] ? [{}, {title: Ox._('Download')}]
@ -2130,6 +2222,10 @@ Ox.VideoPlayer = function(options, self) {
} }
} }
function setAudioTrack() {
// ...
}
function setCensored() { function setCensored() {
var censored = getCensored(); var censored = getCensored();
if (censored != self.censored) { if (censored != self.censored) {
@ -2152,15 +2248,17 @@ Ox.VideoPlayer = function(options, self) {
isEqual(self.options.position, self.options.posterFrame) isEqual(self.options.position, self.options.posterFrame)
? marker.show() : marker.hide(); ? marker.show() : marker.hide();
}); });
self.$pointMarker && Ox.forEach(self.$pointMarker, function(markers, point) { self.$pointMarker && Ox.forEach(
self.$pointMarker,
function(markers, point) {
Ox.forEach(markers, function(marker) { Ox.forEach(markers, function(marker) {
//Ox.Log('Video', self.options.position, self.options[point], isEqual(self.options.position, self.options[point]))
// fixme: there's a bug in jquery and/or webkit // fixme: there's a bug in jquery and/or webkit
// on load, show() doesn't work // on load, show() doesn't work
isEqual(self.options.position, self.options[point]) isEqual(self.options.position, self.options[point])
? marker.css({display: 'block'}) : marker.hide(); ? marker.css({display: 'block'}) : marker.hide();
}); });
}); }
);
} }
function setPoint() { function setPoint() {
@ -2206,7 +2304,7 @@ Ox.VideoPlayer = function(options, self) {
} }
self.loadedMetadata = false; self.loadedMetadata = false;
showLoadingIcon(); showLoadingIcon();
self.video = self.options.video[self.options.resolution]; self.video = getVideo();
self.$video.options({ self.$video.options({
items: self.video items: self.video
}); });
@ -2230,8 +2328,10 @@ Ox.VideoPlayer = function(options, self) {
} }
function setSizes(animate, callback) { function setSizes(animate, callback) {
self.width = self.options.fullscreen ? window.innerWidth : self.options.width; self.width = self.options.fullscreen
self.height = self.options.fullscreen ? window.innerHeight : self.options.height; ? window.innerWidth : self.options.width;
self.height = self.options.fullscreen
? window.innerHeight : self.options.height;
self.videoCSS = getVideoCSS(); self.videoCSS = getVideoCSS();
self.iconSize = Ox.limit(Math.round(self.height / 10), 16, 32); self.iconSize = Ox.limit(Math.round(self.height / 10), 16, 32);
if (self.$timeline || self.$spaceBottom) { if (self.$timeline || self.$spaceBottom) {
@ -2293,24 +2393,59 @@ Ox.VideoPlayer = function(options, self) {
function setSubtitleText() { function setSubtitleText() {
self.$subtitle.html( self.$subtitle.html(
self.subtitle self.subtitle
? Ox.highlight(self.subtitle, self.options.find, 'OxHighlight', true) ? Ox.highlight(
.replace(/\n/g, '<br/>') self.subtitle, self.options.find, 'OxHighlight', true
).replace(/\n/g, '<br/>')
: '&nbsp;<br/>&nbsp;' : '&nbsp;<br/>&nbsp;'
// FIXME: weird bug, only in fullscreen, only in chrome // FIXME: weird bug, only in fullscreen, only in chrome
); );
} }
function setSubtitlesTrack() {
var enableSubtitles = !!self.options.subtitlesTrack;
setSubtitle();
self.$timeline && self.$timeline.options({
subtitles: getSubtitles()
});
if (enableSubtitles != self.options.enableSubtitles) {
self.options.enableSubtitles = enableSubtitles;
that.triggerEvent('subtitles', {
subtitles: self.options.enableSubtitles
});
}
}
function setTimelineType() { function setTimelineType() {
that.triggerEvent('timeline', {timeline: self.options.timelineType}); that.triggerEvent('timeline', {timeline: self.options.timelineType});
} }
function setVideo() { function setVideo() {
if (Ox.isObject(self.options.video)) { if (Ox.isObject(self.options.video[0])) {
self.resolutions = Ox.sort(Object.keys(self.options.video)); self.audioTracks = Ox.sortBy(Ox.unique(
if (!(self.options.resolution in self.options.video)) { self.options.video.map(function(video) {
return {
code: video.track,
name: Ox.getLanguageNameByCode(video.track)
};
})
), 'name');
if (!Ox.contains(
self.audioTracks.map(function(track) {
return track.code;
}),
self.options.audioTrack
)) {
self.options.audioTrack = self.audioTracks[0].code;
}
self.resolutions = Ox.sort(Ox.unique(
self.options.video.map(function(video) {
return video.resolution;
})
));
if (!Ox.contains(self.resolutions, self.options.resolution)) {
self.options.resolution = self.resolutions[0]; self.options.resolution = self.resolutions[0];
} }
self.video = self.options.video[self.options.resolution]; self.video = getVideo();
} else { } else {
self.video = self.options.video; self.video = self.options.video;
} }
@ -2578,7 +2713,10 @@ Ox.VideoPlayer = function(options, self) {
self.playInToOut = false; self.playInToOut = false;
} else { } else {
hidePoster(); hidePoster();
if (self.options.playInToOut && self.options.position > self.options.out - self.secondsPerFrame) { if (
self.options.playInToOut
&& self.options.position > self.options.out - self.secondsPerFrame
) {
setPosition(self.options['in']); setPosition(self.options['in']);
} }
self.$video.play(); self.$video.play();
@ -2639,17 +2777,6 @@ Ox.VideoPlayer = function(options, self) {
}); });
} }
function toggleSubtitles() {
self.options.enableSubtitles = !self.options.enableSubtitles;
setSubtitle();
self.$timeline && self.$timeline.options({
subtitles: self.options.enableSubtitles ? self.options.subtitles : []
});
that.triggerEvent('subtitles', {
subtitles: self.options.enableSubtitles
});
}
function toggleVolume() { function toggleVolume() {
self.$volume.toggle(); self.$volume.toggle();
} }

View file

@ -74,6 +74,7 @@ Ox.VideoPlayerPanel = function(options, self) {
showUsers: false, showUsers: false,
smallTimelineURL: '', smallTimelineURL: '',
subtitles: [], subtitles: [],
subtitlesDefaultTrack: 'en',
timeline: '', timeline: '',
timelineTooltip: 'timeline', timelineTooltip: 'timeline',
video: '', video: '',
@ -227,6 +228,7 @@ Ox.VideoPlayerPanel = function(options, self) {
resolution: self.options.resolution, resolution: self.options.resolution,
scaleToFill: self.options.scaleToFill, scaleToFill: self.options.scaleToFill,
subtitles: self.options.subtitles, subtitles: self.options.subtitles,
subtitlesDefaultTrack: self.options.subtitlesDefaultTrack,
timeline: self.options.smallTimelineURL, timeline: self.options.smallTimelineURL,
video: self.options.video, video: self.options.video,
volume: self.options.volume, volume: self.options.volume,