feat(web): add support for casting (#18231)

* recreate #13966

* gcast button works

* rewrote gcast-player to be GCastDestination and CastManager manages the interface between UI and casting destinations

* remove unneeded imports

* add "Connected to" translation

* Remove css for cast launcher

* fix tests

* fix doc tests

* fix the receiver application ID

* remove casting app ID

* remove cast button from nav bar

It is now present at the following locations:

- shared link album and single asset views
- asset viewer (normal user)
- album view (normal user)

* part 1 of fixes from @danieldietzler code review

* part 2 of code review changes from @danieldietzler and @jsram91

* cleanup documentation

* onVideoStarted missing callback

* add token expiry validation

* cleanup logic and logging

* small cleanup

* rename to ICastDestination

* cast button changes
This commit is contained in:
Brandon Wees
2025-05-20 16:08:23 -05:00
committed by GitHub
parent 12b7a079c1
commit 86db0aafe5
16 changed files with 708 additions and 36 deletions
+45
View File
@@ -0,0 +1,45 @@
<script lang="ts">
import { t } from 'svelte-i18n';
import { onMount } from 'svelte';
import { mdiCast, mdiCastConnected } from '@mdi/js';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { CastDestinationType, castManager } from '$lib/managers/cast-manager.svelte';
import { GCastDestination } from '$lib/utils/cast/gcast-destination.svelte';
import { IconButton } from '@immich/ui';
interface Props {
whiteHover?: boolean;
navBar?: boolean;
}
let { whiteHover, navBar }: Props = $props();
onMount(async () => {
await castManager.initialize();
});
const getButtonColor = () => {
return castManager.isCasting ? 'primary' : whiteHover ? undefined : 'opaque';
};
</script>
{#if castManager.availableDestinations.length > 0 && castManager.availableDestinations[0].type === CastDestinationType.GCAST}
{#if navBar}
<IconButton
shape="round"
variant="ghost"
size="medium"
color={castManager.isCasting ? 'primary' : 'secondary'}
icon={castManager.isCasting ? mdiCastConnected : mdiCast}
onclick={() => void GCastDestination.showCastDialog()}
aria-label={$t('cast')}
/>
{:else}
<CircleIconButton
color={getButtonColor()}
icon={castManager.isCasting ? mdiCastConnected : mdiCast}
onclick={GCastDestination.showCastDialog}
title={$t('cast')}
/>
{/if}
{/if}