Add AirPlay and Chromecast support (#2872)

* dynamically load cast_sender.js
* add https://www.gstatic.com to connectableOrigins
* Add toggle for chromecast
This commit is contained in:
CJ 2023-07-10 22:47:11 -05:00 committed by GitHub
parent c499c20a7b
commit 8e235a26ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 3 deletions

View File

@ -2,7 +2,7 @@
# Build Frontend
FROM node:alpine as frontend
RUN apk add --no-cache make
RUN apk add --no-cache make git
## cache node_modules separately
COPY ./ui/v2.5/package.json ./ui/v2.5/yarn.lock /stash/ui/v2.5/
WORKDIR /stash

View File

@ -490,7 +490,7 @@ func setPageSecurityHeaders(w http.ResponseWriter, r *http.Request) {
// The graphql playground pulls its frontend from a cdn
if r.URL.Path == playgroundEndpoint {
connectSrc += " https://cdn.jsdelivr.net"
scriptSrc += " https://cdn.jsdelivr.net"
scriptSrc += " https://cdn.jsdelivr.net http://www.gstatic.com https://www.gstatic.com"
styleSrc += " https://cdn.jsdelivr.net"
}

View File

@ -29,6 +29,8 @@
"@fortawesome/free-regular-svg-icons": "^6.3.0",
"@fortawesome/free-solid-svg-icons": "^6.3.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@silvermine/videojs-airplay": "^1.2.0",
"@silvermine/videojs-chromecast": "^1.4.1",
"apollo-upload-client": "^17.0.0",
"axios": "^1.3.3",
"base64-blob": "^1.4.1",

View File

@ -8,6 +8,7 @@ import React, {
useState,
} from "react";
import videojs, { VideoJsPlayer, VideoJsPlayerOptions } from "video.js";
import useScript from "src/hooks/useScript";
import "videojs-contrib-dash";
import "videojs-mobile-ui";
import "videojs-seek-buttons";
@ -22,6 +23,12 @@ import "./big-buttons";
import "./track-activity";
import "./vrmode";
import cx from "classnames";
// @ts-ignore
import airplay from "@silvermine/videojs-airplay";
// @ts-ignore
import chromecast from "@silvermine/videojs-chromecast";
import "@silvermine/videojs-chromecast/dist/silvermine-videojs-chromecast.css";
import "@silvermine/videojs-airplay/dist/silvermine-videojs-airplay.css";
import {
useSceneSaveActivity,
useSceneIncrementPlayCount,
@ -211,11 +218,15 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
const started = useRef(false);
const auto = useRef(false);
const interactiveReady = useRef(false);
const minimumPlayPercent = uiConfig?.minimumPlayPercent ?? 0;
const trackActivity = uiConfig?.trackActivity ?? false;
const vrTag = uiConfig?.vrTag ?? undefined;
useScript(
"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1",
uiConfig?.enableChromecast
);
const file = useMemo(
() => (scene.files.length > 0 ? scene.files[0] : undefined),
[scene]
@ -267,6 +278,8 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
// Initialize VideoJS player
useEffect(() => {
airplay(videojs);
chromecast(videojs);
const options: VideoJsPlayerOptions = {
id: VIDEO_PLAYER_ID,
controls: true,
@ -300,12 +313,15 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
inactivityTimeout: 2000,
preload: "none",
playsinline: true,
techOrder: ["chromecast", "html5"],
userActions: {
hotkeys: function (this: VideoJsPlayer, event) {
handleHotkeys(this, event);
},
},
plugins: {
airPlay: {},
chromecast: {},
vttThumbnails: {
showTimestamp: true,
},

View File

@ -62,6 +62,12 @@ $sceneTabWidth: 450px;
}
}
.vjs-airplay-button .vjs-icon-placeholder,
.vjs-chromecast-button .vjs-icon-placeholder {
height: 1.6em;
width: 1.6em;
}
.vjs-touch-overlay .vjs-play-control {
z-index: 1;
}
@ -308,6 +314,12 @@ $sceneTabWidth: 450px;
font-size: 1.5em;
line-height: 2;
}
.vjs-airplay-button .vjs-icon-placeholder,
.vjs-chromecast-button .vjs-icon-placeholder {
height: 1.4em;
width: 1.4em;
}
}
.vjs-menu-button-popup .vjs-menu {

View File

@ -272,6 +272,12 @@ export const SettingsInterfacePanel: React.FC = () => {
</SettingSection>
<SettingSection headingID="config.ui.scene_player.heading">
<BooleanSetting
id="enable-chromecast"
headingID="config.ui.scene_player.options.enable_chromecast"
checked={ui.enableChromecast ?? undefined}
onChange={(v) => saveUI({ enableChromecast: v })}
/>
<BooleanSetting
id="show-scrubber"
headingID="config.ui.scene_player.options.show_scrubber"

View File

@ -43,6 +43,8 @@ export interface IUIConfig {
ratingSystemOptions?: RatingSystemOptions;
// if true the chromecast option will enabled
enableChromecast?: boolean;
// if true continue scene will always play from the beginning
alwaysStartFromBeginning?: boolean;
// if true enable activity tracking

View File

@ -0,0 +1,22 @@
import { useEffect } from "react";
const useScript = (url: string, condition?: boolean) => {
useEffect(() => {
const script = document.createElement("script");
script.src = url;
script.async = true;
if (condition) {
document.head.appendChild(script);
}
return () => {
if (condition) {
document.head.removeChild(script);
}
};
}, [url, condition]);
};
export default useScript;

View File

@ -658,6 +658,7 @@
"description": "Play next scene in queue when video finishes",
"heading": "Continue playlist by default"
},
"enable_chromecast": "Enable Chromecast",
"show_scrubber": "Show Scrubber",
"track_activity": "Track Activity",
"vr_tag": {

View File

@ -2161,6 +2161,18 @@
dependencies:
dequal "^2.0.2"
"@silvermine/videojs-airplay@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@silvermine/videojs-airplay/-/videojs-airplay-1.2.0.tgz#3ca6464c8e94c97bb9f35c2926f59a5aa662cebd"
integrity sha512-4KzHoM/wJaq3au8IBLxnz8+btAB/M/2AdMoAoOdZRpp9DGG8SGU/UPw2w+CRMknvfbGtvlhlNrNiiXVWA6Cn0A==
"@silvermine/videojs-chromecast@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@silvermine/videojs-chromecast/-/videojs-chromecast-1.4.1.tgz#a5763eb85dfd4bc8f47a1b8b150d5dadbb8ea658"
integrity sha512-tXeikkWoNC3WIl2WSkIag1CLMbGsgn+26LM4LoB4qx0TQ8mkg7pgpldCTkvXxLVaDluQ/uEm2uxrgrMmjOc6sw==
dependencies:
webcomponents.js "git+https://git@github.com/webcomponents/webcomponentsjs.git#v0.7.24"
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
@ -8143,6 +8155,10 @@ web-streams-polyfill@^3.2.1:
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
"webcomponents.js@git+https://git@github.com/webcomponents/webcomponentsjs.git#v0.7.24":
version "0.7.24"
resolved "git+https://git@github.com/webcomponents/webcomponentsjs.git#8a2e40557b177e2cca0def2553f84c8269c8f93e"
webcrypto-core@^1.7.4:
version "1.7.6"
resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55"