Add source selector plugin (#2449)

This commit is contained in:
WithoutPants 2022-04-01 08:20:14 +11:00 committed by GitHub
parent 6c3b493323
commit d262d18f08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 4 deletions

View file

@ -6,6 +6,7 @@ import "videojs-seek-buttons";
import "videojs-landscape-fullscreen";
import "./live";
import "./PlaylistButtons";
import "./source-selector";
import "./persist-volume";
import "./markers";
import cx from "classnames";
@ -165,6 +166,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
(player as any).markers();
(player as any).offset();
(player as any).sourceSelector();
(player as any).persistVolume();
player.focus();

View file

@ -36,10 +36,17 @@ const offset = function (this: VideoJsPlayer) {
const srcUrl = new URL(this.src());
srcUrl.searchParams.delete("start");
srcUrl.searchParams.append("start", seconds.toString());
this.src({
src: srcUrl.toString(),
type: "video/webm",
});
const currentSrc = this.currentSource();
const newSources = this.currentSources().map(
(source: videojs.Tech.SourceObject) => {
return {
...source,
src:
source.src === currentSrc.src ? srcUrl.toString() : source.src,
};
}
);
this.src(newSources);
this.play();
return seconds;

View file

@ -0,0 +1,150 @@
import videojs, { VideoJsPlayer } from "video.js";
interface ISource extends videojs.Tech.SourceObject {
label?: string;
selected?: boolean;
sortIndex?: number;
}
const MenuButton = videojs.getComponent("MenuButton");
const MenuItem = videojs.getComponent("MenuItem");
class SourceMenuItem extends MenuItem {
private parent: SourceMenuButton;
public source: ISource;
public index: number;
constructor(
parent: SourceMenuButton,
source: ISource,
index: number,
player: VideoJsPlayer,
options: videojs.MenuItemOptions
) {
options.selectable = true;
options.multiSelectable = false;
super(player, options);
this.parent = parent;
this.source = source;
this.index = index;
}
handleClick() {
this.parent.trigger("selected", this);
}
}
class SourceMenuButton extends MenuButton {
createEl() {
return videojs.dom.createEl("div", {
className:
"vjs-source-selector vjs-menu-button vjs-menu-button-popup vjs-control vjs-button",
});
}
createItems() {
const player = this.player();
const menuButton = this;
// slice so that we don't alter the order of the original array
const sources = player.currentSources().slice() as ISource[];
sources.sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0));
const hasSelected = sources.some((source) => source.selected);
if (!hasSelected && sources.length > 0) {
sources[0].selected = true;
}
menuButton.on("selected", function (e, selectedItem) {
// don't do anything if re-selecting the same source
if (selectedItem.source.selected) {
return;
}
// populate source sortIndex first if not present
const currentSources = (player.currentSources() as ISource[]).map(
(src, i) => {
return {
...src,
sortIndex: src.sortIndex ?? i,
selected: false,
};
}
);
// put the selected source at the top of the list
const selectedIndex = currentSources.findIndex(
(src) => src.sortIndex === selectedItem.index
);
const selectedSrc = currentSources.splice(selectedIndex, 1)[0];
selectedSrc.selected = true;
currentSources.unshift(selectedSrc);
const currentTime = player.currentTime();
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
(player as any).clearOffsetDuration();
player.src(currentSources);
player.currentTime(currentTime);
player.play();
});
return sources.map((source, index) => {
const label = source.label || source.type;
const item = new SourceMenuItem(
menuButton,
source,
index,
this.player(),
{
label: label,
selected: source.selected || (!hasSelected && index === 0),
}
);
menuButton.on("selected", function (selectedItem) {
if (selectedItem !== item) {
item.selected(false);
}
});
item.addClass("vjs-source-menu-item");
return item;
});
}
}
const sourceSelector = function (this: VideoJsPlayer) {
const player = this;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const PlayerConstructor = this.constructor as any;
if (!PlayerConstructor.__sourceSelector) {
PlayerConstructor.__sourceSelector = {
selectSource: PlayerConstructor.prototype.selectSource,
};
}
videojs.registerComponent("SourceMenuButton", SourceMenuButton);
player.on("loadedmetadata", function () {
const { controlBar } = player;
const fullscreenToggle = controlBar.getChild("fullscreenToggle")!.el();
const existingMenuButton = controlBar.getChild("SourceMenuButton");
if (existingMenuButton) controlBar.removeChild(existingMenuButton);
const menuButton = controlBar.addChild("SourceMenuButton");
controlBar.el().insertBefore(menuButton.el(), fullscreenToggle);
});
};
// Register the plugin with video.js.
videojs.registerPlugin("sourceSelector", sourceSelector);
export default sourceSelector;

View file

@ -395,6 +395,17 @@ $sceneTabWidth: 450px;
}
}
.vjs-source-selector {
.vjs-menu li {
font-size: 12px;
}
.vjs-button > .vjs-icon-placeholder::before {
content: "\f110";
font-family: VideoJS;
}
}
.vjs-marker {
background-color: rgba(33, 33, 33, 0.8);
bottom: 0;