From f78e4d391423c3504e075e2d1ce0089458c53bbc Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 19:39:36 +0530 Subject: [PATCH 1/8] Fix live photo cast --- .../components/Theatre/PhotoAuditorium.tsx | 95 ------------------- .../components/Theatre/VideoAuditorium.tsx | 55 ----------- .../cast/src/components/Theatre/index.tsx | 30 ------ web/apps/cast/src/pages/slideshow.tsx | 16 +--- 4 files changed, 4 insertions(+), 192 deletions(-) delete mode 100644 web/apps/cast/src/components/Theatre/PhotoAuditorium.tsx delete mode 100644 web/apps/cast/src/components/Theatre/VideoAuditorium.tsx delete mode 100644 web/apps/cast/src/components/Theatre/index.tsx diff --git a/web/apps/cast/src/components/Theatre/PhotoAuditorium.tsx b/web/apps/cast/src/components/Theatre/PhotoAuditorium.tsx deleted file mode 100644 index 0042dfe953..0000000000 --- a/web/apps/cast/src/components/Theatre/PhotoAuditorium.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { SlideshowContext } from "pages/slideshow"; -import { useContext, useEffect, useState } from "react"; - -export default function PhotoAuditorium({ - url, - nextSlideUrl, -}: { - url: string; - nextSlideUrl: string; -}) { - const { showNextSlide } = useContext(SlideshowContext); - - const [showPreloadedNextSlide, setShowPreloadedNextSlide] = useState(false); - const [nextSlidePrerendered, setNextSlidePrerendered] = useState(false); - const [prerenderTime, setPrerenderTime] = useState(null); - - useEffect(() => { - let timeout: NodeJS.Timeout; - let timeout2: NodeJS.Timeout; - - if (nextSlidePrerendered) { - const elapsedTime = prerenderTime ? Date.now() - prerenderTime : 0; - const delayTime = Math.max(10000 - elapsedTime, 0); - - if (elapsedTime >= 10000) { - setShowPreloadedNextSlide(true); - } else { - timeout = setTimeout(() => { - setShowPreloadedNextSlide(true); - }, delayTime); - } - - if (showNextSlide) { - timeout2 = setTimeout(() => { - showNextSlide(); - setNextSlidePrerendered(false); - setPrerenderTime(null); - setShowPreloadedNextSlide(false); - }, delayTime); - } - } - - return () => { - if (timeout) clearTimeout(timeout); - if (timeout2) clearTimeout(timeout2); - }; - }, [nextSlidePrerendered, showNextSlide, prerenderTime]); - - return ( -
-
- - { - setNextSlidePrerendered(true); - setPrerenderTime(Date.now()); - }} - /> -
-
- ); -} diff --git a/web/apps/cast/src/components/Theatre/VideoAuditorium.tsx b/web/apps/cast/src/components/Theatre/VideoAuditorium.tsx deleted file mode 100644 index 2bf5ed4908..0000000000 --- a/web/apps/cast/src/components/Theatre/VideoAuditorium.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import mime from "mime-types"; -import { SlideshowContext } from "pages/slideshow"; -import { useContext, useEffect, useRef } from "react"; - -export default function VideoAuditorium({ - name, - url, -}: { - name: string; - url: string; -}) { - const { showNextSlide } = useContext(SlideshowContext); - - const videoRef = useRef(null); - - useEffect(() => { - attemptPlay(); - }, [url, videoRef]); - - const attemptPlay = async () => { - if (videoRef.current) { - try { - await videoRef.current.play(); - } catch { - showNextSlide(); - } - } - }; - - return ( -
- -
- ); -} diff --git a/web/apps/cast/src/components/Theatre/index.tsx b/web/apps/cast/src/components/Theatre/index.tsx deleted file mode 100644 index f7cac9c544..0000000000 --- a/web/apps/cast/src/components/Theatre/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { FILE_TYPE } from "constants/file"; -import PhotoAuditorium from "./PhotoAuditorium"; -// import VideoAuditorium from './VideoAuditorium'; - -interface fileProp { - fileName: string; - fileURL: string; - type: FILE_TYPE; -} - -interface IProps { - file1: fileProp; - file2: fileProp; -} - -export default function Theatre(props: IProps) { - switch (props.file1.type && props.file2.type) { - case FILE_TYPE.IMAGE: - return ( - - ); - // case FILE_TYPE.VIDEO: - // return ( - // - // ); - } -} diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 692e611549..60aded0336 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,6 +1,6 @@ import log from "@/next/log"; import PairedSuccessfullyOverlay from "components/PairedSuccessfullyOverlay"; -import Theatre from "components/Theatre"; +import PhotoAuditorium from "components/PhotoAuditorium"; import { FILE_TYPE } from "constants/file"; import { useRouter } from "next/router"; import { createContext, useEffect, useState } from "react"; @@ -171,17 +171,9 @@ export default function Slideshow() { return ( <> - {loading && } From 2bb3e152f8329b07e0abd2c879da6d748ae62366 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 19:54:00 +0530 Subject: [PATCH 2/8] Precache --- web/apps/cast/src/pages/slideshow.tsx | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 60aded0336..8d8b61c703 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -162,12 +162,34 @@ export default function Slideshow() { } }; - useEffect(() => { - if (currentFile) { - getRenderableFileURL(); + const precacheNextRenderableFileURL = async () => { + if (!nextFile) return; + + const cacheValue = renderableFileURLCache.get(nextFile.id); + if (cacheValue) return; + + try { + const blob = await getPreviewableImage( + nextFile as EnteFile, + castToken, + ); + + const url = URL.createObjectURL(blob); + + renderableFileURLCache.set(nextFile?.id, url); + } catch (e) { + return; } + }; + + useEffect(() => { + getRenderableFileURL(); }, [currentFile]); + useEffect(() => { + precacheNextRenderableFileURL(); + }, [nextFile]); + return ( <> From 138b775c133839966d96141fc5f24ad448facac4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 19:57:08 +0530 Subject: [PATCH 3/8] At a more opportune place --- web/apps/cast/src/pages/slideshow.tsx | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 8d8b61c703..f26d3a5851 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -130,6 +130,7 @@ export default function Slideshow() { setCurrentFile(nextFile); setNextFile(nextNextFile); + precacheRenderableFileURL(nextNextFile); }; const [renderableFileURL, setRenderableFileURL] = useState(""); @@ -162,21 +163,12 @@ export default function Slideshow() { } }; - const precacheNextRenderableFileURL = async () => { - if (!nextFile) return; - - const cacheValue = renderableFileURLCache.get(nextFile.id); - if (cacheValue) return; - + const precacheRenderableFileURL = async (file: EnteFile) => { + if (renderableFileURLCache.get(file.id)) return; try { - const blob = await getPreviewableImage( - nextFile as EnteFile, - castToken, - ); - + const blob = await getPreviewableImage(file as EnteFile, castToken); const url = URL.createObjectURL(blob); - - renderableFileURLCache.set(nextFile?.id, url); + renderableFileURLCache.set(file?.id, url); } catch (e) { return; } @@ -186,10 +178,6 @@ export default function Slideshow() { getRenderableFileURL(); }, [currentFile]); - useEffect(() => { - precacheNextRenderableFileURL(); - }, [nextFile]); - return ( <> From 598a885df2d8bedfe36890e376535ac405db5f9a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 20:17:28 +0530 Subject: [PATCH 4/8] Refactor This doesn't fix the initial gap, functionality is still the same, but the flow is now easier to understand. --- web/apps/cast/src/pages/slideshow.tsx | 90 ++++++++++----------------- 1 file changed, 34 insertions(+), 56 deletions(-) diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index f26d3a5851..28061d0a47 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -20,18 +20,17 @@ export const SlideshowContext = createContext<{ const renderableFileURLCache = new Map(); export default function Slideshow() { - const [collectionFiles, setCollectionFiles] = useState([]); - - const [currentFile, setCurrentFile] = useState( - undefined, - ); - const [nextFile, setNextFile] = useState(undefined); - const [loading, setLoading] = useState(true); const [castToken, setCastToken] = useState(""); const [castCollection, setCastCollection] = useState< Collection | undefined >(undefined); + const [collectionFiles, setCollectionFiles] = useState([]); + const [currentFileId, setCurrentFileId] = useState(); + const [currentFileURL, setCurrentFileURL] = useState(); + const [nextFileURL, setNextFileURL] = useState(); + + const router = useRouter(); const syncCastFiles = async (token: string) => { try { @@ -93,8 +92,6 @@ export default function Slideshow() { return true; }; - const router = useRouter(); - useEffect(() => { try { const castToken = window.localStorage.getItem("castToken"); @@ -117,9 +114,9 @@ export default function Slideshow() { showNextSlide(); }, [collectionFiles]); - const showNextSlide = () => { + const showNextSlide = async () => { const currentIndex = collectionFiles.findIndex( - (file) => file.id === currentFile?.id, + (file) => file.id === currentFileId, ); const nextIndex = (currentIndex + 1) % collectionFiles.length; @@ -128,62 +125,43 @@ export default function Slideshow() { const nextFile = collectionFiles[nextIndex]; const nextNextFile = collectionFiles[nextNextIndex]; - setCurrentFile(nextFile); - setNextFile(nextNextFile); - precacheRenderableFileURL(nextNextFile); - }; + let nextURL = renderableFileURLCache.get(nextFile.id); + let nextNextURL = renderableFileURLCache.get(nextNextFile.id); - const [renderableFileURL, setRenderableFileURL] = useState(""); - - const getRenderableFileURL = async () => { - if (!currentFile) return; - - const cacheValue = renderableFileURLCache.get(currentFile.id); - if (cacheValue) { - setRenderableFileURL(cacheValue); - setLoading(false); - return; + if (!nextURL) { + try { + const blob = await getPreviewableImage(nextFile, castToken); + const url = URL.createObjectURL(blob); + renderableFileURLCache.set(nextFile.id, url); + nextURL = url; + } catch (e) { + return; + } } - try { - const blob = await getPreviewableImage( - currentFile as EnteFile, - castToken, - ); - - const url = URL.createObjectURL(blob); - - renderableFileURLCache.set(currentFile?.id, url); - - setRenderableFileURL(url); - } catch (e) { - return; - } finally { - setLoading(false); + if (!nextNextURL) { + try { + const blob = await getPreviewableImage(nextNextFile, castToken); + const url = URL.createObjectURL(blob); + renderableFileURLCache.set(nextNextFile.id, url); + nextNextURL = url; + } catch (e) { + return; + } } - }; - const precacheRenderableFileURL = async (file: EnteFile) => { - if (renderableFileURLCache.get(file.id)) return; - try { - const blob = await getPreviewableImage(file as EnteFile, castToken); - const url = URL.createObjectURL(blob); - renderableFileURLCache.set(file?.id, url); - } catch (e) { - return; - } + setLoading(false); + setCurrentFileId(nextFile.id); + setCurrentFileURL(nextURL); + setNextFileURL(nextNextURL); }; - useEffect(() => { - getRenderableFileURL(); - }, [currentFile]); - return ( <> {loading && } From 0a248e5ce5e0f4c92acc0db7be60feec1d693f23 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 20:22:12 +0530 Subject: [PATCH 5/8] Prepare --- .../cast/src/components/PhotoAuditorium.tsx | 20 +++++++-------- web/apps/cast/src/pages/slideshow.tsx | 25 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/web/apps/cast/src/components/PhotoAuditorium.tsx b/web/apps/cast/src/components/PhotoAuditorium.tsx index 0042dfe953..9f0d9120d9 100644 --- a/web/apps/cast/src/components/PhotoAuditorium.tsx +++ b/web/apps/cast/src/components/PhotoAuditorium.tsx @@ -1,15 +1,15 @@ -import { SlideshowContext } from "pages/slideshow"; -import { useContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; -export default function PhotoAuditorium({ - url, - nextSlideUrl, -}: { +interface PhotoAuditoriumProps { url: string; nextSlideUrl: string; -}) { - const { showNextSlide } = useContext(SlideshowContext); - + showNextSlide: () => void; +} +export const PhotoAuditorium: React.FC = ({ + url, + nextSlideUrl, + showNextSlide, +}) => { const [showPreloadedNextSlide, setShowPreloadedNextSlide] = useState(false); const [nextSlidePrerendered, setNextSlidePrerendered] = useState(false); const [prerenderTime, setPrerenderTime] = useState(null); @@ -92,4 +92,4 @@ export default function PhotoAuditorium({ ); -} +}; diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 28061d0a47..2530159815 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,9 +1,9 @@ import log from "@/next/log"; import PairedSuccessfullyOverlay from "components/PairedSuccessfullyOverlay"; -import PhotoAuditorium from "components/PhotoAuditorium"; +import { PhotoAuditorium } from "components/PhotoAuditorium"; import { FILE_TYPE } from "constants/file"; import { useRouter } from "next/router"; -import { createContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { getCastCollection, getLocalFiles, @@ -13,10 +13,6 @@ import { Collection } from "types/collection"; import { EnteFile } from "types/file"; import { getPreviewableImage, isRawFileFromFileName } from "utils/file"; -export const SlideshowContext = createContext<{ - showNextSlide: () => void; -}>(null); - const renderableFileURLCache = new Map(); export default function Slideshow() { @@ -28,6 +24,7 @@ export default function Slideshow() { const [collectionFiles, setCollectionFiles] = useState([]); const [currentFileId, setCurrentFileId] = useState(); const [currentFileURL, setCurrentFileURL] = useState(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [nextFileURL, setNextFileURL] = useState(); const router = useRouter(); @@ -156,15 +153,13 @@ export default function Slideshow() { setNextFileURL(nextNextURL); }; + if (loading) return ; + return ( - <> - - - - {loading && } - + ); } From 2dd705d7f7140650d1c448ea7dcb3f823fe2852c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 20:49:47 +0530 Subject: [PATCH 6/8] Not better not worse --- .../cast/src/components/PhotoAuditorium.tsx | 57 +++++-------------- web/apps/cast/src/pages/slideshow.tsx | 3 +- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/web/apps/cast/src/components/PhotoAuditorium.tsx b/web/apps/cast/src/components/PhotoAuditorium.tsx index 9f0d9120d9..6aa2c3990b 100644 --- a/web/apps/cast/src/components/PhotoAuditorium.tsx +++ b/web/apps/cast/src/components/PhotoAuditorium.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; interface PhotoAuditoriumProps { url: string; @@ -10,41 +10,15 @@ export const PhotoAuditorium: React.FC = ({ nextSlideUrl, showNextSlide, }) => { - const [showPreloadedNextSlide, setShowPreloadedNextSlide] = useState(false); - const [nextSlidePrerendered, setNextSlidePrerendered] = useState(false); - const [prerenderTime, setPrerenderTime] = useState(null); - useEffect(() => { - let timeout: NodeJS.Timeout; - let timeout2: NodeJS.Timeout; - - if (nextSlidePrerendered) { - const elapsedTime = prerenderTime ? Date.now() - prerenderTime : 0; - const delayTime = Math.max(10000 - elapsedTime, 0); - - if (elapsedTime >= 10000) { - setShowPreloadedNextSlide(true); - } else { - timeout = setTimeout(() => { - setShowPreloadedNextSlide(true); - }, delayTime); - } - - if (showNextSlide) { - timeout2 = setTimeout(() => { - showNextSlide(); - setNextSlidePrerendered(false); - setPrerenderTime(null); - setShowPreloadedNextSlide(false); - }, delayTime); - } - } + const timeoutId = window.setTimeout(() => { + showNextSlide(); + }, 10000); return () => { - if (timeout) clearTimeout(timeout); - if (timeout2) clearTimeout(timeout2); + if (timeoutId) clearTimeout(timeoutId); }; - }, [nextSlidePrerendered, showNextSlide, prerenderTime]); + }, [showNextSlide]); return (
= ({ backdropFilter: "blur(10px)", }} > - { - setNextSlidePrerendered(true); - setPrerenderTime(Date.now()); + /> +
diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 2530159815..3a98efbf5e 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -24,7 +24,6 @@ export default function Slideshow() { const [collectionFiles, setCollectionFiles] = useState([]); const [currentFileId, setCurrentFileId] = useState(); const [currentFileURL, setCurrentFileURL] = useState(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [nextFileURL, setNextFileURL] = useState(); const router = useRouter(); @@ -158,7 +157,7 @@ export default function Slideshow() { return ( ); From c98d5a3e4069026d2803a181d7fc3afbeeb58f79 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 20:59:27 +0530 Subject: [PATCH 7/8] Filter out files with HEIC previews --- web/apps/cast/src/pages/slideshow.tsx | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 3a98efbf5e..774bbd4da9 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -20,7 +20,7 @@ export default function Slideshow() { const [castToken, setCastToken] = useState(""); const [castCollection, setCastCollection] = useState< Collection | undefined - >(undefined); + >(); const [collectionFiles, setCollectionFiles] = useState([]); const [currentFileId, setCurrentFileId] = useState(); const [currentFileURL, setCurrentFileURL] = useState(); @@ -67,23 +67,12 @@ export default function Slideshow() { const isFileEligibleForCast = (file: EnteFile) => { const fileType = file.metadata.fileType; - if (fileType !== FILE_TYPE.IMAGE && fileType !== FILE_TYPE.LIVE_PHOTO) { + if (fileType !== FILE_TYPE.IMAGE && fileType !== FILE_TYPE.LIVE_PHOTO) return false; - } - const fileSizeLimit = 100 * 1024 * 1024; + if (file.info.fileSize > 100 * 1024 * 1024) return false; - if (file.info.fileSize > fileSizeLimit) { - return false; - } - - const name = file.metadata.title; - - if (fileType === FILE_TYPE.IMAGE) { - if (isRawFileFromFileName(name)) { - return false; - } - } + if (isRawFileFromFileName(file.metadata.title)) return false; return true; }; From d80358552f4a96259390801e4a6618ee5aa47c5c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 16 Apr 2024 21:02:57 +0530 Subject: [PATCH 8/8] Trim spaces for more forgiving copy paste --- .../Collections/CollectionOptions/AlbumCastDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx b/web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx index 8a5cb2c909..fdabffe846 100644 --- a/web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx +++ b/web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx @@ -50,7 +50,7 @@ export default function AlbumCastDialog(props: Props) { setFieldError, ) => { try { - await doCast(value); + await doCast(value.trim()); props.onHide(); } catch (e) { const error = e as Error;