From 294e2090d05b32d309dbbd5496906b3aec14a555 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:10:47 +1000 Subject: [PATCH] Scene player presentation improvements (#5145) * Show controls before video plays * Allow interaction with controls while displaying error * Source selector improvements Don't auto-play next source if manually selected. Don't remove errored sources * Show errored sources in different style --- .../components/ScenePlayer/source-selector.ts | 46 ++++++++++++++++--- .../src/components/ScenePlayer/styles.scss | 30 ++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/ui/v2.5/src/components/ScenePlayer/source-selector.ts b/ui/v2.5/src/components/ScenePlayer/source-selector.ts index 3a8337b13..7cf6cfd75 100644 --- a/ui/v2.5/src/components/ScenePlayer/source-selector.ts +++ b/ui/v2.5/src/components/ScenePlayer/source-selector.ts @@ -2,6 +2,7 @@ import videojs, { VideoJsPlayer } from "video.js"; export interface ISource extends videojs.Tech.SourceObject { label?: string; + errored?: boolean; } class SourceMenuItem extends videojs.getComponent("MenuItem") { @@ -81,6 +82,22 @@ class SourceMenuButton extends videojs.getComponent("MenuButton") { return this.items; } + + setSelectedSource(source: ISource) { + this.selectedSource = source; + if (this.items === undefined) return; + + for (const item of this.items) { + item.selected(item.source === this.selectedSource); + } + } + + markSourceErrored(source: ISource) { + const item = this.items.find((i) => i.source.src === source.src); + if (item === undefined) return; + + item.addClass("vjs-source-menu-item-error"); + } } class SourceSelectorPlugin extends videojs.getPlugin("plugin") { @@ -90,6 +107,9 @@ class SourceSelectorPlugin extends videojs.getPlugin("plugin") { private cleanupTextTracks: HTMLTrackElement[] = []; private manualTextTracks: HTMLTrackElement[] = []; + // don't auto play next source if user manually selected a source + private manuallySelected = false; + constructor(player: VideoJsPlayer) { super(player); @@ -99,6 +119,8 @@ class SourceSelectorPlugin extends videojs.getPlugin("plugin") { this.selectedIndex = this.sources.findIndex((src) => src === source); if (this.selectedIndex === -1) return; + this.manuallySelected = true; + const loadSrc = this.sources[this.selectedIndex]; const currentTime = player.currentTime(); @@ -154,14 +176,26 @@ class SourceSelectorPlugin extends videojs.getPlugin("plugin") { const currentSource = player.currentSource() as ISource; console.log(`Source '${currentSource.label}' is unsupported`); - if (this.sources.length > 1) { - if (this.selectedIndex === -1) return; + // mark current source as errored + currentSource.errored = true; + this.menu.markSourceErrored(currentSource); - this.sources.splice(this.selectedIndex, 1); - const newSource = this.sources[0]; + // don't auto play next source if user manually selected a source + if (this.manuallySelected) { + return; + } + + // TODO - make auto play next source configurable + // try the next source in the list + if ( + this.selectedIndex !== -1 && + this.selectedIndex + 1 < this.sources.length + ) { + this.selectedIndex += 1; + const newSource = this.sources[this.selectedIndex]; console.log(`Trying next source in playlist: '${newSource.label}'`); - this.menu.setSources(this.sources); - this.selectedIndex = 0; + this.menu.setSelectedSource(newSource); + player.src(newSource); player.load(); player.play(); diff --git a/ui/v2.5/src/components/ScenePlayer/styles.scss b/ui/v2.5/src/components/ScenePlayer/styles.scss index 0878c7704..e7a3e61ab 100644 --- a/ui/v2.5/src/components/ScenePlayer/styles.scss +++ b/ui/v2.5/src/components/ScenePlayer/styles.scss @@ -39,6 +39,28 @@ $sceneTabWidth: 450px; position: absolute; width: 100%; + &:not(.vjs-has-started) .vjs-control-bar { + display: flex; + } + + // show controls even when an error is displayed + /* stylelint-disable declaration-no-important */ + &.vjs-error .vjs-control-bar { + display: flex !important; + } + /* stylelint-enable declaration-no-important */ + + // allow interaction with the controls when error is displayed + .vjs-error-display, + .vjs-error-display .vjs-modal-dialog-content { + position: static; + } + + // hide spinner when error is displayed + &.vjs-error .vjs-loading-spinner { + display: none; + } + .vjs-button { outline: none; } @@ -197,6 +219,14 @@ $sceneTabWidth: 450px; content: "\f110"; font-family: VideoJS; } + + .vjs-menu-item.vjs-source-menu-item-error:not(.vjs-selected) { + color: $text-muted; + } + + .vjs-menu-item.vjs-source-menu-item-error { + font-style: italic; + } } .vjs-vr-selector {