diff --git a/i18n/en.json b/i18n/en.json index 8e8913263c..dcfa0dc17f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1540,6 +1540,9 @@ "play_memories": "Play memories", "play_motion_photo": "Play Motion Photo", "play_or_pause_video": "Play or pause video", + "play_original_video": "Play original video", + "play_original_video_setting_description": "Prefer playback of original videos rather than transcoded videos. If original asset is not compatible it may not playback correctly.", + "play_transcoded_video": "Play transcoded video", "please_auth_to_access": "Please authenticate to access", "port": "Port", "preferences_settings_subtitle": "Manage the app's preferences", diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index d61af04db6..4a792d7945 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -56,6 +56,7 @@ mdiMagnifyPlusOutline, mdiPresentationPlay, mdiUpload, + mdiVideoOutline, } from '@mdi/js'; import type { Snippet } from 'svelte'; import { t } from 'svelte-i18n'; @@ -78,6 +79,8 @@ // export let showEditorHandler: () => void; onClose: () => void; motionPhoto?: Snippet; + playOriginalVideo: boolean; + setPlayOriginalVideo: (value: boolean) => void; } let { @@ -97,6 +100,8 @@ onShowDetail, onClose, motionPhoto, + playOriginalVideo = false, + setPlayOriginalVideo, }: Props = $props(); const sharedLink = getSharedLink(); @@ -245,6 +250,15 @@ {#if !asset.isTrashed} {/if} + + {#if asset.type === AssetTypeEnum.Video} + setPlayOriginalVideo(!playOriginalVideo)} + text={playOriginalVideo ? $t('play_transcoded_video') : $t('play_original_video')} + /> + {/if} +
void 0); + let playOriginalVideo = $state($alwaysLoadOriginalVideo); + + const setPlayOriginalVideo = (value: boolean) => { + playOriginalVideo = value; + }; const refreshStack = async () => { if (authManager.isSharedLink) { @@ -410,6 +415,8 @@ onPlaySlideshow={() => ($slideshowState = SlideshowState.PlaySlideshow)} onShowDetail={toggleDetailPanel} onClose={closeViewer} + {playOriginalVideo} + {setPlayOriginalVideo} > {#snippet motionPhoto()} navigateAsset()} onVideoStarted={handleVideoStarted} + {playOriginalVideo} /> {/if} {/key} @@ -480,6 +488,7 @@ onPreviousAsset={() => navigateAsset('previous')} onNextAsset={() => navigateAsset('next')} onVideoEnded={() => (shouldPlayMotionPhoto = false)} + {playOriginalVideo} /> {:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath .toLowerCase() @@ -510,6 +519,7 @@ onClose={closeViewer} onVideoEnded={() => navigateAsset()} onVideoStarted={handleVideoStarted} + {playOriginalVideo} /> {/if} {#if $slideshowState === SlideshowState.None && isShared && ((album && album.isActivityEnabled) || activityManager.commentCount > 0) && !activityManager.isLoading} diff --git a/web/src/lib/components/asset-viewer/video-native-viewer.svelte b/web/src/lib/components/asset-viewer/video-native-viewer.svelte index fd3bc1c44f..2ccfb59243 100644 --- a/web/src/lib/components/asset-viewer/video-native-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-native-viewer.svelte @@ -10,7 +10,7 @@ videoViewerMuted, videoViewerVolume, } from '$lib/stores/preferences.store'; - import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils'; + import { getAssetOriginalUrl, getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils'; import { AssetMediaSize } from '@immich/sdk'; import { LoadingSpinner } from '@immich/ui'; import { onDestroy, onMount } from 'svelte'; @@ -21,6 +21,7 @@ assetId: string; loopVideo: boolean; cacheKey: string | null; + playOriginalVideo: boolean; onPreviousAsset?: () => void; onNextAsset?: () => void; onVideoEnded?: () => void; @@ -32,6 +33,7 @@ assetId, loopVideo, cacheKey, + playOriginalVideo, onPreviousAsset = () => {}, onNextAsset = () => {}, onVideoEnded = () => {}, @@ -48,7 +50,12 @@ onMount(() => { // Show video after mount to ensure fading in. showVideo = true; - assetFileUrl = getAssetPlaybackUrl({ id: assetId, cacheKey }); + }); + + $effect(() => { + assetFileUrl = playOriginalVideo + ? getAssetOriginalUrl({ id: assetId, cacheKey }) + : getAssetPlaybackUrl({ id: assetId, cacheKey }); if (videoPlayer) { videoPlayer.load(); } diff --git a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte index a5a94d85d4..748886d901 100644 --- a/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-wrapper-viewer.svelte @@ -1,13 +1,14 @@