Add marker videojs plugin (#2447)

This commit is contained in:
WithoutPants 2022-03-31 08:14:39 +11:00 committed by GitHub
parent df982f7528
commit 510bec655b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 2 deletions

View File

@ -6,6 +6,7 @@ import "videojs-seek-buttons";
import "videojs-landscape-fullscreen"; import "videojs-landscape-fullscreen";
import "./live"; import "./live";
import "./PlaylistButtons"; import "./PlaylistButtons";
import "./markers";
import cx from "classnames"; import cx from "classnames";
import * as GQL from "src/core/generated-graphql"; import * as GQL from "src/core/generated-graphql";
@ -136,6 +137,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
volumePanel: { volumePanel: {
inline: false, inline: false,
}, },
chaptersButton: false,
}, },
nativeControlsForTouch: false, nativeControlsForTouch: false,
playbackRates: [0.75, 1, 1.5, 2, 3, 4], playbackRates: [0.75, 1, 1.5, 2, 3, 4],
@ -160,6 +162,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
}, },
}); });
(player as any).markers();
(player as any).offset(); (player as any).offset();
player.focus(); player.focus();
@ -270,6 +273,12 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
// otherwise, the offset will be applied to the next file when // otherwise, the offset will be applied to the next file when
// currentTime is called. // currentTime is called.
(player as any).clearOffsetDuration(); (player as any).clearOffsetDuration();
const tracks = player.remoteTextTracks();
if (tracks.length > 0) {
player.removeRemoteTextTrack(tracks[0] as any);
}
player.src( player.src(
scene.sceneStreams.map((stream) => ({ scene.sceneStreams.map((stream) => ({
src: stream.url, src: stream.url,
@ -277,6 +286,18 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
label: stream.label ?? undefined, label: stream.label ?? undefined,
})) }))
); );
if (scene.paths.chapters_vtt) {
player.addRemoteTextTrack(
{
src: scene.paths.chapters_vtt,
kind: "chapters",
default: true,
},
true
);
}
player.currentTime(0); player.currentTime(0);
player.loop( player.loop(

View File

@ -0,0 +1,107 @@
import videojs, { VideoJsPlayer } from "video.js";
const markers = function (this: VideoJsPlayer) {
const player = this;
function getPosition(marker: VTTCue) {
return (marker.startTime / player.duration()) * 100;
}
function createMarkerToolTip() {
const tooltip = videojs.dom.createEl("div") as HTMLElement;
tooltip.className = "vjs-marker-tooltip";
return tooltip;
}
function removeMarkerToolTip() {
const div = player
.el()
.querySelector(".vjs-progress-holder .vjs-marker-tooltip");
if (div) div.remove();
}
function createMarkerDiv(marker: VTTCue) {
const markerDiv = videojs.dom.createEl(
"div",
{},
{
"data-marker-time": marker.startTime,
}
) as HTMLElement;
markerDiv.className = "vjs-marker";
markerDiv.style.left = getPosition(marker) + "%";
// bind click event to seek to marker time
markerDiv.addEventListener("click", function () {
const time = this.getAttribute("data-marker-time");
player.currentTime(Number(time));
});
// show tooltip on hover
markerDiv.addEventListener("mouseenter", function () {
// create and show tooltip
const tooltip = createMarkerToolTip();
tooltip.innerText = marker.text;
const parent = player
.el()
.querySelector(".vjs-progress-holder .vjs-mouse-display");
parent?.appendChild(tooltip);
// hide default tooltip
const defaultTooltip = parent?.querySelector(
".vjs-time-tooltip"
) as HTMLElement;
defaultTooltip.style.visibility = "hidden";
});
markerDiv.addEventListener("mouseout", function () {
removeMarkerToolTip();
// show default tooltip
const defaultTooltip = player
.el()
.querySelector(
".vjs-progress-holder .vjs-mouse-display .vjs-time-tooltip"
) as HTMLElement;
if (defaultTooltip) defaultTooltip.style.visibility = "visible";
});
return markerDiv;
}
function removeMarkerDivs() {
const divs = player
.el()
.querySelectorAll(".vjs-progress-holder .vjs-marker");
divs.forEach((div) => {
div.remove();
});
}
this.on("loadedmetadata", function () {
removeMarkerDivs();
removeMarkerToolTip();
const textTracks = player.remoteTextTracks();
const seekBar = player.el().querySelector(".vjs-progress-holder");
if (seekBar && textTracks.length > 0) {
const vttTrack = textTracks[0];
if (!vttTrack || !vttTrack.cues) return;
for (let i = 0; i < vttTrack.cues.length; i++) {
const cue = vttTrack.cues[i];
const markerDiv = createMarkerDiv(cue as VTTCue);
seekBar.appendChild(markerDiv);
}
}
});
};
// Register the plugin with video.js.
videojs.registerPlugin("markers", markers);
export default markers;

View File

@ -153,8 +153,9 @@ $sceneTabWidth: 450px;
font-size: 10em; font-size: 10em;
} }
.jwplayer { .vjs-progress-control .vjs-play-progress .vjs-time-tooltip,
outline: none; .vjs-progress-control:hover .vjs-play-progress .vjs-time-tooltip {
visibility: hidden;
} }
} }
@ -393,3 +394,43 @@ $sceneTabWidth: 450px;
display: inline-block; display: inline-block;
} }
} }
.vjs-marker {
background-color: rgba(33, 33, 33, 0.8);
bottom: 0;
height: 100%;
left: 0;
opacity: 1;
position: absolute;
-webkit-transition: opacity 0.2s ease;
-moz-transition: opacity 0.2s ease;
transition: opacity 0.2s ease;
width: 6px;
z-index: 100;
&:hover {
cursor: pointer;
-webkit-transform: scale(1.3, 1.3);
-moz-transform: scale(1.3, 1.3);
-o-transform: scale(1.3, 1.3);
-ms-transform: scale(1.3, 1.3);
transform: scale(1.3, 1.3);
}
}
.vjs-marker-tooltip {
border-radius: 0.3em;
color: white;
display: block;
float: right;
font-family: Arial, Helvetica, sans-serif;
font-size: 10px;
height: 50px;
padding: 6px 8px 8px 8px;
pointer-events: none;
position: absolute;
right: -80px;
top: -5.4em;
width: 160px;
z-index: 1;
}