diff --git a/src/components/Transition.tsx b/src/components/Transition.tsx index 8406a19b..cda3b945 100644 --- a/src/components/Transition.tsx +++ b/src/components/Transition.tsx @@ -12,7 +12,6 @@ interface Props { animation: TransitionAnimations; className?: string; children?: ReactNode; - appearOnMount?: boolean; isChild?: boolean; } @@ -62,23 +61,14 @@ export function Transition(props: Props) { if (props.isChild) { return ( - +
{props.children}
); } return ( - +
{props.children}
); diff --git a/src/components/layout/Modal.tsx b/src/components/layout/Modal.tsx index bd5b9c47..7a1c3b64 100644 --- a/src/components/layout/Modal.tsx +++ b/src/components/layout/Modal.tsx @@ -13,13 +13,11 @@ export function ModalFrame(props: Props) { diff --git a/src/setup/locales/en/translation.json b/src/setup/locales/en/translation.json index 8d80a1dc..8842b58f 100644 --- a/src/setup/locales/en/translation.json +++ b/src/setup/locales/en/translation.json @@ -79,9 +79,13 @@ } }, "v3": { - "newSiteTitle": "Version 3 has released!", + "newSiteTitle": "New version now released!", "newDomain": "https://movie-web.app", - "newDomainText": "We have a new domain. You can now access our website on <0>https://movie-web.app. Make sure to update all your bookmarks as <1>the old link will stop working on {{date}}.", - "tireless": "We've worked tirelessly on this new update, we hope you will enjoy what we've been cooking up for the past months." + "newDomainText": "movie-web will soon be moving to a new domain: <0>https://movie-web.app. Make sure to update all your bookmarks as <1>the old website will stop working on {{date}}.", + "tireless": "We've worked tirelessly on this new update, we hope you will enjoy what we've been cooking up for the past months.", + "leaveAnnouncement": "Take me there!" + }, + "casting": { + "casting": "Casting to device..." } } diff --git a/src/video/components/VideoPlayer.tsx b/src/video/components/VideoPlayer.tsx index 5b9e7dac..688f42e2 100644 --- a/src/video/components/VideoPlayer.tsx +++ b/src/video/components/VideoPlayer.tsx @@ -29,6 +29,7 @@ import { useControls } from "@/video/state/logic/controls"; import { ReactNode, useCallback, useState } from "react"; import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderAction"; import { ChromecastAction } from "@/video/components/actions/ChromecastAction"; +import { CastingTextAction } from "@/video/components/actions/CastingTextAction"; type Props = VideoPlayerBaseProps; @@ -94,6 +95,9 @@ export function VideoPlayer(props: Props) { + + + diff --git a/src/video/components/actions/CastingTextAction.tsx b/src/video/components/actions/CastingTextAction.tsx new file mode 100644 index 00000000..d95a0351 --- /dev/null +++ b/src/video/components/actions/CastingTextAction.tsx @@ -0,0 +1,22 @@ +import { Icon, Icons } from "@/components/Icon"; +import { useVideoPlayerDescriptor } from "@/video/state/hooks"; +import { useMisc } from "@/video/state/logic/misc"; +import { useTranslation } from "react-i18next"; + +export function CastingTextAction() { + const { t } = useTranslation(); + + const descriptor = useVideoPlayerDescriptor(); + const misc = useMisc(descriptor); + + if (!misc.isCasting) return null; + + return ( +
+
+ +
+

{t("casting.casting")}

+
+ ); +} diff --git a/src/video/components/internal/CastingInternal.tsx b/src/video/components/internal/CastingInternal.tsx index 72979083..9de8c1b1 100644 --- a/src/video/components/internal/CastingInternal.tsx +++ b/src/video/components/internal/CastingInternal.tsx @@ -16,13 +16,17 @@ export function CastingInternal() { useEffect(() => { if (lastValue.current === isCasting) return; - if (!isCasting) return; lastValue.current = isCasting; + if (!isCasting) return; const provider = createCastingStateProvider(descriptor); setProvider(descriptor, provider); const { destroy } = provider.providerStart(); return () => { - unsetStateProvider(descriptor, provider.getId()); + try { + unsetStateProvider(descriptor, provider.getId()); + } catch { + // ignore errors from missing player state, we need to run destroy()! + } destroy(); }; }, [descriptor, isCasting]); diff --git a/src/video/components/internal/VideoElementInternal.tsx b/src/video/components/internal/VideoElementInternal.tsx index f819bf8f..335e8a9f 100644 --- a/src/video/components/internal/VideoElementInternal.tsx +++ b/src/video/components/internal/VideoElementInternal.tsx @@ -27,7 +27,11 @@ function VideoElement(props: Props) { setProvider(descriptor, provider); const { destroy } = provider.providerStart(); return () => { - unsetStateProvider(descriptor, provider.getId()); + try { + unsetStateProvider(descriptor, provider.getId()); + } catch { + // ignore errors from missing player state, we need to run destroy()! + } destroy(); }; }, [descriptor, initalized, stateProviderId]); diff --git a/src/video/state/providers/castingStateProvider.ts b/src/video/state/providers/castingStateProvider.ts index c2f2432a..d78190c1 100644 --- a/src/video/state/providers/castingStateProvider.ts +++ b/src/video/state/providers/castingStateProvider.ts @@ -18,9 +18,7 @@ import { VideoPlayerStateProvider } from "./providerTypes"; import { updateProgress } from "../logic/progress"; // TODO startAt when switching state providers -// TODO cast -> uncast -> cast will break -// TODO chromecast button has incorrect hitbox and badly styled -// TODO casting text middle of screen +// TODO test HLS export function createCastingStateProvider( descriptor: string ): VideoPlayerStateProvider { @@ -112,8 +110,10 @@ export function createCastingStateProvider( const movieMeta = new chrome.cast.media.MovieMediaMetadata(); movieMeta.title = state.meta?.meta.meta.title ?? ""; - // TODO contentId? - const mediaInfo = new chrome.cast.media.MediaInfo("hello", "video/mp4"); + const mediaInfo = new chrome.cast.media.MediaInfo( + state.meta?.meta.meta.id ?? "hello", + "video/mp4" + ); (mediaInfo as any).contentUrl = source?.source; mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED; mediaInfo.metadata = movieMeta; @@ -167,17 +167,16 @@ export function createCastingStateProvider( updateProgress(descriptor, state); break; case "mediaInfo": - state.progress.duration = e.value.duration; - updateProgress(descriptor, state); + if (e.value) { + state.progress.duration = e.value.duration; + updateProgress(descriptor, state); + } break; case "playerState": state.mediaPlaying.isLoading = e.value === "BUFFERING"; - updateMediaPlaying(descriptor, state); - break; - case "isPaused": - state.mediaPlaying.isPaused = e.value; - state.mediaPlaying.isPlaying = !e.value; - if (!e.value) state.mediaPlaying.hasPlayedOnce = true; + state.mediaPlaying.isPaused = e.value !== "PLAYING"; + state.mediaPlaying.isPlaying = e.value === "PLAYING"; + if (e.value === "PLAYING") state.mediaPlaying.hasPlayedOnce = true; updateMediaPlaying(descriptor, state); break; case "isMuted": @@ -188,6 +187,7 @@ export function createCastingStateProvider( case "displayStatus": case "canSeek": case "title": + case "isPaused": break; default: console.log(e.type, e.field, e.value); @@ -229,6 +229,7 @@ export function createCastingStateProvider( state.wrapperElement?.removeEventListener("mouseenter", isFocused); state.wrapperElement?.removeEventListener("mouseleave", isFocused); fscreen.removeEventListener("fullscreenchange", fullscreenchange); + ins?.endCurrentSession(true); }, }; }, diff --git a/src/views/search/HomeView.tsx b/src/views/search/HomeView.tsx index f9494f3b..6d1b6795 100644 --- a/src/views/search/HomeView.tsx +++ b/src/views/search/HomeView.tsx @@ -161,7 +161,7 @@ function NewDomainModal() {