feat: Improved poster styles and image loading
This commit is contained in:
@@ -285,11 +285,11 @@ export const getJellyfinUsers = async (
|
||||
.then((res) => res.data || [])
|
||||
.catch(() => []);
|
||||
|
||||
export const getJellyfinPosterUrl = (item: JellyfinItem, quality = 100) =>
|
||||
export const getJellyfinPosterUrl = (item: JellyfinItem, quality = 100, original = false) =>
|
||||
item.ImageTags?.Primary
|
||||
? `${get(settings).jellyfin.baseUrl}/Items/${item?.Id}/Images/Primary?quality=${quality}&tag=${
|
||||
item?.ImageTags?.Primary
|
||||
}`
|
||||
? `${get(settings).jellyfin.baseUrl}/Items/${item?.Id}/Images/Primary?quality=${quality}${
|
||||
original ? '' : '&fillWidth=432'
|
||||
}&tag=${item?.ImageTags?.Primary}`
|
||||
: '';
|
||||
|
||||
export const getJellyfinBackdrop = (item: JellyfinItem, quality = 100) => {
|
||||
|
||||
@@ -223,8 +223,11 @@ export const getRadarrQualityProfiles = async (
|
||||
)
|
||||
.then((res) => res.data || []);
|
||||
|
||||
export function getRadarrPosterUrl(item: RadarrMovie) {
|
||||
return (
|
||||
get(settings).radarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '')
|
||||
);
|
||||
export function getRadarrPosterUrl(item: RadarrMovie, original = false) {
|
||||
const url =
|
||||
get(settings).radarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '');
|
||||
|
||||
if (!original) return url.replace('poster.jpg', `poster-${500}.jpg`);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -307,8 +307,11 @@ export const getSonarrLanguageProfiles = async (
|
||||
)
|
||||
.then((res) => res.data || []);
|
||||
|
||||
export function getSonarrPosterUrl(item: SonarrSeries) {
|
||||
return (
|
||||
get(settings).sonarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '')
|
||||
);
|
||||
export function getSonarrPosterUrl(item: SonarrSeries, original = false) {
|
||||
const url =
|
||||
get(settings).sonarr.baseUrl + (item.images?.find((i) => i.coverType === 'poster')?.url || '');
|
||||
|
||||
if (!original) return url.replace('poster.jpg', `poster-${500}.jpg`);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
28
src/lib/components/LazyImg.svelte
Normal file
28
src/lib/components/LazyImg.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import classNames from 'classnames';
|
||||
|
||||
export let src: string;
|
||||
export let alt: string = '';
|
||||
|
||||
let loaded = false;
|
||||
|
||||
function handleLoad() {
|
||||
loaded = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<img
|
||||
{src}
|
||||
{alt}
|
||||
class={classNames(
|
||||
'transition-opacity',
|
||||
{
|
||||
'opacity-0': !loaded,
|
||||
'opacity-100': loaded
|
||||
},
|
||||
$$restProps.class
|
||||
)}
|
||||
style="object-fit: cover; width: 100%; height: 100%;"
|
||||
loading="lazy"
|
||||
on:load={handleLoad}
|
||||
/>
|
||||
@@ -4,6 +4,7 @@
|
||||
import PlayButton from '../PlayButton.svelte';
|
||||
import ProgressBar from '../ProgressBar.svelte';
|
||||
import { playerState } from '../VideoPlayer/VideoPlayer';
|
||||
import LazyImg from '../LazyImg.svelte';
|
||||
|
||||
export let tmdbId: number | undefined = undefined;
|
||||
export let tvdbId: number | undefined = undefined;
|
||||
@@ -21,22 +22,20 @@
|
||||
|
||||
<a
|
||||
href={tmdbId || tvdbId ? `/${type}/${tmdbId || tvdbId}` : '#'}
|
||||
style={"background-image: url('" + backdropUrl + "');"}
|
||||
class={classNames(
|
||||
'relative flex shadow-lg rounded-lg aspect-[2/3] bg-center bg-cover w-44 selectable group hover:text-inherit flex-shrink-0',
|
||||
'relative flex shadow-lg rounded-xl aspect-[2/3] w-44 selectable group hover:text-inherit flex-shrink-0 overflow-hidden',
|
||||
{
|
||||
'w-44': size === 'md',
|
||||
'w-full': size === 'dynamic'
|
||||
}
|
||||
)}
|
||||
>
|
||||
<LazyImg src={backdropUrl} class="absolute inset-0 group-hover:scale-105 transition-transform" />
|
||||
<div
|
||||
class={classNames(
|
||||
'flex-1 flex flex-col justify-between bg-darken opacity-0 group-hover:opacity-100 transition-opacity',
|
||||
'flex-1 flex flex-col justify-between bg-darken opacity-0 group-hover:opacity-100 transition-opacity z-[1]',
|
||||
{
|
||||
'py-2 px-3': true
|
||||
// 'pb-4': progress,
|
||||
// 'pb-2': !progress
|
||||
}
|
||||
)}
|
||||
>
|
||||
@@ -60,21 +59,23 @@
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-darken group-hover:opacity-0 transition-opacity"
|
||||
/>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<PlayButton
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
jellyfinId && playerState.streamJellyfinId(jellyfinId);
|
||||
}}
|
||||
class="sm:opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div
|
||||
class="absolute inset-0 bg-gradient-to-t from-darken group-hover:opacity-0 transition-opacity z-[1]"
|
||||
/> -->
|
||||
{#if jellyfinId}
|
||||
<div class="absolute inset-0 flex items-center justify-center z-[1]">
|
||||
<PlayButton
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
jellyfinId && playerState.streamJellyfinId(jellyfinId);
|
||||
}}
|
||||
class="sm:opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if progress}
|
||||
<div
|
||||
class="absolute bottom-2 lg:bottom-3 inset-x-2 lg:inset-x-3 group-hover:opacity-0 transition-opacity bg-gradient-to-t ease-in-out"
|
||||
class="absolute bottom-2 lg:bottom-3 inset-x-2 lg:inset-x-3 group-hover:opacity-0 transition-opacity bg-gradient-to-t ease-in-out z-[1]"
|
||||
>
|
||||
<ProgressBar {progress} />
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LibraryItems from './LibraryItems.svelte';
|
||||
import { capitalize } from '$lib/utils';
|
||||
|
||||
let openNextUpTab: 'downloading' | 'nextUp' = 'downloading';
|
||||
let noItems = false;
|
||||
@@ -38,16 +39,19 @@
|
||||
$servarrDownloadsStore.sonarrDownloads?.map((item) => ({
|
||||
tvdbId: item.series.tvdbId,
|
||||
title: item.series.title || '',
|
||||
subtitle: item.series.genres?.join(', ') || '',
|
||||
subtitle:
|
||||
`S${item.episode?.seasonNumber}E${item.episode?.episodeNumber} • ` +
|
||||
capitalize(item.status || ''),
|
||||
type: 'series',
|
||||
progress: 100 * (((item.size || 0) - (item.sizeleft || 0)) / (item.size || 1)),
|
||||
backdropUrl: item.series.images?.find((i) => i.coverType === 'poster')?.url || ''
|
||||
})) || [];
|
||||
console.log($servarrDownloadsStore.radarrDownloads);
|
||||
|
||||
const radarrProps: ComponentProps<Poster>[] =
|
||||
$servarrDownloadsStore.radarrDownloads?.map((item) => ({
|
||||
tmdbId: item.movie.tmdbId,
|
||||
title: item.movie.title || '',
|
||||
subtitle: item.movie.genres?.join(', ') || '',
|
||||
subtitle: capitalize(item.status || ''),
|
||||
type: 'movie',
|
||||
backdropUrl: item.movie.images?.find((i) => i.coverType === 'poster')?.url || '',
|
||||
progress: 100 * (((item.size || 0) - (item.sizeleft || 0)) / (item.size || 1))
|
||||
@@ -104,7 +108,7 @@
|
||||
type="primary"
|
||||
on:click={() => showcase?.Id && playerState.streamJellyfinId(showcase?.Id)}
|
||||
>
|
||||
Stream<ChevronRight size={20} />
|
||||
Watch<ChevronRight size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
href={`/${showcase?.Type === 'Movie' ? 'movie' : 'series'}/${
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<UiCarousel>
|
||||
<div class="flex gap-6 text-xl font-medium text-zinc-400">
|
||||
<div class="flex gap-6 text-lg font-medium text-zinc-400">
|
||||
<button
|
||||
class={classNames('hover:text-zinc-300 selectable rounded px-1 -mx-1', {
|
||||
'text-zinc-200': openTab === 'available'
|
||||
|
||||
Reference in New Issue
Block a user