diff --git a/src/lib/components/HeroCarousel/HeroBackground.svelte b/src/lib/components/HeroCarousel/HeroBackground.svelte index 07433c0..1fb6bd7 100644 --- a/src/lib/components/HeroCarousel/HeroBackground.svelte +++ b/src/lib/components/HeroCarousel/HeroBackground.svelte @@ -51,8 +51,12 @@ })} style={`background-image: url('${backdropUrl}'); transition: opacity 500ms, transform 500ms;`} > - {#if videoUrl && i === visibleIndex && $localSettings.enableTrailers && $localSettings.autoplayTrailers} - + {#if videoUrl && i === visibleIndex && $localSettings.enableTrailers} + {/if} {/each} diff --git a/src/lib/components/HeroCarousel/HeroCarousel.svelte b/src/lib/components/HeroCarousel/HeroCarousel.svelte index 6df43d2..e316b8d 100644 --- a/src/lib/components/HeroCarousel/HeroCarousel.svelte +++ b/src/lib/components/HeroCarousel/HeroCarousel.svelte @@ -2,7 +2,7 @@ import Container from '../Container.svelte'; import HeroBackground from './HeroBackground.svelte'; import IconButton from '../FloatingIconButton.svelte'; - import { ChevronRight } from 'radix-icons-svelte'; + import { ChevronRight, ChevronUp } from 'radix-icons-svelte'; import PageDots from '../HeroShowcase/PageDots.svelte'; import type { Readable, Writable } from 'svelte/store'; import { createEventDispatcher } from 'svelte'; @@ -71,13 +71,25 @@ bind:hasFocusWithin={heroHasFocusWithin} bind:focusIndex > - +
+
diff --git a/src/lib/components/HeroShowcase/HeroShowcase.svelte b/src/lib/components/HeroShowcase/HeroShowcase.svelte index fe1e954..4a0de56 100644 --- a/src/lib/components/HeroShowcase/HeroShowcase.svelte +++ b/src/lib/components/HeroShowcase/HeroShowcase.svelte @@ -5,6 +5,8 @@ import { TMDB_IMAGES_ORIGINAL, TMDB_POSTER_SMALL } from '../../constants'; import { registrars } from '../../selectable.js'; import HeroCarousel from '../HeroCarousel/HeroCarousel.svelte'; + import { localSettings } from '$lib/stores/localstorage.store'; + import type { TitleInfoProperty } from '$lib/pages/TitlePages/HeroTitleInfo'; type ShowcaseItem = { id: number; @@ -14,7 +16,7 @@ videoUrl?: string; title: string; overview: string; - infoProperties: { label: string; href?: string }[]; + infoProperties: TitleInfoProperty[]; url?: string; }; @@ -38,7 +40,7 @@ items={items.then((items) => items.map((i) => ({ backdropUrl: `${TMDB_IMAGES_ORIGINAL}${i.backdropUri}`, - videoUrl: i.videoUrl + videoUrl: $localSettings.autoplayTrailers ? i.videoUrl : undefined })) )} bind:index={showcaseIndex} diff --git a/src/lib/components/HeroShowcase/TmdbMoviesHeroShowcase.svelte b/src/lib/components/HeroShowcase/TmdbMoviesHeroShowcase.svelte index ab99de1..591c688 100644 --- a/src/lib/components/HeroShowcase/TmdbMoviesHeroShowcase.svelte +++ b/src/lib/components/HeroShowcase/TmdbMoviesHeroShowcase.svelte @@ -4,6 +4,7 @@ import { navigate } from '../StackRouter/StackRouter'; import HeroShowcase from './HeroShowcase.svelte'; import { tmdbApi } from '$lib/apis/tmdb/tmdb-api'; + import { Video } from 'radix-icons-svelte'; export let movies: Promise; @@ -38,7 +39,12 @@ } ] : []), - ...(movie.genres ? [{ label: movie.genres.map((genre) => genre.name).join(', ') }] : []) + ...(movie.genres + ? [{ label: movie.genres.map((genre) => genre.name).join(', ') }] + : []), + ...(videoUrl + ? [{ icon: Video, href: `https://www.youtube.com/watch?v=${videoUrl}` }] + : []) ] }; }) diff --git a/src/lib/components/HeroShowcase/TmdbSeriesHeroShowcase.svelte b/src/lib/components/HeroShowcase/TmdbSeriesHeroShowcase.svelte index e6d8beb..9da72c4 100644 --- a/src/lib/components/HeroShowcase/TmdbSeriesHeroShowcase.svelte +++ b/src/lib/components/HeroShowcase/TmdbSeriesHeroShowcase.svelte @@ -4,6 +4,7 @@ import { navigate } from '../StackRouter/StackRouter'; import HeroShowcase from './HeroShowcase.svelte'; import { tmdbApi } from '$lib/apis/tmdb/tmdb-api'; + import { Video } from 'radix-icons-svelte'; export let series: Promise; @@ -39,7 +40,12 @@ } ] : []), - ...(series.genres ? [{ label: series.genres.map((genre) => genre.name).join(', ') }] : []) + ...(series.genres + ? [{ label: series.genres.map((genre) => genre.name).join(', ') }] + : []), + ...(videoUrl + ? [{ icon: Video, href: `https://www.youtube.com/watch?v=${videoUrl}` }] + : []) ] }; }) diff --git a/src/lib/components/YoutubeVideo.svelte b/src/lib/components/YoutubeVideo.svelte index 63765f1..07f6ca9 100644 --- a/src/lib/components/YoutubeVideo.svelte +++ b/src/lib/components/YoutubeVideo.svelte @@ -6,7 +6,11 @@ const STOP_WHEN_REMAINING = 12; export let videoId: string | null = null; - export let play = false; + // Load video only if visible, pause with delay when not visible + export let visible = true; + // Play/pause video + export let play = true; + // Autoplay after load export let autoplay = true; export let autoplayDelay = 2000; export let loadTime = PLATFORM_TV ? 2500 : 1000; @@ -20,14 +24,20 @@ let checkStopInterval: ReturnType; let autoplayTimeout: ReturnType; let loadTimeout: ReturnType; + let pauseTimeout: ReturnType; - $: if (isInitialized && player?.playVideo && play) { + $: if (isInitialized && player?.playVideo && visible && play) { + clearTimeout(pauseTimeout); player.playVideo(); } else if (isInitialized && player?.pauseVideo && !play) { player.pauseVideo(); + } else if (isInitialized && player?.pauseVideo && !visible) { + pauseTimeout = setTimeout(() => { + player.pauseVideo(); + }, 1000); } - $: if (didMount && !isInitialized && play) loadYouTubeAPI(); + $: if (didMount && !isInitialized && visible && play) loadYouTubeAPI(); function loadYouTubeAPI() { isInitialized = true; console.log('Loading YouTube API for ' + videoId); @@ -49,6 +59,7 @@ clearInterval(checkStopInterval); clearTimeout(autoplayTimeout); clearTimeout(loadTimeout); + clearTimeout(pauseTimeout); isPlayerReady = false; if (!player) return; @@ -169,11 +180,11 @@ }); $: { const el = document.getElementById(playerId); - if (el) el.style.opacity = isPlayerReady && play ? '1' : '0'; + if (el) el.style.opacity = isPlayerReady && visible ? '1' : '0'; } -
+
@@ -185,7 +196,7 @@ width: 100vw; height: 100vh; transform: translate(-50%, -50%) scale(1.6); - transition: transform 0.5s ease-in-out, opacity 1s ease-in-out; + transition: transform 0.5s ease-in-out, opacity 0.5s ease-in-out; z-index: 0; } diff --git a/src/lib/pages/TitlePages/HeroTitleInfo.svelte b/src/lib/pages/TitlePages/HeroTitleInfo.svelte index 15f0c4e..1f4f24f 100644 --- a/src/lib/pages/TitlePages/HeroTitleInfo.svelte +++ b/src/lib/pages/TitlePages/HeroTitleInfo.svelte @@ -1,12 +1,10 @@ @@ -26,18 +24,28 @@
- {#each properties.filter((p) => !!p.label) as property, i} + {#each properties.filter((p) => !!p.label || !!p.icon) as property, i} {#if i !== 0} {/if} {#if property.href}

- {property.label} + + {#if property.label} + {property.label} + {:else if property.icon} + + {/if} +

- {:else} + {:else if property.label}

{property.label}

+ {:else if property.icon} + + + {/if} {/each}
diff --git a/src/lib/pages/TitlePages/HeroTitleInfo.ts b/src/lib/pages/TitlePages/HeroTitleInfo.ts new file mode 100644 index 0000000..465066f --- /dev/null +++ b/src/lib/pages/TitlePages/HeroTitleInfo.ts @@ -0,0 +1,7 @@ +import type { ComponentType } from 'svelte'; + +export type TitleInfoProperty = { + href?: string; + label?: string; + icon?: ComponentType; +}; diff --git a/src/lib/pages/TitlePages/MoviePage/MoviePage.svelte b/src/lib/pages/TitlePages/MoviePage/MoviePage.svelte index 2762877..e43da01 100644 --- a/src/lib/pages/TitlePages/MoviePage/MoviePage.svelte +++ b/src/lib/pages/TitlePages/MoviePage/MoviePage.svelte @@ -12,9 +12,11 @@ import { tmdbMovieDataStore } from '$lib/stores/data.store'; import { useMovieUserData } from '$lib/stores/media-user-data.store'; import { formatMinutesToTime, formatThousands } from '$lib/utils'; - import { Bookmark, Check, ExternalLink, Minus, Play } from 'radix-icons-svelte'; + import { Bookmark, Check, ExternalLink, Minus, Play, Video } from 'radix-icons-svelte'; import { onDestroy } from 'svelte'; import HeroTitleInfo from '../HeroTitleInfo.svelte'; + import { localSettings } from '$lib/stores/localstorage.store'; + import type { TitleInfoProperty } from '../HeroTitleInfo'; export let id: string; const tmdbId = Number(id); @@ -54,8 +56,12 @@ ); }); - let titleProperties: { href?: string; label: string }[] = []; + let titleProperties: TitleInfoProperty[] = []; $tmdbMovie.then((movie) => { + const trailer = movie?.videos?.results?.find( + (video) => video.type === 'Trailer' && video.site === 'YouTube' + )?.key; + if (movie?.runtime) { titleProperties.push({ label: formatMinutesToTime(movie.runtime) @@ -74,6 +80,13 @@ label: movie.genres.map((g) => g.name).join(', ') }); } + + if ($localSettings.enableTrailers && trailer) { + titleProperties.push({ + icon: Video, + href: `https://www.youtube.com/watch?v=${trailer}` + }); + } }); onDestroy(() => { diff --git a/src/lib/pages/TitlePages/SeriesPage/SeriesPage.svelte b/src/lib/pages/TitlePages/SeriesPage/SeriesPage.svelte index 8c5e913..c7da4ed 100644 --- a/src/lib/pages/TitlePages/SeriesPage/SeriesPage.svelte +++ b/src/lib/pages/TitlePages/SeriesPage/SeriesPage.svelte @@ -12,10 +12,12 @@ import { scrollIntoView, useRegistrar } from '$lib/selectable'; import { useSeriesUserData } from '$lib/stores/media-user-data.store'; import { formatThousands } from '$lib/utils'; - import { Bookmark, Check, ExternalLink, Minus, Play } from 'radix-icons-svelte'; + import { Bookmark, Check, ExternalLink, Minus, Play, Video } from 'radix-icons-svelte'; import { onDestroy } from 'svelte'; import TitleProperties from '../HeroTitleInfo.svelte'; import EpisodeGrid from './EpisodeGrid.svelte'; + import { localSettings } from '$lib/stores/localstorage.store'; + import type { TitleInfoProperty } from '../HeroTitleInfo'; export let id: string; const tmdbId = Number(id); @@ -56,8 +58,12 @@ ); }); - let titleProperties: { href?: string; label: string }[] = []; + let titleProperties: TitleInfoProperty[] = []; $tmdbSeries.then((series) => { + const trailer = series?.videos?.results?.find( + (video) => video.type === 'Trailer' && video.site === 'YouTube' + )?.key; + if (series && series.status !== 'Ended') { titleProperties.push({ label: `Since ${new Date(series.first_air_date || Date.now())?.getFullYear()}` @@ -82,6 +88,13 @@ label: series.genres.map((g) => g.name).join(', ') }); } + + if ($localSettings.enableTrailers && trailer) { + titleProperties.push({ + icon: Video, + href: `https://www.youtube.com/watch?v=${trailer}` + }); + } }); onDestroy(() => {