mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-17 13:48:02 +00:00
feat: updates
This commit is contained in:
parent
7ce516877e
commit
ccebe0a67a
74 changed files with 56065 additions and 2810 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import dayjs from 'dayjs'
|
||||
import duration from 'dayjs/plugin/duration'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { CacheAPIs } from '@/shared/CacheAPIs'
|
||||
import { average } from 'color.js'
|
||||
import { colord } from 'colord'
|
||||
import { supportedLanguages } from '../i18n/i18n'
|
||||
|
|
@ -11,10 +11,7 @@ import { supportedLanguages } from '../i18n/i18n'
|
|||
* @param {string} url 封面图片URL
|
||||
* @param {'xs'|'sm'|'md'|'lg'} size - 大小,值对应为 128px | 256px | 512px | 1024px
|
||||
*/
|
||||
export function resizeImage(
|
||||
url: string,
|
||||
size: 'xs' | 'sm' | 'md' | 'lg'
|
||||
): string {
|
||||
export function resizeImage(url: string, size: 'xs' | 'sm' | 'md' | 'lg'): string {
|
||||
if (!url) return ''
|
||||
|
||||
const sizeMap = {
|
||||
|
|
@ -87,9 +84,7 @@ export function formatDuration(
|
|||
const seconds = time.seconds().toString().padStart(2, '0')
|
||||
|
||||
if (format === 'hh:mm:ss') {
|
||||
return hours !== '0'
|
||||
? `${hours}:${mins.padStart(2, '0')}:${seconds}`
|
||||
: `${mins}:${seconds}`
|
||||
return hours !== '0' ? `${hours}:${mins.padStart(2, '0')}:${seconds}` : `${mins}:${seconds}`
|
||||
} else {
|
||||
const units = {
|
||||
'en-US': {
|
||||
|
|
@ -107,23 +102,17 @@ export function formatDuration(
|
|||
} as const
|
||||
|
||||
return hours !== '0'
|
||||
? `${hours} ${units[locale].hours}${
|
||||
mins === '0' ? '' : ` ${mins} ${units[locale].mins}`
|
||||
}`
|
||||
? `${hours} ${units[locale].hours}${mins === '0' ? '' : ` ${mins} ${units[locale].mins}`}`
|
||||
: `${mins} ${units[locale].mins}`
|
||||
}
|
||||
}
|
||||
|
||||
export function scrollToTop(smooth = false) {
|
||||
document
|
||||
.querySelector('#main')
|
||||
?.scrollTo({ top: 0, behavior: smooth ? 'smooth' : 'auto' })
|
||||
document.querySelector('#main')?.scrollTo({ top: 0, behavior: smooth ? 'smooth' : 'auto' })
|
||||
}
|
||||
|
||||
export function scrollToBottom(smooth = false) {
|
||||
document
|
||||
.querySelector('#main')
|
||||
?.scrollTo({ top: 100000, behavior: smooth ? 'smooth' : 'auto' })
|
||||
document.querySelector('#main')?.scrollTo({ top: 100000, behavior: smooth ? 'smooth' : 'auto' })
|
||||
}
|
||||
|
||||
export async function getCoverColor(coverUrl: string) {
|
||||
|
|
@ -137,7 +126,7 @@ export async function getCoverColor(coverUrl: string) {
|
|||
const colorFromCache: string | undefined = await window.ipcRenderer?.invoke(
|
||||
IpcChannels.GetApiCache,
|
||||
{
|
||||
api: APIs.CoverColor,
|
||||
api: CacheAPIs.CoverColor,
|
||||
query: {
|
||||
id,
|
||||
},
|
||||
|
|
@ -177,12 +166,9 @@ export async function calcCoverColor(coverUrl: string) {
|
|||
}
|
||||
|
||||
export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
|
||||
export const isSafari = /^((?!chrome|android).)*safari/i.test(
|
||||
navigator.userAgent
|
||||
)
|
||||
export const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
export const isPWA =
|
||||
(navigator as any).standalone ||
|
||||
window.matchMedia('(display-mode: standalone)').matches
|
||||
(navigator as any).standalone || window.matchMedia('(display-mode: standalone)').matches
|
||||
export const isIosPwa = isIOS && isPWA && isSafari
|
||||
|
||||
export const sleep = (ms: number) => new Promise(r => setTimeout(r, ms))
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ interface TrackListSource {
|
|||
type: TrackListSourceType
|
||||
id: number
|
||||
}
|
||||
interface RemoteDevice {
|
||||
protocol: 'airplay' | 'chromecast'
|
||||
id: string
|
||||
}
|
||||
|
||||
export enum Mode {
|
||||
TrackList = 'trackList',
|
||||
FM = 'fm',
|
||||
|
|
@ -62,7 +59,6 @@ export class Player {
|
|||
fmTrackList: TrackID[] = []
|
||||
shuffle: boolean = false
|
||||
fmTrack: Track | null = null
|
||||
remoteDevice: RemoteDevice | null = null
|
||||
|
||||
init(params: { [key: string]: any }) {
|
||||
if (params._track) this._track = params._track
|
||||
|
|
@ -173,10 +169,6 @@ export class Player {
|
|||
window.ipcRenderer?.send(IpcChannels.Repeat, { mode: this._repeatMode })
|
||||
}
|
||||
|
||||
get _isAirplay() {
|
||||
return this.remoteDevice?.protocol === 'airplay'
|
||||
}
|
||||
|
||||
private async _initFM() {
|
||||
if (this.fmTrackList.length === 0) await this._loadMoreFMTracks()
|
||||
|
||||
|
|
@ -194,27 +186,9 @@ export class Player {
|
|||
}
|
||||
|
||||
private async _setupProgressInterval() {
|
||||
if (this.remoteDevice === null) {
|
||||
// Howler
|
||||
this._progressInterval = setInterval(() => {
|
||||
if (this.state === State.Playing) this._progress = _howler.seek()
|
||||
}, 1000)
|
||||
} else if (this._isAirplay) {
|
||||
// Airplay
|
||||
// let isFetchAirplayPlayingInfo = false
|
||||
// this._progressInterval = setInterval(async () => {
|
||||
// if (isFetchAirplayPlayingInfo) return
|
||||
// isFetchAirplayPlayingInfo = true
|
||||
// const playingInfo = await window?.ipcRenderer?.invoke(
|
||||
// 'airplay-get-playing',
|
||||
// { deviceID: this.remoteDevice?.id }
|
||||
// )
|
||||
// if (playingInfo) {
|
||||
// this._progress = playingInfo.position || 0
|
||||
// }
|
||||
// isFetchAirplayPlayingInfo = false
|
||||
// }, 1000)
|
||||
}
|
||||
this._progressInterval = setInterval(() => {
|
||||
if (this.state === State.Playing) this._progress = _howler.seek()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private async _scrobble() {
|
||||
|
|
@ -285,23 +259,12 @@ export class Player {
|
|||
return
|
||||
}
|
||||
if (this.trackID !== id) return
|
||||
if (this._isAirplay) {
|
||||
this._playAudioViaAirplay(audio)
|
||||
return
|
||||
} else {
|
||||
this._playAudioViaHowler(audio, id, autoplay)
|
||||
}
|
||||
this._playAudioViaHowler(audio, id, autoplay)
|
||||
}
|
||||
|
||||
private async _playAudioViaHowler(
|
||||
audio: string,
|
||||
id: number,
|
||||
autoplay: boolean = true
|
||||
) {
|
||||
private async _playAudioViaHowler(audio: string, id: number, autoplay: boolean = true) {
|
||||
Howler.unload()
|
||||
const url = audio.includes('?')
|
||||
? `${audio}&dash-id=${id}`
|
||||
: `${audio}?dash-id=${id}`
|
||||
const url = audio.includes('?') ? `${audio}&dash-id=${id}` : `${audio}?dash-id=${id}`
|
||||
const howler = new Howl({
|
||||
src: [url],
|
||||
format: ['mp3', 'flac', 'webm'],
|
||||
|
|
@ -325,18 +288,6 @@ export class Player {
|
|||
}
|
||||
}
|
||||
|
||||
private async _playAudioViaAirplay(audio: string) {
|
||||
// if (!this._isAirplay) {
|
||||
// console.log('No airplay device selected')
|
||||
// return
|
||||
// }
|
||||
// const result = await window.ipcRenderer?.invoke('airplay-play-url', {
|
||||
// deviceID: this.remoteDevice?.id,
|
||||
// url: audio,
|
||||
// })
|
||||
// console.log(result)
|
||||
}
|
||||
|
||||
private _howlerOnEndCallback() {
|
||||
if (this.mode !== Mode.FM && this.repeatMode === RepeatMode.One) {
|
||||
_howler.seek(0)
|
||||
|
|
@ -367,18 +318,14 @@ export class Player {
|
|||
if (this.fmTrackList.length === 0) await this._loadMoreFMTracks()
|
||||
this._playTrack()
|
||||
|
||||
this.fmTrackList.length <= 1
|
||||
? await this._loadMoreFMTracks()
|
||||
: this._loadMoreFMTracks()
|
||||
this.fmTrackList.length <= 1 ? await this._loadMoreFMTracks() : this._loadMoreFMTracks()
|
||||
prefetchNextTrack()
|
||||
}
|
||||
|
||||
private async _loadMoreFMTracks() {
|
||||
if (this.fmTrackList.length <= 5) {
|
||||
const response = await fetchPersonalFMWithReactQuery()
|
||||
const ids = (response?.data?.map(r => r.id) ?? []).filter(
|
||||
r => !this.fmTrackList.includes(r)
|
||||
)
|
||||
const ids = (response?.data?.map(r => r.id) ?? []).filter(r => !this.fmTrackList.includes(r))
|
||||
this.fmTrackList.push(...ids)
|
||||
}
|
||||
}
|
||||
|
|
@ -476,9 +423,7 @@ export class Player {
|
|||
this._setStateToLoading()
|
||||
this.mode = Mode.TrackList
|
||||
this.trackList = list
|
||||
this._trackIndex = autoPlayTrackID
|
||||
? list.findIndex(t => t === autoPlayTrackID)
|
||||
: 0
|
||||
this._trackIndex = autoPlayTrackID ? list.findIndex(t => t === autoPlayTrackID) : 0
|
||||
this._playTrack()
|
||||
}
|
||||
|
||||
|
|
@ -552,10 +497,7 @@ export class Player {
|
|||
async playFM() {
|
||||
this._setStateToLoading()
|
||||
this.mode = Mode.FM
|
||||
if (
|
||||
this.fmTrackList.length > 0 &&
|
||||
this.fmTrack?.id === this.fmTrackList[0]
|
||||
) {
|
||||
if (this.fmTrackList.length > 0 && this.fmTrack?.id === this.fmTrackList[0]) {
|
||||
this._track = this.fmTrack
|
||||
this._playAudio()
|
||||
} else {
|
||||
|
|
@ -584,29 +526,12 @@ export class Player {
|
|||
this._playTrack()
|
||||
}
|
||||
|
||||
async switchToThisComputer() {
|
||||
this.remoteDevice = null
|
||||
clearInterval(this._progressInterval)
|
||||
this._setupProgressInterval()
|
||||
}
|
||||
|
||||
async switchToAirplayDevice(deviceID: string) {
|
||||
this.remoteDevice = {
|
||||
protocol: 'airplay',
|
||||
id: deviceID,
|
||||
}
|
||||
clearInterval(this._progressInterval)
|
||||
this._setupProgressInterval()
|
||||
}
|
||||
|
||||
private async _initMediaSession() {
|
||||
console.log('init')
|
||||
if ('mediaSession' in navigator === false) return
|
||||
navigator.mediaSession.setActionHandler('play', () => this.play())
|
||||
navigator.mediaSession.setActionHandler('pause', () => this.pause())
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () =>
|
||||
this.prevTrack()
|
||||
)
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => this.prevTrack())
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => this.nextTrack())
|
||||
navigator.mediaSession.setActionHandler('seekto', event => {
|
||||
if (event.seekTime) this.progress = event.seekTime
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue