mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04: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
|
// Initialize VideoJS player
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function isVrScene() {
|
|
||||||
if (!scene?.id || !vrTag) return false;
|
|
||||||
|
|
||||||
return scene?.tags.some((tag) => {
|
|
||||||
if (vrTag == tag.name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: VideoJsPlayerOptions = {
|
const options: VideoJsPlayerOptions = {
|
||||||
id: VIDEO_PLAYER_ID,
|
id: VIDEO_PLAYER_ID,
|
||||||
controls: true,
|
controls: true,
|
||||||
@@ -330,9 +320,7 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||||||
},
|
},
|
||||||
skipButtons: {},
|
skipButtons: {},
|
||||||
trackActivity: {},
|
trackActivity: {},
|
||||||
vrMenu: {
|
vrMenu: {},
|
||||||
showButton: isVrScene(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -364,7 +352,8 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||||||
// reset sceneId to force reload sources
|
// reset sceneId to force reload sources
|
||||||
sceneId.current = undefined;
|
sceneId.current = undefined;
|
||||||
};
|
};
|
||||||
}, [scene, vrTag]);
|
// empty deps - only init once
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const player = getPlayer();
|
const player = getPlayer();
|
||||||
@@ -388,6 +377,21 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
|||||||
scene?.paths.funscript,
|
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
|
// Player event handlers
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const player = getPlayer();
|
const player = getPlayer();
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import videojs, { VideoJsPlayer } from "video.js";
|
import videojs, { VideoJsPlayer } from "video.js";
|
||||||
import "videojs-vr";
|
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 {
|
export interface VRMenuOptions {
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +18,7 @@ enum VRType {
|
|||||||
Off = "Off",
|
Off = "Off",
|
||||||
}
|
}
|
||||||
|
|
||||||
const vrTypeProjection = {
|
const vrTypeProjection: Record<VRType, ProjectionType> = {
|
||||||
[VRType.Spherical]: "360",
|
[VRType.Spherical]: "360",
|
||||||
[VRType.Off]: "NONE",
|
[VRType.Off]: "NONE",
|
||||||
};
|
};
|
||||||
@@ -29,7 +32,7 @@ class VRMenuItem extends videojs.getComponent("MenuItem") {
|
|||||||
public isSelected = false;
|
public isSelected = false;
|
||||||
|
|
||||||
constructor(parent: VRMenuButton, type: VRType) {
|
constructor(parent: VRMenuButton, type: VRType) {
|
||||||
const options = {} as videojs.MenuItemOptions;
|
const options: videojs.MenuItemOptions = {};
|
||||||
options.selectable = true;
|
options.selectable = true;
|
||||||
options.multiSelectable = false;
|
options.multiSelectable = false;
|
||||||
options.label = type;
|
options.label = type;
|
||||||
@@ -105,27 +108,61 @@ class VRMenuButton extends videojs.getComponent("MenuButton") {
|
|||||||
|
|
||||||
class VRMenuPlugin extends videojs.getPlugin("plugin") {
|
class VRMenuPlugin extends videojs.getPlugin("plugin") {
|
||||||
private menu: VRMenuButton;
|
private menu: VRMenuButton;
|
||||||
|
private showButton: boolean;
|
||||||
|
private vr?: VideoJsVRPlugin;
|
||||||
|
|
||||||
constructor(player: VideoJsPlayer, options: VRMenuOptions) {
|
constructor(player: VideoJsPlayer, options: VRMenuOptions) {
|
||||||
super(player);
|
super(player);
|
||||||
|
|
||||||
this.menu = new VRMenuButton(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) => {
|
this.menu.on("typeselected", (_, type: VRType) => {
|
||||||
const projection = vrTypeProjection[type];
|
this.loadVR(type);
|
||||||
player.vr({ projection });
|
|
||||||
player.load();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
player.on("ready", () => {
|
player.on("ready", () => {
|
||||||
const { controlBar } = player;
|
if (this.showButton) {
|
||||||
const fullscreenToggle = controlBar.getChild("fullscreenToggle")!.el();
|
this.addButton();
|
||||||
controlBar.addChild(this.menu);
|
}
|
||||||
controlBar.el().insertBefore(this.menu.el(), fullscreenToggle);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Register the plugin with video.js.
|
||||||
@@ -136,7 +173,6 @@ videojs.registerPlugin("vrMenu", VRMenuPlugin);
|
|||||||
declare module "video.js" {
|
declare module "video.js" {
|
||||||
interface VideoJsPlayer {
|
interface VideoJsPlayer {
|
||||||
vrMenu: () => VRMenuPlugin;
|
vrMenu: () => VRMenuPlugin;
|
||||||
vr: (options: Object) => void;
|
|
||||||
}
|
}
|
||||||
interface VideoJsPlayerPluginOptions {
|
interface VideoJsPlayerPluginOptions {
|
||||||
vrMenu?: VRMenuOptions;
|
vrMenu?: VRMenuOptions;
|
||||||
|
|||||||
Reference in New Issue
Block a user