
- Flatten directory structure by moving files from astro-church-website/ to root - Remove church-core subdirectory in favor of inline Rust library - Update .gitignore to properly exclude build artifacts, generated files, and dependencies - Add environment variables, IDE files, and coverage reports to gitignore - Include generated CSS files and native bindings in ignore patterns
209 lines
6.7 KiB
JavaScript
209 lines
6.7 KiB
JavaScript
// Shared live status polling functionality
|
|
|
|
class LiveStatusUpdater {
|
|
constructor() {
|
|
this.currentlyLive = false;
|
|
this.currentStreamUrl = '';
|
|
this.pollInterval = null;
|
|
this.apiUrl = 'https://api.rockvilletollandsda.church/api/v1/stream/status';
|
|
this.hls = null;
|
|
}
|
|
|
|
// Initialize for home page live banner
|
|
initHomePage() {
|
|
const liveBanner = document.getElementById('live-status-banner');
|
|
if (!liveBanner) return;
|
|
|
|
this.currentlyLive = liveBanner.style.display !== 'none';
|
|
this.startPolling(() => this.updateHomeBanner(liveBanner));
|
|
}
|
|
|
|
// Initialize for live page video player
|
|
initLivePage() {
|
|
const videoContainer = document.querySelector('.aspect-video');
|
|
if (!videoContainer) return;
|
|
|
|
const hasVideo = videoContainer.querySelector('video') !== null;
|
|
this.currentlyLive = hasVideo;
|
|
this.currentStreamUrl = hasVideo ? videoContainer.querySelector('video')?.src || '' : '';
|
|
this.startPolling(() => this.updateLivePage(videoContainer));
|
|
}
|
|
|
|
startPolling(updateCallback) {
|
|
// Check immediately on load
|
|
this.checkStatus(updateCallback);
|
|
|
|
// Then check every 30 seconds
|
|
this.pollInterval = setInterval(() => {
|
|
this.checkStatus(updateCallback);
|
|
}, 30000);
|
|
}
|
|
|
|
async checkStatus(updateCallback) {
|
|
try {
|
|
console.log('Checking live status...');
|
|
const response = await fetch(this.apiUrl);
|
|
const data = await response.json();
|
|
console.log('API response:', data);
|
|
console.log('Current state:', { currentlyLive: this.currentlyLive, currentStreamUrl: this.currentStreamUrl });
|
|
|
|
// Only update if status changed
|
|
if (data.is_live !== this.currentlyLive || data.stream_url !== this.currentStreamUrl) {
|
|
console.log('Status changed! Updating UI...');
|
|
this.currentlyLive = data.is_live;
|
|
this.currentStreamUrl = data.stream_url || '';
|
|
updateCallback(data);
|
|
} else {
|
|
console.log('No status change detected');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to update live status:', error);
|
|
}
|
|
}
|
|
|
|
updateHomeBanner(liveBanner) {
|
|
if (this.currentlyLive) {
|
|
liveBanner.style.display = 'block';
|
|
} else {
|
|
liveBanner.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Helper method to detect if browser supports native HLS
|
|
supportsNativeHLS() {
|
|
const video = document.createElement('video');
|
|
return video.canPlayType('application/vnd.apple.mpegurl') !== '';
|
|
}
|
|
|
|
// Helper method to clean up existing HLS instance
|
|
destroyHLS() {
|
|
if (this.hls) {
|
|
this.hls.destroy();
|
|
this.hls = null;
|
|
}
|
|
}
|
|
|
|
// Helper method to setup HLS.js for non-Safari browsers
|
|
setupHLS(video, streamUrl) {
|
|
// Clean up any existing HLS instance
|
|
this.destroyHLS();
|
|
|
|
if (this.supportsNativeHLS()) {
|
|
// Safari - use native HLS support
|
|
video.src = streamUrl;
|
|
} else if (window.Hls && window.Hls.isSupported()) {
|
|
// Chrome, Firefox - use HLS.js
|
|
this.hls = new window.Hls({
|
|
enableWorker: true,
|
|
lowLatencyMode: true,
|
|
backBufferLength: 90
|
|
});
|
|
|
|
this.hls.loadSource(streamUrl);
|
|
this.hls.attachMedia(video);
|
|
|
|
this.hls.on(window.Hls.Events.MANIFEST_PARSED, () => {
|
|
console.log('HLS manifest parsed, starting playback');
|
|
video.play().catch(e => console.log('Auto-play prevented:', e));
|
|
});
|
|
|
|
this.hls.on(window.Hls.Events.ERROR, (event, data) => {
|
|
console.error('HLS error:', event, data);
|
|
if (data.fatal) {
|
|
switch (data.type) {
|
|
case window.Hls.ErrorTypes.NETWORK_ERROR:
|
|
console.log('Network error, trying to recover...');
|
|
this.hls.startLoad();
|
|
break;
|
|
case window.Hls.ErrorTypes.MEDIA_ERROR:
|
|
console.log('Media error, trying to recover...');
|
|
this.hls.recoverMediaError();
|
|
break;
|
|
default:
|
|
console.log('Fatal error, destroying HLS instance');
|
|
this.destroyHLS();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
// Fallback for browsers that don't support HLS at all
|
|
console.warn('HLS not supported in this browser');
|
|
video.src = streamUrl;
|
|
}
|
|
}
|
|
|
|
updateLivePage(videoContainer) {
|
|
// Update hero section status indicator
|
|
const heroStatusContainer = document.getElementById('live-status-hero');
|
|
console.log('Hero status container found:', heroStatusContainer);
|
|
if (heroStatusContainer) {
|
|
if (this.currentlyLive) {
|
|
heroStatusContainer.innerHTML = `
|
|
<div class="flex items-center space-x-2 bg-red-500 rounded-full px-4 py-2">
|
|
<div class="w-3 h-3 bg-white rounded-full animate-ping"></div>
|
|
<span class="font-semibold">LIVE NOW</span>
|
|
</div>
|
|
`;
|
|
} else {
|
|
heroStatusContainer.innerHTML = `
|
|
<div class="flex items-center space-x-2 bg-white/20 rounded-full px-4 py-2">
|
|
<div class="w-3 h-3 bg-gray-300 rounded-full"></div>
|
|
<span class="font-semibold">OFFLINE</span>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Update video player
|
|
if (this.currentlyLive && this.currentStreamUrl) {
|
|
// Create/update video element
|
|
videoContainer.innerHTML = `
|
|
<video
|
|
id="live-video-player"
|
|
class="w-full h-full"
|
|
controls
|
|
muted
|
|
playsinline
|
|
preload="none">
|
|
<p class="text-white text-center">Your browser does not support the video tag or HLS streaming.</p>
|
|
</video>
|
|
`;
|
|
|
|
// Setup HLS for the new video element
|
|
const video = document.getElementById('live-video-player');
|
|
if (video) {
|
|
this.setupHLS(video, this.currentStreamUrl);
|
|
}
|
|
} else {
|
|
// Clean up HLS when going offline
|
|
this.destroyHLS();
|
|
|
|
// Show offline message
|
|
videoContainer.innerHTML = `
|
|
<div class="w-full h-full flex items-center justify-center bg-gray-900 text-white">
|
|
<div class="text-center">
|
|
<div class="w-16 h-16 mx-auto mb-4 text-gray-400">
|
|
<svg fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M21 6.5l-4 4V7c0-0.55-0.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-0.55 0-1 0.45-1 1v10c0 0.55 0.45 1 1 1h12c0.21 0 0.39-0.08 0.54-0.18L19.73 21 21 19.73 3.27 2z"/>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-2xl font-semibold mb-2">Stream is Offline</h3>
|
|
<p class="text-gray-400">We'll be back for our next service</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
if (this.pollInterval) {
|
|
clearInterval(this.pollInterval);
|
|
this.pollInterval = null;
|
|
}
|
|
this.destroyHLS();
|
|
}
|
|
}
|
|
|
|
// Global instance
|
|
window.liveStatusUpdater = new LiveStatusUpdater(); |