Merge pull request #159 from jcass77/fix/40_avoid_polling
Avoid polling for current track and time changes.
This commit is contained in:
commit
985a853ca3
@ -69,6 +69,7 @@ v2.2.0 (UNRELEASED)
|
|||||||
- Remove unused iScroll libraries and references.
|
- Remove unused iScroll libraries and references.
|
||||||
- Remove unused jQuery.Mobile.iScrollView libraries and references.
|
- Remove unused jQuery.Mobile.iScrollView libraries and references.
|
||||||
- Remove unused jQuery.Truncate libraries and references.
|
- Remove unused jQuery.Truncate libraries and references.
|
||||||
|
- Avoid polling for current track and time changes. (Fixes: `#40 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/40>`_).
|
||||||
|
|
||||||
**Fixes**
|
**Fixes**
|
||||||
|
|
||||||
|
|||||||
@ -179,11 +179,6 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#songelapsed, #songlength {
|
|
||||||
font-size: 10px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************
|
/************************
|
||||||
* Volume Slider
|
* Volume Slider
|
||||||
***********************/
|
***********************/
|
||||||
@ -432,10 +427,14 @@ a {
|
|||||||
|
|
||||||
.pull-right {
|
.pull-right {
|
||||||
float: right;
|
float: right;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pull-left {
|
.pull-left {
|
||||||
float: left;
|
float: left;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden, #allresultloader, .loader {
|
.hidden, #allresultloader, .loader {
|
||||||
|
|||||||
@ -297,10 +297,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="slidercontainer"><!-- slider for track position -->
|
<div id="slidercontainer"><!-- slider for track position -->
|
||||||
<span id="songelapsed" class="pull-left"> 0:00 </span>
|
<span id="songelapsed" class="pull-left"></span>
|
||||||
<span id="songlength" class="pull-right"> 0:00 </span>
|
<span id="songlength" class="pull-right"></span>
|
||||||
<label for="trackslider" disabled="disabled" class="ui-hidden-accessible">Position</label>
|
<label for="trackslider" disabled="disabled" class="ui-hidden-accessible">Position</label>
|
||||||
<input id="trackslider" name="trackslider" data-mini="true" type="range" onchange="doSeekPos(this.value);" />
|
<input id="trackslider" name="trackslider" data-mini="true" type="range" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /nowplaying -->
|
<!-- /nowplaying -->
|
||||||
@ -469,6 +469,8 @@
|
|||||||
<!-- /page one -->
|
<!-- /page one -->
|
||||||
<script type="text/javascript" src="../mopidy/mopidy.min.js"></script>
|
<script type="text/javascript" src="../mopidy/mopidy.min.js"></script>
|
||||||
<script src="vendors/jquery_cookie/jquery.cookie.js"></script>
|
<script src="vendors/jquery_cookie/jquery.cookie.js"></script>
|
||||||
|
<script src="vendors/media_progress_timer/timer.js"></script>
|
||||||
|
<script src="js/progress_timer.js"></script>
|
||||||
<script src="js/controls.js"></script>
|
<script src="js/controls.js"></script>
|
||||||
<script src="js/library.js"></script>
|
<script src="js/library.js"></script>
|
||||||
<script src="js/functionsvars.js"></script>
|
<script src="js/functionsvars.js"></script>
|
||||||
|
|||||||
61
mopidy_musicbox_webclient/static/js/controls.js
vendored
61
mopidy_musicbox_webclient/static/js/controls.js
vendored
@ -260,11 +260,14 @@ function setPlayState(nwplay) {
|
|||||||
$("#btplayNowPlaying").attr('title', 'Pause');
|
$("#btplayNowPlaying").attr('title', 'Pause');
|
||||||
$("#btplay >i").removeClass('fa-play').addClass('fa-pause');
|
$("#btplay >i").removeClass('fa-play').addClass('fa-pause');
|
||||||
$("#btplay").attr('title', 'Pause');
|
$("#btplay").attr('title', 'Pause');
|
||||||
|
mopidy.playback.getTimePosition().then(processCurrentposition, console.error);
|
||||||
|
startProgressTimer();
|
||||||
} else {
|
} else {
|
||||||
$("#btplayNowPlaying >i").removeClass('fa-pause').addClass('fa-play');
|
$("#btplayNowPlaying >i").removeClass('fa-pause').addClass('fa-play');
|
||||||
$("#btplayNowPlaying").attr('title', 'Play');
|
$("#btplayNowPlaying").attr('title', 'Play');
|
||||||
$("#btplay >i").removeClass('fa-pause').addClass('fa-play');
|
$("#btplay >i").removeClass('fa-pause').addClass('fa-play');
|
||||||
$("#btplay").attr('title', 'Play');
|
$("#btplay").attr('title', 'Play');
|
||||||
|
progressTimer.stop();
|
||||||
}
|
}
|
||||||
play = nwplay;
|
play = nwplay;
|
||||||
}
|
}
|
||||||
@ -357,40 +360,13 @@ function doSingle() {
|
|||||||
* Use a timer to prevent looping of commands *
|
* Use a timer to prevent looping of commands *
|
||||||
***********************************************/
|
***********************************************/
|
||||||
function doSeekPos(value) {
|
function doSeekPos(value) {
|
||||||
var val = $("#trackslider").val();
|
|
||||||
newposition = Math.round(val);
|
|
||||||
if (!initgui) {
|
|
||||||
pausePosTimer();
|
|
||||||
//set timer to not trigger it too much
|
|
||||||
clearTimeout(seekTimer);
|
|
||||||
$("#songelapsed").html(timeFromSeconds(val / 1000));
|
|
||||||
seekTimer = setTimeout(triggerPos, 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function triggerPos() {
|
|
||||||
if (mopidy) {
|
if (mopidy) {
|
||||||
posChanging = true;
|
mopidy.playback.seek({'time_position': Math.round(value)});
|
||||||
mopidy.playback.seek({'time_position': newposition});
|
|
||||||
resumePosTimer();
|
|
||||||
posChanging = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPosition(pos) {
|
function setPosition(pos) {
|
||||||
if (posChanging) {
|
setProgressTimer(pos);
|
||||||
return;
|
|
||||||
}
|
|
||||||
var oldval = initgui;
|
|
||||||
if (pos > songlength) {
|
|
||||||
pos = songlength;
|
|
||||||
pausePosTimer();
|
|
||||||
}
|
|
||||||
currentposition = pos;
|
|
||||||
initgui = true;
|
|
||||||
$("#trackslider").val(currentposition).slider('refresh');
|
|
||||||
initgui = oldval;
|
|
||||||
$("#songelapsed").html(timeFromSeconds(currentposition / 1000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************
|
/***********************************************
|
||||||
@ -430,33 +406,6 @@ function doMute() {
|
|||||||
mopidy.mixer.setMute({'mute': !mute});
|
mopidy.mixer.setMute({'mute': !mute});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************
|
|
||||||
* Track position timer *
|
|
||||||
**************************/
|
|
||||||
|
|
||||||
//timer function to update interface
|
|
||||||
function updatePosTimer() {
|
|
||||||
currentposition += TRACK_TIMER;
|
|
||||||
setPosition(currentposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resumePosTimer() {
|
|
||||||
pausePosTimer();
|
|
||||||
if (songlength > 0) {
|
|
||||||
posTimer = setInterval(updatePosTimer, TRACK_TIMER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initPosTimer() {
|
|
||||||
pausePosTimer();
|
|
||||||
// setPosition(0);
|
|
||||||
resumePosTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function pausePosTimer() {
|
|
||||||
clearInterval(posTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/************
|
/************
|
||||||
* Stream *
|
* Stream *
|
||||||
************/
|
************/
|
||||||
|
|||||||
@ -15,11 +15,8 @@ var single;
|
|||||||
var currentVolume = -1;
|
var currentVolume = -1;
|
||||||
var mute;
|
var mute;
|
||||||
var volumeChanging = false;
|
var volumeChanging = false;
|
||||||
var posChanging = false;
|
|
||||||
|
|
||||||
var posTimer;
|
|
||||||
var volumeTimer;
|
var volumeTimer;
|
||||||
var seekTimer;
|
|
||||||
var initgui = true;
|
var initgui = true;
|
||||||
var currentpos = 0;
|
var currentpos = 0;
|
||||||
var popupData = {};
|
var popupData = {};
|
||||||
@ -74,12 +71,6 @@ PLAY_NOW_SEARCH = 5;
|
|||||||
|
|
||||||
MAX_TABLEROWS = 50;
|
MAX_TABLEROWS = 50;
|
||||||
|
|
||||||
//update track slider timer, milliseconds
|
|
||||||
TRACK_TIMER = 1000;
|
|
||||||
|
|
||||||
//check status timer, every 5 or 10 sec
|
|
||||||
STATUS_TIMER = 10000;
|
|
||||||
|
|
||||||
// the first part of Mopidy extensions which serve radio streams
|
// the first part of Mopidy extensions which serve radio streams
|
||||||
var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict'];
|
var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict'];
|
||||||
|
|
||||||
|
|||||||
@ -7,19 +7,17 @@
|
|||||||
* Song Info Sreen *
|
* Song Info Sreen *
|
||||||
********************/
|
********************/
|
||||||
function resetSong() {
|
function resetSong() {
|
||||||
if (!posChanging) {
|
resetProgressTimer();
|
||||||
pausePosTimer();
|
setPlayState(false);
|
||||||
setPlayState(false);
|
setPosition(0);
|
||||||
setPosition(0);
|
var data = new Object;
|
||||||
var data = new Object;
|
data.tlid = -1;
|
||||||
data.tlid = -1;
|
data.track = new Object;
|
||||||
data.track = new Object;
|
data.track.name = '';
|
||||||
data.track.name = '';
|
data.track.artists = '';
|
||||||
data.track.artists = '';
|
data.track.length = 0;
|
||||||
data.track.length = 0;
|
data.track.uri = ' ';
|
||||||
data.track.uri = ' ';
|
setSongInfo(data);
|
||||||
setSongInfo(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeMb() {
|
function resizeMb() {
|
||||||
@ -104,18 +102,18 @@ function setSongInfo(data) {
|
|||||||
songdata = data;
|
songdata = data;
|
||||||
|
|
||||||
setSongTitle(data.track.name, false);
|
setSongTitle(data.track.name, false);
|
||||||
|
songlength = Infinity;
|
||||||
|
|
||||||
if (!data.track.length || data.track.length == 0) {
|
if (!data.track.length || data.track.length == 0) {
|
||||||
songlength = 0;
|
resetProgressTimer();
|
||||||
$("#songlength").html('');
|
$('#trackslider').next().find('.ui-slider-handle').hide();
|
||||||
pausePosTimer();
|
|
||||||
$('#trackslider').slider('disable');
|
$('#trackslider').slider('disable');
|
||||||
// $('#streamnameinput').val(data.track.name);
|
// $('#streamnameinput').val(data.track.name);
|
||||||
// $('#streamuriinput').val(data.track.uri);
|
// $('#streamuriinput').val(data.track.uri);
|
||||||
} else {
|
} else {
|
||||||
songlength = data.track.length;
|
songlength = data.track.length;
|
||||||
$("#songlength").html(timeFromSeconds(data.track.length / 1000));
|
|
||||||
$('#trackslider').slider('enable');
|
$('#trackslider').slider('enable');
|
||||||
|
$('#trackslider').next().find('.ui-slider-handle').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
var arttmp = '';
|
var arttmp = '';
|
||||||
@ -143,7 +141,8 @@ function setSongInfo(data) {
|
|||||||
$("#modalartist").html(arttmp);
|
$("#modalartist").html(arttmp);
|
||||||
|
|
||||||
$("#trackslider").attr("min", 0);
|
$("#trackslider").attr("min", 0);
|
||||||
$("#trackslider").attr("max", data.track.length);
|
$("#trackslider").attr("max", songlength);
|
||||||
|
progressTimer.reset().set(0, songlength);
|
||||||
|
|
||||||
resizeMb();
|
resizeMb();
|
||||||
}
|
}
|
||||||
@ -248,15 +247,8 @@ function initSocketevents() {
|
|||||||
mopidy.on("event:optionsChanged", updateOptions);
|
mopidy.on("event:optionsChanged", updateOptions);
|
||||||
|
|
||||||
mopidy.on("event:trackPlaybackStarted", function(data) {
|
mopidy.on("event:trackPlaybackStarted", function(data) {
|
||||||
mopidy.playback.getTimePosition().then(processCurrentposition, console.error);
|
|
||||||
setPlayState(true);
|
|
||||||
setSongInfo(data.tl_track);
|
setSongInfo(data.tl_track);
|
||||||
initPosTimer();
|
setPlayState(true);
|
||||||
});
|
|
||||||
|
|
||||||
mopidy.on("event:trackPlaybackPaused", function(data) {
|
|
||||||
pausePosTimer();
|
|
||||||
setPlayState(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
mopidy.on("event:playlistsLoaded", function(data) {
|
mopidy.on("event:playlistsLoaded", function(data) {
|
||||||
@ -276,12 +268,11 @@ function initSocketevents() {
|
|||||||
|
|
||||||
mopidy.on("event:playbackStateChanged", function(data) {
|
mopidy.on("event:playbackStateChanged", function(data) {
|
||||||
switch (data["new_state"]) {
|
switch (data["new_state"]) {
|
||||||
|
case "paused":
|
||||||
case "stopped":
|
case "stopped":
|
||||||
resetSong();
|
setPlayState(false);
|
||||||
break;
|
break;
|
||||||
case "playing":
|
case "playing":
|
||||||
mopidy.playback.getTimePosition().then(processCurrentposition, console.error);
|
|
||||||
resumePosTimer();
|
|
||||||
setPlayState(true);
|
setPlayState(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -293,6 +284,9 @@ function initSocketevents() {
|
|||||||
|
|
||||||
mopidy.on("event:seeked", function(data) {
|
mopidy.on("event:seeked", function(data) {
|
||||||
setPosition(parseInt(data["time_position"]));
|
setPosition(parseInt(data["time_position"]));
|
||||||
|
if (play) {
|
||||||
|
startProgressTimer();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mopidy.on("event:streamTitleChanged", function(data) {
|
mopidy.on("event:streamTitleChanged", function(data) {
|
||||||
@ -354,13 +348,6 @@ function setHeadline(site){
|
|||||||
$('#contentHeadline').html('<a href="#home" onclick="switchContent(\'home\'); return false;">' + str + '</a>');
|
$('#contentHeadline').html('<a href="#home" onclick="switchContent(\'home\'); return false;">' + str + '</a>');
|
||||||
}
|
}
|
||||||
|
|
||||||
//update timer
|
|
||||||
function updateStatusTimer() {
|
|
||||||
mopidy.playback.getCurrentTlTrack().then(processCurrenttrack, console.error);
|
|
||||||
mopidy.playback.getTimePosition().then(processCurrentposition, console.error);
|
|
||||||
//TODO check offline?
|
|
||||||
}
|
|
||||||
|
|
||||||
//update tracklist options.
|
//update tracklist options.
|
||||||
function updateOptions() {
|
function updateOptions() {
|
||||||
mopidy.tracklist.getRepeat().then(processRepeat, console.error);
|
mopidy.tracklist.getRepeat().then(processRepeat, console.error);
|
||||||
@ -483,6 +470,11 @@ $(document).ready(function(event) {
|
|||||||
//initialize events
|
//initialize events
|
||||||
initSocketevents();
|
initSocketevents();
|
||||||
|
|
||||||
|
progressTimer = new ProgressTimer({
|
||||||
|
callback: timerCallback,
|
||||||
|
//updateRate: 2000,
|
||||||
|
});
|
||||||
|
|
||||||
resetSong();
|
resetSong();
|
||||||
|
|
||||||
if (location.hash.length < 2) {
|
if (location.hash.length < 2) {
|
||||||
@ -492,9 +484,6 @@ $(document).ready(function(event) {
|
|||||||
initgui = false;
|
initgui = false;
|
||||||
window.onhashchange = locationHashChanged;
|
window.onhashchange = locationHashChanged;
|
||||||
|
|
||||||
//update gui status every x seconds from mopdidy
|
|
||||||
setInterval(updateStatusTimer, STATUS_TIMER);
|
|
||||||
|
|
||||||
//only show backbutton if in UIWebview
|
//only show backbutton if in UIWebview
|
||||||
if (window.navigator.standalone) {
|
if (window.navigator.standalone) {
|
||||||
$("#btback").show();
|
$("#btback").show();
|
||||||
@ -581,6 +570,10 @@ $(document).ready(function(event) {
|
|||||||
$("#panel").panel("close");
|
$("#panel").panel("close");
|
||||||
event.stopImmediatePropagation(); }
|
event.stopImmediatePropagation(); }
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
$( "#trackslider" ).on( "slidestart", function() { progressTimer.stop(); } )
|
||||||
|
$( "#trackslider" ).on( "slidestop", function() { doSeekPos( $(this).val() ); } );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updatePlayIcons (uri, tlid) {
|
function updatePlayIcons (uri, tlid) {
|
||||||
|
|||||||
@ -60,8 +60,7 @@ function processSingle(data) {
|
|||||||
* process results of current position
|
* process results of current position
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processCurrentposition(data) {
|
function processCurrentposition(data) {
|
||||||
var pos = parseInt(data);
|
setPosition(parseInt(data));
|
||||||
setPosition(pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************
|
/********************************************************
|
||||||
@ -70,7 +69,6 @@ function processCurrentposition(data) {
|
|||||||
function processPlaystate(data) {
|
function processPlaystate(data) {
|
||||||
if (data == 'playing') {
|
if (data == 'playing') {
|
||||||
setPlayState(true);
|
setPlayState(true);
|
||||||
resumePosTimer();
|
|
||||||
} else {
|
} else {
|
||||||
setPlayState(false);
|
setPlayState(false);
|
||||||
}
|
}
|
||||||
|
|||||||
171
mopidy_musicbox_webclient/static/js/progress_timer.js
Normal file
171
mopidy_musicbox_webclient/static/js/progress_timer.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
var progressTimer;
|
||||||
|
var progressElement = document.getElementById('trackslider');
|
||||||
|
var positionNode = document.createTextNode('0:00');
|
||||||
|
var durationNode = document.createTextNode('0:00');
|
||||||
|
|
||||||
|
var START_BEATS = 5 // 0.5 seconds, needs to be less than 1s to prevent unwanted updates.
|
||||||
|
var RUN_BEATS = 300 // 30 seconds assuming default timer update rate of 100ms
|
||||||
|
var callbackHeartbeats = 0; // Timer will check syncs on every n-number of calls.
|
||||||
|
var targetPosition = null;
|
||||||
|
|
||||||
|
var MAX_SYNCS = 5; // Maximum number of consecutive successful syncs to perform.
|
||||||
|
var syncsLeft = MAX_SYNCS;
|
||||||
|
var synced = false;
|
||||||
|
var consecutiveSyncs = 0;
|
||||||
|
|
||||||
|
document.getElementById('songelapsed').appendChild(positionNode);
|
||||||
|
document.getElementById('songlength').appendChild(durationNode);
|
||||||
|
|
||||||
|
function timerCallback(position, duration, isRunning) {
|
||||||
|
updateTimers(position, duration, isRunning);
|
||||||
|
|
||||||
|
if (callbackHeartbeats == 0) {
|
||||||
|
callbackHeartbeats = getHeartbeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mopidy && position > 0) {
|
||||||
|
// Mopidy and timer are both initialized.
|
||||||
|
if (callbackHeartbeats-- == 1) {
|
||||||
|
// Get time position from Mopidy on every nth callback until
|
||||||
|
// synced.
|
||||||
|
mopidy.playback.getTimePosition().then(
|
||||||
|
function(mopidy_position) {
|
||||||
|
syncTimer(position, mopidy_position);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTimers(position, duration, isRunning) {
|
||||||
|
var ready = !(duration == Infinity && position == 0 && !isRunning); // Timer has been properly initialized.
|
||||||
|
var streaming = (duration == Infinity && position > 0); // Playing a stream.
|
||||||
|
var ok = synced && isRunning; // Normal operation.
|
||||||
|
var syncing = !synced && isRunning; // Busy syncing.
|
||||||
|
|
||||||
|
if (!ready) {
|
||||||
|
//Make sure that default values are displayed while the timer is being initialized.
|
||||||
|
positionNode.nodeValue = '';
|
||||||
|
durationNode.nodeValue = '';
|
||||||
|
$("#trackslider").val(0).slider('refresh');
|
||||||
|
} else if (syncing) {
|
||||||
|
if (!targetPosition) {
|
||||||
|
// Waiting for Mopidy to provide a target position.
|
||||||
|
positionNode.nodeValue = '(wait)';
|
||||||
|
} else {
|
||||||
|
// Busy seeking to new target position.
|
||||||
|
positionNode.nodeValue = '(sync)';
|
||||||
|
}
|
||||||
|
} else if (streaming) {
|
||||||
|
positionNode.nodeValue = format(position);;
|
||||||
|
durationNode.nodeValue = '(n/a)';
|
||||||
|
} else if (synced) {
|
||||||
|
positionNode.nodeValue = format(position);
|
||||||
|
durationNode.nodeValue = format(duration || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
// Don't update the track slider unless it is synced and running.
|
||||||
|
// (prevents awkward 'jitter' animation).
|
||||||
|
$("#trackslider").val(position).slider('refresh');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHeartbeat() {
|
||||||
|
if (syncsLeft > 0 && callbackHeartbeats == 0) {
|
||||||
|
// Step back exponentially while increasing heartbeat.
|
||||||
|
return Math.round(delay_exponential(5, 2, MAX_SYNCS - syncsLeft));
|
||||||
|
} else if (syncsLeft == 0 && callbackHeartbeats == 0) {
|
||||||
|
// Sync completed, keep checking using maximum number of heartbeats.
|
||||||
|
return RUN_BEATS;
|
||||||
|
} else {
|
||||||
|
return START_BEATS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncTimer(current, target) {
|
||||||
|
if (target) {
|
||||||
|
var drift = Math.abs(target - current);
|
||||||
|
if (drift <= 500) {
|
||||||
|
syncsLeft--;
|
||||||
|
// Less than 500ms == in sync.
|
||||||
|
if (++consecutiveSyncs == 2) {
|
||||||
|
// Need at least two consecutive syncs to know that Mopidy
|
||||||
|
// is progressing playback and we are in sync.
|
||||||
|
synced = true;
|
||||||
|
targetPosition = null;
|
||||||
|
consecutiveSyncs = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Drift is too large, re-sync with Mopidy.
|
||||||
|
reset();
|
||||||
|
targetPosition = target;
|
||||||
|
progressTimer.set(targetPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toInt(value) {
|
||||||
|
return value.match(/^\w*\d+\w*$/) ? parseInt(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format(milliseconds) {
|
||||||
|
if (milliseconds === Infinity) {
|
||||||
|
return '0:00';
|
||||||
|
}
|
||||||
|
|
||||||
|
var seconds = Math.floor(milliseconds / 1000);
|
||||||
|
var minutes = Math.floor(seconds / 60);
|
||||||
|
seconds = seconds % 60;
|
||||||
|
|
||||||
|
seconds = seconds < 10 ? '0' + seconds : seconds;
|
||||||
|
return minutes + ':' + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay_exponential(base, growthFactor, attempts) {
|
||||||
|
/*Calculate number of beats between syncs based on exponential function.
|
||||||
|
The format is::
|
||||||
|
|
||||||
|
base * growthFactor ^ (attempts - 1)
|
||||||
|
|
||||||
|
If ``base`` is set to 'rand' then a random number between
|
||||||
|
0 and 1 will be used as the base.
|
||||||
|
Base must be greater than 0.
|
||||||
|
*/
|
||||||
|
if (base == 'rand') {
|
||||||
|
base = Math.random();
|
||||||
|
}
|
||||||
|
beats = base * (Math.pow(growthFactor, (attempts - 1)));
|
||||||
|
return beats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
synced = false;
|
||||||
|
consecutiveSyncs = 0;
|
||||||
|
syncsLeft = MAX_SYNCS;
|
||||||
|
callbackHeartbeats = START_BEATS;
|
||||||
|
targetPosition = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgressTimer(pos) {
|
||||||
|
reset();
|
||||||
|
targetPosition = pos;
|
||||||
|
progressTimer.set(pos);
|
||||||
|
if (!play) {
|
||||||
|
// Set lapsed time and slider position directly as timer callback is not currently
|
||||||
|
// running.
|
||||||
|
positionNode.nodeValue = format(pos);
|
||||||
|
$("#trackslider").val(pos).slider('refresh');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startProgressTimer() {
|
||||||
|
reset();
|
||||||
|
progressTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetProgressTimer() {
|
||||||
|
progressTimer.reset();
|
||||||
|
reset();
|
||||||
|
targetPosition = 0;
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
|
|
||||||
# 2016-02-14:v1
|
# 2016-02-14:v2
|
||||||
|
|
||||||
NETWORK:
|
NETWORK:
|
||||||
*
|
*
|
||||||
@ -27,6 +27,7 @@ js/gui.js
|
|||||||
js/images.js
|
js/images.js
|
||||||
js/library.js
|
js/library.js
|
||||||
js/process_ws.js
|
js/process_ws.js
|
||||||
|
js/progress_timer.js
|
||||||
mb.manifest
|
mb.manifest
|
||||||
system.html
|
system.html
|
||||||
vendors/font_awesome/css/font-awesome.css
|
vendors/font_awesome/css/font-awesome.css
|
||||||
@ -100,3 +101,4 @@ vendors/jquery_mobile_flat_ui_theme/jquery.mobile.flatui.min.css
|
|||||||
vendors/lastfm/lastfm.api.cache.js
|
vendors/lastfm/lastfm.api.cache.js
|
||||||
vendors/lastfm/lastfm.api.js
|
vendors/lastfm/lastfm.api.js
|
||||||
vendors/lastfm/lastfm.api.md5.js
|
vendors/lastfm/lastfm.api.md5.js
|
||||||
|
vendors/media_progress_timer/timer.js
|
||||||
|
|||||||
125
mopidy_musicbox_webclient/static/vendors/media_progress_timer/timer.js
vendored
Normal file
125
mopidy_musicbox_webclient/static/vendors/media_progress_timer/timer.js
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*! timer.js v2.0.2
|
||||||
|
* https://github.com/adamcik/media-progress-timer
|
||||||
|
* Copyright (c) 2015 Thomas Adamcik
|
||||||
|
* Licensed under the Apache License, Version 2.0 */
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var now = typeof window.performance !== 'undefined' &&
|
||||||
|
typeof window.performance.now !== 'undefined' &&
|
||||||
|
window.performance.now.bind(window.performance) || Date.now ||
|
||||||
|
function() { return new Date().getTime(); };
|
||||||
|
|
||||||
|
function ProgressTimer(options) {
|
||||||
|
if (!(this instanceof ProgressTimer)) {
|
||||||
|
return new ProgressTimer(options);
|
||||||
|
} else if (typeof options === 'function') {
|
||||||
|
options = {'callback': options};
|
||||||
|
} else if (typeof options !== 'object') {
|
||||||
|
throw 'ProgressTimer must be called with a callback or options.';
|
||||||
|
} else if (typeof options.callback !== 'function') {
|
||||||
|
throw 'ProgressTimer needs a callback to operate.';
|
||||||
|
}
|
||||||
|
|
||||||
|
this._running = false;
|
||||||
|
this._updateRate = Math.max(options.updateRate || 100, 10);
|
||||||
|
this._callback = options.callback;
|
||||||
|
this._fallback = typeof window.requestAnimationFrame === 'undefined' ||
|
||||||
|
options.disableRequestAnimationFrame|| false;
|
||||||
|
|
||||||
|
if (!this._fallback) {
|
||||||
|
this._callUpdate = this._scheduleAnimationFrame;
|
||||||
|
this._scheduleUpdate = this._scheduleAnimationFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._boundCallUpdate = this._callUpdate.bind(this);
|
||||||
|
this._boundUpdate = this._update.bind(this);
|
||||||
|
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressTimer.prototype.set = function(position, duration) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
throw 'set requires at least a position argument.';
|
||||||
|
} else if (arguments.length === 1) {
|
||||||
|
duration = this._state.duration;
|
||||||
|
} else {
|
||||||
|
duration = Math.floor(
|
||||||
|
Math.max(duration === null ? Infinity : duration || 0, 0));
|
||||||
|
}
|
||||||
|
position = Math.floor(Math.min(Math.max(position || 0, 0), duration));
|
||||||
|
|
||||||
|
this._state = {
|
||||||
|
initialTimestamp: null,
|
||||||
|
initialPosition: position,
|
||||||
|
previousPosition: position,
|
||||||
|
duration: duration
|
||||||
|
};
|
||||||
|
|
||||||
|
this._callback(position, duration, this._running);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype.start = function() {
|
||||||
|
this._running = true;
|
||||||
|
this._callUpdate();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype.stop = function() {
|
||||||
|
this._running = false;
|
||||||
|
var state = this._state;
|
||||||
|
return this.set(state.previousPosition, state.duration);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype.reset = function() {
|
||||||
|
this._running = false;
|
||||||
|
return this.set(0, Infinity);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype._callUpdate = function() {
|
||||||
|
this._update(now());
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype._scheduleUpdate = function(timestamp) {
|
||||||
|
var adjustedTimeout = timestamp + this._updateRate - now();
|
||||||
|
setTimeout(this._boundCallUpdate, adjustedTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype._scheduleAnimationFrame = function() {
|
||||||
|
window.requestAnimationFrame(this._boundUpdate);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressTimer.prototype._update = function(timestamp) {
|
||||||
|
if (!this._running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = this._state;
|
||||||
|
state.initialTimestamp = state.initialTimestamp || timestamp;
|
||||||
|
|
||||||
|
var position = state.initialPosition + timestamp - state.initialTimestamp;
|
||||||
|
var duration = state.duration;
|
||||||
|
|
||||||
|
if (position < duration || duration === null) {
|
||||||
|
var delta = position - state.previousPosition;
|
||||||
|
if (delta >= this._updateRate || this._fallback) {
|
||||||
|
this._callback(Math.floor(position), duration, this._running);
|
||||||
|
state.previousPosition = position;
|
||||||
|
}
|
||||||
|
this._scheduleUpdate(timestamp);
|
||||||
|
} else {
|
||||||
|
this._running = false;
|
||||||
|
this._callback(duration, duration, this._running);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(typeof module !== 'undefined') {
|
||||||
|
module.exports = ProgressTimer;
|
||||||
|
} else {
|
||||||
|
window.ProgressTimer = ProgressTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
||||||
Loading…
Reference in New Issue
Block a user