Skip to content

Commit

Permalink
chore(error-handling): adds MEDIA_ERROR event
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-heimbuch committed Dec 1, 2018
1 parent 5234aae commit da7a644
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 96 deletions.
21 changes: 4 additions & 17 deletions example/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import 'file-loader?name=index.html!./index.html'
import m4a from 'file-loader!./audio-files/example.m4a'
import mp3 from 'file-loader!./audio-files/example.mp3'
import ogg from 'file-loader!./audio-files/example.ogg'
import stereoFile from 'file-loader!./audio-files/stereo-example.mp3'
import monoFile from 'file-loader!./audio-files/mono-example.mp3'

import { audio } from '@podlove/html5-audio-driver'
import { mono, stereo } from '@podlove/html5-audio-driver/filters'

import { registerActions } from './src/actions'
import { registerEvents } from './src/events'
Expand All @@ -24,17 +21,7 @@ const myAudio = audio([{
mimeType: 'audio/pgg'
}])

const myStereoExample = audio([{
url: stereoFile,
mimeType: 'audio/mp3'
}])

const myMonoExample = audio([{
url: monoFile,
mimeType: 'audio/mp3'
}])

registerEvents(myMonoExample)
registerActions(myMonoExample)
registerInputs(myMonoExample)
registerFilters(myMonoExample)
registerEvents(myAudio)
registerActions(myAudio)
registerInputs(myAudio)
registerFilters(myAudio)
2 changes: 2 additions & 0 deletions example/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const unmuteButton = document.getElementById('unmute')
export const registerActions = node => {
const mediaActions = actions(node)

window.actions = mediaActions

loadButton.addEventListener('click', mediaActions.load)
playButton.addEventListener('click', mediaActions.play)
pauseButton.addEventListener('click', mediaActions.pause)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@podlove/html5-audio-driver",
"version": "1.3.5",
"version": "1.3.6",
"description": "Pure html5 audio driver",
"author": "Alexander Heimbuch <github@heimbu.ch>",
"license": "MIT",
Expand Down
82 changes: 43 additions & 39 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,77 +5,81 @@ import { collectProperties } from './utils'
* ACTIONS
*/

// load :: AudioElement -> () -> AudioElement
const load = audio => () => {
audio.load()
return audio
// load :: MediaElement -> () -> MediaElement
const load = media => () => {
media.load()
return media
}

// play :: AudioElement -> () -> AudioElement
const play = audio => () => {
// play :: MediaElement -> () -> MediaElement
const play = media => () => {
// Some browsers doesn't implement it as a promise
try {
audio
media
.play()
// safe play, fixes hides inconsistency in audio API
.catch()
} catch (e) {}

return audio
// safe play, fixes inconsistency in media API
.catch(e => {
media.dispatchEvent(new CustomEvent('error-media', { detail: e }))
})
} catch (e) {
media.dispatchEvent(new CustomEvent('error-media', { detail: e }))
}

return media
}

// pause :: AudioElement -> () -> AudioElement
const pause = audio => () => {
audio.pause()
return audio
// pause :: MediaElement -> () -> MediaElement
const pause = media => () => {
media.pause()
return media
}

// mute :: AudioElement -> () -> AudioElement
const mute = audio => () => {
audio.muted = true
return audio
// mute :: MediaElement -> () -> MediaElement
const mute = media => () => {
media.muted = true
return media
}

// unmute :: AudioElement -> () -> AudioElement
const unmute = audio => () => {
audio.muted = false
return audio
// unmute :: MediaElement -> () -> MediaElement
const unmute = media => () => {
media.muted = false
return media
}

// setVolume :: AudioElement -> Number -> AudioElement
const setVolume = audio => (volume = 1) => {
// setVolume :: MediaElement -> Number -> MediaElement
const setVolume = media => (volume = 1) => {
volume = parseFloat(volume)
volume = volume < 0 ? 0 : volume
volume = volume > 1 ? 1 : volume

audio.volume = volume
return audio
media.volume = volume
return media
}

// setRate :: AudioElement -> Number -> AudioElement
const setRate = audio => (rate = 1) => {
// setRate :: MediaElement -> Number -> MediaElement
const setRate = media => (rate = 1) => {
rate = parseFloat(rate)
rate = rate > 4 ? 4 : rate
rate = rate < 0.5 ? 0.5 : rate
audio.playbackRate = parseFloat(rate)
media.playbackRate = parseFloat(rate)

return audio
return media
}

// setPlaytime :: AudioElement -> Number -> AudioElement
const setPlaytime = audio => (time = 0) => {
const audioDuration = duration(audio)
// setPlaytime :: MediaElement -> Number -> MediaElement
const setPlaytime = media => (time = 0) => {
const mediaDuration = duration(media)
time = parseFloat(time)
time = time > audioDuration ? audioDuration : time
time = time > mediaDuration ? mediaDuration : time
time = time < 0 ? 0 : time

// Safe play for IE11+
try {
audio.playtime = time
audio.currentTime = time
media.playtime = time
media.custom.currentTime = time
} catch (e) {}

return audio
return media
}

const actions = collectProperties({
Expand Down
13 changes: 10 additions & 3 deletions src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,28 @@ const onError = curry((media, callback) => {

switch (networkState || this.networkState) {
case HTMLMediaElement.NETWORK_NO_SOURCE:
return callback('NETWORK_NO_SOURCE')
return callback('NETWORK_NO_SOURCE', {})

case HTMLMediaElement.NETWORK_EMPTY:
return callback('NETWORK_EMPTY')
return callback('NETWORK_EMPTY', {})

case HTMLMediaElement.NETWORK_IDLE:
return callback('NETWORK_IDLE')

case HTMLMediaElement.NETWORK_LOADING:
return callback('NETWORK_LOADING')
return callback('NETWORK_LOADING', {})
}
},
true
)

media.addEventListener(
'error-media',
function ({ detail }) {
callback('MEDIA_ERROR', detail)
}, false
)

return media
})

Expand Down
2 changes: 1 addition & 1 deletion src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const attatchStream = media => {

// Finally start loading
hls.on(Hls.Events.MANIFEST_PARSED, () => {
hls.startLoad(media.currentTime)
hls.startLoad(media.custom.currentTime)
})

// Translate errors to native media errors
Expand Down
40 changes: 11 additions & 29 deletions src/polyfills.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const setMediaDefaults = node => {
node.loop = false
node.preload = 'none'
node.controls = false
node.custom = { currentTime: 0 }

return node
}
Expand All @@ -21,32 +22,17 @@ const setMediaDefaults = node => {
*
* Adds ability for Safari to set the playtime without the need of loading the full file
*/
const updatePlaytimeToCurrentTime = audio => {
audio.playtime = audio.currentTime
return audio
const updatePlaytimeToCurrentTime = media => {
media.playtime = media.custom.currentTime
return media
}

const updateCurrentTimeToPlaytime = audio => {
const updateCurrentTimeToPlaytime = media => {
try {
audio.currentTime = audio.playtime
media.custom.currentTime = media.playtime
} catch (e) {}

return audio
}

// Probe if html implemnetation has quirks (for Safari and IE)
const needsVirtualPlaytime = audioNode => {
try {
audioNode.currentTime = 10

if (audioNode.currentTime === 0) {
return true
}
} catch (e) {
return true
}

return false
return media
}

// HTML Audio implementation 101 quirks: on Safari and iOS you just can set currentTime after loading
Expand All @@ -55,14 +41,10 @@ const polyfillPlaytime = node => {

node.addEventListener('timeupdate', compose(updatePlaytimeToCurrentTime, getNodeFromEvent))

if (needsVirtualPlaytime(node)) {
node.addEventListener('canplay',
compose(updateCurrentTimeToPlaytime, getNodeFromEvent), { once: true })
node.addEventListener('play',
compose(updateCurrentTimeToPlaytime, getNodeFromEvent))
} else {
node.currentTime = 0
}
node.addEventListener('canplay',
compose(updateCurrentTimeToPlaytime, getNodeFromEvent), { once: true })
node.addEventListener('play',
compose(updateCurrentTimeToPlaytime, getNodeFromEvent))

return node
}
Expand Down
2 changes: 1 addition & 1 deletion src/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const state = compose(state => {

// TODO: make functional
const playing = media =>
media.currentTime > 0 &&
media.custom.currentTime > 0 &&
!media.paused &&
!media.ended &&
media.readyState > 2
Expand Down
4 changes: 3 additions & 1 deletion test/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { audio } from '@podlove/html5-audio-driver'
import { onPlay } from '@podlove/html5-audio-driver/events'
import { actions, setPlaytime, play, pause, load, mute, unmute, setVolume, setRate } from '@podlove/html5-audio-driver/actions'
import { duration, playing, muted, volume, rate } from '@podlove/html5-audio-driver/props'
import { onError } from '@podlove/html5-audio-driver/events'
import { audioFixture } from 'test/fixtures'
import { testLoader, onDesktopIt } from 'test/helpers'

Expand All @@ -12,6 +13,7 @@ describe('actions', () => {

beforeEach(() => {
audioElement = audio(audioFixture)
onError(audioElement, console.log)
})

afterEach(() => {
Expand Down Expand Up @@ -54,7 +56,7 @@ describe('actions', () => {
playtimeSetter(50)
playAction()
expect(audioElement.playtime).to.be.at.least(50)
expect(audioElement.currentTime).to.be.at.least(50)
expect(audioElement.custom.currentTime).to.be.at.least(50)
}, duration))
})
})
Expand Down
50 changes: 46 additions & 4 deletions test/testbed.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,59 @@
<title>Mocha Tests</title>
</head>
<body>
<style>
.start-tests {
position: fixed;
width: 250px;
height: 30px;
top: calc(50% - 15px);
left: calc(50% - 125px);
background: none;
color: inherit;
border: none;
padding: 0;
cursor: pointer;
outline: inherit;
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
border-radius: .4rem;
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 1.1rem;
font-weight: 700;
height: 3.8rem;
letter-spacing: .1rem;
line-height: 3.8rem;
padding: 0 3.0rem;
text-align: center;
text-decoration: none;
text-transform: uppercase;
white-space: nowrap;
}
</style>


<button class="start-tests" id="start-tests">Start Tests</button>

<div id="mocha"></div>
<script src="runtime.js"></script>
<script>mocha.setup({ ui: 'bdd', timeout: 20000 })</script>
<script>
var btn = document.getElementById('start-tests')
mocha.setup({ ui: 'bdd', timeout: 20000 })
btn.addEventListener('click', function () {
mocha.run()
document.body.removeChild(btn)
})
</script>

{{!-- Test Files --}}
{{#each scripts}}
<script src="{{this}}"></script>
{{/each}}

<script>
mocha.run();
</script>

</body>
</html>

0 comments on commit da7a644

Please # to comment.