1023 lines
37 KiB
JavaScript
1023 lines
37 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function () {
|
|
const BASE_PATH = window.BASE_PATH || '';
|
|
|
|
let currentPage = getCurrentPageFromPath();
|
|
let audioPlayer = null;
|
|
let hlsInstance = null;
|
|
let currentXHR = null;
|
|
let isLoading = false;
|
|
let loadingOverlay = null;
|
|
|
|
// WeakMap per tracciare i listener degli elementi
|
|
const linkListeners = new WeakMap();
|
|
|
|
loadHlsLibrary();
|
|
|
|
function loadHlsLibrary() {
|
|
if (window.Hls) return;
|
|
|
|
const script = document.createElement('script');
|
|
script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest';
|
|
script.onload = () => console.log('HLS.js caricato con successo');
|
|
script.onerror = () => console.error('Errore nel caricamento di HLS.js');
|
|
document.head.appendChild(script);
|
|
}
|
|
|
|
function getCurrentPageFromPath() {
|
|
let path = window.location.pathname;
|
|
|
|
// Rimuovi il base path se presente
|
|
if (BASE_PATH && path.startsWith(BASE_PATH)) {
|
|
path = path.substring(BASE_PATH.length);
|
|
}
|
|
|
|
// Rimuovi slash iniziali e finali
|
|
path = path.replace(/^\/|\/$/g, '');
|
|
|
|
// IMPORTANTE: Se il path è vuoto o è solo "index.php", controlla il parametro redirect
|
|
if (!path || path === '' || path === 'index.php') {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const redirect = urlParams.get('redirect');
|
|
|
|
if (redirect) {
|
|
console.log('📍 Trovato parametro redirect:', redirect);
|
|
return redirect;
|
|
}
|
|
}
|
|
|
|
console.log('📍 Path rilevato:', path || 'home');
|
|
return path || 'home';
|
|
}
|
|
|
|
/**
|
|
* Pulizia completa del player audio
|
|
* Previene memory leak rimuovendo istanze HLS e player audio
|
|
*/
|
|
function cleanupPlayer() {
|
|
console.log('Pulizia player audio...');
|
|
|
|
// Distruggi istanza HLS
|
|
if (hlsInstance) {
|
|
try {
|
|
hlsInstance.destroy();
|
|
console.log('HLS instance distrutta');
|
|
} catch (e) {
|
|
console.error('Errore nella distruzione di HLS:', e);
|
|
}
|
|
hlsInstance = null;
|
|
}
|
|
|
|
// Pulisci audio player
|
|
if (audioPlayer) {
|
|
try {
|
|
audioPlayer.pause();
|
|
audioPlayer.src = '';
|
|
audioPlayer.load(); // Libera risorse
|
|
|
|
// Rimuovi tutti gli event listeners clonando il nodo
|
|
const parent = audioPlayer.parentNode;
|
|
if (parent) {
|
|
const newPlayer = audioPlayer.cloneNode(true);
|
|
parent.replaceChild(newPlayer, audioPlayer);
|
|
}
|
|
|
|
console.log('Audio player pulito');
|
|
} catch (e) {
|
|
console.error('Errore nella pulizia del player audio:', e);
|
|
}
|
|
audioPlayer = null;
|
|
}
|
|
|
|
// Reset Media Session
|
|
if ('mediaSession' in navigator) {
|
|
navigator.mediaSession.metadata = null;
|
|
navigator.mediaSession.playbackState = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mostra l'overlay di caricamento
|
|
*/
|
|
function showLoadingOverlay() {
|
|
if (!loadingOverlay) {
|
|
loadingOverlay = document.getElementById('loadingOverlay');
|
|
}
|
|
if (loadingOverlay) {
|
|
loadingOverlay.classList.add('active');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Nasconde l'overlay di caricamento
|
|
*/
|
|
function hideLoadingOverlay() {
|
|
if (loadingOverlay) {
|
|
loadingOverlay.classList.remove('active');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Caricamento pagina con protezione da race condition
|
|
*/
|
|
function loadPage(page) {
|
|
// Previeni richieste multiple simultanee
|
|
if (isLoading) {
|
|
console.log('Caricamento già in corso, richiesta ignorata');
|
|
return;
|
|
}
|
|
|
|
// Annulla richiesta XHR precedente se presente
|
|
if (currentXHR) {
|
|
console.log('Annullamento richiesta precedente');
|
|
currentXHR.abort();
|
|
currentXHR = null;
|
|
}
|
|
|
|
isLoading = true;
|
|
const contentContainer = document.getElementById('content');
|
|
|
|
if (!contentContainer) {
|
|
console.error('Container contenuto non trovato');
|
|
isLoading = false;
|
|
return;
|
|
}
|
|
|
|
// Mostra l'overlay di caricamento
|
|
showLoadingOverlay();
|
|
|
|
// Animazione fade out
|
|
contentContainer.classList.remove('fade-in');
|
|
contentContainer.classList.add('fade-out');
|
|
|
|
setTimeout(() => {
|
|
const url = BASE_PATH + '/index.php/' + page;
|
|
|
|
currentXHR = new XMLHttpRequest();
|
|
currentXHR.open('GET', url, true);
|
|
currentXHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
|
|
|
currentXHR.onload = function () {
|
|
isLoading = false;
|
|
currentXHR = null;
|
|
|
|
if (this.status === 200) {
|
|
contentContainer.innerHTML = this.responseText;
|
|
|
|
// Nascondi overlay di caricamento
|
|
hideLoadingOverlay();
|
|
|
|
// Animazione fade in
|
|
contentContainer.classList.remove('fade-out');
|
|
contentContainer.classList.add('fade-in');
|
|
|
|
// Aggiorna URL nella history
|
|
const historyUrl = page === 'home' ? BASE_PATH + '/' : BASE_PATH + '/' + page;
|
|
history.pushState({ page: page }, null, historyUrl);
|
|
currentPage = page;
|
|
|
|
// Aggiorna navigazione attiva
|
|
updateActiveNavigation(page);
|
|
|
|
// Inizializza componenti specifici della pagina
|
|
if (page.startsWith('play/')) {
|
|
setTimeout(() => initializePlayer(), 100);
|
|
}
|
|
|
|
if (page.startsWith('playtv/')) {
|
|
setTimeout(() => initializeTVPlayer(), 100);
|
|
}
|
|
|
|
// Riattacca i listener ai link
|
|
attachLinkListeners();
|
|
|
|
// Inizializza form contatti se presente
|
|
if (page === 'page/contact') {
|
|
setTimeout(() => initializeContactForm(), 100);
|
|
}
|
|
|
|
// Scroll to top
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
|
|
console.log('Pagina caricata:', page);
|
|
} else {
|
|
hideLoadingOverlay();
|
|
contentContainer.innerHTML = '<div class="content-page"><h2>Errore</h2><p>Impossibile caricare la pagina. Codice errore: ' + this.status + '</p></div>';
|
|
contentContainer.classList.remove('fade-out');
|
|
contentContainer.classList.add('fade-in');
|
|
console.error('Errore caricamento pagina:', this.status);
|
|
}
|
|
};
|
|
|
|
currentXHR.onerror = function () {
|
|
isLoading = false;
|
|
currentXHR = null;
|
|
hideLoadingOverlay();
|
|
contentContainer.innerHTML = '<div class="content-page"><h2>Errore di connessione</h2><p>Controlla la tua connessione internet e riprova.</p></div>';
|
|
contentContainer.classList.remove('fade-out');
|
|
contentContainer.classList.add('fade-in');
|
|
console.error('Errore di rete durante il caricamento della pagina');
|
|
};
|
|
|
|
currentXHR.onabort = function () {
|
|
isLoading = false;
|
|
currentXHR = null;
|
|
hideLoadingOverlay();
|
|
console.log('Richiesta XHR annullata');
|
|
};
|
|
|
|
currentXHR.send();
|
|
}, 200);
|
|
}
|
|
|
|
/**
|
|
* Aggiorna la navigazione attiva
|
|
*/
|
|
function updateActiveNavigation(page) {
|
|
console.log('Aggiornamento navigazione per pagina:', page); // Debug
|
|
|
|
const navLinks = document.querySelectorAll('.navLink');
|
|
navLinks.forEach(link => {
|
|
link.classList.remove('active');
|
|
|
|
const linkPage = link.getAttribute('data-page');
|
|
|
|
// Log di debug per vedere i confronti
|
|
console.log('Confronto:', linkPage, 'con', page);
|
|
|
|
if (linkPage === page) {
|
|
link.classList.add('active');
|
|
console.log('✓ Match esatto:', linkPage);
|
|
} else if (page.startsWith('play/') && linkPage === 'radio') {
|
|
link.classList.add('active');
|
|
console.log('✓ Match play/ -> radio');
|
|
} else if (page.startsWith('playtv/') && linkPage === 'tv') {
|
|
link.classList.add('active');
|
|
console.log('✓ Match playtv/ -> tv');
|
|
} else if (page.startsWith('page/') && linkPage === page) {
|
|
link.classList.add('active');
|
|
console.log('✓ Match page/');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup Media Session API per controlli sistema
|
|
*/
|
|
function setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon) {
|
|
if (!('mediaSession' in navigator)) {
|
|
console.log('Media Session API non supportata su questo browser');
|
|
return;
|
|
}
|
|
|
|
console.log('Configurazione Media Session:', stationName);
|
|
|
|
// Imposta i metadati della riproduzione
|
|
navigator.mediaSession.metadata = new MediaMetadata({
|
|
title: stationName,
|
|
artist: stationSlogan,
|
|
album: 'RPIGroup Play',
|
|
artwork: [
|
|
{ src: stationLogo, sizes: '96x96', type: 'image/png' },
|
|
{ src: stationLogo, sizes: '128x128', type: 'image/png' },
|
|
{ src: stationLogo, sizes: '192x192', type: 'image/png' },
|
|
{ src: stationLogo, sizes: '256x256', type: 'image/png' },
|
|
{ src: stationLogo, sizes: '384x384', type: 'image/png' },
|
|
{ src: stationLogo, sizes: '512x512', type: 'image/png' }
|
|
]
|
|
});
|
|
|
|
// Handler per il comando Play dal sistema
|
|
navigator.mediaSession.setActionHandler('play', () => {
|
|
console.log('Media Session: Play richiesto');
|
|
if (audioPlayer) {
|
|
audioPlayer.play().then(() => {
|
|
if (playIcon && pauseIcon) {
|
|
playIcon.style.display = 'none';
|
|
pauseIcon.style.display = 'block';
|
|
}
|
|
}).catch(err => {
|
|
console.error('Errore play da Media Session:', err);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Handler per il comando Pause dal sistema
|
|
navigator.mediaSession.setActionHandler('pause', () => {
|
|
console.log('Media Session: Pause richiesto');
|
|
if (audioPlayer) {
|
|
audioPlayer.pause();
|
|
if (playIcon && pauseIcon) {
|
|
playIcon.style.display = 'block';
|
|
pauseIcon.style.display = 'none';
|
|
}
|
|
}
|
|
});
|
|
|
|
// Disabilita i controlli non utilizzati per radio streaming
|
|
navigator.mediaSession.setActionHandler('seekbackward', null);
|
|
navigator.mediaSession.setActionHandler('seekforward', null);
|
|
navigator.mediaSession.setActionHandler('previoustrack', null);
|
|
navigator.mediaSession.setActionHandler('nexttrack', null);
|
|
|
|
console.log('Media Session configurata correttamente');
|
|
}
|
|
|
|
/**
|
|
* Aggiorna lo stato di riproduzione nella Media Session
|
|
*/
|
|
function updateMediaSessionPlaybackState(state) {
|
|
if ('mediaSession' in navigator) {
|
|
navigator.mediaSession.playbackState = state;
|
|
console.log('Media Session playback state:', state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inizializza il player audio con gestione HLS migliorata
|
|
*/
|
|
|
|
function initializePlayer() {
|
|
console.log('Inizializzazione player audio (v2.1 - Format Toggle)...');
|
|
|
|
const playPauseBtn = document.getElementById('playPauseBtn');
|
|
const playIcon = document.querySelector('.play-icon');
|
|
const pauseIcon = document.querySelector('.pause-icon');
|
|
const playerStatus = document.getElementById('playerStatus');
|
|
const formatToggleBtn = document.getElementById('formatToggleBtn');
|
|
const formatLabel = document.getElementById('formatLabel');
|
|
|
|
if (!playPauseBtn) return;
|
|
|
|
// 1. Cleanup Preventivo
|
|
cleanupPlayer();
|
|
|
|
// 2. Setup Elementi
|
|
audioPlayer = document.getElementById('hlsAudioPlayer');
|
|
if (!audioPlayer) return;
|
|
|
|
const streamHLS = playPauseBtn.getAttribute('data-stream-hls');
|
|
const streamFallback = playPauseBtn.getAttribute('data-stream-fallback');
|
|
const stationName = playPauseBtn.getAttribute('data-station-name');
|
|
|
|
// Stato formato audio (preferenza salvata in localStorage)
|
|
const storageKey = 'audioFormat_' + stationName;
|
|
let currentFormat = localStorage.getItem(storageKey) || 'hls'; // 'hls' o 'direct'
|
|
let isPlaying = false;
|
|
|
|
// Aggiorna label del formato
|
|
function updateFormatLabel() {
|
|
if (formatLabel) {
|
|
formatLabel.textContent = currentFormat === 'hls' ? 'HLS' : 'MP3';
|
|
}
|
|
}
|
|
updateFormatLabel();
|
|
|
|
// Configurazione HLS.js Ottimizzata per Qualità
|
|
const hlsConfig = {
|
|
debug: false,
|
|
enableWorker: true,
|
|
lowLatencyMode: false, // Disabilitato per stabilità e buffer migliore
|
|
backBufferLength: 90,
|
|
maxBufferLength: 30, // Aumentato buffer (default 30s)
|
|
startLevel: -1, // Auto start
|
|
xhrSetup: function (xhr, url) {
|
|
xhr.withCredentials = false; // Fix CORS issues sometimes
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Logica di Selezione Player
|
|
* Priorità: HLS.js (PC/Android) > Nativo (iOS) > Fallback (Direct Stream)
|
|
*/
|
|
function initStream(forceFormat = null) {
|
|
const preferredFormat = forceFormat || currentFormat;
|
|
|
|
// Se l'utente ha scelto formato diretto, usa subito MP3/AAC
|
|
if (preferredFormat === 'direct') {
|
|
console.log('User preference: Direct stream (MP3/AAC)');
|
|
useDirectStream();
|
|
return;
|
|
}
|
|
|
|
// STRATEGIA 1: HLS.js (Preferita per PC e Android)
|
|
if (Hls.isSupported()) {
|
|
console.log('Strategy: HLS.js (High Quality Control)');
|
|
|
|
hlsInstance = new Hls(hlsConfig);
|
|
hlsInstance.loadSource(streamHLS);
|
|
hlsInstance.attachMedia(audioPlayer);
|
|
|
|
hlsInstance.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
|
console.log(`HLS Manifest Loaded. Levels: ${data.levels.length}`);
|
|
|
|
// Log dettagliato di tutti i livelli disponibili per debug
|
|
data.levels.forEach((level, index) => {
|
|
console.log(`Level ${index}: ${level.bitrate} bps (${(level.bitrate / 1000).toFixed(0)} kbps)`);
|
|
});
|
|
|
|
// CRITICAL: Forza partenza dal livello massimo per garantire qualità alta
|
|
if (data.levels.length > 1) {
|
|
const maxLevel = data.levels.length - 1;
|
|
hlsInstance.startLevel = maxLevel;
|
|
console.log(`✓ Forced START level to MAX: ${maxLevel} (${(data.levels[maxLevel].bitrate / 1000).toFixed(0)} kbps)`);
|
|
}
|
|
|
|
attemptPlay();
|
|
});
|
|
|
|
// Track quality switches per debug
|
|
hlsInstance.on(Hls.Events.LEVEL_SWITCHING, function (event, data) {
|
|
console.log(`→ Switching to level ${data.level}...`);
|
|
});
|
|
|
|
hlsInstance.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
|
|
const currentLevel = hlsInstance.levels[data.level];
|
|
console.log(`✓ Switched to level ${data.level}: ${(currentLevel.bitrate / 1000).toFixed(0)} kbps`);
|
|
});
|
|
|
|
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
|
if (data.fatal) {
|
|
console.warn('HLS Fatal Error:', data.type);
|
|
switch (data.type) {
|
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
hlsInstance.startLoad();
|
|
break;
|
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
hlsInstance.recoverMediaError();
|
|
break;
|
|
default:
|
|
useDirectStream();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
// STRATEGIA 2: Nativo (Obbligatorio per iOS)
|
|
if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
|
|
console.log('Strategy: Native HLS (iOS/Safari)');
|
|
audioPlayer.src = streamHLS;
|
|
audioPlayer.preload = 'auto'; // Suggerisce al browser di caricare dati
|
|
|
|
// Hack per iOS: Alcune volte serve un tocco utente, gestito dal click play
|
|
attemptPlay();
|
|
return;
|
|
}
|
|
|
|
// STRATEGIA 3: Fallback (Stream MP3 diretto)
|
|
console.log('Strategy: Direct Fallback');
|
|
useDirectStream();
|
|
}
|
|
|
|
function useDirectStream() {
|
|
if (hlsInstance) {
|
|
hlsInstance.destroy();
|
|
hlsInstance = null;
|
|
}
|
|
console.log('Using direct stream:', streamFallback);
|
|
audioPlayer.src = streamFallback;
|
|
attemptPlay();
|
|
}
|
|
|
|
// Funzione per cambiare formato
|
|
function toggleFormat() {
|
|
const wasPlaying = !audioPlayer.paused;
|
|
|
|
// Cambia formato
|
|
currentFormat = currentFormat === 'hls' ? 'direct' : 'hls';
|
|
localStorage.setItem(storageKey, currentFormat);
|
|
console.log('Switching to format:', currentFormat);
|
|
|
|
// Aggiorna label
|
|
updateFormatLabel();
|
|
|
|
// Ferma riproduzione corrente
|
|
if (audioPlayer) {
|
|
audioPlayer.pause();
|
|
}
|
|
|
|
// Pulisci player precedente
|
|
if (hlsInstance) {
|
|
hlsInstance.destroy();
|
|
hlsInstance = null;
|
|
}
|
|
audioPlayer.src = '';
|
|
|
|
// Reinizializza con nuovo formato
|
|
initStream(currentFormat);
|
|
|
|
// Riprendi riproduzione se era in corso
|
|
if (wasPlaying) {
|
|
setTimeout(() => {
|
|
togglePlay();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
function attemptPlay() {
|
|
// Nota: chiamare .play() senza interazione utente fallirà.
|
|
// Questa funzione prepara lo stato. Il vero play avviene al click.
|
|
if (playerStatus) playerStatus.textContent = 'Pronto alla riproduzione';
|
|
}
|
|
|
|
function togglePlay() {
|
|
if (audioPlayer.paused) {
|
|
const playPromise = audioPlayer.play();
|
|
if (playPromise !== undefined) {
|
|
playPromise.then(() => {
|
|
updateUI(true);
|
|
}).catch(error => {
|
|
console.error('Play prevented:', error);
|
|
// Se HLS fallisce al play (es. stale manifest), prova fallback
|
|
if (!audioPlayer.src.includes(streamFallback) && !hlsInstance) {
|
|
fallbackToDirectStream();
|
|
audioPlayer.play().catch(e => console.error('Fallback failed:', e));
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
audioPlayer.pause();
|
|
updateUI(false);
|
|
}
|
|
}
|
|
|
|
function updateUI(playing) {
|
|
isPlaying = playing;
|
|
playIcon.style.display = playing ? 'none' : 'block';
|
|
pauseIcon.style.display = playing ? 'block' : 'none';
|
|
if (playerStatus) {
|
|
playerStatus.textContent = playing ? 'In riproduzione' : 'In pausa';
|
|
}
|
|
}
|
|
|
|
// Listeners
|
|
playPauseBtn.onclick = (e) => {
|
|
e.preventDefault();
|
|
// Se non inizializzato (primo click), avvia stream
|
|
if (!audioPlayer.src && !hlsInstance) {
|
|
initStream();
|
|
// Piccolo timeout per dare tempo al setup prima del play effettivo
|
|
setTimeout(togglePlay, 50);
|
|
} else {
|
|
togglePlay();
|
|
}
|
|
};
|
|
|
|
audioPlayer.onplay = () => updateUI(true);
|
|
audioPlayer.onpause = () => updateUI(false);
|
|
|
|
audioPlayer.onerror = (e) => {
|
|
console.error('Audio Error:', audioPlayer.error);
|
|
if (playerStatus) playerStatus.textContent = 'Errore riproduzione';
|
|
updateUI(false);
|
|
};
|
|
|
|
// Format toggle button listener
|
|
if (formatToggleBtn) {
|
|
formatToggleBtn.onclick = (e) => {
|
|
e.preventDefault();
|
|
toggleFormat();
|
|
};
|
|
}
|
|
|
|
// Setup Media Session
|
|
setupMediaSession(stationName, playPauseBtn.getAttribute('data-station-slogan'), playPauseBtn.getAttribute('data-station-logo'), playIcon, pauseIcon);
|
|
|
|
console.log('Player System Ready with format toggle.');
|
|
}
|
|
|
|
/**
|
|
* Inizializza player TV
|
|
*/
|
|
function initializeTVPlayer() {
|
|
console.log('TV Player inizializzato');
|
|
|
|
const video = document.getElementById('tvVideoPlayer');
|
|
if (!video) {
|
|
console.log('Elemento video TV non trovato');
|
|
return;
|
|
}
|
|
|
|
// Cleanup istanza precedente se esiste
|
|
if (video.hlsInstance) {
|
|
video.hlsInstance.destroy();
|
|
delete video.hlsInstance;
|
|
}
|
|
|
|
const videoSrc = video.getAttribute('data-src');
|
|
console.log('Video Source:', videoSrc);
|
|
|
|
if (!videoSrc) {
|
|
console.error('Nessuna sorgente video specificata in data-src');
|
|
return;
|
|
}
|
|
|
|
// Creazione elemento per errori se non esiste già
|
|
let errorDisplay = video.parentNode.querySelector('.tv-error-display');
|
|
if (!errorDisplay) {
|
|
errorDisplay = document.createElement('div');
|
|
errorDisplay.className = 'tv-error-display';
|
|
errorDisplay.style.display = 'none';
|
|
errorDisplay.style.position = 'absolute';
|
|
errorDisplay.style.top = '50%';
|
|
errorDisplay.style.left = '50%';
|
|
errorDisplay.style.transform = 'translate(-50%, -50%)';
|
|
errorDisplay.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
errorDisplay.style.color = '#fff';
|
|
errorDisplay.style.padding = '20px';
|
|
errorDisplay.style.borderRadius = '8px';
|
|
errorDisplay.style.textAlign = 'center';
|
|
errorDisplay.style.zIndex = '2000';
|
|
errorDisplay.style.maxWidth = '90%';
|
|
if (video.parentNode) {
|
|
video.parentNode.appendChild(errorDisplay);
|
|
// Assicurati che il parent sia relative per il posizionamento absolute
|
|
if (getComputedStyle(video.parentNode).position === 'static') {
|
|
video.parentNode.style.position = 'relative';
|
|
}
|
|
}
|
|
}
|
|
|
|
function showError(msg, details) {
|
|
console.error(msg, details);
|
|
errorDisplay.innerHTML = '<strong>Errore:</strong><br>' + msg + (details ? '<br><small>' + details + '</small>' : '');
|
|
errorDisplay.style.display = 'block';
|
|
}
|
|
|
|
// Controllo libreria HLS
|
|
if (typeof Hls === 'undefined') {
|
|
// Riprova tra poco se magari sta ancora caricando
|
|
setTimeout(() => {
|
|
if (typeof Hls === 'undefined') {
|
|
showError('Libreria HLS non caricata correttamente.');
|
|
} else {
|
|
initializeTVPlayer(); // Riprova inizializzazione
|
|
}
|
|
}, 500);
|
|
return;
|
|
}
|
|
|
|
if (Hls.isSupported()) {
|
|
console.log('HLS Supported - Loading stream');
|
|
var hls = new Hls({
|
|
debug: false,
|
|
enableWorker: true
|
|
});
|
|
|
|
hls.loadSource(videoSrc);
|
|
hls.attachMedia(video);
|
|
|
|
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
|
console.log('TV Manifest Parsed - Ready to play');
|
|
var playPromise = video.play();
|
|
if (playPromise !== undefined) {
|
|
playPromise.catch(error => {
|
|
console.log('Autoplay blocked or failed:', error);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Gestione errori estesa
|
|
hls.on(Hls.Events.ERROR, function (event, data) {
|
|
console.warn('HLS Error:', data);
|
|
if (data.fatal) {
|
|
switch (data.type) {
|
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
console.log('Network error, recovering...');
|
|
hls.startLoad();
|
|
break;
|
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
console.log('Media error, recovering...');
|
|
hls.recoverMediaError();
|
|
break;
|
|
default:
|
|
showError('Errore fatale riproduzione.', data.type);
|
|
hls.destroy();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
video.hlsInstance = hls;
|
|
|
|
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
console.log('Native HLS Supported (Safari/iOS)');
|
|
video.src = videoSrc;
|
|
video.addEventListener('loadedmetadata', function () {
|
|
video.play().catch(e => console.log("Autoplay iOS prevented"));
|
|
});
|
|
video.addEventListener('error', function (e) {
|
|
showError('Errore riproduzione nativa.', e.message);
|
|
});
|
|
} else {
|
|
showError('Il tuo browser non supporta HLS.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inizializza form contatti
|
|
*/
|
|
function initializeContactForm() {
|
|
const contactForm = document.getElementById('contactForm');
|
|
if (!contactForm) {
|
|
console.log('Form contatti non trovato');
|
|
return;
|
|
}
|
|
|
|
contactForm.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(contactForm);
|
|
const formResponse = document.getElementById('formResponse');
|
|
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Invio in corso...';
|
|
formResponse.className = 'form-response';
|
|
formResponse.style.display = 'block';
|
|
}
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open('POST', BASE_PATH + '/process_contact.php', true);
|
|
|
|
xhr.onload = function () {
|
|
if (xhr.status === 200) {
|
|
try {
|
|
const response = JSON.parse(xhr.responseText);
|
|
if (response.success) {
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Messaggio inviato con successo! Ti risponderemo presto.';
|
|
formResponse.className = 'form-response success';
|
|
}
|
|
contactForm.reset();
|
|
} else {
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Errore: ' + (response.message || 'Si è verificato un errore durante l\'invio del messaggio.');
|
|
formResponse.className = 'form-response error';
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Errore parsing risposta:', e);
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Si è verificato un errore durante l\'elaborazione della risposta.';
|
|
formResponse.className = 'form-response error';
|
|
}
|
|
}
|
|
} else {
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Si è verificato un errore durante l\'invio del messaggio.';
|
|
formResponse.className = 'form-response error';
|
|
}
|
|
}
|
|
};
|
|
|
|
xhr.onerror = function () {
|
|
if (formResponse) {
|
|
formResponse.innerHTML = 'Errore di connessione. Controlla la tua connessione internet.';
|
|
formResponse.className = 'form-response error';
|
|
}
|
|
};
|
|
|
|
xhr.send(formData);
|
|
});
|
|
|
|
console.log('Form contatti inizializzato');
|
|
}
|
|
|
|
/**
|
|
* Attacca event listener ai link con gestione migliorata
|
|
*/
|
|
function attachLinkListeners() {
|
|
const navLinks = document.querySelectorAll('.navLink, .nav-link, .station-link, .linkBox');
|
|
|
|
navLinks.forEach(link => {
|
|
// Rimuovi listener precedente se esiste
|
|
const oldListener = linkListeners.get(link);
|
|
if (oldListener) {
|
|
link.removeEventListener('click', oldListener);
|
|
}
|
|
|
|
const clickHandler = function (e) {
|
|
const href = this.getAttribute('href');
|
|
const target = this.getAttribute('target');
|
|
const page = this.getAttribute('data-page');
|
|
|
|
// Verifica se è un link esterno
|
|
const isExternal = href && (
|
|
href.startsWith('http://') ||
|
|
href.startsWith('https://') ||
|
|
href.startsWith('//') ||
|
|
target === '_blank'
|
|
);
|
|
|
|
if (isExternal) {
|
|
console.log('Link esterno:', href);
|
|
return; // Lascia che il browser gestisca normalmente
|
|
}
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
console.log('Navigazione a:', page);
|
|
|
|
// Naviga solo se la pagina è diversa e non stiamo già caricando
|
|
if (page && page !== currentPage && !isLoading) {
|
|
// Metti in pausa l'audio se in riproduzione
|
|
if (audioPlayer && !audioPlayer.paused) {
|
|
audioPlayer.pause();
|
|
}
|
|
loadPage(page);
|
|
}
|
|
};
|
|
|
|
link.addEventListener('click', clickHandler);
|
|
linkListeners.set(link, clickHandler);
|
|
});
|
|
|
|
console.log('Event listener attaccati a', navLinks.length, 'link');
|
|
}
|
|
|
|
/**
|
|
* Setup pulsante apertura app (desktop)
|
|
*/
|
|
function setupOpenAppButton() {
|
|
const openAppBtn = document.getElementById('openAppBtn');
|
|
if (openAppBtn) {
|
|
openAppBtn.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
const currentPath = window.location.pathname;
|
|
const pathWithoutBase = currentPath.replace(BASE_PATH, '').replace(/^\//, '');
|
|
const redirectParam = pathWithoutBase ? '&redirect=' + encodeURIComponent(pathWithoutBase) : '';
|
|
|
|
const width = 375;
|
|
const height = 667;
|
|
const left = (window.screen.width / 2) - (width / 2);
|
|
const top = (window.screen.height / 2) - (height / 2);
|
|
|
|
const windowFeatures = 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',resizable=yes,scrollbars=yes,status=yes';
|
|
window.open(BASE_PATH + '/?app=true' + redirectParam, 'RadioApp', windowFeatures);
|
|
});
|
|
|
|
console.log('Pulsante apertura app configurato');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup navigazione con history API
|
|
*/
|
|
function setupHistoryNavigation() {
|
|
window.addEventListener('popstate', function (e) {
|
|
if (e.state && e.state.page) {
|
|
if (e.state.page !== currentPage) {
|
|
loadPage(e.state.page);
|
|
}
|
|
} else {
|
|
if (currentPage !== 'home') {
|
|
loadPage('home');
|
|
}
|
|
}
|
|
});
|
|
|
|
history.replaceState({ page: currentPage }, null, window.location.pathname);
|
|
console.log('Navigazione history configurata');
|
|
}
|
|
|
|
/**
|
|
* Setup modalità standalone (PWA)
|
|
*/
|
|
function setupStandaloneMode() {
|
|
const appContainer = document.querySelector('.container') || document.querySelector('.container-fluid');
|
|
if (!appContainer) return;
|
|
|
|
if (window.opener && !window.opener.closed) {
|
|
appContainer.classList.add('standalone-app');
|
|
console.log('App in modalità finestra popup');
|
|
}
|
|
|
|
if (window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches) {
|
|
appContainer.classList.add('standalone-app');
|
|
console.log('App in modalità standalone (PWA)');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inizializza la pagina corrente
|
|
*/
|
|
function initializeCurrentPage() {
|
|
console.log('Inizializzazione pagina corrente:', currentPage);
|
|
|
|
updateActiveNavigation(currentPage);
|
|
|
|
if (currentPage.startsWith('play/')) {
|
|
setTimeout(() => initializePlayer(), 100);
|
|
}
|
|
|
|
if (currentPage.startsWith('playtv/')) {
|
|
setTimeout(() => initializeTVPlayer(), 100);
|
|
}
|
|
|
|
if (currentPage === 'page/contact') {
|
|
setTimeout(() => initializeContactForm(), 100);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inizializzazione principale
|
|
*/
|
|
function init() {
|
|
console.log('Inizializzazione app...');
|
|
|
|
setupOpenAppButton();
|
|
|
|
if (document.querySelector('.container') || document.querySelector('.container-fluid')) {
|
|
setupStandaloneMode();
|
|
attachLinkListeners();
|
|
setupHistoryNavigation();
|
|
initializeCurrentPage();
|
|
}
|
|
|
|
console.log('App inizializzata correttamente');
|
|
}
|
|
|
|
// Avvia l'inizializzazione
|
|
init();
|
|
});
|
|
|
|
|
|
/**
|
|
* GESTIONE ORIENTAMENTO SCHERMO
|
|
*/
|
|
|
|
/**
|
|
* Blocca l'orientamento in portrait (funziona solo in PWA/fullscreen)
|
|
*/
|
|
function lockScreenOrientation() {
|
|
// Screen Orientation API
|
|
if (screen.orientation && screen.orientation.lock) {
|
|
screen.orientation.lock('portrait').then(() => {
|
|
console.log('Orientamento bloccato in portrait');
|
|
}).catch((err) => {
|
|
console.log('Impossibile bloccare orientamento:', err.message);
|
|
});
|
|
}
|
|
|
|
// Fallback per browser più vecchi
|
|
if (screen.lockOrientation) {
|
|
screen.lockOrientation('portrait');
|
|
} else if (screen.mozLockOrientation) {
|
|
screen.mozLockOrientation('portrait');
|
|
} else if (screen.msLockOrientation) {
|
|
screen.msLockOrientation('portrait');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gestisce il cambio di orientamento
|
|
*/
|
|
function handleOrientationChange() {
|
|
const orientation = window.orientation || (screen.orientation && screen.orientation.angle) || 0;
|
|
console.log('Orientamento schermo:', orientation);
|
|
|
|
// Puoi aggiungere logica specifica qui se necessario
|
|
}
|
|
|
|
/**
|
|
* Tenta di bloccare l'orientamento quando appropriato
|
|
*/
|
|
function tryLockOrientation() {
|
|
const isStandalone = window.navigator.standalone ||
|
|
window.matchMedia('(display-mode: standalone)').matches;
|
|
|
|
if (isStandalone) {
|
|
lockScreenOrientation();
|
|
}
|
|
|
|
document.addEventListener('fullscreenchange', () => {
|
|
if (document.fullscreenElement) {
|
|
lockScreenOrientation();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Event listeners per cambio orientamento
|
|
window.addEventListener('orientationchange', handleOrientationChange);
|
|
window.addEventListener('resize', handleOrientationChange);
|
|
|
|
// Inizializza al caricamento
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
tryLockOrientation();
|
|
handleOrientationChange();
|
|
});
|
|
|
|
// Inizializza subito se il DOM è già pronto
|
|
if (document.readyState !== 'loading') {
|
|
tryLockOrientation();
|
|
handleOrientationChange();
|
|
}
|
|
|