From 0f43f56cc0c9e07d5ca9df64ce41fa4fe2ab4e34 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 25 Mar 2024 14:28:08 -0400 Subject: [PATCH 01/24] refactor basic media controls into useMediaControls --- .../components/composables/media-player.ts | 46 ++++++++++++ client/src/components/players/OmniPlayer.vue | 72 +++++++++++-------- client/src/views/Room.vue | 13 ++-- 3 files changed, 94 insertions(+), 37 deletions(-) diff --git a/client/src/components/composables/media-player.ts b/client/src/components/composables/media-player.ts index ba08a9fbc..dc1038562 100644 --- a/client/src/components/composables/media-player.ts +++ b/client/src/components/composables/media-player.ts @@ -16,3 +16,49 @@ export function useVolume() { return volume; } + +export class MediaController { + player: MediaPlayerV2 | null = null; + + setPlayer(player: MediaPlayerV2) { + this.player = player; + } + + async play() { + if (this.player) { + await this.player.play(); + } + } + + async pause() { + if (this.player) { + await this.player.pause(); + } + } + + getPosition() { + return this.player ? this.player.getPosition() : 0; + } + + setPosition(position: number) { + if (this.player) { + this.player.setPosition(position); + } + } +} + +export abstract class MediaPlayerV2 { + playing = ref(false); + + abstract play(): Promise; + abstract pause(): Promise; + + abstract getPosition(): number; + abstract setPosition(position: number): void; +} + +export const mediaController = new MediaController(); + +export function useMediaControls() { + return mediaController; +} diff --git a/client/src/components/players/OmniPlayer.vue b/client/src/components/players/OmniPlayer.vue index f60398301..5e9f1370e 100644 --- a/client/src/components/players/OmniPlayer.vue +++ b/client/src/components/players/OmniPlayer.vue @@ -123,7 +123,7 @@ import { PlayerStatus } from "ott-common/models/types"; import { QueueItem } from "ott-common/models/video"; import { calculateCurrentPosition } from "ott-common/timestamp"; import { defineComponent, defineAsyncComponent, PropType, ref, Ref, computed, watch } from "vue"; -import { useVolume } from "../composables"; +import { MediaPlayerV2, useMediaControls, useVolume } from "../composables"; const services = [ "youtube", @@ -150,8 +150,8 @@ export interface MediaPlayer { */ pause(): void | Promise; setVolume(volume: number): void | Promise; - getPosition(): number | Promise; - setPosition(position: number): void | Promise; + getPosition(): number; + setPosition(position: number): void; isCaptionsSupported(): boolean; getAvailablePlaybackRates(): number[]; @@ -193,6 +193,41 @@ export default defineComponent({ const player: Ref = ref(null); + class OmniMediaPlayer extends MediaPlayerV2 { + constructor() { + super(); + } + + async play(): Promise { + if (!checkForPlayer(player.value)) { + return Promise.reject("Player not available yet"); + } + return player.value.play(); + } + async pause(): Promise { + if (!checkForPlayer(player.value)) { + return Promise.reject("Player not available yet"); + } + return player.value.pause(); + } + getPosition(): number { + if (!checkForPlayer(player.value)) { + return 0; + } + return player.value.getPosition(); + } + setPosition(position: number): void { + if (!checkForPlayer(player.value)) { + return; + } + return player.value.setPosition(position); + } + } + + const player2: MediaPlayerV2 = new OmniMediaPlayer(); + const controls = ref(useMediaControls()); + controls.value.setPlayer(player2); + function checkForPlayer(p: MediaPlayer | null): p is MediaPlayer { if (!p) { console.warn( @@ -214,36 +249,12 @@ export default defineComponent({ const isPlayerPresent = computed(() => !!player.value); - function play(): void | Promise { - if (!checkForPlayer(player.value)) { - return Promise.reject("Player not available yet"); - } - return player.value.play(); - } - function pause(): void | Promise { - if (!checkForPlayer(player.value)) { - return Promise.reject("Player not available yet"); - } - return player.value.pause(); - } function setVolume(volume: number) { if (!checkForPlayer(player.value)) { return Promise.reject("Player not available yet"); } return player.value.setVolume(volume); } - function getPosition() { - if (!checkForPlayer(player.value)) { - return 0; - } - return player.value.getPosition(); - } - function setPosition(position: number) { - if (!checkForPlayer(player.value)) { - return; - } - return player.value.setPosition(position); - } function isCaptionsSupported() { if (!checkForPlayer(player.value)) { return false; @@ -363,11 +374,13 @@ export default defineComponent({ function onPlaying() { hackReadyEdgeCase(); + player2.playing.value = true; emit("playing"); } function onPaused() { hackReadyEdgeCase(); + player2.playing.value = false; emit("paused"); } @@ -433,11 +446,8 @@ export default defineComponent({ isPlayerPresent, showBufferWarning, renderedSpans, - play, - pause, + controls, setVolume, - getPosition, - setPosition, isCaptionsSupported, isCaptionsEnabled, setCaptionsEnabled, diff --git a/client/src/views/Room.vue b/client/src/views/Room.vue index e66081e89..c655b4f1c 100644 --- a/client/src/views/Room.vue +++ b/client/src/views/Room.vue @@ -236,7 +236,7 @@ import VoteSkip from "@/components/VoteSkip.vue"; import { waitForToken } from "@/util/token"; import { useSfx } from "@/plugins/sfx"; import { secondsToTimestamp } from "@/util/timestamp"; -import { useVolume } from "@/components/composables"; +import { useMediaControls, useVolume } from "@/components/composables"; const VIDEO_CONTROLS_HIDE_TIMEOUT = 3000; @@ -342,10 +342,10 @@ export default defineComponent({ if (!isPlayerPresent(player)) { return; } - const currentTime = await player.value.getPosition(); + const currentTime = mediaControls.getPosition(); if (Math.abs(newPosition - currentTime) > 1 && !mediaPlaybackBlocked.value) { - player.value.setPosition(newPosition); + mediaControls.setPosition(newPosition); } }); @@ -455,6 +455,7 @@ export default defineComponent({ // Indicates that starting playback is blocked by the browser. This usually means that the user needs // to interact with the page before playback can start. This is because browsers block autoplaying videos. const mediaPlaybackBlocked = ref(false); + const mediaControls = useMediaControls(); async function applyIsPlaying(playing: boolean): Promise { await waitForPlayer(); @@ -463,9 +464,9 @@ export default defineComponent({ } try { if (playing) { - await player.value.play(); + await mediaControls.play(); } else { - await player.value.pause(); + await mediaControls.pause(); } mediaPlaybackBlocked.value = false; return; @@ -479,7 +480,7 @@ export default defineComponent({ } function onClickUnblockPlayback(): void { - player.value?.setPosition(truePosition.value); + mediaControls.setPosition(truePosition.value); applyIsPlaying(store.state.room.isPlaying); } From 316bd99de0aa801e7cfd8cf774bed8360c359563 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 25 Mar 2024 17:40:53 -0400 Subject: [PATCH 02/24] refactor captions controls into useCaptions it feels pretty janky right now missing changes --- .../components/composables/media-player.ts | 90 ++++++++++++------- .../src/components/controls/VideoControls.vue | 53 ++++------- client/src/components/players/OmniPlayer.vue | 23 ++++- client/src/views/Room.vue | 37 +++----- 4 files changed, 104 insertions(+), 99 deletions(-) diff --git a/client/src/components/composables/media-player.ts b/client/src/components/composables/media-player.ts index dc1038562..79b88cf43 100644 --- a/client/src/components/composables/media-player.ts +++ b/client/src/components/composables/media-player.ts @@ -1,5 +1,5 @@ import { useStore } from "@/store"; -import { onMounted, ref, watch } from "vue"; +import { computed, onMounted, ref, watch, type Ref } from "vue"; const volume = ref(100); @@ -17,48 +17,72 @@ export function useVolume() { return volume; } -export class MediaController { - player: MediaPlayerV2 | null = null; - - setPlayer(player: MediaPlayerV2) { - this.player = player; - } - - async play() { - if (this.player) { - await this.player.play(); - } - } - - async pause() { - if (this.player) { - await this.player.pause(); - } - } - - getPosition() { - return this.player ? this.player.getPosition() : 0; - } - - setPosition(position: number) { - if (this.player) { - this.player.setPosition(position); - } - } -} +// export class MediaController { +// player: MediaPlayerV2 | null = null; +// isCaptionsSupported = computed(() => this.player?.isCaptionsSupported.value ?? false); + +// setPlayer(player: MediaPlayerV2) { +// this.player = player; +// } + +// async play() { +// if (this.player) { +// await this.player.play(); +// } +// } + +// async pause() { +// if (this.player) { +// await this.player.pause(); +// } +// } + +// getPosition() { +// return this.player ? this.player.getPosition() : 0; +// } + +// setPosition(position: number) { +// if (this.player) { +// this.player.setPosition(position); +// } +// } +// } export abstract class MediaPlayerV2 { playing = ref(false); + isCaptionsSupported = ref(false); abstract play(): Promise; abstract pause(): Promise; abstract getPosition(): number; abstract setPosition(position: number): void; + + getAvailablePlaybackRates(): number[] { + return [1]; + } } -export const mediaController = new MediaController(); +const player: Ref = ref(undefined); + +export function useMediaPlayer() { + return player; +} + +export function isPlayerPresent(p: typeof player): p is Ref { + return !!p.value; +} -export function useMediaControls() { - return mediaController; +const isCaptionsSupported: Ref = ref(false); +const isCaptionsEnabled: Ref = ref(false); +const captionsTracks: Ref = ref([]); +const currentTrack: Ref = ref(null); + +export function useCaptions() { + return { + isCaptionsSupported, + isCaptionsEnabled, + captionsTracks, + currentTrack, + }; } diff --git a/client/src/components/controls/VideoControls.vue b/client/src/components/controls/VideoControls.vue index f49ef9fc8..312122408 100644 --- a/client/src/components/controls/VideoControls.vue +++ b/client/src/components/controls/VideoControls.vue @@ -15,10 +15,14 @@