<!DOCTYPE html>
<!-----------------------------------------------------------------------------
Copyright 2014-2025 Jesper Larsson
This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
Klipspringer is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
Klipspringer is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with Klipspringer. If not, see <https://www.gnu.org/licenses/>.
--------------------------------------------------------------------------->
<html>
<head>
<meta charset="UTF-8">
<style>$(klipstyle.css:file)</style>
<title>Play</title>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
$(klipicons.html:file)
</head>
<body>
$(controls_common.html:file)
<script>
'use strict';
$(sendcmd.js:file)
const source = $(source); // the source element from sources.json
let updateTimeTimer; // timer for updating displayed time as time goes by
let trackTimeWhen;
// If parameter is undefined, the time displayed is stopped and
// nothing more happens. Otherwise, unless paused or page is not
// visible, a loop is started to keep updating displayed time as time
// goes by.
const setTime = (trackTime) => {
trackTimeWhen = Date.now();
const updateTime = () => {
if (playStatus.paused) {
showTime(trackTime);
} else {
showTime(trackTime + (Date.now() - trackTimeWhen)/1000);
if (document.visibilityState === 'visible') { updateTimeTimer = setTimeout(updateTime, 500) }
}
}
clearTimeout(updateTimeTimer); // there can be only one
if (trackTime === undefined) { return } // blink reduction
showTime(trackTime);
if (trackTime !== undefined && !playStatus.paused) {
updateTimeTimer = setTimeout(updateTime, (1 - trackTime%1) * 1000);
}
}
let retryTimer; // timer for retrying status check after it failed
let retryTime = 200; // time (milliseconds) until next retry, increases on multiple failures
// Blanks all info fields.
const clearPage = () => {
clearTimeout(updateTimeTimer);
blankInfo();
playStatus.status_timestamp = 0;
}
let statusRequest = null;
const statusError = (err) => {
console.error("Failed to get info, "+err);
clearPage();
if (retryTime >= 10000) {
window.location.href = "/"; // give up and go home
} else {
retryTimer = setTimeout(updateStatusLoop, retryTime); // loop
retryTime = Math.min(2*retryTime, 10000); // but slow down
}
}
// Called when:
// - the page is first loaded,
// - a status request is satisfied,
// - the page becomes visible, and
// - it is time to retry, after a previous failure.
// Cancels any pending status request or retry.
const updateStatusLoop = () => {
clearTimeout(retryTimer); // there can be only one
if (document.visibilityState !== 'visible') { return } // we will get back here when page becomes visible
if (statusRequest) {
statusRequest.cancelled = true;
statusRequest.controller.abort()
}
const req = statusRequest = { controller: new AbortController() }
sendCmd('get_status', '&info_id='+playerInfoId+'&seen='+(playStatus.status_timestamp || "0"), { signal: req.controller.signal }).then(ans => {
if (ans.err) {
statusError(ans.err);
} else {
playStatus = ans;
updatePlayerInfo(playStatus);
updateTrackInfo(ans);
setTime(ans.track_time);
retryTimer = setTimeout(updateStatusLoop, retryTime = 200); // loop, with moderation
}
}).catch(err => {
if (req.cancelled) { return }
statusError(err);
});
}
const play = () => { sendCmd('play') }
const setMonomix = (on) => { sendCmd('seekr', '&time=0&monomix='+(on ? "on" : "off")) }
defineButtons({
prev: () => { sendCmd('prev') },
next: () => { sendCmd('next') },
stop: () => { sendCmd('quit'); clearPage() },
pause: () => { sendCmd('pause') },
play: () => { play() },
xrew: () => { sendCmd('seekr', '&time=-30') },
rew: () => { sendCmd('seekr', '&time=-5') },
ffd: () => { sendCmd('seekr', '&time=5') },
xffd: () => { sendCmd('seekr', '&time=30') },
home: () => { window.location.href = "/" },
phones: source.phones_mirror === "" || isNaN(source.phones_mirror)
? null
: () => {
const href = "/choice?source="+source.phones_mirror
+"&path="+encodeURIComponent(sourcePath)
+"&track_number="+playStatus.track_number
+"&track_time="+(playStatus.track_time + (Date.now() - trackTimeWhen)/1000);
sendCmd('quit').then(() => { window.location.href = href });
}
});
const jumpTrack = (i) => { sendCmd('seeka', '&track='+i) }
updateStatusLoop();
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
playStatus.status_timestamp = 0;
updateStatusLoop();
} else {
if (statusRequest) {
statusRequest.cancelled = true;
statusRequest.controller.abort();
}
statusRequest = null;
}
});
</script>
</body>
</html>
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home