mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2024-11-29 12:03:54 +08:00
feat: updates
This commit is contained in:
parent
719a3a60d4
commit
86f088e5c9
|
@ -14,7 +14,8 @@
|
|||
"dev:renderer": "vite dev",
|
||||
"build:main": "node scripts/build.main.mjs",
|
||||
"build:renderer": "vite build",
|
||||
"build": "npm run typecheck && npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js --dir",
|
||||
"build": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js",
|
||||
"build-dir": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js --dir",
|
||||
"typecheck": "tsc --noEmit --project src/renderer/tsconfig.json",
|
||||
"debug": "cross-env-shell NODE_ENV=debug \"npm run typecheck && node scripts/build.mjs && vite ./src/renderer\"",
|
||||
"eslint": "eslint --ext .ts,.js ./",
|
||||
|
|
|
@ -57,7 +57,7 @@ const PlayingTrack = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<IconButton onClick={() => toast('Work in progress')}>
|
||||
<IconButton onClick={() => toast('施工中...')}>
|
||||
<SvgIcon
|
||||
className='h-6 w-6 text-black dark:text-white'
|
||||
name='heart-outline'
|
||||
|
@ -115,23 +115,30 @@ const Others = () => {
|
|||
const playerSnapshot = useSnapshot(player)
|
||||
const mode = useMemo(() => playerSnapshot.mode, [playerSnapshot.mode])
|
||||
|
||||
const isFM = () => mode === PlayerMode.FM
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-end gap-2 pr-2 text-black dark:text-white'>
|
||||
<IconButton onClick={() => toast('Work in progress')} disabled={isFM()}>
|
||||
<IconButton
|
||||
onClick={() => toast('Work in progress')}
|
||||
disabled={mode === PlayerMode.FM}
|
||||
>
|
||||
<SvgIcon className='h-6 w-6' name='playlist' />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => toast('Work in progress')} disabled={isFM()}>
|
||||
<IconButton
|
||||
onClick={() => toast('施工中...')}
|
||||
disabled={mode === PlayerMode.FM}
|
||||
>
|
||||
<SvgIcon className='h-6 w-6' name='repeat' />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => toast('Work in progress')} disabled={isFM()}>
|
||||
<IconButton
|
||||
onClick={() => toast('施工中...')}
|
||||
disabled={mode === PlayerMode.FM}
|
||||
>
|
||||
<SvgIcon className='h-6 w-6' name='shuffle' />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => toast('Work in progress')}>
|
||||
<IconButton onClick={() => toast('施工中...')}>
|
||||
<SvgIcon className='h-6 w-6' name='volume' />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => toast('Work in progress')}>
|
||||
<IconButton onClick={() => toast('施工中...')}>
|
||||
<SvgIcon className='h-6 w-6' name='lyrics' />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
|
|
@ -68,7 +68,10 @@ const SearchBox = () => {
|
|||
|
||||
const Settings = () => {
|
||||
return (
|
||||
<div className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'>
|
||||
<div
|
||||
onClick={() => toast('施工中...')}
|
||||
className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'
|
||||
>
|
||||
<SvgIcon className='h-[1.125rem] w-[1.125rem]' name='settings' />
|
||||
</div>
|
||||
)
|
||||
|
@ -78,20 +81,27 @@ const Avatar = () => {
|
|||
const navigate = useNavigate()
|
||||
const { data: user } = useUser()
|
||||
|
||||
const avatarUrl = resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
|
||||
const avatarUrl = user?.profile?.avatarUrl
|
||||
? resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
|
||||
: ''
|
||||
|
||||
return (
|
||||
<img
|
||||
src={avatarUrl}
|
||||
onClick={() => navigate('/login')}
|
||||
className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700'
|
||||
/>
|
||||
// <div onClick={() => navigate('/login')}>
|
||||
// <SvgIcon
|
||||
// name='user'
|
||||
// className='h-9 w-9 rounded-full bg-gray-400/10 p-1 text-gray-400'
|
||||
// />
|
||||
// </div>
|
||||
<>
|
||||
{avatarUrl ? (
|
||||
<img
|
||||
src={avatarUrl}
|
||||
onClick={() => navigate('/login')}
|
||||
className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700'
|
||||
/>
|
||||
) : (
|
||||
<div onClick={() => navigate('/login')}>
|
||||
<SvgIcon
|
||||
name='user'
|
||||
className='h-9 w-9 rounded-full bg-black/[.06] p-1 text-gray-500'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ const Header = ({
|
|||
color={ButtonColor.Gray}
|
||||
iconColor={ButtonColor.Gray}
|
||||
isSkelton={isLoading}
|
||||
onClick={() => toast('Work in progress')}
|
||||
onClick={() => toast('施工中...')}
|
||||
>
|
||||
<SvgIcon name='heart-outline' className='h-6 w-6' />
|
||||
</Button>
|
||||
|
@ -200,7 +200,7 @@ const Header = ({
|
|||
color={ButtonColor.Gray}
|
||||
iconColor={ButtonColor.Gray}
|
||||
isSkelton={isLoading}
|
||||
onClick={() => toast('Work in progress')}
|
||||
onClick={() => toast('施工中...')}
|
||||
>
|
||||
<SvgIcon name='more' className='h-6 w-6' />
|
||||
</Button>
|
||||
|
|
|
@ -8,6 +8,21 @@ import DailyTracksCard from '@/components/DailyTracksCard'
|
|||
import FMCard from '@/components/FMCard'
|
||||
|
||||
export default function Home() {
|
||||
const {
|
||||
data: dailyRecommendPlaylists,
|
||||
isLoading: isLoadingDailyRecommendPlaylists,
|
||||
} = useQuery(
|
||||
PlaylistApiNames.FETCH_DAILY_RECOMMEND_PLAYLISTS,
|
||||
fetchDailyRecommendPlaylists,
|
||||
{
|
||||
retry: false,
|
||||
placeholderData: () =>
|
||||
window.ipcRenderer?.sendSync('getApiCacheSync', {
|
||||
api: 'recommend/resource',
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const {
|
||||
data: recommendedPlaylists,
|
||||
isLoading: isLoadingRecommendedPlaylists,
|
||||
|
@ -24,20 +39,6 @@ export default function Home() {
|
|||
}
|
||||
)
|
||||
|
||||
const {
|
||||
data: dailyRecommendPlaylists,
|
||||
isLoading: isLoadingDailyRecommendPlaylists,
|
||||
} = useQuery(
|
||||
PlaylistApiNames.FETCH_DAILY_RECOMMEND_PLAYLISTS,
|
||||
fetchDailyRecommendPlaylists,
|
||||
{
|
||||
placeholderData: () =>
|
||||
window.ipcRenderer?.sendSync('getApiCacheSync', {
|
||||
api: 'recommend/resource',
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const playlists = [
|
||||
...(dailyRecommendPlaylists?.recommend ?? []),
|
||||
...(recommendedPlaylists?.result ?? []),
|
||||
|
|
|
@ -28,7 +28,7 @@ const EmailInput = ({
|
|||
return (
|
||||
<div className='w-full'>
|
||||
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
Email
|
||||
邮箱
|
||||
</div>
|
||||
<input
|
||||
value={email}
|
||||
|
@ -54,7 +54,7 @@ const PhoneInput = ({
|
|||
return (
|
||||
<div className='w-full'>
|
||||
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
Phone
|
||||
手机
|
||||
</div>
|
||||
<div className='flex w-full'>
|
||||
<input
|
||||
|
@ -91,7 +91,7 @@ const PasswordInput = ({
|
|||
return (
|
||||
<div className='mt-3 flex w-full flex-col'>
|
||||
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
|
||||
Password
|
||||
密码
|
||||
</div>
|
||||
<div className='flex w-full'>
|
||||
<input
|
||||
|
@ -135,7 +135,7 @@ const LoginButton = ({
|
|||
'bg-brand-100 text-brand-300 dark:bg-brand-700 dark:text-white/50'
|
||||
)}
|
||||
>
|
||||
Login
|
||||
登录
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
@ -153,15 +153,15 @@ const OtherLoginMethods = ({
|
|||
}[] = [
|
||||
{
|
||||
id: Method.QRCODE,
|
||||
name: 'QR Code',
|
||||
name: '二维码',
|
||||
},
|
||||
{
|
||||
id: Method.EMAIL,
|
||||
name: 'Email',
|
||||
name: '邮箱',
|
||||
},
|
||||
{
|
||||
id: Method.PHONE,
|
||||
name: 'Phone',
|
||||
name: '手机',
|
||||
},
|
||||
]
|
||||
return (
|
||||
|
@ -178,7 +178,7 @@ const OtherLoginMethods = ({
|
|||
<button
|
||||
key={id}
|
||||
onClick={() => setMethod(id)}
|
||||
className='flex w-full cursor-default items-center justify-center rounded-lg bg-gray-100 py-2 font-medium text-gray-600 transition duration-300 hover:bg-gray-200 hover:text-gray-800 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500 dark:hover:text-gray-100'
|
||||
className='flex w-full cursor-default items-center justify-center rounded-lg bg-gray-100 py-2 text-gray-600 transition duration-300 hover:bg-gray-200 hover:text-gray-800 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500 dark:hover:text-gray-100'
|
||||
>
|
||||
<SvgIcon className='mr-2 h-5 w-5' name={id} />
|
||||
<span>{name}</span>
|
||||
|
@ -312,7 +312,7 @@ const LoginWithPhone = () => {
|
|||
|
||||
// Login with QRCode
|
||||
const LoginWithQRCode = () => {
|
||||
const [qrCodeMessage, setQrCodeMessage] = useState('扫码登录')
|
||||
const [qrCodeMessage, setQrCodeMessage] = useState('打开网易云音乐,扫码登录')
|
||||
const [qrCodeImage, setQrCodeImage] = useState('')
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
@ -344,7 +344,7 @@ const LoginWithQRCode = () => {
|
|||
refetchKey()
|
||||
break
|
||||
case 801:
|
||||
setQrCodeMessage('等待扫码')
|
||||
setQrCodeMessage('打开网易云音乐,扫码登录')
|
||||
break
|
||||
case 802:
|
||||
setQrCodeMessage('等待确认')
|
||||
|
|
|
@ -118,7 +118,7 @@ const Header = memo(
|
|||
color={ButtonColor.Gray}
|
||||
iconColor={ButtonColor.Gray}
|
||||
isSkelton={isLoading}
|
||||
onClick={() => toast('Work in progress')}
|
||||
onClick={() => toast('施工中...')}
|
||||
>
|
||||
<SvgIcon name='heart-outline' className='h-6 w-6' />
|
||||
</Button>
|
||||
|
@ -127,7 +127,7 @@ const Header = memo(
|
|||
color={ButtonColor.Gray}
|
||||
iconColor={ButtonColor.Gray}
|
||||
isSkelton={isLoading}
|
||||
onClick={() => toast('Work in progress')}
|
||||
onClick={() => toast('施工中...')}
|
||||
>
|
||||
<SvgIcon name='more' className='h-6 w-6' />
|
||||
</Button>
|
||||
|
|
|
@ -157,7 +157,10 @@ export class Player {
|
|||
*/
|
||||
private async _fetchAudioSource(trackID: TrackID) {
|
||||
const response = await fetchAudioSourceWithReactQuery({ id: trackID })
|
||||
if (response.data?.[0]?.url) return response.data[0].url
|
||||
return {
|
||||
audio: response.data?.[0]?.url,
|
||||
id: trackID,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,14 +177,14 @@ export class Player {
|
|||
* Play audio via howler
|
||||
*/
|
||||
private async _playAudio() {
|
||||
const audio = await this._fetchAudioSource(this.trackID)
|
||||
const { audio, id } = await this._fetchAudioSource(this.trackID)
|
||||
if (!audio) {
|
||||
toast('Failed to load audio source')
|
||||
return
|
||||
}
|
||||
Howler.unload()
|
||||
const howler = new Howl({
|
||||
src: [audio],
|
||||
src: [`${audio}?id=${id}`],
|
||||
format: ['mp3', 'flac'],
|
||||
html5: true,
|
||||
autoplay: true,
|
||||
|
@ -191,8 +194,9 @@ export class Player {
|
|||
_howler = howler
|
||||
this.play()
|
||||
this.state = State.PLAYING
|
||||
|
||||
this._cacheAudio(this.trackID, audio)
|
||||
_howler.once('load', () => {
|
||||
this._cacheAudio(_howler._src)
|
||||
})
|
||||
|
||||
if (!this._progressInterval) {
|
||||
this._setupProgressInterval()
|
||||
|
@ -209,8 +213,10 @@ export class Player {
|
|||
}
|
||||
}
|
||||
|
||||
private _cacheAudio(id: number, audio: string) {
|
||||
private _cacheAudio(audio: string) {
|
||||
if (audio.includes('yesplaymusic')) return
|
||||
const id = Number(audio.split('?id=')[1])
|
||||
if (isNaN(id) || !id) return
|
||||
cacheAudio(id, audio)
|
||||
}
|
||||
|
||||
|
@ -242,18 +248,13 @@ export class Player {
|
|||
play(fade: boolean = false) {
|
||||
if (_howler.playing()) return
|
||||
|
||||
const setPlayState = () => {
|
||||
this.state = State.PLAYING
|
||||
}
|
||||
|
||||
_howler.play()
|
||||
if (fade) {
|
||||
_howler.once('play', () => {
|
||||
_howler.fade(0, this._volume, PLAY_PAUSE_FADE_DURATION)
|
||||
setPlayState()
|
||||
})
|
||||
} else {
|
||||
setPlayState()
|
||||
this.state = State.PLAYING
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,18 +263,15 @@ export class Player {
|
|||
* @param {boolean} fade fade out
|
||||
*/
|
||||
pause(fade: boolean = false) {
|
||||
const setPauseState = () => {
|
||||
_howler.pause()
|
||||
this.state = State.PAUSED
|
||||
}
|
||||
|
||||
if (fade) {
|
||||
_howler.fade(this._volume, 0, PLAY_PAUSE_FADE_DURATION)
|
||||
this.state = State.PAUSED
|
||||
_howler.once('fade', () => {
|
||||
setPauseState()
|
||||
_howler.pause()
|
||||
})
|
||||
} else {
|
||||
setPauseState()
|
||||
this.state = State.PAUSED
|
||||
_howler.pause()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ export default defineConfig({
|
|||
],
|
||||
base: './',
|
||||
build: {
|
||||
target: process.env.IS_ELECTRON ? 'esnext' : 'modules',
|
||||
sourcemap: process.env.NODE_ENV === 'debug',
|
||||
outDir: '../../dist/renderer',
|
||||
emptyOutDir: true,
|
||||
|
|
Loading…
Reference in New Issue
Block a user