Skip to content

Commit 32d0462

Browse files
committed
Add autoplay support & fix startTime bug
1 parent 648ed41 commit 32d0462

6 files changed

Lines changed: 97 additions & 27 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ The following attributes are supported on both the \<audio\> and \<video\> eleme
166166
- **id** - required; any unique ID
167167
- **data-able-player** - required
168168
- **data-start-time** - optional; time at which you want the audio to start playing (in seconds)
169+
- **autoplay** - optional; play media automatically when page loads. For accessibility reasons, this is *not* recommended unless user is sure to *expect* media to automatically start. For example, autoplay could reasonably be used in conjunction with data-start-time in a media search application.
169170
- **data-transcript-div** - optional; id of an external div in which to display the interactive transcript.
170171
The transcript is generated automatically if captions and/or descriptions are available.
171172
If this attribute is not provided the transcript will be displayed in its default container

build/ableplayer.js

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@
6262
return;
6363
}
6464

65+
if ($(media).attr('autoplay') !== undefined && $(media).attr('autoplay') !== "false") {
66+
this.autoplay = true;
67+
}
68+
else {
69+
this.autoplay = false;
70+
}
71+
6572
// override defaults with values of data-* attributes
6673

6774
var includeTranscript = media.data('include-transcript');
@@ -184,6 +191,13 @@
184191
if (thisObj.countProperties(thisObj.tt) > 50) {
185192
// close enough to ensure that most text variables are populated
186193
thisObj.setup();
194+
if (thisObj.startTime > 0 && !thisObj.autoplay) {
195+
// scrub ahead to startTime, but don't start playing
196+
// can't do this in media event listener
197+
// because in some browsers no media events are fired until media.play is requested
198+
// even if preload="auto"
199+
thisObj.onMediaUpdateTime();
200+
}
187201
}
188202
else {
189203
// can't continue loading player with no text
@@ -199,9 +213,8 @@
199213
AblePlayer.prototype.setup = function() {
200214
var thisObj = this;
201215
if (this.debug && this.startTime > 0) {
202-
console.log('Will start media at ' + startTime + ' seconds');
216+
console.log('Will start media at ' + this.startTime + ' seconds');
203217
}
204-
205218
this.reinitialize().then(function () {
206219
if (!thisObj.player) {
207220
// No player for this media, show last-line fallback.
@@ -3960,14 +3973,14 @@
39603973
})();
39613974
(function () {
39623975
AblePlayer.prototype.seekTo = function (newTime) {
3963-
// TODO: How do we want startTime functionality to work?
39643976

39653977
if (this.player === 'html5') {
39663978
var seekable;
39673979

39683980
this.startTime = newTime;
39693981
// Check HTML5 media "seekable" property to be sure media is seekable to startTime
39703982
seekable = this.media.seekable;
3983+
39713984
if (seekable.length > 0 && this.startTime >= seekable.start(0) && this.startTime <= seekable.end(0)) {
39723985
this.media.currentTime = this.startTime;
39733986
}
@@ -4244,7 +4257,7 @@
42444257
else if (this.player === 'youtube') {
42454258
this.youtubePlayer.playVideo();
42464259
}
4247-
this.startedPlaying = true;
4260+
this.startedPlaying = true;
42484261
};
42494262

42504263
// Right now, update the seekBar values based on current duration and time.
@@ -4508,7 +4521,6 @@
45084521
else {
45094522
this.pauseMedia();
45104523
}
4511-
45124524
this.refreshControls();
45134525
};
45144526

@@ -5359,12 +5371,28 @@
53595371
(function () {
53605372
// Media events
53615373
AblePlayer.prototype.onMediaUpdateTime = function () {
5362-
if (this.startTime && !this.startedPlaying) {
5363-
// try seeking again, if seeking failed on canplay or canplaythrough
5364-
this.seekTo(this.startTime);
5365-
this.playMedia();
5366-
}
5367-
5374+
if (!this.startedPlaying) {
5375+
if (this.startTime) {
5376+
if (this.startTime === this.media.currentTime) {
5377+
// media has already scrubbed to start time
5378+
if (this.autoplay) {
5379+
this.playMedia();
5380+
}
5381+
}
5382+
else {
5383+
// continue seeking ahead until currentTime == startTime
5384+
this.seekTo(this.startTime);
5385+
}
5386+
}
5387+
else {
5388+
// autoplay should generally be avoided unless a startTime is provided
5389+
// but we'll trust the developer to be using this feature responsibly
5390+
if (this.autoplay) {
5391+
this.playMedia();
5392+
}
5393+
}
5394+
}
5395+
53685396
// show highlight in transcript
53695397
if (this.prefHighlight === 1) {
53705398
this.highlightTranscript(this.getElapsed());
@@ -5659,11 +5687,17 @@
56595687
thisObj.onMediaNewSourceLoad();
56605688
})
56615689
.on('canplay',function() {
5690+
if (thisObj.debug) {
5691+
console.log('canplay event');
5692+
}
56625693
if (thisObj.startTime && !thisObj.startedPlaying) {
56635694
thisObj.seekTo(thisObj.startTime);
56645695
}
56655696
})
56665697
.on('canplaythrough',function() {
5698+
if (thisObj.debug) {
5699+
console.log('canplaythrough event');
5700+
}
56675701
if (thisObj.startTime && !thisObj.startedPlaying) {
56685702
// try again, if seeking failed on canplay
56695703
thisObj.seekTo(thisObj.startTime);
@@ -5678,7 +5712,7 @@
56785712
.on('progress', function() {
56795713
thisObj.refreshControls();
56805714
})
5681-
.on('waiting',function() {
5715+
.on('waiting',function() {
56825716
thisObj.refreshControls();
56835717
})
56845718
.on('durationchange',function() {

scripts/ableplayer-base.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@
6262
return;
6363
}
6464

65+
if ($(media).attr('autoplay') !== undefined && $(media).attr('autoplay') !== "false") {
66+
this.autoplay = true;
67+
}
68+
else {
69+
this.autoplay = false;
70+
}
71+
6572
// override defaults with values of data-* attributes
6673

6774
var includeTranscript = media.data('include-transcript');
@@ -184,6 +191,13 @@
184191
if (thisObj.countProperties(thisObj.tt) > 50) {
185192
// close enough to ensure that most text variables are populated
186193
thisObj.setup();
194+
if (thisObj.startTime > 0 && !thisObj.autoplay) {
195+
// scrub ahead to startTime, but don't start playing
196+
// can't do this in media event listener
197+
// because in some browsers no media events are fired until media.play is requested
198+
// even if preload="auto"
199+
thisObj.onMediaUpdateTime();
200+
}
187201
}
188202
else {
189203
// can't continue loading player with no text
@@ -199,9 +213,8 @@
199213
AblePlayer.prototype.setup = function() {
200214
var thisObj = this;
201215
if (this.debug && this.startTime > 0) {
202-
console.log('Will start media at ' + startTime + ' seconds');
216+
console.log('Will start media at ' + this.startTime + ' seconds');
203217
}
204-
205218
this.reinitialize().then(function () {
206219
if (!thisObj.player) {
207220
// No player for this media, show last-line fallback.

scripts/control.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
(function () {
22
AblePlayer.prototype.seekTo = function (newTime) {
3-
// TODO: How do we want startTime functionality to work?
43

54
if (this.player === 'html5') {
65
var seekable;
76

87
this.startTime = newTime;
98
// Check HTML5 media "seekable" property to be sure media is seekable to startTime
109
seekable = this.media.seekable;
10+
1111
if (seekable.length > 0 && this.startTime >= seekable.start(0) && this.startTime <= seekable.end(0)) {
1212
this.media.currentTime = this.startTime;
1313
}
@@ -284,7 +284,7 @@
284284
else if (this.player === 'youtube') {
285285
this.youtubePlayer.playVideo();
286286
}
287-
this.startedPlaying = true;
287+
this.startedPlaying = true;
288288
};
289289

290290
// Right now, update the seekBar values based on current duration and time.
@@ -548,7 +548,6 @@
548548
else {
549549
this.pauseMedia();
550550
}
551-
552551
this.refreshControls();
553552
};
554553

scripts/event.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
(function () {
22
// Media events
33
AblePlayer.prototype.onMediaUpdateTime = function () {
4-
if (this.startTime && !this.startedPlaying) {
5-
// try seeking again, if seeking failed on canplay or canplaythrough
6-
this.seekTo(this.startTime);
7-
this.playMedia();
8-
}
9-
4+
if (!this.startedPlaying) {
5+
if (this.startTime) {
6+
if (this.startTime === this.media.currentTime) {
7+
// media has already scrubbed to start time
8+
if (this.autoplay) {
9+
this.playMedia();
10+
}
11+
}
12+
else {
13+
// continue seeking ahead until currentTime == startTime
14+
this.seekTo(this.startTime);
15+
}
16+
}
17+
else {
18+
// autoplay should generally be avoided unless a startTime is provided
19+
// but we'll trust the developer to be using this feature responsibly
20+
if (this.autoplay) {
21+
this.playMedia();
22+
}
23+
}
24+
}
25+
1026
// show highlight in transcript
1127
if (this.prefHighlight === 1) {
1228
this.highlightTranscript(this.getElapsed());
@@ -301,11 +317,17 @@
301317
thisObj.onMediaNewSourceLoad();
302318
})
303319
.on('canplay',function() {
320+
if (thisObj.debug) {
321+
console.log('canplay event');
322+
}
304323
if (thisObj.startTime && !thisObj.startedPlaying) {
305324
thisObj.seekTo(thisObj.startTime);
306325
}
307326
})
308327
.on('canplaythrough',function() {
328+
if (thisObj.debug) {
329+
console.log('canplaythrough event');
330+
}
309331
if (thisObj.startTime && !thisObj.startedPlaying) {
310332
// try again, if seeking failed on canplay
311333
thisObj.seekTo(thisObj.startTime);
@@ -320,7 +342,7 @@
320342
.on('progress', function() {
321343
thisObj.refreshControls();
322344
})
323-
.on('waiting',function() {
345+
.on('waiting',function() {
324346
thisObj.refreshControls();
325347
})
326348
.on('durationchange',function() {

tests/test2.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323
<h1>Able Player Test #2:<br/>Audio player with non-zero start time</h1>
2424

2525
<p>This player is initialized to start automatically, at 2:20 into the track (just prior to the guitar solo).
26-
This can be used to start the media from search results or an interactive transcript.
26+
This can be used to start the media from search results or an interactive transcript.
27+
In order to play the media automatically at the target start time, the media element must also include the <em>autoplay</em> attribute.
2728
</p>
2829

2930
<p>For additional tests see the <a href="index.html">Index of <em>Able Player</em> Tests</a>.</p>
3031

3132
<!-- use the following markup for each media element -->
32-
<audio id="audio1" preload="auto" data-able-player data-start-time="140">
33+
<audio id="audio1" preload="auto" data-able-player data-start-time="140" autoplay>
3334
<source type="audio/ogg" src="../media/smallf.ogg"/>
34-
<source type="audio/mpeg" src="../media/smallf.ogg"/>
35+
<source type="audio/mpeg" src="../media/smallf.mp3"/>
3536
</audio>
3637

3738
</body>

0 commit comments

Comments
 (0)