phantasmobile/app/static/js/edits.js
2024-02-17 10:59:22 +00:00

266 lines
8.4 KiB
JavaScript

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) {
var aValue, bValue, index = 0, key, ret = 0;
while (ret == 0 && index < by.length) {
key = by[index].key;
aValue = getSortValue(a[key])
bValue = getSortValue(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;
});
};
async function sortClips(edit, sort) {
var key = sort.key, index;
if (key == 'position') {
key = 'in';
}
if ([
'id', 'index', 'in', 'out', 'duration',
'title', 'director', 'year', 'videoRatio'
].indexOf(key) > -1) {
sortBy(sort);
index = 0;
edit.clips.forEach(function(clip) {
clip.sort = index++;
if (sort.operator == '-') {
clip.sort = -clip.sort;
}
});
} else {
var response = await pandoraAPI('sortClips', {
edit: edit.id,
sort: [sort]
})
edit.clips.forEach(function(clip) {
clip.sort = response.data.clips.indexOf(clip.id);
if (sort.operator == '-') {
clip.sort = -clip.sort;
}
});
sortBy({
key: 'sort',
operator: '+'
});
}
function sortBy(key) {
edit.clips = sortByKey(edit.clips, [key]);
}
}
function getClip(edit, position) {
const response = {}
let pos = 0
edit.clips.forEach(function(clip) {
if (clip.position < position && clip.position + clip.duration > position) {
response.item = clip.item
response.position = position - clip.position
if (clip['in']) {
response.position += clip['in']
}
}
});
return response
}
async function loadEdit(id, args) {
var data = window.data = {}
data.id = id
data.site = pandora.hostname
var response = await pandoraAPI('getEdit', {
id: data.id,
keys: [
]
})
if (response.status.code != 200) {
return {
site: data.site,
error: response.status
}
}
data.edit = response['data']
if (data.edit.status !== 'public') {
return {
site: data.site,
error: {
code: 403,
text: 'permission denied'
}
}
}
data.layers = {}
data.videos = []
if (args.sort) {
await sortClips(data.edit, args.sort)
}
data.edit.duration = 0;
data.edit.clips.forEach(function(clip) {
clip.position = data.edit.duration;
data.edit.duration += clip.duration;
});
data.edit.clips.forEach(clip => {
var start = clip['in'] || 0, end = clip.out, position = 0;
clip.durations.forEach((duration, idx) => {
if (!duration) {
return
}
if (position + duration <= start || position > end) {
// pass
} else {
var video = {}
var oshash = clip.streams[idx]
video.src = getVideoURL(clip.item, pandora.resolution, idx+1, '', oshash)
/*
if (clip['in'] && clip.out) {
video.src += `#t=${clip['in']},${clip.out}`
}
*/
if (isNumber(clip.volume)) {
video.volume = clip.volume;
}
if (
position <= start
&& position + duration > start
) {
video['in'] = start - position;
}
if (position + duration >= end) {
video.out = end - position;
}
if (video['in'] && video.out) {
video.duration = video.out - video['in']
} else if (video.out) {
video.duration = video.out;
} else if (!isUndefined(video['in'])) {
video.duration = duration - video['in'];
video.out = duration;
} else {
video.duration = duration;
video['in'] = 0;
video.out = video.duration;
}
data.videos.push(video)
}
position += duration
})
Object.keys(clip.layers).forEach(layer => {
clip.layers[layer].forEach(annotation => {
if (args.users && !args.users.includes(annotation.user)) {
return
}
if (args.layers && !args.layers.includes(layer)) {
return
}
var a = {...annotation}
a['id'] = clip['id'] + '/' + a['id'];
a['in'] = Math.max(
clip['position'],
a['in'] - clip['in'] + clip['position']
);
a.out = Math.min(
clip['position'] + clip['duration'],
a.out - clip['in'] + clip['position']
);
data.layers[layer] = data.layers[layer] || []
data.layers[layer].push(a)
})
})
})
if (data.layers[pandora.subtitleLayer]) {
var previous;
data.layers[pandora.subtitleLayer].forEach(annotation => {
if (previous) {
previous.out = annotation['in']
}
previous = annotation
})
}
var value = []
pandora.layerKeys.forEach(layer => {
if (!data.layers[layer]) {
return
}
var html = []
var layerData = getObjectById(pandora.site.layers, layer)
html.push(`<h3>
<span class="icon">${icon.down}</span>
${layerData.title}
</h3>`)
data.layers[layer].forEach(annotation => {
html.push(`
<div class="annotation ${layerData.type}" data-in="${annotation.in}" data-out="${annotation.out}">
${annotation.value}
</div>
`)
})
var layerClass = ""
if (layerData.isSubtitles) {
layerClass = " is-subtitles"
}
value.push('<div class="layer'+layerClass+'">' + html.join('\n') + '</div>')
})
data.value = value.join('\n')
data.title = data.edit.name
data.byline = data.edit.description
data.link = `${pandora.proto}://${data.site}/edits/${data.edit.id}`
let poster = data.edit.posterFrames[0]
if (args.parts[2] && args.parts[2].indexOf(':') > -1) {
poster = getClip(data.edit, parseDuration(args.parts[2]))
}
if (poster && poster.item) {
data.poster = `${pandora.proto}://${data.site}/${poster.item}/${pandora.resolution}p${poster.position.toFixed(3)}.jpg`
} else {
data.poster = data.videos[0].src.split('/48')[0] + `/${pandora.resolution}p${data.videos[0].in.toFixed(3)}.jpg`
}
data.aspectratio = data.edit.clips[0].videoRatio
data.duration = data.edit.duration
return data
}