mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2025-02-22 07:49:38 +08:00
feat: updates
This commit is contained in:
parent
3b9d728410
commit
46349e8314
@ -80,7 +80,7 @@ export function getCache(
|
||||
break
|
||||
}
|
||||
case 'user/playlist': {
|
||||
if (!query.uid) return
|
||||
if (isNaN(Number(query.uid))) return
|
||||
const userPlaylists = db.get(
|
||||
ModelNames.USER_PLAYLISTS,
|
||||
Number(query?.uid)
|
||||
@ -90,7 +90,16 @@ export function getCache(
|
||||
}
|
||||
case 'song/detail': {
|
||||
const ids: string[] = query?.ids.split(',')
|
||||
if (ids.length === 0) return
|
||||
|
||||
let isIDsValid = true
|
||||
ids.forEach(id => {
|
||||
if (id === '' || isNaN(Number(id))) isIDsValid = false
|
||||
})
|
||||
if (!isIDsValid) return
|
||||
|
||||
const idsQuery = ids.map(id => `id = ${id}`).join(' OR ')
|
||||
console.log(idsQuery)
|
||||
const tracksRaw = realm
|
||||
.objects(ModelNames.TRACK)
|
||||
.filtered(`(${idsQuery})`)
|
||||
@ -109,28 +118,28 @@ export function getCache(
|
||||
}
|
||||
}
|
||||
case 'album': {
|
||||
if (!query?.id) return
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const album = db.get(ModelNames.ALBUM, Number(query?.id)) as any
|
||||
if (checkIsExpired && isCacheExpired(album?.updateAt, 24 * 60)) return
|
||||
if (album?.json) return JSON.parse(album.json)
|
||||
break
|
||||
}
|
||||
case 'playlist/detail': {
|
||||
if (!query?.id) return
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const playlist = db.get(ModelNames.PLAYLIST, Number(query?.id)) as any
|
||||
if (checkIsExpired && isCacheExpired(playlist?.updateAt, 10)) return
|
||||
if (playlist?.json) return JSON.parse(playlist.json)
|
||||
break
|
||||
}
|
||||
case 'artists': {
|
||||
if (!query?.id) return
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const artist = db.get(ModelNames.ARTIST, Number(query?.id)) as any
|
||||
if (checkIsExpired && isCacheExpired(artist?.updateAt, 30)) return
|
||||
if (artist?.json) return JSON.parse(artist.json)
|
||||
break
|
||||
}
|
||||
case 'artist/album': {
|
||||
if (!query?.id) return
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const artistAlbums = db.get(
|
||||
ModelNames.ARTIST_ALBUMS,
|
||||
Number(query?.id)
|
||||
|
3
packages/renderer/src/assets/icons/explicit.svg
Normal file
3
packages/renderer/src/assets/icons/explicit.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2C3.34315 2 2 3.34315 2 5V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V5C22 3.34315 20.6569 2 19 2H5ZM9 6C8.44772 6 8 6.44772 8 7V12V17C8 17.5523 8.44772 18 9 18H15C15.5523 18 16 17.5523 16 17C16 16.4477 15.5523 16 15 16H10V13H15C15.5523 13 16 12.5523 16 12C16 11.4477 15.5523 11 15 11H10V8H15C15.5523 8 16 7.55228 16 7C16 6.44772 15.5523 6 15 6H9Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 542 B |
@ -170,6 +170,16 @@ const CoverRow = ({
|
||||
className='mr-1 mb-1 inline-block h-3 w-3 text-gray-300'
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Explicit icon */}
|
||||
{(item as Album)?.mark === 1056768 && (
|
||||
<SvgIcon
|
||||
name='explicit'
|
||||
className='float-right mt-[2px] h-4 w-4 text-gray-300'
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Name */}
|
||||
<span
|
||||
onClick={() => goTo(item.id)}
|
||||
className='decoration-gray-600 decoration-2 hover:underline dark:text-white dark:decoration-gray-200'
|
||||
|
@ -16,17 +16,17 @@ interface PrimaryTab extends Tab {
|
||||
|
||||
const primaryTabs: PrimaryTab[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
name: '主页',
|
||||
icon: 'home',
|
||||
route: '/',
|
||||
},
|
||||
{
|
||||
name: 'Podcast',
|
||||
name: '播客',
|
||||
icon: 'podcast',
|
||||
route: '/podcast',
|
||||
},
|
||||
{
|
||||
name: 'Library',
|
||||
name: '音乐库',
|
||||
icon: 'music-library',
|
||||
route: '/library',
|
||||
},
|
||||
|
@ -115,7 +115,15 @@ const Track = memo(
|
||||
isHighlight ? 'text-brand-500' : 'text-black dark:text-white'
|
||||
)}
|
||||
>
|
||||
<span>{track.name}</span>
|
||||
<span className='flex items-center'>
|
||||
{track.name}
|
||||
{track.mark === 1318912 && (
|
||||
<SvgIcon
|
||||
name='explicit'
|
||||
className='ml-1.5 mt-[2px] h-4 w-4 text-gray-300 dark:text-gray-500'
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
{subtitle && (
|
||||
<span
|
||||
title={subtitle}
|
||||
@ -227,10 +235,10 @@ const TracksAlbum = ({
|
||||
<div className='mx-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'>
|
||||
<div className='col-span-6 grid grid-cols-[2rem_auto]'>
|
||||
<div>#</div>
|
||||
<div>TITLE</div>
|
||||
<div>标题</div>
|
||||
</div>
|
||||
<div className='col-span-4'>ARTIST</div>
|
||||
<div className='col-span-2 justify-self-end'>TIME</div>
|
||||
<div className='col-span-4'>艺人</div>
|
||||
<div className='col-span-2 justify-self-end'>时长</div>
|
||||
</div>
|
||||
|
||||
{/* Tracks */}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import ArtistInline from '@/components/ArtistsInline'
|
||||
import Skeleton from '@/components/Skeleton'
|
||||
import { resizeImage } from '@/utils/common'
|
||||
import { Fragment } from 'react'
|
||||
import SvgIcon from './SvgIcon'
|
||||
|
||||
const Track = ({
|
||||
track,
|
||||
@ -52,11 +54,18 @@ const Track = ({
|
||||
)}
|
||||
|
||||
<div className='text-xs text-gray-500 dark:text-gray-400'>
|
||||
{!isSkeleton && (
|
||||
<ArtistInline artists={track.ar} disableLink={true} />
|
||||
)}
|
||||
{isSkeleton && (
|
||||
{isSkeleton ? (
|
||||
<Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton>
|
||||
) : (
|
||||
<span className='flex items-center'>
|
||||
{track.mark === 1318912 && (
|
||||
<SvgIcon
|
||||
name='explicit'
|
||||
className='mr-1 h-3 w-3 text-gray-300 dark:text-gray-500'
|
||||
/>
|
||||
)}
|
||||
<ArtistInline artists={track.ar} disableLink={true} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -87,7 +87,15 @@ const Track = memo(
|
||||
{isSkeleton ? (
|
||||
<Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton>
|
||||
) : (
|
||||
<ArtistInline artists={track.ar} />
|
||||
<span className='inline-flex items-center'>
|
||||
{track.mark === 1318912 && (
|
||||
<SvgIcon
|
||||
name='explicit'
|
||||
className='mr-1 h-3.5 w-3.5 text-gray-300 dark:text-gray-500'
|
||||
/>
|
||||
)}
|
||||
<ArtistInline artists={track.ar} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -191,10 +199,10 @@ const TracksList = memo(
|
||||
<div className='ml-2 mr-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'>
|
||||
<div className='col-span-6 grid grid-cols-[4.2rem_auto]'>
|
||||
<div></div>
|
||||
<div>TITLE</div>
|
||||
<div>标题</div>
|
||||
</div>
|
||||
<div className='col-span-4'>ALBUM</div>
|
||||
<div className='col-span-2 justify-self-end'>TIME</div>
|
||||
<div className='col-span-4'>专辑</div>
|
||||
<div className='col-span-2 justify-self-end'>时长</div>
|
||||
</div>
|
||||
|
||||
<div className='grid w-full'>
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
resizeImage,
|
||||
scrollToTop,
|
||||
} from '@/utils/common'
|
||||
import useTracks from '@/hooks/useTracks'
|
||||
|
||||
const PlayButton = ({
|
||||
album,
|
||||
@ -95,7 +96,7 @@ const Header = ({
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
<div className='absolute top-0 h-full w-full bg-gradient-to-b from-white/[.84] to-white dark:from-black/[.5] dark:to-[#1d1d1d]'></div>
|
||||
<div className='absolute top-0 h-full w-full bg-gradient-to-b from-white/75 to-white dark:from-black/50 dark:to-[#1d1d1d]'></div>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-[17rem_auto] items-center gap-9'>
|
||||
@ -285,6 +286,10 @@ const Album = () => {
|
||||
id: Number(params.id) || 0,
|
||||
})
|
||||
|
||||
const { data: tracks } = useTracks({
|
||||
ids: album?.songs?.map(track => track.id) ?? [],
|
||||
})
|
||||
|
||||
const handlePlay = async (trackID: number | null = null) => {
|
||||
const realAlbum = album?.album
|
||||
if (!realAlbum) {
|
||||
@ -302,7 +307,7 @@ const Album = () => {
|
||||
handlePlay={handlePlay}
|
||||
/>
|
||||
<TracksAlbum
|
||||
tracks={album?.album.songs ?? []}
|
||||
tracks={tracks?.songs ?? album?.album.songs ?? []}
|
||||
onTrackDoubleClick={handlePlay}
|
||||
isSkeleton={isLoading}
|
||||
/>
|
||||
|
@ -9,6 +9,7 @@ import TracksGrid from '@/components/TracksGrid'
|
||||
import CoverRow, { Subtitle } from '@/components/CoverRow'
|
||||
import Skeleton from '@/components/Skeleton'
|
||||
import { Fragment } from 'react'
|
||||
import useTracks from '@/hooks/useTracks'
|
||||
|
||||
const Artist = () => {
|
||||
const params = useParams()
|
||||
@ -22,25 +23,48 @@ const Artist = () => {
|
||||
limit: 1000,
|
||||
})
|
||||
|
||||
const { data: tracks, isLoading: isLoadingTracks } = useTracks({
|
||||
ids: artist?.hotSongs?.slice(0, 10)?.map(t => t.id) ?? [],
|
||||
})
|
||||
|
||||
const albums = useMemo(() => {
|
||||
if (!albumsRaw?.hotAlbums) return []
|
||||
return albumsRaw.hotAlbums.filter(
|
||||
album =>
|
||||
album.type === '专辑' &&
|
||||
['混音版', '精选集', 'Remix'].includes(album.subType) === false &&
|
||||
album.size > 1
|
||||
)
|
||||
const albums: Album[] = []
|
||||
albumsRaw.hotAlbums.forEach(album => {
|
||||
if (album.type !== '专辑') return false
|
||||
if (['混音版', '精选集', 'Remix'].includes(album.subType)) return false
|
||||
|
||||
// No singles
|
||||
if (album.size <= 1) return false
|
||||
|
||||
// No remixes
|
||||
if (
|
||||
/(\(|\[)(.*)(Remix|remix)(.*)(\)|\])/.test(
|
||||
album.name.toLocaleLowerCase()
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If have same name album only keep the Explicit version
|
||||
const sameNameAlbumIndex = albums.findIndex(a => a.name === album.name)
|
||||
if (sameNameAlbumIndex !== -1) {
|
||||
if (album.mark === 1056768) albums[sameNameAlbumIndex] = album
|
||||
return
|
||||
}
|
||||
|
||||
albums.push(album)
|
||||
})
|
||||
return albums
|
||||
}, [albumsRaw?.hotAlbums])
|
||||
|
||||
const singles = useMemo(() => {
|
||||
if (!albumsRaw?.hotAlbums) return []
|
||||
const albumsIds = albums.map(album => album.id)
|
||||
return albumsRaw.hotAlbums.filter(
|
||||
album =>
|
||||
album.type !== '专辑' ||
|
||||
['混音版', '精选集', 'Remix'].includes(album.subType) ||
|
||||
album.size === 1
|
||||
album => albumsIds.includes(album.id) === false
|
||||
)
|
||||
}, [albumsRaw?.hotAlbums])
|
||||
}, [albums, albumsRaw?.hotAlbums])
|
||||
|
||||
const latestAlbum = useMemo(() => {
|
||||
if (!albumsRaw || !albumsRaw.hotAlbums) return
|
||||
@ -89,7 +113,7 @@ const Artist = () => {
|
||||
<div className='mt-12 grid h-[20rem] grid-cols-[14rem,_auto] grid-rows-1 gap-16 px-2'>
|
||||
{/* Latest release */}
|
||||
<div>
|
||||
<div className='mb-6 text-2xl font-semibold dark:text-white'>
|
||||
<div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
|
||||
最新发行
|
||||
</div>
|
||||
<div className='flex-grow rounded-xl '>
|
||||
@ -110,12 +134,12 @@ const Artist = () => {
|
||||
|
||||
{/* Popular tracks */}
|
||||
<div>
|
||||
<div className='mb-6 text-2xl font-semibold dark:text-white'>
|
||||
<div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
|
||||
热门歌曲
|
||||
</div>
|
||||
<div className='rounded-xl'>
|
||||
<TracksGrid
|
||||
tracks={artist?.hotSongs.slice(0, 10) ?? []}
|
||||
tracks={tracks?.songs ?? artist?.hotSongs.slice(0, 10) ?? []}
|
||||
isSkeleton={isLoading}
|
||||
/>
|
||||
</div>
|
||||
@ -125,7 +149,7 @@ const Artist = () => {
|
||||
{/* Albums */}
|
||||
{albums.length !== 0 && (
|
||||
<div className='mt-20 px-2'>
|
||||
<div className='mb-6 text-2xl font-semibold dark:text-white'>
|
||||
<div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
|
||||
专辑
|
||||
</div>
|
||||
<CoverRow
|
||||
@ -137,7 +161,7 @@ const Artist = () => {
|
||||
|
||||
{/* Singles/EP */}
|
||||
<div className='mt-16 px-2'>
|
||||
<div className='mb-6 text-2xl font-semibold dark:text-white'>
|
||||
<div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
|
||||
单曲和EP
|
||||
</div>
|
||||
<CoverRow
|
||||
|
@ -178,7 +178,9 @@ export class Player {
|
||||
_howler = howler
|
||||
this.play()
|
||||
this.state = State.PLAYING
|
||||
_howler.once('load', () => this._cacheAudio(this.trackID, audio))
|
||||
|
||||
const id = this.trackID
|
||||
_howler.once('load', () => this._cacheAudio(id, audio))
|
||||
|
||||
if (!this._progressInterval) {
|
||||
this._setupProgressInterval()
|
||||
|
Loading…
x
Reference in New Issue
Block a user