mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Fix videojs-vr issues (#3793)
* Add videojs-vr.d.ts * Improve dynamic VR toggling
This commit is contained in:
116
ui/v2.5/src/@types/videojs-vr.d.ts
vendored
Normal file
116
ui/v2.5/src/@types/videojs-vr.d.ts
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
declare module "videojs-vr" {
|
||||
import videojs from "video.js";
|
||||
|
||||
declare function videojsVR(options?: videojsVR.Options): videojsVR.Plugin;
|
||||
|
||||
declare namespace videojsVR {
|
||||
const VERSION: typeof videojs.VERSION;
|
||||
|
||||
type ProjectionType =
|
||||
// The video is half sphere and the user should not be able to look behind themselves
|
||||
| "180"
|
||||
// Used for side-by-side 180 videos The video is half sphere and the user should not be able to look behind themselves
|
||||
| "180_LR"
|
||||
// Used for monoscopic 180 videos The video is half sphere and the user should not be able to look behind themselves
|
||||
| "180_MONO"
|
||||
// The video is a sphere
|
||||
| "360"
|
||||
| "Sphere"
|
||||
| "equirectangular"
|
||||
// The video is a cube
|
||||
| "360_CUBE"
|
||||
| "Cube"
|
||||
// This video is not a 360 video
|
||||
| "NONE"
|
||||
// Check player.mediainfo.projection to see if the current video is a 360 video.
|
||||
| "AUTO"
|
||||
// Used for side-by-side 360 videos
|
||||
| "360_LR"
|
||||
// Used for top-to-bottom 360 videos
|
||||
| "360_TB"
|
||||
// Used for Equi-Angular Cubemap videos
|
||||
| "EAC"
|
||||
// Used for side-by-side Equi-Angular Cubemap videos
|
||||
| "EAC_LR";
|
||||
|
||||
interface Options {
|
||||
/**
|
||||
* Force the cardboard button to display on all devices even if we don't think they support it.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
forceCardboard?: boolean;
|
||||
|
||||
/**
|
||||
* Whether motion/gyro controls should be enabled.
|
||||
*
|
||||
* @default true on iOS and Android
|
||||
*/
|
||||
motionControls?: boolean;
|
||||
|
||||
/**
|
||||
* Defines the projection type.
|
||||
*
|
||||
* @default "AUTO"
|
||||
*/
|
||||
projection?: ProjectionType;
|
||||
|
||||
/**
|
||||
* This alters the number of segments in the spherical mesh onto which equirectangular videos are projected.
|
||||
* The default is 32 but in some circumstances you may notice artifacts and need to increase this number.
|
||||
*
|
||||
* @default 32
|
||||
*/
|
||||
sphereDetail?: number;
|
||||
|
||||
/**
|
||||
* Enable debug logging for this plugin
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Use this property to pass the Omnitone library object to the plugin. Please be aware of, the Omnitone library is not included in the build files.
|
||||
*/
|
||||
omnitone?: object;
|
||||
|
||||
/**
|
||||
* Default options for the Omnitone library. Please check available options on https://github.com/GoogleChrome/omnitone
|
||||
*/
|
||||
omnitoneOptions?: object;
|
||||
|
||||
/**
|
||||
* Feature to disable the togglePlay manually. This functionality is useful in live events so that users cannot stop the live, but still have a controlBar available.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
disableTogglePlay?: boolean;
|
||||
}
|
||||
|
||||
interface PlayerMediaInfo {
|
||||
/**
|
||||
* This should be set on a source-by-source basis to turn 360 videos on an off depending upon the video.
|
||||
* Note that AUTO is the same as NONE for player.mediainfo.projection.
|
||||
*/
|
||||
projection?: ProjectionType;
|
||||
}
|
||||
|
||||
class Plugin extends videojs.Plugin {
|
||||
setProjection(projection: ProjectionType): void;
|
||||
init(): void;
|
||||
reset(): void;
|
||||
}
|
||||
}
|
||||
|
||||
export = videojsVR;
|
||||
|
||||
declare module "video.js" {
|
||||
interface VideoJsPlayer {
|
||||
vr: typeof videojsVR;
|
||||
mediainfo?: videojsVR.PlayerMediaInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,16 +267,6 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
||||
|
||||
// Initialize VideoJS player
|
||||
useEffect(() => {
|
||||
function isVrScene() {
|
||||
if (!scene?.id || !vrTag) return false;
|
||||
|
||||
return scene?.tags.some((tag) => {
|
||||
if (vrTag == tag.name) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const options: VideoJsPlayerOptions = {
|
||||
id: VIDEO_PLAYER_ID,
|
||||
controls: true,
|
||||
@@ -330,9 +320,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
||||
},
|
||||
skipButtons: {},
|
||||
trackActivity: {},
|
||||
vrMenu: {
|
||||
showButton: isVrScene(),
|
||||
},
|
||||
vrMenu: {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -364,7 +352,8 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
||||
// reset sceneId to force reload sources
|
||||
sceneId.current = undefined;
|
||||
};
|
||||
}, [scene, vrTag]);
|
||||
// empty deps - only init once
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const player = getPlayer();
|
||||
@@ -388,6 +377,21 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
||||
scene?.paths.funscript,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const player = getPlayer();
|
||||
if (!player) return;
|
||||
|
||||
const vrMenu = player.vrMenu();
|
||||
|
||||
let showButton = false;
|
||||
|
||||
if (scene && vrTag) {
|
||||
showButton = scene.tags.some((tag) => vrTag === tag.name);
|
||||
}
|
||||
|
||||
vrMenu.setShowButton(showButton);
|
||||
}, [getPlayer, scene, vrTag]);
|
||||
|
||||
// Player event handlers
|
||||
useEffect(() => {
|
||||
const player = getPlayer();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import videojs, { VideoJsPlayer } from "video.js";
|
||||
import "videojs-vr";
|
||||
// separate type import, otherwise typescript elides the above import
|
||||
// and the plugin does not get initialized
|
||||
import type { ProjectionType, Plugin as VideoJsVRPlugin } from "videojs-vr";
|
||||
|
||||
export interface VRMenuOptions {
|
||||
/**
|
||||
@@ -15,7 +18,7 @@ enum VRType {
|
||||
Off = "Off",
|
||||
}
|
||||
|
||||
const vrTypeProjection = {
|
||||
const vrTypeProjection: Record<VRType, ProjectionType> = {
|
||||
[VRType.Spherical]: "360",
|
||||
[VRType.Off]: "NONE",
|
||||
};
|
||||
@@ -29,7 +32,7 @@ class VRMenuItem extends videojs.getComponent("MenuItem") {
|
||||
public isSelected = false;
|
||||
|
||||
constructor(parent: VRMenuButton, type: VRType) {
|
||||
const options = {} as videojs.MenuItemOptions;
|
||||
const options: videojs.MenuItemOptions = {};
|
||||
options.selectable = true;
|
||||
options.multiSelectable = false;
|
||||
options.label = type;
|
||||
@@ -105,27 +108,61 @@ class VRMenuButton extends videojs.getComponent("MenuButton") {
|
||||
|
||||
class VRMenuPlugin extends videojs.getPlugin("plugin") {
|
||||
private menu: VRMenuButton;
|
||||
private showButton: boolean;
|
||||
private vr?: VideoJsVRPlugin;
|
||||
|
||||
constructor(player: VideoJsPlayer, options: VRMenuOptions) {
|
||||
super(player);
|
||||
|
||||
this.menu = new VRMenuButton(player);
|
||||
this.showButton = options.showButton ?? false;
|
||||
|
||||
if (isVrDevice() || !options.showButton) return;
|
||||
if (isVrDevice()) return;
|
||||
|
||||
this.vr = this.player.vr();
|
||||
|
||||
this.menu.on("typeselected", (_, type: VRType) => {
|
||||
const projection = vrTypeProjection[type];
|
||||
player.vr({ projection });
|
||||
player.load();
|
||||
this.loadVR(type);
|
||||
});
|
||||
|
||||
player.on("ready", () => {
|
||||
const { controlBar } = player;
|
||||
const fullscreenToggle = controlBar.getChild("fullscreenToggle")!.el();
|
||||
controlBar.addChild(this.menu);
|
||||
controlBar.el().insertBefore(this.menu.el(), fullscreenToggle);
|
||||
if (this.showButton) {
|
||||
this.addButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private loadVR(type: VRType) {
|
||||
const projection = vrTypeProjection[type];
|
||||
this.vr?.setProjection(projection);
|
||||
this.vr?.init();
|
||||
}
|
||||
|
||||
private addButton() {
|
||||
const { controlBar } = this.player;
|
||||
const fullscreenToggle = controlBar.getChild("fullscreenToggle")!.el();
|
||||
controlBar.addChild(this.menu);
|
||||
controlBar.el().insertBefore(this.menu.el(), fullscreenToggle);
|
||||
}
|
||||
|
||||
private removeButton() {
|
||||
const { controlBar } = this.player;
|
||||
controlBar.removeChild(this.menu);
|
||||
}
|
||||
|
||||
public setShowButton(showButton: boolean) {
|
||||
if (isVrDevice()) return;
|
||||
|
||||
if (showButton === this.showButton) return;
|
||||
|
||||
this.showButton = showButton;
|
||||
if (showButton) {
|
||||
this.addButton();
|
||||
} else {
|
||||
this.removeButton();
|
||||
this.loadVR(VRType.Off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the plugin with video.js.
|
||||
@@ -136,7 +173,6 @@ videojs.registerPlugin("vrMenu", VRMenuPlugin);
|
||||
declare module "video.js" {
|
||||
interface VideoJsPlayer {
|
||||
vrMenu: () => VRMenuPlugin;
|
||||
vr: (options: Object) => void;
|
||||
}
|
||||
interface VideoJsPlayerPluginOptions {
|
||||
vrMenu?: VRMenuOptions;
|
||||
|
||||
Reference in New Issue
Block a user