immich/web/src/lib/components/asset-viewer/video-remote-viewer.svelte
Brandon Wees a02e1f5e7c
chore(web): migrate CircleIconButton to @immich/ui IconButton (#18486)
* remove import and referenced file

* first pass at replacing all CircleIconButtons

* fix linting issues

* fix combobox formatting issues

* fix button context menu coloring

* remove circle icon button from search history box

* use theme switcher from UI lib

* dark mode force the asset viewer icons

* fix forced dark mode icons

* dark mode memory viewer icons

* fix: back button in memory viewer

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-06-02 14:47:23 +00:00

105 lines
2.8 KiB
Svelte

<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
import { castManager, CastState } from '$lib/managers/cast-manager.svelte';
import { handleError } from '$lib/utils/handle-error';
import { IconButton } from '@immich/ui';
import { mdiCastConnected, mdiPause, mdiPlay } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
poster: string;
assetFileUrl: string;
onVideoStarted: () => void;
onVideoEnded: () => void;
}
let { poster, assetFileUrl, onVideoEnded, onVideoStarted }: Props = $props();
let previousPlayerState: CastState | null = $state(null);
const handlePlayPauseButton = async () => {
switch (castManager.castState) {
case CastState.PLAYING: {
castManager.pause();
break;
}
case CastState.IDLE: {
await cast(assetFileUrl, true);
break;
}
default: {
castManager.play();
break;
}
}
};
$effect(() => {
if (assetFileUrl) {
// this can't be in an async context with $effect
void cast(assetFileUrl);
}
});
$effect(() => {
if (castManager.castState === CastState.IDLE && previousPlayerState !== CastState.PAUSED) {
onVideoEnded();
}
previousPlayerState = castManager.castState;
});
const cast = async (url: string, force: boolean = false) => {
if (!url || !castManager.isCasting) {
return;
}
const fullUrl = new URL(url, globalThis.location.href);
try {
await castManager.loadMedia(fullUrl.href, force);
onVideoStarted();
} catch (error) {
handleError(error, 'Unable to cast');
return;
}
};
function handleSeek(event: Event) {
const newTime = Number.parseFloat((event.target as HTMLInputElement).value);
castManager.seekTo(newTime);
}
</script>
<span class="flex items-center space-x-2 text-gray-200 text-2xl font-bold">
<Icon path={mdiCastConnected} class="text-primary" size="36" />
<span>{$t('connected_to')} {castManager.receiverName}</span>
</span>
<img src={poster} alt="poster" class="rounded-xl m-4" />
<div class="flex place-content-center place-items-center">
{#if castManager.castState == CastState.BUFFERING}
<div class="p-3">
<LoadingSpinner />
</div>
{:else}
<IconButton
color="primary"
shape="round"
variant="ghost"
icon={castManager.castState == CastState.PLAYING ? mdiPause : mdiPlay}
onclick={() => handlePlayPauseButton()}
aria-label={castManager.castState == CastState.PLAYING ? 'Pause' : 'Play'}
/>
{/if}
<input
type="range"
min="0"
max={castManager.duration}
value={castManager.currentTime ?? 0}
onchange={handleSeek}
class="w-full h-4 bg-primary"
/>
</div>