Updated Jellyfin media item parsers, added startIndex && limit params to YTMusic getPlaylistItems() method

This commit is contained in:
Eclypsed
2024-05-29 01:25:17 -04:00
parent 11497f8b91
commit a98b258a03
10 changed files with 303 additions and 230 deletions
@@ -0,0 +1,24 @@
<script lang="ts">
import Loader from '$lib/components/util/loader.svelte'
import type { PageData } from './$types'
export let data: PageData
</script>
<main>
{#await data.playlistDetails}
<Loader />
{:then [playlist, items]}
<section class="flex gap-8">
<img class="h-60" src="/api/remoteImage?url={playlist.thumbnailUrl}" alt="{playlist.name} cover art" />
<div>
<div class="text-4xl">{playlist.name}</div>
</div>
</section>
{#each items as item}
<div>{item.name}</div>
{/each}
{:catch}
<div>Failed to fetch playlist</div>
{/await}
</main>
@@ -0,0 +1,22 @@
import type { PageLoad } from './$types'
export const load: PageLoad = async ({ fetch, url }) => {
const connectionId = url.searchParams.get('connection')
const id = url.searchParams.get('id')
async function getPlaylist() {
const playlistResponse = (await fetch(`/api/connections/${connectionId}/playlist?id=${id}`, {
credentials: 'include',
}).then((response) => response.json())) as { playlist: Playlist }
return playlistResponse.playlist
}
async function getPlaylistItems() {
const itemsResponse = (await fetch(`/api/connections/${connectionId}/playlist/${id}/items`, {
credentials: 'include',
}).then((response) => response.json())) as { items: Song[] }
return itemsResponse.items
}
return { playlistDetails: Promise.all([getPlaylist(), getPlaylistItems()]) }
}
+3 -53
View File
@@ -1,66 +1,16 @@
<script lang="ts">
import { goto } from '$app/navigation'
import { queue } from '$lib/stores'
import type { PageServerData } from './$types'
let queueRef = $queue // This nonsense is to prevent an bug that causes svelte to throw an error when setting a property of the queue directly
import MediaCard from '$lib/components/media/mediaCard.svelte'
export let data: PageServerData
const formatTime = (seconds: number): string => {
seconds = Math.floor(seconds)
const hours = Math.floor(seconds / 3600)
seconds = seconds - hours * 3600
const minutes = Math.floor(seconds / 60)
seconds = seconds - minutes * 60
return hours > 0 ? `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` : `${minutes}:${seconds.toString().padStart(2, '0')}`
}
</script>
{#if data.searchResults}
{#await data.searchResults then searchResults}
<section class="flex w-full flex-col items-center gap-2">
<section class="flex w-full flex-wrap gap-6">
{#each searchResults as searchResult}
<div class="flex h-20 w-full max-w-screen-md gap-4 bg-black p-2">
<button
id="searchResult"
on:click={() => {
if (searchResult.type === 'song') {
queueRef.current = searchResult
} else {
goto(`/details/${searchResult.type}?id=${searchResult.id}&connection=${searchResult.connection.id}`)
}
}}
class="grid aspect-square h-full place-items-center bg-cover bg-center bg-no-repeat"
style="--thumbnail: url('/api/remoteImage?url={'thumbnailUrl' in searchResult ? searchResult.thumbnailUrl : searchResult.profilePicture}')"
>
<i class="fa-solid fa-play opacity-0" />
</button>
<div>
<div>{searchResult.name}{searchResult.type === 'song' && searchResult.album?.name ? ` - ${searchResult.album.name}` : ''}</div>
{#if 'artists' in searchResult && searchResult.artists}
<div>{searchResult.artists === 'Various Artists' ? searchResult.artists : searchResult.artists.map((artist) => artist.name).join(', ')}</div>
{:else if 'createdBy' in searchResult && searchResult.createdBy}
<div>{searchResult.createdBy?.name}</div>
{/if}
</div>
{#if 'duration' in searchResult && searchResult.duration}
<span class="justify-self-end">{formatTime(searchResult.duration)}</span>
{/if}
</div>
<MediaCard mediaItem={searchResult} />
{/each}
</section>
{/await}
{/if}
<style>
#searchResult {
background-image: var(--thumbnail);
}
#searchResult:hover {
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), var(--thumbnail);
}
#searchResult:hover > i {
opacity: 100%;
}
</style>