document.addEventListener('DOMContentLoaded', function() { const songTitle = document.getElementById('songTitle'); const artist = document.getElementById('artist'); const statusElement = document.getElementById('status'); const listenersElement = document.getElementById('listeners'); const bitrateElement = document.getElementById('bitrate'); const uptimeElement = document.getElementById('uptime'); const audio = new Audio('https://broadcast.cif.su/main'); audio.crossOrigin = 'anonymous'; let isPlaying = false; let metadataInterval; let statsInterval; let currentShow = ''; // Volume Knob Variables var knobPositionX; var knobPositionY; var mouseX; var mouseY; var knobCenterX; var knobCenterY; var adjacentSide; var oppositeSide; var currentRadiansAngle; var getRadiansInDegrees; var finalAngleInDegrees; var volumeSetting = 0; // Set initial volume to 0% var tickHighlightPosition; var startingTickAngle = -135; var tickContainer = document.getElementById("tickContainer"); var volumeKnob = document.getElementById("knob"); var boundingRectangle = volumeKnob.getBoundingClientRect(); // Set initial volume audio.volume = volumeSetting / 100; // Format time (seconds to HH:MM:SS) function formatTime(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); return [ hours.toString().padStart(2, '0'), minutes.toString().padStart(2, '0'), secs.toString().padStart(2, '0') ].join(':'); } // Fetch Icecast stats async function fetchStats() { try { const response = await fetch('https://broadcast.cif.su/status-json.xsl'); if (!response.ok) throw new Error('Network response was not ok'); const data = await response.json(); const source = data.icestats.source; if (source) { // Update stats listenersElement.textContent = source.listeners || '0'; if (source.server_start_iso8601) { const startTime = new Date(source.server_start_iso8601); const uptimeSeconds = Math.floor((new Date() - startTime) / 1000); uptimeElement.textContent = formatTime(uptimeSeconds); } } } catch (error) { console.error('Error fetching stats:', error); } } // Fetch Libretime current track info async function fetchCurrentTrack() { try { const response = await fetch('https://fm.cif.su/api/live-info'); if (!response.ok) throw new Error('Network response was not ok'); const data = await response.json(); if (data.current && data.current.metadata) { const track = data.current.metadata; // Update song info songTitle.textContent = track.track_title || 'Unknown Track'; artist.textContent = track.artist_name || 'Unknown Artist'; // Update show info if available if (data.current.show && data.current.show.name !== currentShow) { currentShow = data.current.show.name; statusElement.textContent = isPlaying ? 'Playing' : `Current Show: ${currentShow}`; } } } catch (error) { console.error('Error fetching current track:', error); // Fallback to Icecast metadata if Libretime API fails fetchIcecastMetadata(); } } // Fallback to Icecast metadata if needed async function fetchIcecastMetadata() { try { const response = await fetch('https://broadcast.cif.su/status-json.xsl'); if (!response.ok) throw new Error('Network response was not ok'); const data = await response.json(); const source = data.icestats.source; if (source && source.title) { const titleParts = source.title.split(' - '); if (titleParts.length > 1) { artist.textContent = titleParts[0]; songTitle.textContent = titleParts.slice(1).join(' - '); } else { songTitle.textContent = source.title; } } } catch (error) { console.error('Error fetching Icecast metadata:', error); } } // Volume Knob Functions function initVolumeKnob() { // Calculate initial rotation angle based on initial volume (0%) const initialAngle = (volumeSetting / 100) * 270; volumeKnob.style.transform = "rotate(" + initialAngle + "deg)"; // Calculate how many ticks to highlight (0% of 27 ticks is 0) tickHighlightPosition = Math.round((volumeSetting * 2.7) / 10); createTicks(27, tickHighlightPosition); volumeKnob.addEventListener(getMouseDown(), onMouseDown); document.addEventListener(getMouseUp(), onMouseUp); } //on mouse button down function onMouseDown() { document.addEventListener(getMouseMove(), onMouseMove); //start drag } //on mouse button release function onMouseUp() { document.removeEventListener(getMouseMove(), onMouseMove); //stop drag } //compute mouse angle relative to center of volume knob function onMouseMove(event) { knobPositionX = boundingRectangle.left; knobPositionY = boundingRectangle.top; if(detectMobile() == "desktop") { mouseX = event.pageX; mouseY = event.pageY; } else { mouseX = event.touches[0].pageX; mouseY = event.touches[0].pageY; } knobCenterX = boundingRectangle.width / 2 + knobPositionX; knobCenterY = boundingRectangle.height / 2 + knobPositionY; adjacentSide = knobCenterX - mouseX; oppositeSide = knobCenterY - mouseY; currentRadiansAngle = Math.atan2(adjacentSide, oppositeSide); getRadiansInDegrees = currentRadiansAngle * 180 / Math.PI; finalAngleInDegrees = -(getRadiansInDegrees - 135); if(finalAngleInDegrees >= 0 && finalAngleInDegrees <= 270) { volumeKnob.style.transform = "rotate(" + finalAngleInDegrees + "deg)"; volumeSetting = Math.floor(finalAngleInDegrees / (270 / 100)); tickHighlightPosition = Math.round((volumeSetting * 2.7) / 10); createTicks(27, tickHighlightPosition); // Handle play/pause based on volume setting if (volumeSetting > 0 && !isPlaying) { audio.play() .then(() => { statusElement.textContent = 'Playing'; isPlaying = true; // Don't clear the metadata interval when pausing }) .catch(error => { console.error('Error playing stream:', error); statusElement.textContent = 'Error: ' + error.message; }); } else if (volumeSetting === 0 && isPlaying) { audio.pause(); statusElement.textContent = currentShow ? `Current Show: ${currentShow}` : 'Paused'; isPlaying = false; // Don't clear the metadata interval when pausing } audio.volume = volumeSetting / 100; document.getElementById("volumeValue").textContent = volumeSetting + "%"; } } //dynamically create volume knob "ticks" function createTicks(numTicks, highlightNumTicks) { while(tickContainer.firstChild) { tickContainer.removeChild(tickContainer.firstChild); } for(var i=0;i