Mixes are their own concept, not playlists: they live in their own Mixes/MixItems tables, cannot be edited and never show up among the user's playlists. Each item is either a Musify track or a YouTube Music reference, so YouTube songs stream directly instead of being downloaded. A daily Hangfire job seeds the mixes from the listening history: one discovery mix, one daily mix and one per top artist. The per-user YouTube filler query was already in the working tree and rides along here because it shares files with this change.
40 lines
963 B
Svelte
40 lines
963 B
Svelte
<script lang="ts">
|
|
import Cover from './Cover.svelte';
|
|
import { gradientForHue, hueFor } from '$lib/theme/color';
|
|
import { mixItemKey, type MixItem } from '$lib/mixes';
|
|
|
|
interface Props {
|
|
items: MixItem[];
|
|
hue: number;
|
|
class?: string;
|
|
}
|
|
|
|
let { items, hue, class: klass = '' }: Props = $props();
|
|
|
|
const tiles = $derived(items.slice(0, 4));
|
|
</script>
|
|
|
|
{#if tiles.length >= 4}
|
|
<div class="grid grid-cols-2 grid-rows-2 overflow-hidden {klass}">
|
|
{#each tiles as item (mixItemKey(item))}
|
|
<Cover
|
|
trackId={item.trackId ?? item.videoId ?? ''}
|
|
src={item.thumbnailUrl}
|
|
hue={hueFor(mixItemKey(item))}
|
|
size="small"
|
|
class="h-full w-full"
|
|
/>
|
|
{/each}
|
|
</div>
|
|
{:else if tiles.length > 0}
|
|
<Cover
|
|
trackId={tiles[0].trackId ?? tiles[0].videoId ?? ''}
|
|
src={tiles[0].thumbnailUrl}
|
|
hue={hueFor(mixItemKey(tiles[0]))}
|
|
size="medium"
|
|
class={klass}
|
|
/>
|
|
{:else}
|
|
<div class={klass} style="background:{gradientForHue(hue)}"></div>
|
|
{/if}
|