check if infinite stream
All checks were successful
CI/CD Pipeline / test (push) Successful in 2m35s
CI/CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
2025-08-08 13:09:01 +02:00
parent 240f7d7f8d
commit 162b329ed4
2 changed files with 125 additions and 3 deletions

View File

@@ -22,6 +22,8 @@ elms.forEach(function(elm) {
var Player = function(playlist) {
this.playlist = playlist;
this.index = 0;
this.isStream = false; // Track if current audio is a stream
this.durationCheckInterval = null; // For periodic duration validation
// Display the title of the first track.
track.innerHTML = '1. ' + playlist[0].title;
@@ -54,15 +56,23 @@ Player.prototype = {
if (data.howl) {
sound = data.howl;
} else {
// Detect if this is a streaming URL
self.isStream = (data.file === 'stream' || data.file.indexOf('stream') !== -1);
sound = data.howl = new Howl({
src: [ './' + data.file ],
html5: true, // Force to HTML5 so that the audio can stream in (best for large files).
onplay: function() {
// Display the duration.
duration.innerHTML = self.formatTime(Math.round(sound.duration()));
// Display the duration using our validation logic
duration.innerHTML = self.formatDuration(sound.duration());
// Start updating the progress of the track.
requestAnimationFrame(self.step.bind(self));
// Start periodic duration checking for streams
if (self.isStream) {
self.startDurationCheck();
}
// Start the wave animation if we have already loaded
wave.container.style.display = 'block';
@@ -76,17 +86,26 @@ Player.prototype = {
loading.style.display = 'none';
},
onend: function() {
// Stop duration checking
self.stopDurationCheck();
// Stop the wave animation.
wave.container.style.display = 'none';
bar.style.display = 'block';
self.skip('next');
},
onpause: function() {
// Stop duration checking when paused
self.stopDurationCheck();
// Stop the wave animation.
wave.container.style.display = 'none';
bar.style.display = 'block';
},
onstop: function() {
// Stop duration checking
self.stopDurationCheck();
// Stop the wave animation.
wave.container.style.display = 'none';
bar.style.display = 'block';
@@ -222,7 +241,17 @@ Player.prototype = {
// Determine our current seek position.
var seek = sound.seek() || 0;
timer.innerHTML = self.formatTime(Math.round(seek));
progress.style.width = (((seek / sound.duration()) * 100) || 0) + '%';
// Calculate progress, handling infinite duration
var currentDuration = sound.duration();
var correctedDuration = self.validateDuration(currentDuration);
if (correctedDuration === Infinity || !isFinite(correctedDuration)) {
// For streams, don't show progress bar
progress.style.width = '0%';
} else {
progress.style.width = (((seek / correctedDuration) * 100) || 0) + '%';
}
// If the sound is still playing, continue stepping.
if (sound.playing()) {
@@ -266,6 +295,83 @@ Player.prototype = {
var seconds = (secs - minutes * 60) || 0;
return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
},
/**
* Validate and correct duration for streaming audio
* @param {Number} duration Raw duration from Howler
* @return {Number} Corrected duration
*/
validateDuration: function(duration) {
// If duration is Infinity, keep it as is
if (duration === Infinity || !isFinite(duration)) {
return Infinity;
}
// If duration is suspiciously large (> 24 hours), treat as stream
if (duration > 86400) {
return Infinity;
}
// For streaming URLs, always return Infinity
if (this.isStream) {
return Infinity;
}
return duration;
},
/**
* Format duration for display, handling streaming audio
* @param {Number} duration Duration in seconds
* @return {String} Formatted duration string
*/
formatDuration: function(duration) {
var correctedDuration = this.validateDuration(duration);
if (correctedDuration === Infinity || !isFinite(correctedDuration)) {
// Add live class for styling
duration.classList.add('live');
return 'LIVE';
}
// Remove live class for non-streaming content
duration.classList.remove('live');
return this.formatTime(Math.round(correctedDuration));
},
/**
* Start periodic duration checking for streams
*/
startDurationCheck: function() {
var self = this;
if (self.durationCheckInterval) {
clearInterval(self.durationCheckInterval);
}
self.durationCheckInterval = setInterval(function() {
var sound = self.playlist[self.index].howl;
if (sound && sound.playing()) {
var currentDuration = sound.duration();
var correctedDuration = self.validateDuration(currentDuration);
// If duration changed from finite to infinite or vice versa, update display
if ((correctedDuration === Infinity) !== (duration.innerHTML === 'LIVE')) {
duration.innerHTML = self.formatDuration(currentDuration);
}
}
}, 5000); // Check every 5 seconds
},
/**
* Stop periodic duration checking
*/
stopDurationCheck: function() {
if (this.durationCheckInterval) {
clearInterval(this.durationCheckInterval);
this.durationCheckInterval = null;
}
}
};

View File

@@ -62,6 +62,22 @@ body {
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
}
/* Stream indicator styling for LIVE duration */
#duration.live {
background: rgba(255, 0, 0, 0.8);
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
opacity: 0.9;
animation: livePulse 2s infinite;
}
@keyframes livePulse {
0% { opacity: 0.9; }
50% { opacity: 0.6; }
100% { opacity: 0.9; }
}
/* Controls */
.controlsOuter {
position: absolute;