From 83a851b55650c93318b45b14903cd3407c999dab Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Sun, 16 Jun 2024 17:37:25 +0200 Subject: [PATCH] fix(web): play video muted when blocked by browser (#10383) --- .../asset-viewer/video-native-viewer.svelte | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) 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 1fae59a0da..62dec38eab 100644 --- a/web/src/lib/components/asset-viewer/video-native-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-native-viewer.svelte @@ -4,7 +4,7 @@ import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { AssetMediaSize } from '@immich/sdk'; - import { createEventDispatcher } from 'svelte'; + import { createEventDispatcher, tick } from 'svelte'; import { fade } from 'svelte/transition'; import { t } from 'svelte-i18n'; @@ -15,28 +15,40 @@ let element: HTMLVideoElement | undefined = undefined; let isVideoLoading = true; let assetFileUrl: string; + let forceMuted = false; - $: { - const next = getAssetPlaybackUrl({ id: assetId, checksum }); - if (assetFileUrl !== next) { - assetFileUrl = next; - element && element.load(); - } + $: if (element) { + assetFileUrl = getAssetPlaybackUrl({ id: assetId, checksum }); + forceMuted = false; + element.load(); } const dispatch = createEventDispatcher<{ onVideoEnded: void; onVideoStarted: void }>(); - const handleCanPlay = async (event: Event) => { + const handleCanPlay = async (video: HTMLVideoElement) => { try { - const video = event.currentTarget as HTMLVideoElement; await video.play(); dispatch('onVideoStarted'); } catch (error) { + if (error instanceof DOMException && error.name === 'NotAllowedError' && !forceMuted) { + await tryForceMutedPlay(video); + return; + } handleError(error, $t('errors.unable_to_play_video')); } finally { isVideoLoading = false; } }; + + const tryForceMutedPlay = async (video: HTMLVideoElement) => { + try { + forceMuted = true; + await tick(); + await handleCanPlay(video); + } catch (error) { + handleError(error, $t('errors.unable_to_play_video')); + } + };
handleCanPlay(e.currentTarget)} on:ended={() => dispatch('onVideoEnded')} - bind:muted={$videoViewerMuted} + on:volumechange={(e) => { + if (!forceMuted) { + $videoViewerMuted = e.currentTarget.muted; + } + }} + muted={forceMuted || $videoViewerMuted} bind:volume={$videoViewerVolume} poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, checksum })} >