mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add cast button
This commit is contained in:
parent
3488aeb03b
commit
c985a972f0
@ -2,5 +2,8 @@
|
||||
"navbar": {
|
||||
"home": "Home",
|
||||
"login": "Login"
|
||||
},
|
||||
"cast": {
|
||||
"start": "Cast to device"
|
||||
}
|
||||
}
|
||||
|
@ -2,5 +2,8 @@
|
||||
"navbar": {
|
||||
"home": "Accueil",
|
||||
"login": "Connexion"
|
||||
},
|
||||
"cast": {
|
||||
"start": "Caster sur un appareil"
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
"zod": "^3.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chromecast-caf-sender": "^1.0.5",
|
||||
"@types/node": "18.0.3",
|
||||
"@types/react": "18.0.15",
|
||||
"@types/react-dom": "18.0.6",
|
||||
|
@ -29,6 +29,7 @@ import { defaultTheme } from "~/utils/themes/default-theme";
|
||||
import superjson from "superjson";
|
||||
import Head from "next/head";
|
||||
import { useMobileHover } from "~/utils/utils";
|
||||
import { CastProvider } from "~/player/cast/cast-provider";
|
||||
|
||||
// Simply silence a SSR warning (see https://github.com/facebook/react/issues/14927 for more details)
|
||||
if (typeof window === "undefined") {
|
||||
@ -39,6 +40,7 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
const [queryClient] = useState(() => createQueryClient());
|
||||
const { queryState, ...props } = superjson.deserialize<any>(pageProps ?? {});
|
||||
const getLayout = (Component as QueryPage).getLayout ?? ((page) => page);
|
||||
const castEnabled = true;
|
||||
|
||||
useMobileHover();
|
||||
|
||||
@ -70,7 +72,10 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
</Head>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Hydrate state={queryState}>
|
||||
<ThemeProvider theme={defaultTheme}>{getLayout(<Component {...props} />)}</ThemeProvider>
|
||||
<ThemeProvider theme={defaultTheme}>
|
||||
{getLayout(<Component {...props} />)}
|
||||
{castEnabled && <CastProvider />}
|
||||
</ThemeProvider>
|
||||
</Hydrate>
|
||||
</QueryClientProvider>
|
||||
</>
|
||||
|
39
front/src/player/cast/cast-button.tsx
Normal file
39
front/src/player/cast/cast-button.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Box, styled } from "@mui/material";
|
||||
import { BoxProps } from "@mui/system";
|
||||
import { ComponentProps, forwardRef } from "react";
|
||||
|
||||
|
||||
type CastProps = { class?: string };
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"google-cast-launcher": CastProps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const _CastButton = forwardRef<HTMLDivElement, ComponentProps<"div">>(function Cast({className, ...props}, ref) {
|
||||
return <google-cast-launcher ref={ref} class={className} {...props} />;
|
||||
});
|
||||
export const CastButton = styled(_CastButton)({});
|
42
front/src/player/cast/cast-provider.tsx
Normal file
42
front/src/player/cast/cast-provider.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Script from "next/script";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const CastProvider = () => {
|
||||
useEffect(() => {
|
||||
window.__onGCastApiAvailable = (isAvailable) => {
|
||||
if (!isAvailable) return;
|
||||
cast.framework.CastContext.getInstance().setOptions({
|
||||
receiverApplicationId:
|
||||
process.env.APPLICATION_ID ?? chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
|
||||
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Script
|
||||
src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"
|
||||
strategy="lazyOnload"
|
||||
/>
|
||||
);
|
||||
};
|
@ -26,6 +26,7 @@ import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { Font, Track } from "~/models/resources/watch-item";
|
||||
import { Link } from "~/utils/link";
|
||||
import { CastButton } from "../cast/cast-button";
|
||||
import { fullscreenAtom, subtitleAtom } from "../state";
|
||||
|
||||
export const RightButtons = ({
|
||||
@ -40,6 +41,7 @@ export const RightButtons = ({
|
||||
onMenuClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation("player");
|
||||
const { t: tc } = useTranslation("common");
|
||||
const [subtitleAnchor, setSubtitleAnchor] = useState<HTMLButtonElement | null>(null);
|
||||
const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
|
||||
|
||||
@ -71,6 +73,16 @@ export const RightButtons = ({
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title={tc("cast.start")}>
|
||||
<CastButton
|
||||
sx={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
"--connected-color": "white",
|
||||
"--disconnected-color": "white",
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("fullscreen")}>
|
||||
<IconButton
|
||||
onClick={() => setFullscreen(!isFullscreen)}
|
||||
|
@ -24,8 +24,8 @@ import { WatchItem, WatchItemP } from "~/models/resources/watch-item";
|
||||
import { useFetch } from "~/utils/query";
|
||||
import { ErrorPage } from "~/components/errors";
|
||||
import { useState, useEffect, PointerEvent as ReactPointerEvent } from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { Box, styled } from "@mui/material";
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
import { Hover, LoadingIndicator } from "./components/hover";
|
||||
import { fullscreenAtom, playAtom, useSubtitleController, useVideoController } from "./state";
|
||||
import { useRouter } from "next/router";
|
||||
@ -35,6 +35,8 @@ import { episodeDisplayNumber } from "~/components/episode";
|
||||
import { useVideoKeyboard } from "./keyboard";
|
||||
import { MediaSessionManager } from "./media-session";
|
||||
|
||||
const Video = styled("video")({});
|
||||
|
||||
// Callback used to hide the controls when the mouse goes iddle. This is stored globally to clear the old timeout
|
||||
// if the mouse moves again (if this is stored as a state, the whole page is redrawn on mouse move)
|
||||
let mouseCallback: NodeJS.Timeout;
|
||||
@ -132,8 +134,7 @@ const Player: QueryPage<{ slug: string }> = ({ slug }) => {
|
||||
onMouseLeave={() => setMouseMoved(false)}
|
||||
sx={{ cursor: displayControls ? "unset" : "none" }}
|
||||
>
|
||||
<Box
|
||||
component="video"
|
||||
<Video
|
||||
{...videoProps}
|
||||
onPointerDown={(e: ReactPointerEvent<HTMLVideoElement>) => {
|
||||
if (e.pointerType === "mouse") {
|
||||
|
@ -18,10 +18,9 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { BoxProps } from "@mui/material";
|
||||
import { atom, useAtom, useSetAtom } from "jotai";
|
||||
import { useRouter } from "next/router";
|
||||
import { RefObject, useEffect, useRef } from "react";
|
||||
import { ComponentProps, RefObject, useEffect, useRef } from "react";
|
||||
import { Font, Track } from "~/models/resources/watch-item";
|
||||
import { bakedAtom } from "~/utils/jotai-utils";
|
||||
// @ts-ignore
|
||||
@ -139,7 +138,7 @@ export const useVideoController = (links?: { direct: string; transmux: string })
|
||||
setDuration(player.current.duration);
|
||||
}, [player, setDuration]);
|
||||
|
||||
const videoProps: BoxProps<"video"> = {
|
||||
const videoProps: ComponentProps<"video"> = {
|
||||
ref: player,
|
||||
onDoubleClick: () => {
|
||||
setFullscreen(!document.fullscreenElement);
|
||||
|
@ -402,6 +402,21 @@
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@types/chrome@*":
|
||||
version "0.0.197"
|
||||
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.197.tgz#c1b50cdb72ee40f9bc1411506031a9f8a925ab35"
|
||||
integrity sha512-m1NfS5bOjaypyqQfaX6CxmJodZVcvj5+Mt/K94EBHkflYjPNmXHAzbxfifdLMa0YM3PDyOxohoTS5ug/e6p5jA==
|
||||
dependencies:
|
||||
"@types/filesystem" "*"
|
||||
"@types/har-format" "*"
|
||||
|
||||
"@types/chromecast-caf-sender@^1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/chromecast-caf-sender/-/chromecast-caf-sender-1.0.5.tgz#197bfae77efb7399818ceaee5d2c303a4b72d51f"
|
||||
integrity sha512-8d6RRCOYYiKzDyFJKAYKOp7Eo0kUfj9imnLQj0uuh/QGSz8euL9OOeKmh8XizqTcKW5tXva6li0mRYtnvzVIcA==
|
||||
dependencies:
|
||||
"@types/chrome" "*"
|
||||
|
||||
"@types/debug@^4.0.0":
|
||||
version "4.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
|
||||
@ -409,6 +424,23 @@
|
||||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/filesystem@*":
|
||||
version "0.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.32.tgz#307df7cc084a2293c3c1a31151b178063e0a8edf"
|
||||
integrity sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==
|
||||
dependencies:
|
||||
"@types/filewriter" "*"
|
||||
|
||||
"@types/filewriter@*":
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee"
|
||||
integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==
|
||||
|
||||
"@types/har-format@*":
|
||||
version "1.2.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.9.tgz#b9b3a9bfc33a078e7d898a00b09662910577f4a4"
|
||||
integrity sha512-rffW6MhQ9yoa75bdNi+rjZBAvu2HhehWJXlhuWXnWdENeuKe82wUgAwxYOb7KRKKmxYN+D/iRKd2NDQMLqlUmg==
|
||||
|
||||
"@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
|
Loading…
x
Reference in New Issue
Block a user