182 lines
5.8 KiB
JavaScript
182 lines
5.8 KiB
JavaScript
|
import Plyr from '../vendor/plyr-3.7.3.mjs';
|
||
|
|
||
|
const container = document.getElementById('container');
|
||
|
|
||
|
function displayError(message) {
|
||
|
const errorBox = document.createElement('div');
|
||
|
errorBox.classList.add('error');
|
||
|
errorBox.innerText = message;
|
||
|
container.appendChild(errorBox);
|
||
|
throw new Error(); // abort
|
||
|
}
|
||
|
|
||
|
function loadPlaybackState(videoUrl) {
|
||
|
const item = localStorage.getItem('wfm__playerstate');
|
||
|
if (item) {
|
||
|
const data = JSON.parse(item);
|
||
|
if (Object.prototype.hasOwnProperty.call(data, videoUrl)) {
|
||
|
return data[videoUrl];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
time: 0,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function savePlaybackState(videoUrl, state) {
|
||
|
const item = localStorage.getItem('wfm__playerstate');
|
||
|
let data;
|
||
|
if (item) {
|
||
|
data = JSON.parse(item);
|
||
|
} else {
|
||
|
data = {};
|
||
|
}
|
||
|
data[videoUrl] = state;
|
||
|
localStorage.setItem('wfm__playerstate', JSON.stringify(data));
|
||
|
}
|
||
|
|
||
|
function parseQuery() {
|
||
|
if (window.location.hash.length > 1) {
|
||
|
const qs = window.location.hash.substring(1);
|
||
|
const query = new URLSearchParams(qs);
|
||
|
const source = query.get('source');
|
||
|
const sourceType = query.get('source_type');
|
||
|
const captions = query.getAll('captions');
|
||
|
const captionLanguages = query.getAll('caption_langs');
|
||
|
const captionLabels = query.getAll('caption_labels');
|
||
|
if (!source || !sourceType) {
|
||
|
displayError('missing source or sourceType parameter');
|
||
|
}
|
||
|
if (captions.length !== captionLanguages.length || captions.length !== captionLabels.length) {
|
||
|
displayError('mismatch between captions, caption_langs and caption_labels length');
|
||
|
}
|
||
|
return {
|
||
|
source, sourceType,
|
||
|
captions, captionLanguages, captionLabels,
|
||
|
}
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function parseTimestamp(timestamp) {
|
||
|
const results = /^(0|[1-9]+):([0-9]{2}):([0-9]{2}).([0-9]+)$/g.exec(timestamp);
|
||
|
const [_, hours, mins, secs, frac] = results;
|
||
|
return {
|
||
|
hours, mins, secs, frac,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function formatTimestamp({ hours, mins, secs, frac }) {
|
||
|
const ms = `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||
|
if (hours !== 0) {
|
||
|
return `${hours}:${ms}`;
|
||
|
} else {
|
||
|
return ms;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const query = parseQuery();
|
||
|
|
||
|
if (query !== null) {
|
||
|
const {
|
||
|
source, sourceType,
|
||
|
captions, captionLanguages, captionLabels
|
||
|
} = query;
|
||
|
const video = document.createElement('video');
|
||
|
video.controls = true;
|
||
|
video.crossOrigin = 'anonymous';
|
||
|
|
||
|
const sourceEle = document.createElement('source');
|
||
|
sourceEle.src = source;
|
||
|
sourceEle.type = sourceType;
|
||
|
video.appendChild(sourceEle);
|
||
|
|
||
|
for (let i = 0; i < captions.length; i++) {
|
||
|
const url = captions[i];
|
||
|
const lang = captionLanguages[i];
|
||
|
const label = captionLabels[i];
|
||
|
const track = document.createElement('track');
|
||
|
track.kind = 'subtitles';
|
||
|
track.label = label;
|
||
|
track.srclang = lang;
|
||
|
track.src = url;
|
||
|
video.appendChild(track);
|
||
|
}
|
||
|
|
||
|
video.autoplay = true;
|
||
|
|
||
|
container.appendChild(video);
|
||
|
|
||
|
const player = new Plyr(video, {
|
||
|
captions: {
|
||
|
active: true,
|
||
|
},
|
||
|
iconUrl: 'vendor/plyr-3.7.3.svg',
|
||
|
});
|
||
|
|
||
|
player.on('ready', () => {
|
||
|
const state = loadPlaybackState(source);
|
||
|
console.log('setting start time', state);
|
||
|
player.currentTime = state.time;
|
||
|
console.log(player.currentTime);
|
||
|
});
|
||
|
|
||
|
player.on('timeupdate', () => {
|
||
|
savePlaybackState(source, {
|
||
|
time: player.currentTime,
|
||
|
});
|
||
|
});
|
||
|
} else {
|
||
|
const BASE = '/videos/';
|
||
|
|
||
|
fetch(BASE+'media.json')
|
||
|
.then(res => res.json())
|
||
|
.then(({ videos }) => {
|
||
|
const ul = document.createElement('ul');
|
||
|
ul.classList.add('video-list');
|
||
|
for (const video of videos) {
|
||
|
//if (!video.filename.includes('telescope')) continue;
|
||
|
const query = [
|
||
|
['source', BASE+video.filename],
|
||
|
['source_type', 'video/mp4'],
|
||
|
...video.subtitles.map(subName => ['captions', BASE+subName]),
|
||
|
...video.subtitles.map(subName => {
|
||
|
const s = subName.split('.');
|
||
|
return ['caption_langs', s[s.length - 2]];
|
||
|
}),
|
||
|
...video.subtitles.map(subName => ['caption_labels', subName]),
|
||
|
];
|
||
|
const duration = parseTimestamp(video.duration);
|
||
|
const thumbnail = video.thumbnail;
|
||
|
const thumbnailContainer = document.createElement('div');
|
||
|
thumbnailContainer.classList.add('thumbnail-container');
|
||
|
const durationEle = document.createElement('p');
|
||
|
durationEle.classList.add('thumbnail-duration');
|
||
|
durationEle.innerText = formatTimestamp(duration);
|
||
|
const img = document.createElement('img');
|
||
|
img.src = BASE+thumbnail;
|
||
|
img.classList.add('thumbnail');
|
||
|
const qs = new URLSearchParams(query).toString();
|
||
|
const ele = document.createElement('li');
|
||
|
const a = document.createElement('a');
|
||
|
a.classList.add('video-card');
|
||
|
a.href = '#' + qs;
|
||
|
const title = document.createElement('h2');
|
||
|
title.innerText = video.filename;
|
||
|
thumbnailContainer.appendChild(img);
|
||
|
thumbnailContainer.appendChild(durationEle);
|
||
|
a.appendChild(thumbnailContainer);
|
||
|
a.appendChild(title);
|
||
|
ele.appendChild(a);
|
||
|
ul.appendChild(ele);
|
||
|
}
|
||
|
container.appendChild(ul);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
window.addEventListener('hashchange', (e) => {
|
||
|
window.location.reload();
|
||
|
});
|