mirror of https://github.com/stashapp/stash.git
Add marker videojs plugin (#2447)
This commit is contained in:
parent
df982f7528
commit
510bec655b
|
@ -6,6 +6,7 @@ import "videojs-seek-buttons";
|
|||
import "videojs-landscape-fullscreen";
|
||||
import "./live";
|
||||
import "./PlaylistButtons";
|
||||
import "./markers";
|
||||
import cx from "classnames";
|
||||
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
|
@ -136,6 +137,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||
volumePanel: {
|
||||
inline: false,
|
||||
},
|
||||
chaptersButton: false,
|
||||
},
|
||||
nativeControlsForTouch: false,
|
||||
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.focus();
|
||||
|
@ -270,6 +273,12 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||
// otherwise, the offset will be applied to the next file when
|
||||
// currentTime is called.
|
||||
(player as any).clearOffsetDuration();
|
||||
|
||||
const tracks = player.remoteTextTracks();
|
||||
if (tracks.length > 0) {
|
||||
player.removeRemoteTextTrack(tracks[0] as any);
|
||||
}
|
||||
|
||||
player.src(
|
||||
scene.sceneStreams.map((stream) => ({
|
||||
src: stream.url,
|
||||
|
@ -277,6 +286,18 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||
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.loop(
|
||||
|
|
|
@ -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;
|
|
@ -153,8 +153,9 @@ $sceneTabWidth: 450px;
|
|||
font-size: 10em;
|
||||
}
|
||||
|
||||
.jwplayer {
|
||||
outline: none;
|
||||
.vjs-progress-control .vjs-play-progress .vjs-time-tooltip,
|
||||
.vjs-progress-control:hover .vjs-play-progress .vjs-time-tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,3 +394,43 @@ $sceneTabWidth: 450px;
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue