[+] Playlist page
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
<script>
|
||||
let { icon, children, ...rest } = $props();
|
||||
<script lang="ts">
|
||||
let { icon, children, ...rest }: {
|
||||
icon?: string
|
||||
children?: any
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<button class="hbox h-40px rounded-12px px-16px py-10px gap-8px mfg-on-primary-container mbg-inverse-primary m3-font-label-large" {...rest}>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
<script lang="ts">
|
||||
import IconButton from "../IconButton.svelte";
|
||||
|
||||
let { title, account, settings }: {
|
||||
interface Icon {
|
||||
icon: string
|
||||
onclick: () => void
|
||||
}
|
||||
|
||||
let { title, account, right }: {
|
||||
title?: string
|
||||
account?: () => void
|
||||
settings?: () => void
|
||||
right?: Icon[]
|
||||
} = $props()
|
||||
</script>
|
||||
|
||||
@@ -15,7 +20,11 @@
|
||||
<IconButton icon="i-material-symbols:arrow-back-rounded" onclick={() => history.back()} aria-label="Account" />
|
||||
{/if}
|
||||
<div class="m3-font-title-large flex-1">{title}</div>
|
||||
{#if settings}
|
||||
<IconButton icon="i-material-symbols:settings-rounded" onclick={settings} aria-label="Settings" />
|
||||
{/if}
|
||||
<!--{#if right}-->
|
||||
<!-- <IconButton icon="i-material-symbols:settings-rounded" onclick={right} aria-label="Settings" />-->
|
||||
<!-- {/if}-->
|
||||
|
||||
{#each right as item}
|
||||
<IconButton icon={item.icon} onclick={item.onclick} />
|
||||
{/each}
|
||||
</div>
|
||||
@@ -35,7 +35,7 @@ function parsePlaylistRef(ref: string): number {
|
||||
/**
|
||||
* Get raw playlist data from cache or netease API.
|
||||
*/
|
||||
const getPlaylistRaw = cached('playlists',
|
||||
export const getPlaylistRaw = cached('playlists',
|
||||
async (id: number) => {
|
||||
const pl = ((await ne.playlist_detail({ id })).body as any)
|
||||
|
||||
@@ -67,10 +67,10 @@ export const parseBrief = (songData: any): NeteaseSongBrief => ({
|
||||
/**
|
||||
* Get a list of songs from a playlist reference.
|
||||
*/
|
||||
export async function getSongsFromPlaylist(ref: string): Promise<any[]> {
|
||||
export async function getSongsFromPlaylist(ref: string): Promise<{meta: any, songs: NeteaseSongBrief[]}> {
|
||||
const playlistId = parsePlaylistRef(ref)
|
||||
const plData = await getPlaylistRaw(playlistId)
|
||||
return plData.playlist.tracks.map(parseBrief)
|
||||
return {meta: plData.playlist, songs: plData.playlist.tracks.map(parseBrief)}
|
||||
}
|
||||
|
||||
interface NeteaseLyricsResponse { lrc: { lyric: string } }
|
||||
@@ -85,3 +85,7 @@ export const getLyricsProcessed = cached('lyrics_processed',
|
||||
return aiParseLyrics(raw.lrc.lyric)
|
||||
})
|
||||
|
||||
await getSongsFromPlaylist("13555799996")
|
||||
await getSongsFromPlaylist("https://music.163.com/playlist?id=14348145982")
|
||||
await getSongsFromPlaylist("https://music.163.com/playlist?id=14392963638")
|
||||
await getSongsFromPlaylist("https://music.163.com/playlist?id=580208139")
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
</script>
|
||||
|
||||
|
||||
<AppBar account={() => alert('Account clicked')} settings={() => alert('Settings clicked')} />
|
||||
<AppBar account={() => alert('Account clicked')} right={[
|
||||
{icon: "i-material-symbols:settings-rounded", onclick: () => alert('Settings clicked')}
|
||||
]} />
|
||||
|
||||
<div class="vbox gap-16px">
|
||||
{#if data.last}
|
||||
@@ -49,13 +51,13 @@
|
||||
<TitleHeader title="推荐歌单"/>
|
||||
<div class="p-content hbox gap-8px w-auto overflow-x-auto py-8px">
|
||||
{#each data.recPlaylists as playlist}
|
||||
<div class="vbox flex-shrink-0 p-8px gap-8px rounded-12px mbg-surface-container-high">
|
||||
<a class="vbox flex-shrink-0 p-8px gap-8px rounded-12px mbg-surface-container-high" href="/playlist/{playlist.id}">
|
||||
<img src="{playlist.coverImgUrl}" alt="" class="size-116px rounded-8px">
|
||||
<div>
|
||||
<div class="m3-font-title-small font-bold truncate">{playlist.name}</div>
|
||||
<div class="m3-font-body-small truncate">{playlist.creator.nickname} 创建</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { getSongsFromPlaylist, listPlaylists } from "$lib/server/songs.ts";
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => ({
|
||||
playlist: await getSongsFromPlaylist(params.slug)
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from "./$types"
|
||||
import AppBar from "../../../components/appbar/AppBar.svelte";
|
||||
import Button from "../../../components/Button.svelte";
|
||||
|
||||
let { data }: PageProps = $props()
|
||||
|
||||
let {meta, songs} = data.playlist
|
||||
</script>
|
||||
|
||||
<AppBar title="歌单详情" right={[
|
||||
{icon: "i-material-symbols:bookmark-add-outline-rounded", onclick: () => alert('Bookmark clicked')},
|
||||
{icon: "i-material-symbols:more-vert", onclick: () => alert('More clicked')}
|
||||
]} />
|
||||
|
||||
<div class="hbox px-16px py-8px gap-24px">
|
||||
<img src="{meta.coverImgUrl}" alt="" class="size-128px rounded-16px">
|
||||
<div class="vbox flex-1 py-8px self-stretch min-h-full">
|
||||
<div class="m3-font-headline-small font-bold">{meta.name}</div>
|
||||
<div class="flex-1">
|
||||
<div class="m3-font-body-small text-surface-variant">创建者: {meta.creator.nickname}</div>
|
||||
<div class="m3-font-body-small text-surface-variant">歌曲数: {meta.trackCount}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button>开始练习</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user