From fac5ab5079cb28efc484eabb1a47bc2f5e58500f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 27 May 2025 17:32:07 +0530 Subject: [PATCH] [desktop] Enable stream generation for non-internal users --- desktop/CHANGELOG.md | 6 ++++ docs/docs/photos/faq/video-streaming.md | 29 ++++++++++++++++++- web/apps/photos/src/components/Sidebar.tsx | 2 +- web/apps/photos/src/pages/_app.tsx | 10 +++---- .../gallery/services/upload/upload-service.ts | 7 +---- web/packages/gallery/services/video.ts | 23 ++++----------- .../new/photos/components/SearchBar.tsx | 2 +- 7 files changed, 48 insertions(+), 31 deletions(-) diff --git a/desktop/CHANGELOG.md b/desktop/CHANGELOG.md index 4470e11864..88ddccfbfb 100644 --- a/desktop/CHANGELOG.md +++ b/desktop/CHANGELOG.md @@ -2,6 +2,12 @@ ## v1.7.13 (Unreleased) +- Generate streams for videos (beta) + + > Streamable videos can be enabled in Settings > Preferences. For more + > information, see the + > [video streaming FAQ](https://help.ente.io/photos/faq/video-streaming). + - Support Turkish translations. - . diff --git a/docs/docs/photos/faq/video-streaming.md b/docs/docs/photos/faq/video-streaming.md index 59a34907f6..91b2db1001 100644 --- a/docs/docs/photos/faq/video-streaming.md +++ b/docs/docs/photos/faq/video-streaming.md @@ -8,22 +8,43 @@ description: > [!NOTE] > -> Video streaming is available in beta on mobile apps starting v0.9.98. +> Video streaming is available in beta on mobile apps starting v0.9.98 and on +> desktop starting v1.7.13. ### How to enable video streaming? +#### On mobile + - Open Settings -> General -> Advanced - Switch on the toggle for `Video streaming` +#### On desktop + +- Open Settings -> Preferences +- Enable the toggle for `Streamable videos` + ### What happens when I enable video streaming? +#### On mobile + Enabling video streaming will start processing videos captured in the last 30 days, generating streams for each. Both local and remote videos will be processed, so this may consume bandwidth for downloading of remote files and uploading of the generated streams. +#### On desktop + +When enabled, the desktop app will generate streams both for new uploads, and +also for all existing videos that were previously uploaded. + +Stream generation is CPU intensive and can take time so the app will continue +processing them in the background. Clicking on search bar will show "Processing +videos..." when stream generation is happening. + ### How can I view video streams? +### On mobile + Settings -> Backup > Backup status will show details regarding the processing status for videos. Processed videos will have a green play button next to them. You can open these videos by tapping on them. @@ -34,6 +55,12 @@ play the stream. Clicking on the `Info` icon within the original video will show details about the generated stream. +### On desktop and web + +Desktop and web app will automatically play the streaming version of a video if +it has been already generated. The quality selector will show "Auto" when +playing the stream. + ### What is a stream? Stream is an encrypted HLS file with an `.m3u8` playlist that helps play a video diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index 670e53f445..73f25db465 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -819,7 +819,7 @@ const Preferences: React.FC = ({ label={t("advanced")} onClick={showAdvancedSettings} /> - {isHLSGenerationSupported() && ( + {isHLSGenerationSupported && ( }> {t("labs")} diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 5375e7544c..cdd5849886 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -22,6 +22,10 @@ import { BaseContext, deriveBaseContext } from "ente-base/context"; import log from "ente-base/log"; import { logStartupBanner } from "ente-base/log-web"; import { AppUpdate } from "ente-base/types/ipc"; +import { + initVideoProcessing, + isHLSGenerationSupported, +} from "ente-gallery/services/video"; import { Notification } from "ente-new/photos/components/Notification"; import { ThemedLoadingBar } from "ente-new/photos/components/ThemedLoadingBar"; import { @@ -46,10 +50,6 @@ import { useRouter } from "next/router"; import { useCallback, useEffect, useMemo, useState } from "react"; import { photosLogout } from "services/logout"; -import { - initVideoProcessing, - isHLSGenerationSupportedTemp, -} from "ente-gallery/services/video"; import "photoswipe/dist/photoswipe.css"; import "styles/global.css"; import "styles/photoswipe.css"; @@ -112,7 +112,7 @@ const App: React.FC = ({ Component, pageProps }) => { }; if (isMLSupported) initML(); - if (isHLSGenerationSupportedTemp()) void initVideoProcessing(); + if (isHLSGenerationSupported) void initVideoProcessing(); electron.onOpenEnteURL(handleOpenEnteURL); electron.onAppUpdateAvailable(showUpdateDialog); diff --git a/web/packages/gallery/services/upload/upload-service.ts b/web/packages/gallery/services/upload/upload-service.ts index b5df37c325..08a6b181e8 100644 --- a/web/packages/gallery/services/upload/upload-service.ts +++ b/web/packages/gallery/services/upload/upload-service.ts @@ -45,7 +45,6 @@ import { import { FileType, type FileTypeInfo } from "ente-media/file-type"; import { encodeLivePhoto } from "ente-media/live-photo"; import { addToCollection } from "ente-new/photos/services/collection"; -import { settingsSnapshot } from "ente-new/photos/services/settings"; import { CustomError, CustomErrorMessage, @@ -1105,11 +1104,7 @@ const extractImageOrVideoMetadata = async ( // Video duration let duration: number | undefined; - if ( - fileType == FileType.video && - // TODO(HLS): - settingsSnapshot().isInternalUser - ) { + if (fileType == FileType.video) { duration = await tryDetermineVideoDuration(uploadItem); } diff --git a/web/packages/gallery/services/video.ts b/web/packages/gallery/services/video.ts index 07b620842d..1db8315a09 100644 --- a/web/packages/gallery/services/video.ts +++ b/web/packages/gallery/services/video.ts @@ -23,7 +23,6 @@ import { getLocalTrashFileIDs, uniqueFilesByID, } from "ente-new/photos/services/files"; -import { settingsSnapshot } from "ente-new/photos/services/settings"; import { gunzip, gzip } from "ente-new/photos/utils/gzip"; import { randomSample } from "ente-utils/array"; import { ensurePrecondition } from "ente-utils/ensure"; @@ -208,18 +207,8 @@ const updateSnapshotIfNeeded = ( /** * Return `true` if this client is capable of generating HLS streams for * uploaded videos. - * - * This function implementation is fast and can be called many times (e.g. - * during UI rendering). */ -export const isHLSGenerationSupported = () => - // Keep this check fast, we get called many times. - isDesktop && - // TODO(HLS): - settingsSnapshot().isInternalUser; - -// TODO(HLS): Only the isDesktop flag is needed eventually. -export const isHLSGenerationSupportedTemp = () => isDesktop; +export const isHLSGenerationSupported = isDesktop; /** * Initialize the video processing subsystem if the user has enabled HLS @@ -257,7 +246,7 @@ const saveGenerateHLS = (enabled: boolean) => setKV("generateHLS", enabled); * Precondition: {@link isHLSGenerationSupported} must be `true`. */ export const toggleHLSGeneration = async () => { - if (!isHLSGenerationSupported()) { + if (!isHLSGenerationSupported) { assertionFailed(); return; } @@ -673,7 +662,7 @@ const syncProcessedFileIDs = async () => export const videoPrunePermanentlyDeletedFileIDsIfNeeded = async ( deletedFileIDs: Set, ) => { - if (!isHLSGenerationSupported()) return; + if (!isHLSGenerationSupported) return; const existing = await savedProcessedVideoFileIDs(); if (existing.size > 0) { @@ -702,7 +691,7 @@ export const videoPrunePermanentlyDeletedFileIDsIfNeeded = async ( * that have already been processed elsewhere. */ export const videoProcessingSyncIfNeeded = async () => { - if (!isHLSGenerationSupported()) return; + if (!isHLSGenerationSupported) return; // The `haveSyncedOnce` flag tracks whether or not a sync has happened for // the app, and is not specific to video processing. We always set it even @@ -745,7 +734,7 @@ export const processVideoNewUpload = ( file: EnteFile, processableUploadItem: ProcessableUploadItem, ) => { - if (!isHLSGenerationSupported()) return; + if (!isHLSGenerationSupported) return; if (!isHLSGenerationEnabled()) return; if (file.metadata.fileType !== FileType.video) return; if (processableUploadItem instanceof File) { @@ -830,7 +819,7 @@ export const isHLSGenerationEnabled = () => _state.isHLSGenerationEnabled; * batches, and the externally triggered processing of live uploads. */ const processQueue = async () => { - if (!isHLSGenerationSupported() || !isHLSGenerationEnabled()) { + if (!isHLSGenerationSupported || !isHLSGenerationEnabled()) { assertionFailed(); /* we shouldn't have come here */ return; } diff --git a/web/packages/new/photos/components/SearchBar.tsx b/web/packages/new/photos/components/SearchBar.tsx index 6245ea04d6..f242aee020 100644 --- a/web/packages/new/photos/components/SearchBar.tsx +++ b/web/packages/new/photos/components/SearchBar.tsx @@ -389,7 +389,7 @@ const shouldShowEmptyState = (inputValue: string) => { // Don't show empty state if there is no ML related information AND we're // not processing videos. - if (!isMLSupported && !isHLSGenerationSupported()) { + if (!isMLSupported && !isHLSGenerationSupported) { // Neither of ML or HLS generation is supported on current client. This // is the code path for web. return false;