AutoImage replaced LazyImage && general improvements to components with style props

This commit is contained in:
Eclypsed
2024-07-22 02:53:52 -04:00
parent f10a184284
commit 455a01982a
32 changed files with 883 additions and 161 deletions

View File

@@ -9,25 +9,32 @@
$: currentlyPlaying = $queue.current
$: shuffled = $queue.isShuffled
let playerWidth: number
</script>
<main id="grid-wrapper" class="h-full">
<main id="grid-wrapper" class="relative h-full">
<Navbar on:opensidebar={sidebar.open} />
<Sidebar bind:this={sidebar} />
<section id="content-wrapper" class="overflow-y-scroll no-scrollbar">
<slot />
<div class="my-8">
<slot />
</div>
{#if currentlyPlaying}
<div bind:clientWidth={playerWidth} class="sticky {playerWidth > 800 ? 'bottom-0' : 'bottom-3 mx-3'} transition-all">
<MediaPlayer
mediaItem={currentlyPlaying}
{shuffled}
mediaSession={'mediaSession' in navigator ? navigator.mediaSession : null}
--border-radius={playerWidth > 800 ? '0' : '0.5rem'}
on:stop={() => $queue.clear()}
on:next={() => $queue.next()}
on:previous={() => $queue.previous()}
on:toggleShuffle={() => $queue.toggleShuffle()}
/>
</div>
{/if}
</section>
{#if currentlyPlaying}
<MediaPlayer
mediaItem={currentlyPlaying}
{shuffled}
mediaSession={'mediaSession' in navigator ? navigator.mediaSession : null}
on:stop={() => $queue.clear()}
on:next={() => $queue.next()}
on:previous={() => $queue.previous()}
on:toggleShuffle={() => $queue.toggleShuffle()}
/>
{/if}
</main>
<style>
@@ -35,4 +42,8 @@
display: grid;
grid-template-rows: min-content auto;
}
#content-wrapper {
display: grid;
grid-template-rows: auto min-content;
}
</style>

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import type { PageData } from './$types'
import Loader from '$lib/components/util/loader.svelte'
import MediaCard from '$lib/components/media/mediaCard.svelte'
import AlbumCard from '$lib/components/media/albumCard.svelte'
import PlaylistCard from '$lib/components/media/playlistCard.svelte'
export let data: PageData
</script>
@@ -11,9 +11,13 @@
{#await data.recommendations}
<Loader />
{:then recommendations}
<div id="card-wrapper" class="grid w-full gap-4 justify-self-center px-[5%] pt-8">
{#each recommendations.filter((item) => item.type === 'album') as album}
<AlbumCard {album} />
<div id="card-wrapper" class="grid w-full gap-4 justify-self-center px-[5%]">
{#each recommendations as mediaItem}
{#if mediaItem.type === 'album'}
<AlbumCard album={mediaItem} />
{:else if mediaItem.type === 'playlist'}
<PlaylistCard playlist={mediaItem} />
{/if}
{/each}
</div>
{/await}

View File

@@ -0,0 +1,14 @@
<script lang="ts">
import IconButton from '$lib/components/util/iconButton.svelte'
import IcBaselinePlus from '~icons/ic/baseline-plus'
</script>
<main class="flex flex-wrap justify-center">
<div class="grid aspect-square h-56 place-items-center">
<div class="aspect-square h-16">
<IconButton halo={true}>
<IcBaselinePlus slot="icon" class="text-4xl text-neutral-300" />
</IconButton>
</div>
</div>
</main>

View File

@@ -7,7 +7,6 @@
import { newestAlert } from '$lib/stores.js'
import type { PageServerData } from './$types.js'
import type { SubmitFunction } from '@sveltejs/kit'
import { getDeviceUUID } from '$lib/utils'
import { SvelteComponent, type ComponentType } from 'svelte'
import ConnectionProfile from './connectionProfile.svelte'
import { enhance } from '$app/forms'
@@ -21,6 +20,15 @@
data.connections.then((userConnections) => ('error' in userConnections ? (errorMessage = userConnections.error) : (connections = userConnections)))
function getDeviceUUID(): string {
const existingUUID = localStorage.getItem('deviceUUID')
if (existingUUID) return existingUUID
const newUUID = '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c: any) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16))
localStorage.setItem('deviceUUID', newUUID)
return newUUID
}
const authenticateJellyfin: SubmitFunction = ({ formData, cancel }) => {
const { serverUrl, username, password } = Object.fromEntries(formData)

View File

@@ -64,12 +64,12 @@ function modifyImageURL(imageURL: URL, options?: { maxWidth?: number; maxHeight?
switch (imageURL.origin) {
case 'https://i.ytimg.com':
case 'https://www.gstatic.com':
// These two origins correspond to images that can't have their size modified with search params, so we just return them at the default res
case 'https://music.youtube.com':
// These origins correspond to images that can't have their size modified with search params, so we just return them at the default res
return baseURL
case 'https://lh3.googleusercontent.com':
case 'https://yt3.googleusercontent.com':
case 'https://yt3.ggpht.com':
case 'https://music.youtube.com':
const fakeQueryParams = []
if (maxWidth) fakeQueryParams.push(`w${Math.min(maxWidth, MAX_YOUTUBE_THUMBNAIL_SCALAR_SIZE)}`)
if (maxHeight) fakeQueryParams.push(`h${Math.min(maxHeight, MAX_YOUTUBE_THUMBNAIL_SCALAR_SIZE)}`)

View File

@@ -0,0 +1,19 @@
import type { RequestHandler } from '@sveltejs/kit'
import { ConnectionFactory } from '$lib/server/api-helper'
export const GET: RequestHandler = async ({ url }) => {
const ids = url.searchParams.get('id')?.replace(/\s/g, '').split(',')
if (!ids) return new Response('Missing id query parameter', { status: 400 })
const connections = (await Promise.all(ids.map((id) => ConnectionFactory.getConnection(id).catch(() => null)))).filter((result): result is Connection => result !== null)
const getConnectionInfo = (connection: Connection) =>
connection.getConnectionInfo().catch((reason) => {
console.error(`Failed to fetch connection info: ${reason}`)
return null
})
const connectionInfo = (await Promise.all(connections.map(getConnectionInfo))).filter((connectionInfo): connectionInfo is ConnectionInfo => connectionInfo !== null)
return Response.json({ connections: connectionInfo })
}

View File

@@ -0,0 +1,14 @@
import type { RequestHandler } from '@sveltejs/kit'
import { ConnectionFactory } from '$lib/server/api-helper'
export const GET: RequestHandler = async ({ params, url }) => {
const connection = await ConnectionFactory.getConnection(params.connectionId!)
const albumId = url.searchParams.get('id')
if (!albumId) return new Response(`Missing id search parameter`, { status: 400 })
const album = await connection.getAlbum(albumId).catch(() => undefined)
if (!album) return new Response(`Failed to fetch album with id: ${albumId}`, { status: 400 })
return Response.json({ album })
}

View File

@@ -0,0 +1,11 @@
import type { RequestHandler } from '@sveltejs/kit'
import { ConnectionFactory } from '$lib/server/api-helper'
export const GET: RequestHandler = async ({ params }) => {
const connection = await ConnectionFactory.getConnection(params.connectionId!)
const items = await connection.getAlbumItems(params.albumId!).catch(() => null)
if (!items) return new Response(`Failed to fetch album with id: ${params.albumId!}`, { status: 400 })
return Response.json({ items })
}

View File

@@ -0,0 +1,19 @@
import type { RequestHandler } from '@sveltejs/kit'
import { ConnectionFactory } from '$lib/server/api-helper'
export const GET: RequestHandler = async ({ params, url }) => {
const connection = await ConnectionFactory.getConnection(params.connectionId!)
const playlistId = url.searchParams.get('id')
if (!playlistId) return new Response(`Missing id search parameter`, { status: 400 })
const response = await connection
.getPlaylistItems(playlistId)
.then((playlist) => Response.json({ playlist }))
.catch((error: TypeError | Error) => {
if (error instanceof TypeError) return new Response('Bad Request', { status: 400 })
return new Response('Failed to fetch playlist items', { status: 502 })
})
return response
}

View File

@@ -0,0 +1,25 @@
import type { RequestHandler } from '@sveltejs/kit'
import { ConnectionFactory } from '$lib/server/api-helper'
export const GET: RequestHandler = async ({ params, url }) => {
const connection = await ConnectionFactory.getConnection(params.connectionId!)
const startIndexString = url.searchParams.get('startIndex')
const limitString = url.searchParams.get('limit')
const numberStartIndex = Number(startIndexString)
const numberLimit = Number(limitString)
const startIndex = Number.isInteger(numberStartIndex) && numberStartIndex > 0 ? numberStartIndex : undefined
const limit = Number.isInteger(numberLimit) && numberLimit > 0 ? numberLimit : undefined
const response = await connection
.getPlaylistItems(params.playlistId!, { startIndex, limit })
.then((items) => Response.json({ items }))
.catch((error: TypeError | Error) => {
if (error instanceof TypeError) return new Response('Bad Request', { status: 400 })
return new Response('Failed to fetch playlist items', { status: 502 })
})
return response
}