Database overhall with knex.js, some things untested
This commit is contained in:
@@ -12,18 +12,16 @@
|
||||
export let linked = true
|
||||
</script>
|
||||
|
||||
<div class="break-keep">
|
||||
<div class="break-words break-keep">
|
||||
{#if 'artists' in mediaItem && mediaItem.artists && typeof mediaItem.artists === 'string'}
|
||||
{mediaItem.artists}
|
||||
{:else if 'artists' in mediaItem && mediaItem.artists && typeof mediaItem.artists !== 'string' && mediaItem.artists.length > 0}
|
||||
{#each mediaItem.artists as artist, index}
|
||||
{@const needsComma = index < mediaItem.artists.length - 1}
|
||||
{#if linked}
|
||||
<a class="hover:underline focus:underline" href="/details/artist?id={artist.id}&connection={mediaItem.connection.id}">{artist.name}</a>
|
||||
<a class:needsComma class="hover:underline focus:underline" href="/details/artist?id={artist.id}&connection={mediaItem.connection.id}">{artist.name}</a>
|
||||
{:else}
|
||||
<span>{artist.name}</span>
|
||||
{/if}
|
||||
{#if index < mediaItem.artists.length - 1}
|
||||
<span style="margin-left: -0.25em; margin-right: 0.25em">,</span>
|
||||
<span class:needsComma class="artist-name">{artist.name}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if 'uploader' in mediaItem && mediaItem.uploader}
|
||||
@@ -40,3 +38,10 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.needsComma::after {
|
||||
content: ',';
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
|
||||
let imageContainer: HTMLDivElement
|
||||
|
||||
// TODO: Implement auto-resizing
|
||||
function updateImage(newThumbnailURL: string) {
|
||||
if (!imageContainer) return
|
||||
|
||||
const width = imageContainer.clientWidth
|
||||
const height = imageContainer.clientHeight
|
||||
const width = imageContainer.clientWidth * 1.5 // 1.5x is a good compromise between sharpness and performance
|
||||
const height = imageContainer.clientHeight * 1.5
|
||||
|
||||
const newImage = new Image(width, height)
|
||||
imageContainer.appendChild(newImage)
|
||||
@@ -57,8 +58,8 @@
|
||||
}
|
||||
|
||||
newImage.onerror = () => {
|
||||
removeOldImage()
|
||||
newImage.style.opacity = '1'
|
||||
console.error(`Image from url: ${newThumbnailURL} failed to update`)
|
||||
imageContainer.removeChild(newImage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
import ScrollingText from '$lib/components/util/scrollingText.svelte'
|
||||
import ArtistList from './artistList.svelte'
|
||||
|
||||
// NEW IDEA: Only have the miniplayer for controls and for the expanded view just make it one large Videoplayer.
|
||||
// That way we can target the player to be the size of YouTube's default player. Then move the Queue view to it's own
|
||||
// dedicated sidebar like in spotify.
|
||||
|
||||
$: currentlyPlaying = $queue.current
|
||||
|
||||
let expanded = false
|
||||
@@ -45,15 +49,15 @@
|
||||
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: media.name,
|
||||
artist: media.artists?.map((artist) => artist.name).join(', ') || media.uploader?.name,
|
||||
artist: media.artists?.map((artist) => artist.name).join(', ') ?? media.uploader?.name,
|
||||
album: media.album?.name,
|
||||
artwork: [
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=96`, sizes: '96x96', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=128`, sizes: '128x128', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=192`, sizes: '192x192', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=256`, sizes: '256x256', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=384`, sizes: '384x384', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=512`, sizes: '512x512', type: 'image/png' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=96`, sizes: '96x96' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=128`, sizes: '128x128' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=192`, sizes: '192x192' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=256`, sizes: '256x256' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=384`, sizes: '384x384' },
|
||||
{ src: `/api/remoteImage?url=${media.thumbnailUrl}&maxWidth=512`, sizes: '512x512' },
|
||||
],
|
||||
})
|
||||
}
|
||||
@@ -103,7 +107,7 @@
|
||||
{#if !expanded}
|
||||
<main in:fade={{ duration: 75, delay: 500 }} out:fade={{ duration: 75 }} class="flex h-20 w-full gap-10 pr-8">
|
||||
<section class="flex w-80 gap-3">
|
||||
<div class="relative h-full w-20 min-w-20">
|
||||
<div class="relative h-full w-20 min-w-20 overflow-clip rounded-xl">
|
||||
<LazyImage thumbnailUrl={currentlyPlaying.thumbnailUrl} alt={`${currentlyPlaying.name} jacket`} objectFit={'cover'} />
|
||||
</div>
|
||||
<section class="flex flex-grow flex-col justify-center gap-1">
|
||||
@@ -121,16 +125,14 @@
|
||||
<IconButton on:click={() => $queue.previous()}>
|
||||
<i slot="icon" class="fa-solid fa-backward-step text-xl" />
|
||||
</IconButton>
|
||||
<div class="aspect-square h-full rounded-full border border-neutral-700">
|
||||
<IconButton on:click={() => (paused = !paused)}>
|
||||
<div slot="icon">
|
||||
{#if waiting}
|
||||
<Loader size={1.5} />
|
||||
{:else}
|
||||
<i class="fa-solid {paused ? 'fa-play' : 'fa-pause'}" />
|
||||
{/if}
|
||||
</div>
|
||||
</IconButton>
|
||||
<div class="relative aspect-square h-full rounded-full border border-neutral-700">
|
||||
{#if waiting}
|
||||
<Loader size={1.5} />
|
||||
{:else}
|
||||
<IconButton on:click={() => (paused = !paused)}>
|
||||
<i slot="icon" class="fa-solid {paused ? 'fa-play' : 'fa-pause'}" />
|
||||
</IconButton>
|
||||
{/if}
|
||||
</div>
|
||||
<IconButton on:click={() => $queue.clear()}>
|
||||
<i slot="icon" class="fa-solid fa-stop text-xl" />
|
||||
@@ -156,7 +158,10 @@
|
||||
</div>
|
||||
</section>
|
||||
<section class="flex items-center justify-end gap-2.5 py-6 text-lg">
|
||||
<div id="volume-slider" class="mx-4 flex h-10 w-44 flex-row-reverse items-center gap-3">
|
||||
<div id="volume-slider" class="mx-4 flex h-10 w-44 items-center gap-3">
|
||||
<IconButton on:click={() => (volume = volume > 0 ? 0 : Number(localStorage.getItem('volume')))}>
|
||||
<i slot="icon" class="fa-solid {volume > maxVolume / 2 ? 'fa-volume-high' : volume > 0 ? 'fa-volume-low' : 'fa-volume-xmark'}" />
|
||||
</IconButton>
|
||||
<Slider
|
||||
bind:value={volume}
|
||||
max={maxVolume}
|
||||
@@ -164,9 +169,6 @@
|
||||
if (volume > 0) localStorage.setItem('volume', volume.toString())
|
||||
}}
|
||||
/>
|
||||
<IconButton on:click={() => (volume = volume > 0 ? 0 : Number(localStorage.getItem('volume')))}>
|
||||
<i slot="icon" class="fa-solid {volume > maxVolume / 2 ? 'fa-volume-high' : volume > 0 ? 'fa-volume-low' : 'fa-volume-xmark'}" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<IconButton on:click={() => (shuffled ? $queue.reorder() : $queue.shuffle())}>
|
||||
<i slot="icon" class="fa-solid fa-shuffle {shuffled ? 'text-lazuli-primary' : 'text-white'}" />
|
||||
@@ -213,7 +215,7 @@
|
||||
</section>
|
||||
</section>
|
||||
<section class="px-8">
|
||||
<div id="progress-bar-expanded" class="mb-7">
|
||||
<div id="progress-bar-expanded" class="mb-6">
|
||||
<span bind:this={expandedCurrentTimeTimestamp} class="text-right" />
|
||||
<Slider
|
||||
bind:this={expandedProgressBar}
|
||||
@@ -324,7 +326,7 @@
|
||||
}
|
||||
#expanded-player {
|
||||
display: grid;
|
||||
grid-template-rows: calc(100% - 12rem) 12rem;
|
||||
grid-template-rows: calc(100% - 11rem) 11rem;
|
||||
}
|
||||
#song-queue-wrapper {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user