Updated Jellyfin media item parsers, added startIndex && limit params to YTMusic getPlaylistItems() method
This commit is contained in:
@@ -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()]) }
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user