feat: Add downloads to library page

This commit is contained in:
Aleksi Lassila
2024-05-31 18:25:57 +03:00
parent 8d779d4d7b
commit 8acfd0f4d9
9 changed files with 220 additions and 157 deletions

View File

@@ -9,7 +9,7 @@
<div
class={classNames(
'relative',
'relative hover:scale-105',
{
'scale-105': hasFocus
// 'transition-all': enabled

View File

@@ -8,12 +8,14 @@
import type { Readable } from 'svelte/store';
import AnimatedSelection from '../AnimateScale.svelte';
import { navigate } from '../StackRouter/StackRouter';
import { getCardDimensions } from '../../utils';
export let tmdbId: number | undefined = undefined;
export let tvdbId: number | undefined = undefined;
export let jellyfinId: string = '';
export let type: TitleType = 'movie';
export let backdropUrl: string;
export let group = false;
export let title = '';
export let subtitle = '';
@@ -27,125 +29,129 @@
let hasFocus: Readable<boolean>;
let dimensions = getDimensions(window.innerWidth);
function getDimensions(viewportWidth: number) {
const minWidth = 240;
const margin = 128;
const gap = 32;
const cols = Math.floor((gap - 2 * margin + viewportWidth) / (minWidth + gap));
const scale = -(gap * (cols - 1) + 2 * margin - viewportWidth) / (cols * minWidth);
const newWidth = minWidth * scale;
const newHeight = (3 / 2) * newWidth;
return {
width: newWidth,
height: newHeight
};
}
let dimensions = getCardDimensions(window.innerWidth);
</script>
<svelte:window on:resize={(e) => (dimensions = getDimensions(e.currentTarget.innerWidth))} />
<svelte:window on:resize={(e) => (dimensions = getCardDimensions(e.currentTarget.innerWidth))} />
<AnimatedSelection hasFocus={$hasFocus}>
<Container
{disabled}
on:clickOrSelect={() => {
if (tmdbId || tvdbId) {
// navigate(navigateWithType ? `${type}/${tmdbId || tvdbId}` : `${tmdbId || tvdbId}`);
navigate(`/${type}/${tmdbId || tvdbId}`);
}
}}
on:enter
class={classNames(
'relative flex flex-shrink-0 rounded-xl group hover:text-inherit overflow-hidden text-left cursor-pointer',
'selectable',
{
'aspect-video': orientation === 'landscape',
'aspect-[2/3]': orientation === 'portrait',
'w-32 h-48': size === 'sm' && orientation === 'portrait',
'h-32 w-56': size === 'sm' && orientation === 'landscape',
'w-44 h-64': size === 'md' && orientation === 'portrait',
'h-44 w-80': size === 'md' && orientation === 'landscape',
// 'w-60 h-96': size === 'lg' && orientation === 'portrait',
'h-60 w-96': size === 'lg' && orientation === 'landscape',
'w-full h-96': size === 'dynamic',
'shadow-lg': shadow
}
)}
style={`width: ${dimensions.width}px; height: ${dimensions.height}px;`}
focusOnClick
bind:hasFocus
>
<LazyImg
src={backdropUrl}
class="absolute inset-0 group-hover:scale-105 transition-transform"
/>
<!-- This is the tinted and blurred hover overlay -->
<!-- <div-->
<!-- class="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity bg-black"-->
<!-- style="filter: blur(50px); transform: scale(3);"-->
<!-- >-->
<!-- <LazyImg src={backdropUrl} />-->
<!-- </div>-->
<div class="relative">
{#if group}
<div class="absolute inset-0 scale-95 translate-y-3.5 opacity-50">
<LazyImg src={backdropUrl} class="absolute inset-0 rounded-xl" />
<div class="absolute inset-0 bg-white/10 rounded-xl" />
<!-- Mouse hover details -->
<!-- <div-->
<!-- class={classNames(-->
<!-- 'flex-1 flex flex-col justify-between bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity z-[1]',-->
<!-- {-->
<!-- 'py-2 px-3': true-->
<!-- }-->
<!-- )}-->
<!-- >-->
<!-- <div class="flex justify-self-start justify-between">-->
<!-- <slot name="top-left">-->
<!-- <div>-->
<!-- <h1 class="text-zinc-100 font-bold line-clamp-2 text-lg">{title}</h1>-->
<!-- <h2 class="text-zinc-300 text-sm font-medium line-clamp-2">{subtitle}</h2>-->
<!-- </div>-->
<!-- </slot>-->
<!-- <slot name="top-right">-->
<!-- <div />-->
<!-- </slot>-->
<!-- </div>-->
<!-- <div class="flex justify-self-end justify-between">-->
<!-- <slot name="bottom-left">-->
<!-- <div>-->
<!-- {#if rating}-->
<!-- <h2 class="flex items-center gap-1.5 text-sm text-zinc-300 font-medium">-->
<!-- <Star />{rating.toFixed(1)}-->
<!-- </h2>-->
<!-- {/if}-->
<!-- </div>-->
<!-- </slot>-->
<!-- <slot name="bottom-right">-->
<!-- <div />-->
<!-- </slot>-->
<!-- </div>-->
<!-- </div>-->
<LazyImg
src={backdropUrl}
class="absolute inset-0 scale-95 translate-y-4 rounded-xl opacity-25"
/>
<div class="absolute inset-0 scale-95 translate-y-4 rounded-xl bg-white/10 opacity-25" />
</div>
{/if}
<AnimatedSelection hasFocus={$hasFocus}>
<Container
{disabled}
on:clickOrSelect={() => {
if (tmdbId || tvdbId) navigate(`/${type}/${tmdbId || tvdbId}`);
}}
on:enter
class={classNames(
'relative flex flex-shrink-0 rounded-xl group hover:text-inherit overflow-hidden text-left cursor-pointer',
'selectable',
{
'aspect-video': orientation === 'landscape',
'aspect-[2/3]': orientation === 'portrait',
'w-32 h-48': size === 'sm' && orientation === 'portrait',
'h-32 w-56': size === 'sm' && orientation === 'landscape',
'w-44 h-64': size === 'md' && orientation === 'portrait',
'h-44 w-80': size === 'md' && orientation === 'landscape',
// 'w-60 h-96': size === 'lg' && orientation === 'portrait',
'h-60 w-96': size === 'lg' && orientation === 'landscape',
'w-full h-96': size === 'dynamic',
'shadow-lg': shadow
}
)}
style={`width: ${dimensions.width}px; height: ${dimensions.height}px;`}
focusOnClick
bind:hasFocus
>
<!--{#if !group}-->
<LazyImg src={backdropUrl} class="absolute inset-0" />
<!--{:else}-->
<!-- <LazyImg src={backdropUrl} class="absolute inset-0 opacity-10 " />-->
<!-- <div class="absolute inset-0 bg-white/10 opacity-10" />-->
<!-- <LazyImg-->
<!-- src={backdropUrl}-->
<!-- class="absolute inset-0 scale-95 translate-y-[0.5rem] rounded-xl opacity-25"-->
<!-- />-->
<!-- <div class="absolute inset-0 bg-white/10 opacity-10" />-->
<!-- <LazyImg-->
<!-- src={backdropUrl}-->
<!-- class="absolute inset-0 scale-90 translate-y-[1.125rem] rounded-xl "-->
<!-- />-->
<!--{/if}-->
<!-- This is the tinted and blurred hover overlay -->
<!-- <div-->
<!-- class="absolute inset-0 opacity-0 group-hover:opacity-30 transition-opacity bg-black"-->
<!-- style="filter: blur(50px); transform: scale(3);"-->
<!-- >-->
<!-- <LazyImg src={backdropUrl} />-->
<!-- </div>-->
<!-- Play Button -->
{#if jellyfinId}
<div class="absolute inset-0 flex items-center justify-center z-[1]">
<PlayButton
on:click={(e) => {
e.preventDefault();
jellyfinId && true; //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 bg-gradient-to-t ease-in-out z-[1]"
>
<ProgressBar {progress} />
</div>
{/if}
</Container>
</AnimatedSelection>
<!-- Mouse hover details -->
<!-- <div-->
<!-- class={classNames(-->
<!-- 'flex-1 flex flex-col justify-between bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity z-[1]',-->
<!-- {-->
<!-- 'py-2 px-3': true-->
<!-- }-->
<!-- )}-->
<!-- >-->
<!-- <div class="flex justify-self-start justify-between">-->
<!-- <slot name="top-left">-->
<!-- <div>-->
<!-- <h1 class="text-zinc-100 font-bold line-clamp-2 text-lg">{title}</h1>-->
<!-- <h2 class="text-zinc-300 text-sm font-medium line-clamp-2">{subtitle}</h2>-->
<!-- </div>-->
<!-- </slot>-->
<!-- <slot name="top-right">-->
<!-- <div />-->
<!-- </slot>-->
<!-- </div>-->
<!-- <div class="flex justify-self-end justify-between">-->
<!-- <slot name="bottom-left">-->
<!-- <div>-->
<!-- {#if rating}-->
<!-- <h2 class="flex items-center gap-1.5 text-sm text-zinc-300 font-medium">-->
<!-- <Star />{rating.toFixed(1)}-->
<!-- </h2>-->
<!-- {/if}-->
<!-- </div>-->
<!-- </slot>-->
<!-- <slot name="bottom-right">-->
<!-- <div />-->
<!-- </slot>-->
<!-- </div>-->
<!-- </div>-->
<!-- Play Button -->
{#if jellyfinId}
<div class="absolute inset-0 flex items-center justify-center z-[1]">
<PlayButton
on:click={(e) => {
e.preventDefault();
jellyfinId && true; //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 bg-gradient-to-t ease-in-out z-[1]"
>
<ProgressBar {progress} />
</div>
{/if}
</Container>
</AnimatedSelection>
</div>

View File

@@ -2,10 +2,14 @@
import Container from '../../Container.svelte';
import { onMount } from 'svelte';
import classNames from 'classnames';
import { getCardDimensions } from '../utils';
export let direction: 'horizontal' | 'vertical' = 'vertical';
let cols: number = 1;
let cols = getCardDimensions(window.innerWidth).columns;
$: console.log('cols', cols);
// let cols: number = 1;
const calculateRows = () => {
const width = window.innerWidth;
if (direction === 'vertical') {
@@ -34,11 +38,13 @@
}
};
onMount(() => {
calculateRows();
});
// onMount(() => {
// calculateRows();
// });
</script>
<svelte:window on:resize={(e) => (cols = getCardDimensions(e.currentTarget.innerWidth).columns)} />
<Container
direction="grid"
gridCols={cols}
@@ -46,15 +52,16 @@
'grid gap-x-8 gap-y-8',
{
'grid-cols-1 md:grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 3xl:grid-cols-4':
direction === 'horizontal',
'grid-cols-4 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6': direction === 'vertical'
direction === 'horizontal'
// 'grid-cols-4 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6': direction === 'vertical'
},
$$restProps.class
)}
style={`grid-template-columns: repeat(${cols}, minmax(0, 1fr));`}
on:mount
>
<slot />
</Container>
<svelte:window on:resize={calculateRows} />
<!--<svelte:window on:resize={calculateRows} />-->

View File

@@ -13,7 +13,7 @@
<div
class={classNames(
'transition-opacity duration-300',
'transition-opacity duration-300 overflow-hidden',
{
'opacity-0': !loaded,
'opacity-100': loaded