feat: updates
|
@ -10,6 +10,7 @@
|
|||
"repository": "github:qier222/YesPlayMusic",
|
||||
"main": "dist/main/index.js",
|
||||
"scripts": {
|
||||
"install": "node scripts/build.sqlite3.mjs",
|
||||
"dev": "concurrently -n=vite,main -c=#646cff,#74b1be \"npm run dev:renderer\" \"node scripts/build.main.mjs --watch\"",
|
||||
"dev:renderer": "vite dev",
|
||||
"build:main": "node scripts/build.main.mjs",
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer)
|
||||
contextBridge.exposeInMainWorld('isElectron', true)
|
||||
contextBridge.exposeInMainWorld('env', {
|
||||
isElectron: true,
|
||||
isLinux: process.platform === 'linux',
|
||||
isMac: process.platform === 'darwin',
|
||||
isWin: process.platform === 'win32',
|
||||
})
|
||||
|
|
|
@ -5,10 +5,13 @@ import Player from '@/components/Player'
|
|||
import Sidebar from '@/components/Sidebar'
|
||||
import reactQueryClient from '@/utils/reactQueryClient'
|
||||
import Main from './components/Main'
|
||||
import TitleBar from './components/Titlebar'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<QueryClientProvider client={reactQueryClient}>
|
||||
{window.env?.isWin && <TitleBar />}
|
||||
|
||||
<div id='layout' className='grid select-none grid-cols-[16rem_auto]'>
|
||||
<Sidebar />
|
||||
<Main />
|
||||
|
|
|
@ -3,6 +3,7 @@ import request from '@/utils/request'
|
|||
export enum TrackApiNames {
|
||||
FETCH_TRACKS = 'fetchTracks',
|
||||
FETCH_AUDIO_SOURCE = 'fetchAudioSource',
|
||||
FETCH_LYRIC = 'fetchLyric',
|
||||
}
|
||||
|
||||
// 获取歌曲详情
|
||||
|
@ -71,3 +72,51 @@ export function fetchAudioSource(
|
|||
params,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取歌词
|
||||
export interface FetchLyricParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchLyricResponse {
|
||||
code: number
|
||||
sgc: boolean
|
||||
sfy: boolean
|
||||
qfy: boolean
|
||||
lyricUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
transUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
lrc: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
klyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
tlyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
}
|
||||
export function fetchLyric(
|
||||
params: FetchLyricParams
|
||||
): Promise<FetchLyricResponse> {
|
||||
return request({
|
||||
url: '/lyric',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.0656 5.86692L12 6.71938L10.9344 5.86692C8.73386 4.10647 5.47034 4.82325 4.21005 7.34384C3.35797 9.04799 3.69197 11.1062 5.03922 12.4534L10.9393 18.3536C11.5251 18.9393 12.4749 18.9393 13.0607 18.3536L18.9608 12.4534C20.308 11.1062 20.642 9.04799 19.79 7.34384C18.5297 4.82325 15.2661 4.10647 13.0656 5.86692Z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6877 7.75013C11.8703 7.89621 12.1297 7.89621 12.3124 7.75013L13.6903 6.64779C15.3937 5.28505 17.92 5.8399 18.8955 7.79106C19.5551 9.11022 19.2966 10.7034 18.2537 11.7463L12.3536 17.6465C12.1583 17.8417 11.8417 17.8417 11.6465 17.6465L5.74633 11.7463C4.70344 10.7034 4.4449 9.11022 5.10448 7.79106C6.08006 5.8399 8.60631 5.28505 10.3097 6.64779L11.6877 7.75013ZM12 5.43875L12.4409 5.08605C15.1386 2.92789 19.1394 3.80661 20.6844 6.89663C21.729 8.98577 21.3195 11.5089 19.6679 13.1605L13.7678 19.0607C12.7915 20.037 11.2085 20.037 10.2322 19.0607L4.33212 13.1605C2.6805 11.5089 2.27106 8.98577 3.31562 6.89663C4.86063 3.80661 8.86143 2.92789 11.5591 5.08605L12 5.43875Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6963 7.8793C11.8739 8.02633 12.1261 8.02633 12.3037 7.8793L13.6433 6.76983C15.2993 5.39827 17.7553 5.95672 18.7038 7.9205C19.345 9.24819 19.0937 10.8517 18.0798 11.9014L12.3437 17.8397C12.1539 18.0362 11.8461 18.0362 11.6563 17.8397L5.92022 11.9014C4.90633 10.8517 4.65498 9.24819 5.29622 7.9205C6.24467 5.95672 8.70067 5.39827 10.3567 6.76983L11.6963 7.8793ZM12 5.55297L12.4286 5.19799C15.0513 3.02586 18.9408 3.91027 20.4429 7.02028C21.4584 9.12294 21.0603 11.6624 19.4547 13.3247L13.7186 19.263C12.7694 20.2457 11.2305 20.2457 10.2814 19.263L4.54533 13.3247C2.93965 11.6624 2.54158 9.12294 3.55711 7.02028C5.05915 3.91027 8.9487 3.02586 11.5714 5.19799L12 5.55297Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 847 B |
|
@ -1,5 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.0656 5.86692L12 6.71938L10.9344 5.86692C8.73386 4.10647 5.47034 4.82325 4.21005 7.34384C3.35797 9.04799 3.69197 11.1062 5.03922 12.4534L10.9393 18.3536C11.5251 18.9393 12.4749 18.9393 13.0607 18.3536L18.9608 12.4534C20.308 11.1062 20.642 9.04799 19.79 7.34384C18.5297 4.82325 15.2661 4.10647 13.0656 5.86692Z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M13.6903 6.64779L12.3124 7.75012C12.1297 7.89621 11.8703 7.89621 11.6877 7.75012L10.3097 6.64779C8.60632 5.28504 6.08006 5.8399 5.10448 7.79105C4.4449 9.11021 4.70345 10.7034 5.74633 11.7463L11.6465 17.6464C11.8417 17.8417 12.1583 17.8417 12.3536 17.6464L18.2537 11.7463C19.2966 10.7034 19.5551 9.11021 18.8955 7.79105C17.92 5.8399 15.3937 5.28504 13.6903 6.64779Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5.43875L12.4409 5.08605C15.1386 2.92789 19.1394 3.80661 20.6844 6.89663C21.729 8.98577 21.3195 11.5089 19.6679 13.1605L13.7678 19.0607C12.7915 20.037 11.2085 20.037 10.2322 19.0607L4.33212 13.1605C2.6805 11.5089 2.27106 8.98577 3.31562 6.89663C4.86063 3.80661 8.86143 2.92789 11.5591 5.08605L12 5.43875ZM12.3124 7.75013L13.6903 6.64779C15.3937 5.28505 17.92 5.8399 18.8955 7.79106C19.5551 9.11022 19.2966 10.7034 18.2537 11.7463L12.3536 17.6465C12.1583 17.8417 11.8417 17.8417 11.6465 17.6465L5.74633 11.7463C4.70344 10.7034 4.4449 9.11022 5.10448 7.79106C6.08006 5.8399 8.60631 5.28505 10.3097 6.64779L11.6877 7.75013C11.8703 7.89621 12.1297 7.89621 12.3124 7.75013Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5.55297L12.4286 5.19799C15.0513 3.02586 18.9408 3.91027 20.4429 7.02028C21.4584 9.12294 21.0603 11.6624 19.4547 13.3247L13.7186 19.263C12.7694 20.2457 11.2305 20.2457 10.2814 19.263L4.54533 13.3247C2.93965 11.6624 2.54158 9.12294 3.55711 7.02028C5.05915 3.91027 8.9487 3.02586 11.5714 5.19799L12 5.55297Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 484 B |
1
src/renderer/assets/icons/windows-close.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m4.397 4.554.073-.084a.75.75 0 0 1 .976-.073l.084.073L12 10.939l6.47-6.47a.75.75 0 1 1 1.06 1.061L13.061 12l6.47 6.47a.75.75 0 0 1 .072.976l-.073.084a.75.75 0 0 1-.976.073l-.084-.073L12 13.061l-6.47 6.47a.75.75 0 0 1-1.06-1.061L10.939 12l-6.47-6.47a.75.75 0 0 1-.072-.976l.073-.084-.073.084Z" fill="currentColor"/></svg>
|
After Width: | Height: | Size: 425 B |
1
src/renderer/assets/icons/windows-max.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 5.75A2.75 2.75 0 0 1 5.75 3h12.5A2.75 2.75 0 0 1 21 5.75v12.5A2.75 2.75 0 0 1 18.25 21H5.75A2.75 2.75 0 0 1 3 18.25V5.75ZM5.75 4.5c-.69 0-1.25.56-1.25 1.25v12.5c0 .69.56 1.25 1.25 1.25h12.5c.69 0 1.25-.56 1.25-1.25V5.75c0-.69-.56-1.25-1.25-1.25H5.75Z" fill="currentColor"/></svg>
|
After Width: | Height: | Size: 387 B |
1
src/renderer/assets/icons/windows-min.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3.755 12.5h16.492a.75.75 0 0 0 0-1.5H3.755a.75.75 0 0 0 0 1.5Z" fill="currentColor"/></svg>
|
After Width: | Height: | Size: 197 B |
1
src/renderer/assets/icons/windows-unmax.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7.518 5H6.009a3.25 3.25 0 0 1 3.24-3h8.001A4.75 4.75 0 0 1 22 6.75v8a3.25 3.25 0 0 1-3 3.24v-1.508a1.75 1.75 0 0 0 1.5-1.732v-8a3.25 3.25 0 0 0-3.25-3.25h-8A1.75 1.75 0 0 0 7.518 5ZM5.25 6A3.25 3.25 0 0 0 2 9.25v9.5A3.25 3.25 0 0 0 5.25 22h9.5A3.25 3.25 0 0 0 18 18.75v-9.5A3.25 3.25 0 0 0 14.75 6h-9.5ZM3.5 9.25c0-.966.784-1.75 1.75-1.75h9.5c.967 0 1.75.784 1.75 1.75v9.5a1.75 1.75 0 0 1-1.75 1.75h-9.5a1.75 1.75 0 0 1-1.75-1.75v-9.5Z" fill="currentColor"/></svg>
|
After Width: | Height: | Size: 570 B |
|
@ -5,7 +5,6 @@ import { resizeImage } from '@/utils/common'
|
|||
import SvgIcon from '@/components/SvgIcon'
|
||||
import ArtistInline from '@/components/ArtistsInline'
|
||||
import { State as PlayerState, Mode as PlayerMode } from '@/utils/player'
|
||||
import Skeleton from './Skeleton'
|
||||
|
||||
const MediaControls = () => {
|
||||
const classes =
|
||||
|
@ -66,8 +65,9 @@ const FMCard = () => {
|
|||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (coverUrl) {
|
||||
average(coverUrl, { amount: 1, format: 'hex', sample: 1 }).then(color => {
|
||||
const cover = resizeImage(playerSnapshot.fmTrack?.al?.picUrl ?? '', 'xs')
|
||||
if (cover) {
|
||||
average(cover, { amount: 1, format: 'hex', sample: 1 }).then(color => {
|
||||
let c = colord(color as string)
|
||||
if (c.isLight()) c = c.darken(0.15)
|
||||
else if (c.isDark()) c = c.lighten(0.1)
|
||||
|
@ -75,7 +75,7 @@ const FMCard = () => {
|
|||
setBackground(`linear-gradient(to bottom right, ${c.toHex()}, ${to})`)
|
||||
})
|
||||
}
|
||||
}, [coverUrl])
|
||||
}, [playerSnapshot.fmTrack?.al?.picUrl])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -67,7 +67,7 @@ const PlayingTrack = () => {
|
|||
|
||||
<IconButton onClick={() => toast('施工中...')}>
|
||||
<SvgIcon
|
||||
className='h-6 w-6 text-black dark:text-white'
|
||||
className='h-5 w-5 text-black dark:text-white'
|
||||
name={
|
||||
track?.id && userLikedSongs?.ids?.includes(track.id)
|
||||
? 'heart'
|
||||
|
|
|
@ -6,12 +6,17 @@ import Login from '@/pages/Login'
|
|||
import Playlist from '@/pages/Playlist'
|
||||
import Artist from '@/pages/Artist'
|
||||
import Search from '@/pages/Search'
|
||||
import Library from '@/pages/Library'
|
||||
|
||||
const routes: RouteObject[] = [
|
||||
{
|
||||
path: '/',
|
||||
element: <Home />,
|
||||
},
|
||||
{
|
||||
path: '/library',
|
||||
element: <Library />,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
element: <Login />,
|
||||
|
|
22
src/renderer/components/TitleBar.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import SvgIcon from './SvgIcon'
|
||||
|
||||
const TitleBar = () => {
|
||||
return (
|
||||
<div className='flex h-8 w-screen items-center justify-between bg-gray-50'>
|
||||
<div className='ml-2 text-sm text-gray-500'>YesPlayMusic</div>
|
||||
<div className='flex h-full'>
|
||||
<button className='flex w-[2.875rem] items-center justify-center hover:bg-[#e9e9e9]'>
|
||||
<SvgIcon className='h-3 w-3' name='windows-min' />
|
||||
</button>
|
||||
<button className='flex w-[2.875rem] items-center justify-center hover:bg-[#e9e9e9]'>
|
||||
<SvgIcon className='h-3 w-3' name='windows-max' />
|
||||
</button>
|
||||
<button className='flex w-[2.875rem] items-center justify-center hover:bg-[#c42b1c] hover:text-white'>
|
||||
<SvgIcon className='h-3 w-3' name='windows-close' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TitleBar
|
7
src/renderer/global.d.ts
vendored
|
@ -4,7 +4,12 @@ declare global {
|
|||
interface Window {
|
||||
// Expose some Api through preload script
|
||||
ipcRenderer?: import('electron').IpcRenderer
|
||||
isElectron?: boolean
|
||||
env?: {
|
||||
isElectron: boolean
|
||||
isLinux: boolean
|
||||
isMac: boolean
|
||||
isWin: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
40
src/renderer/hooks/useLyric.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { TrackApiNames, fetchLyric } from '@/api/track'
|
||||
import type { FetchLyricParams, FetchLyricResponse } from '@/api/track'
|
||||
import reactQueryClient from '@/utils/reactQueryClient'
|
||||
|
||||
export default function useLyric(params: FetchLyricParams) {
|
||||
return useQuery(
|
||||
[TrackApiNames.FETCH_LYRIC, params],
|
||||
() => {
|
||||
return fetchLyric(params)
|
||||
},
|
||||
{
|
||||
enabled: !!params.id && params.id !== 0,
|
||||
refetchInterval: false,
|
||||
staleTime: Infinity,
|
||||
initialData: (): FetchLyricResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync('getApiCacheSync', {
|
||||
api: 'lyric',
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchTracksWithReactQuery(params: FetchLyricParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[TrackApiNames.FETCH_LYRIC, params],
|
||||
() => {
|
||||
return fetchLyric(params)
|
||||
},
|
||||
{
|
||||
retry: 4,
|
||||
retryDelay: (retryCount: number) => {
|
||||
return retryCount * 500
|
||||
},
|
||||
staleTime: Infinity,
|
||||
}
|
||||
)
|
||||
}
|
|
@ -40,9 +40,11 @@ export default function Home() {
|
|||
)
|
||||
|
||||
const playlists = [
|
||||
...(dailyRecommendPlaylists?.recommend ?? []),
|
||||
...(dailyRecommendPlaylists?.recommend?.slice(1) ?? []),
|
||||
...(recommendedPlaylists?.result ?? []),
|
||||
].slice(0, 10)
|
||||
]
|
||||
.slice(0, 10)
|
||||
.reverse()
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
155
src/renderer/pages/Library.tsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
import SvgIcon from '@/components/SvgIcon'
|
||||
import useLyric from '@/hooks/useLyric'
|
||||
import usePlaylist from '@/hooks/usePlaylist'
|
||||
import useUser from '@/hooks/useUser'
|
||||
import useUserPlaylists from '@/hooks/useUserPlaylists'
|
||||
import { player } from '@/store'
|
||||
import { resizeImage } from '@/utils/common'
|
||||
import { sample, chunk } from 'lodash-es'
|
||||
|
||||
const LikedTracksCard = ({ className }: { className?: string }) => {
|
||||
const navigate = useNavigate()
|
||||
const { data: user } = useUser()
|
||||
|
||||
const { data: playlists } = useUserPlaylists({
|
||||
uid: user?.account?.id ?? 0,
|
||||
offset: 0,
|
||||
})
|
||||
|
||||
const { data: likedSongsPlaylist } = usePlaylist({
|
||||
id: playlists?.playlist?.[0].id ?? 0,
|
||||
})
|
||||
|
||||
// Lyric
|
||||
const [trackID, setTrackID] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (trackID === 0) {
|
||||
setTrackID(
|
||||
sample(likedSongsPlaylist?.playlist.trackIds?.map(t => t.id) ?? []) ?? 0
|
||||
)
|
||||
}
|
||||
}, [likedSongsPlaylist?.playlist.trackIds, trackID])
|
||||
|
||||
const { data: lyric } = useLyric({
|
||||
id: trackID,
|
||||
})
|
||||
|
||||
const lyricLines = useMemo(() => {
|
||||
return (
|
||||
sample(
|
||||
chunk(
|
||||
lyric?.lrc.lyric
|
||||
?.split('\n')
|
||||
?.map(l => l.split(']')[1]?.trim())
|
||||
?.filter(
|
||||
l =>
|
||||
l &&
|
||||
!l.includes('作词') &&
|
||||
!l.includes('作曲') &&
|
||||
!l.includes('纯音乐,请欣赏')
|
||||
),
|
||||
3
|
||||
)
|
||||
) ?? []
|
||||
)
|
||||
}, [lyric])
|
||||
|
||||
const handlePlay = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation()
|
||||
if (!likedSongsPlaylist?.playlist.id) {
|
||||
toast('无法播放歌单')
|
||||
return
|
||||
}
|
||||
player.playPlaylist(likedSongsPlaylist.playlist.id)
|
||||
},
|
||||
[likedSongsPlaylist?.playlist.id]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() =>
|
||||
likedSongsPlaylist?.playlist.id &&
|
||||
navigate(`/playlist/${likedSongsPlaylist.playlist.id}`)
|
||||
}
|
||||
className={classNames(
|
||||
'relative flex h-full w-full flex-col justify-between rounded-2xl bg-brand-50 py-5 px-6 text-brand-600 ',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className='text-sm'>
|
||||
{lyricLines.map((line, index) => (
|
||||
<div key={`${index}-${line}`}>{line}</div>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<div className='text-2xl font-bold'>我喜欢的音乐</div>
|
||||
<div className='mt-0.5 text-[15px]'>
|
||||
{likedSongsPlaylist?.playlist.trackCount ?? 0} 首歌
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handlePlay}
|
||||
className='btn-pressed-animation absolute bottom-6 right-6 grid h-11 w-11 cursor-default place-content-center rounded-full bg-brand-600 text-brand-50 shadow-lg'
|
||||
>
|
||||
<SvgIcon name='play-fill' className='ml-0.5 h-6 w-6' />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const OtherCard = ({
|
||||
name,
|
||||
icon,
|
||||
className,
|
||||
}: {
|
||||
name: string
|
||||
icon: string
|
||||
className?: string
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex h-full w-full flex-col justify-between rounded-2xl bg-gray-100 text-lg font-bold',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SvgIcon name={icon} className='ml-3 mt-3 h-12 w-12' />
|
||||
<span className='m-4'>{name}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Tabs = () => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
const Library = () => {
|
||||
const { data: user } = useUser()
|
||||
|
||||
const avatarUrl = useMemo(
|
||||
() => resizeImage(user?.profile?.avatarUrl ?? '', 'sm'),
|
||||
[user?.profile?.avatarUrl]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='mt-8'>
|
||||
<div className='flex items-center text-[2.625rem] font-semibold'>
|
||||
<img src={avatarUrl} className='mr-3 mt-1 h-12 w-12 rounded-full' />
|
||||
{user?.profile?.nickname}的音乐库
|
||||
</div>
|
||||
|
||||
<div className='mt-8 grid grid-cols-[2fr_1fr_1fr] grid-rows-2 gap-4'>
|
||||
<LikedTracksCard className='row-span-2' />
|
||||
<OtherCard name='云盘' icon='fm' className='' />
|
||||
<OtherCard name='本地音乐' icon='music-note' className='' />
|
||||
<OtherCard name='最近播放' icon='playlist' className='' />
|
||||
<OtherCard name='听歌排行' icon='music-library' className='' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Library
|
|
@ -244,7 +244,10 @@ export class Player {
|
|||
const prefetchNextTrack = async () => {
|
||||
const prefetchTrackID = this.fmTrackList[1]
|
||||
const track = await this._fetchTrack(prefetchTrackID)
|
||||
if (track?.al.picUrl) axios.get(resizeImage(track.al.picUrl, 'md'))
|
||||
if (track?.al.picUrl) {
|
||||
axios.get(resizeImage(track.al.picUrl, 'md'))
|
||||
axios.get(resizeImage(track.al.picUrl, 'xs'))
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fmTrackList.length === 0) await loadMoreTracks()
|
||||
|
|