diff --git a/package.json b/package.json index caeacac..cfc8b46 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,14 @@ }, "dependencies": { "@tweenjs/tween.js": "^17.3.5", - "aframe": "^0.9.2", + "aframe": "^1.0.4", "aframe-stereo-component": "^0.6.0", "buttplug": "^0.12.2", "d3": "^5.9.2", "haptic-movie-file-reader": "^0.0.10", "matomo-tracker": "^2.2.1", "mousetrap": "^1.6.3", + "videojs-hotkeys": "^0.2.27", "videojs-youtube": "^2.6.0", "viewport-units-buggyfill": "^0.6.2", "vue": "^2.6.10", @@ -35,7 +36,8 @@ "vue-buttplug-material-component": "^0.3.1", "vue-touch": "next", "vue-video-player": "^5.0.2", - "vuetify": "^1.5.14" + "vuetify": "^1.5.14", + "yarn.lock": "^0.0.1-security" }, "devDependencies": { "@types/aframe": "^0.8.3", diff --git a/src/App.vue b/src/App.vue index 5635d53..0288eba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -348,5 +348,9 @@ justify-content: center; } + /* disable the duplicate vr button */ + .a-enter-vr { + display: none; + } diff --git a/src/components/VideoPlayer/VideoPlayer.ts b/src/components/VideoPlayer/VideoPlayer.ts index 0573b6e..19dd60e 100644 --- a/src/components/VideoPlayer/VideoPlayer.ts +++ b/src/components/VideoPlayer/VideoPlayer.ts @@ -1,6 +1,7 @@ import Vue from "vue"; import { Component, Model, Prop, Watch } from "vue-property-decorator"; import videojs from "video.js"; +import "videojs-hotkeys"; const vjsyoutube = require("./videojs-youtube"); const videoPlayer = require("vue-video-player").videoPlayer; @@ -24,6 +25,9 @@ export default class VideoPlayer extends Vue { private vrControlButton: videojs.Component | null = null; private currentPlayer: videojs.Player | null = null; + private showPlaybackSpeed: boolean = false; + private playbackSpeed: number = 1; + // This is a combination of videojs's Playback options plus some extra stuff // from vue-video-player, so it has to be an any instead of a // videojs.PlaybackOptions until we derive something that supports both. @@ -35,6 +39,50 @@ export default class VideoPlayer extends Vue { sources: [{}], start: 0, loop: this.loopVideo, + plugins: { + hotkeys: { + enableNumbers: false, + enableVolumeScroll: false, + skipInitialFocus: true, + enableInactiveFocus: false, + captureDocumentHotkeys: true, + documentHotkeysFocusElementFilter: (e: HTMLElement) => { + const tagName = e.tagName.toLowerCase(); + return e.id === "content" || tagName === "body" || tagName === "video"; + }, + + forwardKey: ({ key }: KeyboardEvent) => { + return key === "ArrowRight" || key === "ArrowUp"; + }, + rewindKey: ({ key }: KeyboardEvent) => { + return key === "ArrowLeft" || key === "ArrowDown"; + }, + + seekStep: ({ key, shiftKey }: KeyboardEvent) => { + // mimic mpv seek behavior, 5s for left/right, 60s for up/down + if (key === "ArrowUp" || key === "ArrowDown") { + return 60; + } else if (shiftKey && (key === "ArrowLeft" || key === "ArrowRight")) { + // might be helpful to use while scripting + return 1; + } else { + return 5; + } + }, + + customKeys: { + // mimic mpv the playback behavior, change playback rate by 10% + incPlayback: { + key: ({ key }: KeyboardEvent) => key === "]", + handler: () => this.setPlaybackSpeed(0.1), + }, + decPlayback: { + key: ({ key }: KeyboardEvent) => key === "[", + handler: () => this.setPlaybackSpeed(-0.1), + }, + }, + }, + }, }; public mounted() { @@ -143,6 +191,29 @@ export default class VideoPlayer extends Vue { this.$emit("videoPaused"); } + private setPlaybackSpeed(playbackSpeedChange: number) { + const player = this.currentPlayer; + if (!player) { + return; + } + // not a great way to handle the playback type here + const currentSpeed = player.playbackRate() as any; + if (currentSpeed > 0.1 || playbackSpeedChange > 0) { + // set the playback speed, then show a speed notification over the video + const newSpeed = +(currentSpeed + playbackSpeedChange).toPrecision(2); + player.playbackRate(newSpeed); + this.playbackSpeed = newSpeed; + this.showPlaybackSpeed = true; + + // hide the speed notification after 3 seconds + const id = setInterval(() => this.showPlaybackSpeed = true, 1); + setTimeout(() => { + clearInterval(id); + this.showPlaybackSpeed = false; + }, 3000); + } + } + private runTimeUpdateLoop() { window.requestAnimationFrame(() => { if (this.currentPlayer!.paused()) { diff --git a/src/components/VideoPlayer/VideoPlayer.vue b/src/components/VideoPlayer/VideoPlayer.vue index 483df8e..b3fccae 100644 --- a/src/components/VideoPlayer/VideoPlayer.vue +++ b/src/components/VideoPlayer/VideoPlayer.vue @@ -19,6 +19,7 @@ + Speed: {{ playbackSpeed }} @@ -27,6 +28,14 @@ display: flex; max-width: 100vw; } + + .speed-notification { + color: white; + font-size: 24px; + position: absolute; + top: 15px; + left: 15px; + }