lbry-desktop/ui/component/viewers/videoViewer/internal/chapters.jsx

209 lines
5.7 KiB
React
Raw Normal View History

2022-05-02 10:55:08 +02:00
// @flow
import { platform } from 'util/platform';
2022-05-02 10:55:08 +02:00
const REQUIRED_DELAY_FOR_IOS_MS = 10;
const MIN_SECONDS_BETWEEN_CHAPTERS = 10;
const MIN_CHAPTERS = 3;
type TimestampData = Array<{ seconds: number, label: string }>;
function isValidTimestamp(str: string) {
let isValidTimestamp;
switch (str.length) {
case 4: // "9:59"
isValidTimestamp = /^[0-9]:[0-5][0-9]$/.test(str);
break;
case 5: // "59:59"
isValidTimestamp = /^[0-5][0-9]:[0-5][0-9]$/.test(str);
break;
case 7: // "9:59:59"
isValidTimestamp = /^[0-9]:[0-5][0-9]:[0-5][0-9]$/.test(str);
break;
case 8: // "99:59:59"
isValidTimestamp = /^[0-9][0-9]:[0-5][0-9]:[0-5][0-9]$/.test(str);
break;
default:
// Reject
isValidTimestamp = false;
break;
}
return isValidTimestamp;
}
function timestampStrToSeconds(ts: string) {
const parts = ts.split(':').reverse();
let seconds = 0;
for (let i = 0; i < parts.length; ++i) {
const part = parts[i];
const parsed = parseInt(part);
if (!isNaN(parsed)) {
seconds += parsed * Math.pow(60, i);
}
}
return seconds;
}
function parse(claim: StreamClaim) {
// - Must have at least 3 timestamps.
// - First one must be 0:00.
// - Chapters must be at least 10 seconds apart.
// - Format: one per line, "0:00 Blah..."
const description = claim?.value?.description;
if (!description) {
return null;
}
const lines = description.split('\n');
const timestamps = [];
lines.forEach((line) => {
if (line.length > 0) {
const splitIndex = line.indexOf(' ');
if (splitIndex >= 0 && splitIndex < line.length - 2) {
const ts = line.substring(0, splitIndex);
2022-05-04 19:08:51 +02:00
const label = line.substring(splitIndex + 1);
2022-05-02 10:55:08 +02:00
if (ts && label && isValidTimestamp(ts)) {
const seconds = timestampStrToSeconds(ts);
if (timestamps.length !== 0) {
const prevSeconds = timestamps[timestamps.length - 1].seconds;
if (seconds - prevSeconds < MIN_SECONDS_BETWEEN_CHAPTERS) {
return null;
}
} else {
if (seconds !== 0) {
return null;
}
}
timestamps.push({ seconds, label });
}
}
}
});
return timestamps.length >= MIN_CHAPTERS ? timestamps : null;
}
function overrideHoverTooltip(player: any, tsData: TimestampData, duration: number) {
try {
const timeTooltip = player
.getChild('controlBar')
.getChild('progressControl')
.getChild('seekBar')
.getChild('mouseTimeDisplay')
.getChild('timeTooltip');
Reuse videojs instance between video reload, return mobile UI plugin for iOS (#1512) * add mobile plugin back on ios * further touchups and fix ios * finish mobile functionality * dont show big play button on mobile * remove logs * proof of concept * dont go full screen on rotate * add back functionality * replace dispose event with navigate away * bugfix * turn off show if you liked button and nag only on homepage * add back old functionality * ending event not working * test here * working but needs cleanup * more player touchups * bugfix * add settings button on mobile * more touchups * more cleanups * touchup loading functionality * fix hover thumbnails * touchup and eslint fix * fix repopulation bug * change recsys event name * bugfix events * change the way buttons are removed and added * finish chapters button * refactor to use videojs methods * refactor to fix autoplay next * ux touchups * seems to be behaving properly * control bar behaving how it should * fix control bar on ios * working on flow and eslint errors * bugfix and flow fixes * bring back nudge * fix playlist button bug * remove chapter markers properly * show big play button * bugfix recsys closed event * fix analytics bug * fix embeds * bugfix * possible bugfix for kp * bugfix playlist buttons * fix issue with mobile ui plugin * fix firefox autoplay issue * fix bug for play on floating player closed * bugfix volume control for ios * instantiate new player if switching between claim types * fix flow and lint errors * fix control bar not showing up when switching sources * dispose old player if recreating * bugfix save position * reset recsys data between videos * fix audio upload posters * clear claimSrcVhs on reload * bugfix errant image previews showing up * reset player value of having already switched quality * fix watch position not being used * bugfix switching between sources not perserving position * fix save position bug * fix playlist buttons * bugfix * code cleanup and add back 5 second feature
2022-06-10 18:18:58 +02:00
// sometimes old 'right' rule is persisted and messes up styling
timeTooltip.el().style.removeProperty('right');
2022-05-02 10:55:08 +02:00
timeTooltip.update = function (seekBarRect, seekBarPoint, time) {
const values = Object.values(tsData);
// $FlowIssue: mixed
const seconds = values.map((v) => v.seconds);
const curSeconds = timestampStrToSeconds(time);
let i = 0;
for (; i < seconds.length; ++i) {
const s0 = seconds[i];
const s1 = i === seconds.length - 1 ? duration : seconds[i + 1];
if (curSeconds >= s0 && curSeconds < s1) {
break;
}
}
if (i < seconds.length) {
// $FlowIssue: mixed
this.write(`${time} - ${values[i].label}`);
} else {
console.error('Chapters: oob ' + player?.claim?.name);
this.write(time);
}
};
} catch {}
}
function load(player: any, timestampData: TimestampData, duration: number) {
player.one('loadedmetadata', () => {
const textTrack = player.addTextTrack('chapters', __('Chapters'), 'en');
2022-05-02 10:55:08 +02:00
setTimeout(() => {
const values = Object.values(timestampData);
values.forEach((ts, index) => {
// $FlowIssue: mixed
const start = ts.seconds;
// $FlowIssue: mixed
const end = index === values.length - 1 ? duration : values[index + 1].seconds;
// $FlowIssue: mixed
textTrack.addCue(new window.VTTCue(start, end, ts.label));
});
addMarkersOnProgressBar(
// $FlowIssue: mixed
values.map((v) => v.seconds),
duration
);
const chaptersButton = player?.controlBar?.chaptersButton;
if (chaptersButton) {
chaptersButton.update();
}
2022-05-02 10:55:08 +02:00
}, REQUIRED_DELAY_FOR_IOS_MS);
});
}
Reuse videojs instance between video reload, return mobile UI plugin for iOS (#1512) * add mobile plugin back on ios * further touchups and fix ios * finish mobile functionality * dont show big play button on mobile * remove logs * proof of concept * dont go full screen on rotate * add back functionality * replace dispose event with navigate away * bugfix * turn off show if you liked button and nag only on homepage * add back old functionality * ending event not working * test here * working but needs cleanup * more player touchups * bugfix * add settings button on mobile * more touchups * more cleanups * touchup loading functionality * fix hover thumbnails * touchup and eslint fix * fix repopulation bug * change recsys event name * bugfix events * change the way buttons are removed and added * finish chapters button * refactor to use videojs methods * refactor to fix autoplay next * ux touchups * seems to be behaving properly * control bar behaving how it should * fix control bar on ios * working on flow and eslint errors * bugfix and flow fixes * bring back nudge * fix playlist button bug * remove chapter markers properly * show big play button * bugfix recsys closed event * fix analytics bug * fix embeds * bugfix * possible bugfix for kp * bugfix playlist buttons * fix issue with mobile ui plugin * fix firefox autoplay issue * fix bug for play on floating player closed * bugfix volume control for ios * instantiate new player if switching between claim types * fix flow and lint errors * fix control bar not showing up when switching sources * dispose old player if recreating * bugfix save position * reset recsys data between videos * fix audio upload posters * clear claimSrcVhs on reload * bugfix errant image previews showing up * reset player value of having already switched quality * fix watch position not being used * bugfix switching between sources not perserving position * fix save position bug * fix playlist buttons * bugfix * code cleanup and add back 5 second feature
2022-06-10 18:18:58 +02:00
function deleteHoverInformation(player) {
try {
const timeTooltip = player
.getChild('controlBar')
.getChild('progressControl')
.getChild('seekBar')
.getChild('mouseTimeDisplay')
.getChild('timeTooltip');
delete timeTooltip.update;
} catch {}
}
2022-05-02 10:55:08 +02:00
export function parseAndLoad(player: any, claim: StreamClaim) {
console.assert(claim, 'null claim');
if (platform.isMobile()) {
return;
}
2022-05-02 10:55:08 +02:00
const tsData = parse(claim);
const duration = claim?.value?.video?.duration || claim?.value?.audio?.duration;
if (tsData && duration) {
load(player, tsData, duration);
overrideHoverTooltip(player, tsData, duration);
Reuse videojs instance between video reload, return mobile UI plugin for iOS (#1512) * add mobile plugin back on ios * further touchups and fix ios * finish mobile functionality * dont show big play button on mobile * remove logs * proof of concept * dont go full screen on rotate * add back functionality * replace dispose event with navigate away * bugfix * turn off show if you liked button and nag only on homepage * add back old functionality * ending event not working * test here * working but needs cleanup * more player touchups * bugfix * add settings button on mobile * more touchups * more cleanups * touchup loading functionality * fix hover thumbnails * touchup and eslint fix * fix repopulation bug * change recsys event name * bugfix events * change the way buttons are removed and added * finish chapters button * refactor to use videojs methods * refactor to fix autoplay next * ux touchups * seems to be behaving properly * control bar behaving how it should * fix control bar on ios * working on flow and eslint errors * bugfix and flow fixes * bring back nudge * fix playlist button bug * remove chapter markers properly * show big play button * bugfix recsys closed event * fix analytics bug * fix embeds * bugfix * possible bugfix for kp * bugfix playlist buttons * fix issue with mobile ui plugin * fix firefox autoplay issue * fix bug for play on floating player closed * bugfix volume control for ios * instantiate new player if switching between claim types * fix flow and lint errors * fix control bar not showing up when switching sources * dispose old player if recreating * bugfix save position * reset recsys data between videos * fix audio upload posters * clear claimSrcVhs on reload * bugfix errant image previews showing up * reset player value of having already switched quality * fix watch position not being used * bugfix switching between sources not perserving position * fix save position bug * fix playlist buttons * bugfix * code cleanup and add back 5 second feature
2022-06-10 18:18:58 +02:00
} else {
deleteHoverInformation(player);
// $FlowIssue
player?.controlBar?.getChild('ChaptersButton')?.hide();
2022-05-02 10:55:08 +02:00
}
}
function addMarkersOnProgressBar(chapterStartTimes: Array<number>, videoDuration: number) {
const progressControl = document.getElementsByClassName('vjs-progress-holder vjs-slider vjs-slider-horizontal')[0];
if (!progressControl) {
console.error('Failed to find progress-control');
return;
}
for (let i = 0; i < chapterStartTimes.length; ++i) {
const elem = document.createElement('div');
// $FlowIssue
elem['className'] = 'vjs-chapter-marker';
// $FlowIssue
elem['id'] = 'chapter' + i;
elem.style.left = `${(chapterStartTimes[i] / videoDuration) * 100}%`;
progressControl.appendChild(elem);
}
}