Skip to content

Commit

Permalink
fix: optimize audio load and play function
Browse files Browse the repository at this point in the history
  • Loading branch information
lijinke666 committed Sep 17, 2020
1 parent 5aa109a commit 504461d
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 91 deletions.
4 changes: 4 additions & 0 deletions __tests__/tests/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,9 @@ describe('<ReactJkMusicPlayer/>', () => {
})
it('should trigger onAudioPlay hook when audio track list change', () => {
const onAudioPlay = jest.fn()
window.HTMLMediaElement.prototype.play = () => {
onAudioPlay()
}
const wrapper = mount(
<ReactJkMusicPlayer
audioLists={[
Expand All @@ -468,6 +471,7 @@ describe('<ReactJkMusicPlayer/>', () => {
onAudioPlay={onAudioPlay}
/>,
)
wrapper.setState({ canPlay: true })
wrapper.find('.next-audio').simulate('click')
expect(onAudioPlay).toHaveBeenCalled()
})
Expand Down
8 changes: 1 addition & 7 deletions example/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,7 @@ const options = {

// audio load failed error handle
onAudioError(errMsg, currentPlayId, audioLists, audioInfo) {
console.error(
'audio load error',
errMsg,
currentPlayId,
audioLists,
audioInfo,
)
console.error('audio error', errMsg, currentPlayId, audioLists, audioInfo)
},

// theme change handle
Expand Down
15 changes: 15 additions & 0 deletions src/config/audioState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const AUDIO_NETWORK_STATE = {
NETWORK_EMPTY: 0, // 未初始化
NETWORK_IDLE: 1, // 未使用网络 304 缓存
NETWORK_LOADING: 2, // 浏览器正在下载数据
NETWORK_NO_SOURCE: 3, // 未找到资源
NETWORK_READY_SUCCESS_STATE: 4, // 成功
}

export const AUDIO_READY_STATE = {
HAVE_NOTHING: 0, // 没有关于音频/视频是否就绪的信息
HAVE_METADATA: 1, // 关于音频/视频就绪的元数据
HAVE_CURRENT_DATA: 2, // 关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
HAVE_FUTURE_DATA: 3, // 当前及至少下一帧的数据是可用的
HAVE_ENOUGH_DATA: 4, // 可用数据足以开始播放
}
7 changes: 0 additions & 7 deletions src/config/networkState.js

This file was deleted.

171 changes: 94 additions & 77 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
*/

// FIXME: quietUpdate 改坏了
// FIXME: appendAudio 的时候 如果开启了 autoplayInitLoadPlayList 没有加载
// TODO: 列表loading 和 面板 loading 时间不一致
// FIXME: 直接点击播放歌曲, 如果还没有加载 会有问题, 特别是大文件
import cls from 'classnames'
import download from 'downloadjs'
import getIsMobile from 'is-mobile'
Expand Down Expand Up @@ -48,7 +45,7 @@ import { SPACE_BAR_KEYCODE } from './config/keycode'
import LOCALE from './config/locale'
import { MEDIA_QUERY } from './config/mediaQuery'
import { MODE } from './config/mode'
import NETWORK_STATE from './config/networkState'
import { AUDIO_NETWORK_STATE, AUDIO_READY_STATE } from './config/audioState'
import PLAY_MODE from './config/playMode'
import PROP_TYPES from './config/propTypes'
import { sliderBaseOptions } from './config/slider'
Expand Down Expand Up @@ -761,7 +758,13 @@ export default class ReactJkMusicPlayer extends PureComponent {
* @description: ignore 如果 为 true playId相同则不暂停 适用于 随机播放,重新播放等逻辑
*/
audioListsPlay = (playId, ignore = false, state = this.state) => {
const { playId: currentPlayId, playing, audioLists, loading } = state
const {
playId: currentPlayId,
playing,
audioLists,
loading,
canPlay,
} = state
if (Array.isArray(audioLists) && audioLists.length === 0) {
// eslint-disable-next-line no-console
return console.warn(
Expand All @@ -771,12 +774,6 @@ export default class ReactJkMusicPlayer extends PureComponent {
if (loading && playId === currentPlayId) {
return
}
// 如果点击当前项 就暂停 或者播放
if (playId === currentPlayId && !ignore) {
this.setState({ playing: !playing })
return !playing ? this.audio.play() : this.audio.pause()
}

const playIndex = audioLists.findIndex((audio) => audio.id === playId)
const { name, cover, musicSrc, singer, lyric = '' } =
audioLists[playIndex] || {}
Expand Down Expand Up @@ -807,16 +804,27 @@ export default class ReactJkMusicPlayer extends PureComponent {
}, 0)
},
)
this.props.onAudioPlay && this.props.onAudioPlay(this.getBaseAudioInfo())
this.props.onAudioPlayTrackChange &&
this.props.onAudioPlayTrackChange(
playId,
audioLists,
this.getBaseAudioInfo(),
)
this.props.onPlayIndexChange && this.props.onPlayIndexChange(playIndex)
}
// 如果点击当前项 就暂停 或者播放
if (playId === currentPlayId && !ignore) {
this.setState({ playing: !playing })
if (!playing) {
if (canPlay) {
return this.audio.play()
}
return loadAudio(musicSrc)
}
return this.audio.pause()
}

this.props.onAudioPlayTrackChange &&
this.props.onAudioPlayTrackChange(
playId,
audioLists,
this.getBaseAudioInfo(),
)
this.props.onPlayIndexChange && this.props.onPlayIndexChange(playIndex)

if (!this.checkCurrentPlayingAudioIsInUpdatedAudioLists()) {
switch (typeof musicSrc) {
case 'function':
Expand All @@ -832,16 +840,12 @@ export default class ReactJkMusicPlayer extends PureComponent {
this.audio.pause()
this.lyric && this.lyric.stop()
this.initPlayInfo([])
this.setState({
currentTime: 0,
loading: false,
playing: false,
canPlay: false,
lyric: '',
currentLyric: '',
playId: this.initPlayId,
loadedProgress: 0,
})
this.resetAudioPlayStatus()
this.resetAudioPlayId()
}

resetAudioPlayId = () => {
this.setState({ playId: this.initPlayId })
}

clearAudioLists = () => {
Expand Down Expand Up @@ -1072,6 +1076,23 @@ export default class ReactJkMusicPlayer extends PureComponent {
)
}

resetAudioPlayStatus = () => {
return new Promise((res) => {
this.setState(
{
currentTime: 0,
loading: false,
playing: false,
canPlay: false,
lyric: '',
currentLyric: '',
loadedProgress: 0,
},
res,
)
})
}

// 返回给使用者的 音乐信息
getBaseAudioInfo() {
const {
Expand Down Expand Up @@ -1135,7 +1156,9 @@ export default class ReactJkMusicPlayer extends PureComponent {

playAudio = (isLoaded = false) => {
if (this.isAudioCanPlay || isLoaded) {
this.setAudioLoaded()
if (isLoaded) {
this.setAudioLoaded()
}
this.loadAndPlayAudio(isLoaded)
}
}
Expand Down Expand Up @@ -1176,24 +1199,37 @@ export default class ReactJkMusicPlayer extends PureComponent {
loadAndPlayAudio = (isLoaded = false) => {
const { remember } = this.props
const { isInitRemember, musicSrc } = this.state
const { networkState } = this.audio
const { networkState, readyState } = this.audio

if (!musicSrc) {
return
}

if (
networkState === AUDIO_NETWORK_STATE.NETWORK_NO_SOURCE ||
networkState === AUDIO_NETWORK_STATE.NETWORK_EMPTY
) {
return this.onAudioError({
reason: `
[loadAndPlayAudio]: Failed to load because no supported source was found.
current network status is ${networkState}.
`,
})
}

this.setState({ playing: false, loading: true })

if (networkState !== NETWORK_STATE.NETWORK_NO_SOURCE) {
if (isLoaded || readyState >= AUDIO_READY_STATE.HAVE_FUTURE_DATA) {
const { playing } = this.getLastPlayStatus()
const isLastPause = remember && !isInitRemember && !playing
const canPlay = remember ? !isLastPause : this.isAudioCanPlay
this.setState(
{
playing: remember ? !isLastPause : this.isAudioCanPlay,
loading: !isLoaded,
playing: canPlay,
loading: false,
},
() => {
if (remember ? !isLastPause : this.isAudioCanPlay) {
if (canPlay) {
this.audio.play()
}
this.setState({
Expand All @@ -1204,10 +1240,7 @@ export default class ReactJkMusicPlayer extends PureComponent {
},
)
} else {
this.onAudioError({
reason:
'[loadAndPlayAudio]: Failed to load because no supported source was found.',
})
this.audio.load()
}
}

Expand Down Expand Up @@ -1379,9 +1412,7 @@ export default class ReactJkMusicPlayer extends PureComponent {
if (this.state.audioLists.length >= 1) {
this.lyric && this.lyric.seek(this.audio.currentTime * 1000)
if (this.state.playing) {
this.setState({ playing: true }, () => {
this.loadAndPlayAudio()
})
this.loadAndPlayAudio()
} else {
this.lyric && this.lyric.stop()
}
Expand Down Expand Up @@ -1410,12 +1441,6 @@ export default class ReactJkMusicPlayer extends PureComponent {
const mergedAudioInfo = { ...e, ...audioInfo }
this.props.onAudioAbort &&
this.props.onAudioAbort(playId, audioLists, mergedAudioInfo)

if (audioLists.length) {
this.audio.pause()
this.state.isInitAutoPlay && this.audio.play()
this.lyric && this.lyric.stop()
}
}

// 切换播放器模式
Expand Down Expand Up @@ -1564,7 +1589,7 @@ export default class ReactJkMusicPlayer extends PureComponent {
target = this.audio,
eventsNames = {
waiting: this.loadAndPlayAudio,
canplaythrough: this.onAudioCanPlay,
canplay: this.onAudioCanPlay,
error: this.onAudioError,
ended: this.onAudioEnd,
pause: this.onAudioPause,
Expand All @@ -1574,7 +1599,6 @@ export default class ReactJkMusicPlayer extends PureComponent {
stalled: this.onAudioError, // 当浏览器尝试获取媒体数据,但数据不可用时
abort: this.onAudioAbort,
progress: this.onSetAudioLoadedProgress,
loadeddata: this.setAudioLoaded,
},
bind = true,
) => {
Expand Down Expand Up @@ -1841,34 +1865,28 @@ export default class ReactJkMusicPlayer extends PureComponent {
switch (typeof info.musicSrc) {
case 'function':
info.musicSrc().then((musicSrc) => {
this.setState(
{
...audioInfo,
musicSrc,
},
() => {
this.audio.load()
},
)
this.setState({
...audioInfo,
musicSrc,
})
}, this.onAudioError)
break
default:
this.setState(audioInfo, () => {
// 预加载, 防止非自动播放, 直接点击播放列表无法播放的情况
this.audio.load()
})
this.setState(audioInfo)
}
}

resetPlayId = (cb) => {
this.setState({ playId: this.initPlayId }, cb)
resetPlayId = () => {
return new Promise((res) => {
this.setState({ playId: this.initPlayId }, res)
})
}

changeAudioLists = (nextProps) => {
if (!this.checkCurrentPlayingAudioIsInUpdatedAudioLists(nextProps)) {
this.resetAudioStatus()
}
this.resetPlayId(() => {
this.resetPlayId().then(() => {
this.loadNewAudioLists(nextProps)
this.props.onAudioListsChange &&
this.props.onAudioListsChange(
Expand All @@ -1880,15 +1898,17 @@ export default class ReactJkMusicPlayer extends PureComponent {
}

updatePlayIndex = (playIndex) => {
const currentPlayIndex = this.getCurrentPlayIndex()
if (playIndex !== undefined && currentPlayIndex !== playIndex) {
const currentPlay = this.state.audioLists[this.getPlayIndex(playIndex)]
// TODO: 调用两次
if (currentPlay && currentPlay.id) {
this.audioListsPlay(currentPlay.id, true)
this.resetAudioPlayStatus().then(() => {
const currentPlayIndex = this.getCurrentPlayIndex()
if (playIndex !== undefined && currentPlayIndex !== playIndex) {
const currentPlayAudio = this.state.audioLists[
this.getPlayIndex(playIndex)
]
if (currentPlayAudio && currentPlayAudio.id) {
this.audioListsPlay(currentPlayAudio.id, true)
}
}
}
this.setState({ isAutoPlayWhenUserClicked: false })
})
}

playByIndex = (index) => {
Expand All @@ -1910,9 +1930,6 @@ export default class ReactJkMusicPlayer extends PureComponent {
})
newAudioLists.splice(fromIndex, 0, ...addedAudioLists)
this.changeAudioLists({ ...this.props, audioLists: newAudioLists })
// this.setAudioLoaded()
// this.audioListsPlay()
// this.playAudio()
}

getEnhanceAudio = () => {
Expand Down

0 comments on commit 504461d

Please sign in to comment.