merge fixes from pandora
This commit is contained in:
parent
7459299bf2
commit
fcf7c24c23
7 changed files with 154 additions and 91 deletions
|
|
@ -8,6 +8,7 @@ VideoElement <f> VideoElement Object
|
|||
loop <b|false> loop playback
|
||||
playbackRate <n|1> playback rate
|
||||
position <n|0> start position
|
||||
in <n|0> start offset
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:Element> VideoElement Object
|
||||
loadedmetadata <!> loadedmetadata
|
||||
|
|
@ -38,6 +39,7 @@ window.VideoElement = function(options) {
|
|||
muted: false,
|
||||
playbackRate: 1,
|
||||
position: 0,
|
||||
"in": 0,
|
||||
volume: 1
|
||||
}
|
||||
Object.assign(self.options, options);
|
||||
|
|
@ -166,9 +168,10 @@ window.VideoElement = function(options) {
|
|||
|
||||
function getCurrentTime() {
|
||||
var item = self.items[self.currentItem];
|
||||
return self.seeking || self.loading
|
||||
var currentTime = self.seeking || self.loading
|
||||
? self.currentTime
|
||||
: item ? item.position + self.video.currentTime - item['in'] : 0;
|
||||
: item ? item.position + self.video.currentTime - item['in'] - self.options["in"] : 0;
|
||||
return currentTime
|
||||
}
|
||||
|
||||
function getset(key, value) {
|
||||
|
|
@ -508,6 +511,7 @@ window.VideoElement = function(options) {
|
|||
}
|
||||
|
||||
function setCurrentItemTime(currentTime) {
|
||||
currentTime += self.options["in"]
|
||||
debug('Video', 'sCIT', currentTime, self.video.currentTime,
|
||||
'delta', currentTime - self.video.currentTime);
|
||||
isReady(self.video, function(video) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ window.VideoPlayer = function(options) {
|
|||
loop: false,
|
||||
muted: false,
|
||||
playbackRate: 1,
|
||||
"in": 0,
|
||||
position: 0,
|
||||
volume: 1
|
||||
}
|
||||
|
|
@ -74,7 +75,6 @@ window.VideoPlayer = function(options) {
|
|||
height: 32px;
|
||||
}
|
||||
.mx-controls .controls .position {
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
}
|
||||
.mx-controls .toggle svg {
|
||||
|
|
@ -154,9 +154,11 @@ window.VideoPlayer = function(options) {
|
|||
${icon.mute}
|
||||
</div>
|
||||
<div class="position">
|
||||
<div class="bar">
|
||||
<div class="progress"></div>
|
||||
|
||||
<div class="seekbar">
|
||||
<input type="range" value="0" min='0' max='100' step='.25' />
|
||||
<div class="seekbar-progress">
|
||||
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="38" style="width: 0%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time">
|
||||
|
|
@ -224,15 +226,31 @@ window.VideoPlayer = function(options) {
|
|||
}
|
||||
}
|
||||
var showControls
|
||||
function hideControlsLater() {
|
||||
if (showControls) {
|
||||
clearTimeout(showControls)
|
||||
}
|
||||
showControls = setTimeout(() => {
|
||||
if (touching) {
|
||||
hideControlsLater()
|
||||
} else {
|
||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||
showControls = null
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
var toggleControls = event => {
|
||||
if (event.target.tagName == "INPUT") {
|
||||
if (showControls) {
|
||||
clearTimeout(showControls)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (self.controls.style.opacity == '0') {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
self.controls.style.opacity = '1'
|
||||
showControls = setTimeout(() => {
|
||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||
showControls = null
|
||||
}, 3000)
|
||||
hideControlsLater()
|
||||
} else {
|
||||
self.controls.style.opacity = '0'
|
||||
}
|
||||
|
|
@ -242,10 +260,7 @@ window.VideoPlayer = function(options) {
|
|||
clearTimeout(showControls)
|
||||
}
|
||||
self.controls.style.opacity = '1'
|
||||
showControls = setTimeout(() => {
|
||||
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||
showControls = null
|
||||
}, 3000)
|
||||
hideControlsLater()
|
||||
})
|
||||
self.controls.addEventListener("mouseleave", event => {
|
||||
if (showControls) {
|
||||
|
|
@ -254,7 +269,13 @@ window.VideoPlayer = function(options) {
|
|||
self.controls.style.opacity = that.paused ? '1' : '0'
|
||||
showControls = null
|
||||
})
|
||||
self.controls.addEventListener("touchstart", event => {
|
||||
touching = true
|
||||
})
|
||||
self.controls.addEventListener("touchstart", toggleControls)
|
||||
self.controls.addEventListener("touchend", event => {
|
||||
touching = false
|
||||
})
|
||||
self.controls.querySelector('.toggle').addEventListener("click", toggleVideo)
|
||||
self.controls.querySelector('.volume').addEventListener("click", toggleSound)
|
||||
self.controls.querySelector('.fullscreen-btn').addEventListener("click", toggleFullscreen)
|
||||
|
|
@ -311,6 +332,7 @@ window.VideoPlayer = function(options) {
|
|||
that.append(unblock)
|
||||
})
|
||||
var loading = true
|
||||
var touching = false
|
||||
that.brightness(0)
|
||||
that.addEventListener("loadedmetadata", event => {
|
||||
//
|
||||
|
|
@ -332,45 +354,40 @@ window.VideoPlayer = function(options) {
|
|||
}
|
||||
})
|
||||
|
||||
var time = that.querySelector('.controls .time div'),
|
||||
progress = that.querySelector('.controls .position .progress')
|
||||
that.querySelector('.controls .position').addEventListener("click", event => {
|
||||
var bar = event.target
|
||||
if (bar && bar.classList.contains('position')) {
|
||||
bar = bar.querySelector('.bar')
|
||||
}
|
||||
while (bar && !bar.classList.contains('bar')) {
|
||||
bar = bar.parentElement
|
||||
}
|
||||
if (bar && bar.classList.contains('bar')) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
var rect = bar.getBoundingClientRect()
|
||||
var x = event.clientX - rect.x
|
||||
var percent = x / rect.width
|
||||
var position = percent * self.options.duration
|
||||
if (self.options.position) {
|
||||
position += self.options.position
|
||||
}
|
||||
progress.style.width = (100 * percent) + '%'
|
||||
that.currentTime(position)
|
||||
}
|
||||
var time = that.querySelector('.controls .time div');
|
||||
const progressbar = that.querySelector('.seekbar div[role="progressbar"]');
|
||||
function setProgressPosition(value) {
|
||||
progressbar.style.width = value + '%';
|
||||
progressbar.setAttribute('aria-valuenow', value);
|
||||
|
||||
}
|
||||
that.querySelector('.controls .position input').addEventListener('input', event => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setProgressPosition(event.target.value)
|
||||
var position = event.target.value/100 * self.options.duration
|
||||
displayTime(position)
|
||||
that.currentTime(position)
|
||||
hideControlsLater()
|
||||
})
|
||||
that.addEventListener("timeupdate", event => {
|
||||
var currentTime = that.currentTime(),
|
||||
duration = self.options.duration
|
||||
if (self.options.position) {
|
||||
currentTime -= self.options.position
|
||||
}
|
||||
progress.style.width = (100 * currentTime / duration) + '%'
|
||||
duration = formatDuration(duration)
|
||||
function displayTime(currentTime) {
|
||||
duration = formatDuration(self.options.duration)
|
||||
currentTime = formatDuration(currentTime)
|
||||
while (duration && duration.startsWith('00:')) {
|
||||
duration = duration.slice(3)
|
||||
}
|
||||
currentTime = currentTime.slice(currentTime.length - duration.length)
|
||||
time.innerText = `${currentTime} / ${duration}`
|
||||
}
|
||||
|
||||
that.addEventListener("timeupdate", event => {
|
||||
var currentTime = that.currentTime(),
|
||||
duration = self.options.duration
|
||||
if (self.options.position) {
|
||||
currentTime -= self.options.position
|
||||
}
|
||||
setProgressPosition(100 * currentTime / duration)
|
||||
displayTime(currentTime)
|
||||
})
|
||||
|
||||
that.addEventListener("play", event => {
|
||||
|
|
|
|||
|
|
@ -1,35 +1,4 @@
|
|||
|
||||
const getSortValue = function(value) {
|
||||
var sortValue = value;
|
||||
function trim(value) {
|
||||
return value.replace(/^\W+(?=\w)/, '');
|
||||
}
|
||||
if (
|
||||
isEmpty(value)
|
||||
|| isNull(value)
|
||||
|| isUndefined(value)
|
||||
) {
|
||||
sortValue = null;
|
||||
} else if (isString(value)) {
|
||||
// make lowercase and remove leading non-word characters
|
||||
sortValue = trim(value.toLowerCase());
|
||||
// move leading articles to the end
|
||||
// and remove leading non-word characters
|
||||
['a', 'an', 'the'].forEach(function(article) {
|
||||
if (new RegExp('^' + article + ' ').test(sortValue)) {
|
||||
sortValue = trim(sortValue.slice(article.length + 1))
|
||||
+ ', ' + sortValue.slice(0, article.length);
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
// remove thousand separators and pad numbers
|
||||
sortValue = sortValue.replace(/(\d),(?=(\d{3}))/g, '$1')
|
||||
.replace(/\d+/g, function(match) {
|
||||
return match.padStart(64, '0')
|
||||
});
|
||||
}
|
||||
return sortValue;
|
||||
};
|
||||
|
||||
const sortByKey = function(array, by) {
|
||||
return array.sort(function(a, b) {
|
||||
|
|
@ -122,7 +91,7 @@ async function loadEdit(id, args) {
|
|||
}
|
||||
}
|
||||
data.edit = response['data']
|
||||
if (data.edit.status !== 'public') {
|
||||
if (['public', 'featured'].indexOf(data.edit.status) == -1) {
|
||||
return {
|
||||
site: data.site,
|
||||
error: {
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ async function loadData(id, args) {
|
|||
<span class="icon">${icon.down}</span>
|
||||
${layerData.title}
|
||||
</h3>`)
|
||||
data.layers[layer] = sortBy(data.layers[layer], [
|
||||
{key: "in", operator: "+"},
|
||||
{key: "created", operator: "+"}
|
||||
])
|
||||
data.layers[layer].forEach(annotation => {
|
||||
if (pandora.url) {
|
||||
annotation.value = annotation.value.replace(
|
||||
|
|
@ -137,9 +141,13 @@ async function loadData(id, args) {
|
|||
/href="\//g, `href="${pandora.url.origin}/`
|
||||
)
|
||||
}
|
||||
let content = annotation.value
|
||||
if (!layerData.isSubtitles && layerData.type == "text" && args.show && args.show.includes("user")) {
|
||||
content += `\n<div class="user">— ${annotation.user}</div>`
|
||||
}
|
||||
html.push(`
|
||||
<div class="annotation ${layerData.type}" data-in="${annotation.in}" data-out="${annotation.out}">
|
||||
${annotation.value}
|
||||
${content}
|
||||
</div>
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function parseURL() {
|
|||
var kv = arg.split('=')
|
||||
k = kv.shift()
|
||||
v = kv.join('=')
|
||||
if (['users', 'layers'].includes(k)) {
|
||||
if (['users', 'layers', 'show'].includes(k)) {
|
||||
v = v.split(',')
|
||||
}
|
||||
return [k, v]
|
||||
|
|
@ -67,6 +67,7 @@ function parseURL() {
|
|||
id = id.replace('/editor/', '/').replace('/player/', '/')
|
||||
type = "item"
|
||||
}
|
||||
//console.log(type, id, args)
|
||||
return [type, id, args]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ function renderItem(data) {
|
|||
var video = window.video = VideoPlayer({
|
||||
items: data.videos,
|
||||
poster: data.poster,
|
||||
position: data["in"] || 0,
|
||||
"in": data["in"] || 0,
|
||||
position: 0,
|
||||
duration: data.duration,
|
||||
aspectratio: data.aspectratio
|
||||
})
|
||||
|
|
@ -85,16 +86,10 @@ function renderItem(data) {
|
|||
video.addEventListener("loadedmetadata", event => {
|
||||
//
|
||||
})
|
||||
video.addEventListener("timeupdate", event => {
|
||||
var currentTime = video.currentTime()
|
||||
if (currentTime >= data['out']) {
|
||||
if (!video.paused) {
|
||||
video.pause()
|
||||
}
|
||||
video.currentTime(data['in'])
|
||||
}
|
||||
|
||||
function updateAnnotations(currentTime) {
|
||||
div.querySelectorAll('.annotation').forEach(annot => {
|
||||
var now = currentTime
|
||||
var now = currentTime + (data["in"] || 0)
|
||||
var start = parseFloat(annot.dataset.in)
|
||||
var end = parseFloat(annot.dataset.out)
|
||||
if (now >= start && now <= end) {
|
||||
|
|
@ -107,8 +102,18 @@ function renderItem(data) {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
video.addEventListener("timeupdate", event => {
|
||||
var currentTime = video.currentTime()
|
||||
if ((currentTime + (data["in"] || 0)) >= data['out']) {
|
||||
if (!video.paused) {
|
||||
video.pause()
|
||||
}
|
||||
video.currentTime(0)
|
||||
}
|
||||
updateAnnotations(currentTime)
|
||||
})
|
||||
updateAnnotations(data["position"] || 0)
|
||||
if (item.next || item.previous) {
|
||||
var nav = document.createElement('nav')
|
||||
nav.classList.add('items')
|
||||
|
|
|
|||
|
|
@ -125,7 +125,10 @@ const clickLink = function(event) {
|
|||
}
|
||||
document.location.hash = '#' + link.slice(1)
|
||||
} else {
|
||||
if (!link.startsWith('/m')) {
|
||||
if (link.includes('/download/')) {
|
||||
document.location.href = link
|
||||
return
|
||||
} else if (!link.startsWith('/m')) {
|
||||
link = '/m' + link
|
||||
}
|
||||
history.pushState({}, '', link);
|
||||
|
|
@ -161,3 +164,59 @@ const getVideoURL = function(id, resolution, part, track, streamId) {
|
|||
return prefix + '/' + getVideoURLName(id, resolution, part, track, streamId);
|
||||
};
|
||||
|
||||
const getSortValue = function(value) {
|
||||
var sortValue = value;
|
||||
function trim(value) {
|
||||
return value.replace(/^\W+(?=\w)/, '');
|
||||
}
|
||||
if (
|
||||
isEmpty(value)
|
||||
|| isNull(value)
|
||||
|| isUndefined(value)
|
||||
) {
|
||||
sortValue = null;
|
||||
} else if (isString(value)) {
|
||||
// make lowercase and remove leading non-word characters
|
||||
sortValue = trim(value.toLowerCase());
|
||||
// move leading articles to the end
|
||||
// and remove leading non-word characters
|
||||
['a', 'an', 'the'].forEach(function(article) {
|
||||
if (new RegExp('^' + article + ' ').test(sortValue)) {
|
||||
sortValue = trim(sortValue.slice(article.length + 1))
|
||||
+ ', ' + sortValue.slice(0, article.length);
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
// remove thousand separators and pad numbers
|
||||
sortValue = sortValue.replace(/(\d),(?=(\d{3}))/g, '$1')
|
||||
.replace(/\d+/g, function(match) {
|
||||
return match.padStart(64, '0')
|
||||
});
|
||||
}
|
||||
return sortValue;
|
||||
};
|
||||
|
||||
function sortBy(array, by, map) {
|
||||
return array.sort(function(a, b) {
|
||||
var aValue, bValue, index = 0, key, ret = 0;
|
||||
while (ret == 0 && index < by.length) {
|
||||
key = by[index].key;
|
||||
aValue = getSortValue(
|
||||
map && map[key] ? map[key](a[key], a) : a[key]
|
||||
);
|
||||
bValue = getSortValue(
|
||||
map && map[key] ? map[key](b[key], b) : b[key]
|
||||
);
|
||||
if ((aValue === null) != (bValue === null)) {
|
||||
ret = aValue === null ? 1 : -1;
|
||||
} else if (aValue < bValue) {
|
||||
ret = by[index].operator == '+' ? -1 : 1;
|
||||
} else if (aValue > bValue) {
|
||||
ret = by[index].operator == '+' ? 1 : -1;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue