Errore
Impossibile caricare la pagina. Codice errore: ' + xhr.status + '
';
- contentContainer.classList.remove('fade-out');
- contentContainer.classList.add('fade-in');
- }
- };
-
- xhr.onerror = function() {
- contentContainer.innerHTML = 'Errore di connessione
Controlla la tua connessione internet.
';
- contentContainer.classList.remove('fade-out');
- contentContainer.classList.add('fade-in');
- };
-
- xhr.send();
- }, 200);
- }
-
- function updateActiveNavigation(page) {
- const navLinks = document.querySelectorAll('.navLink');
- navLinks.forEach(link => {
- link.classList.remove('active');
-
- const linkPage = link.getAttribute('data-page');
-
- if (linkPage === page) {
- link.classList.add('active');
- } else if (page.startsWith('play/') && linkPage === 'radio') {
- link.classList.add('active');
- } else if (page.startsWith('playtv/') && linkPage === 'tv') {
- link.classList.add('active');
- } else if (page.startsWith('page/')) {
- if (linkPage === page) {
- link.classList.add('active');
- }
- }
- });
- }
-
- // NUOVA FUNZIONE: Setup Media Session API
- function setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon) {
- if (!('mediaSession' in navigator)) {
- console.log('Media Session API non supportata');
- return;
- }
-
- console.log('Setup Media Session:', stationName, stationSlogan);
-
- // Imposta i metadati
- 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' }
- ]
- });
-
- // Gestisci i controlli del centro notifiche
- navigator.mediaSession.setActionHandler('play', () => {
- console.log('Media Session: Play richiesto dal centro notifiche');
- if (audioPlayer) {
- audioPlayer.play().then(() => {
- // Aggiorna UI
- if (playIcon && pauseIcon) {
- playIcon.style.display = 'none';
- pauseIcon.style.display = 'block';
- }
- });
- }
- });
-
- navigator.mediaSession.setActionHandler('pause', () => {
- console.log('Media Session: Pause richiesto dal centro notifiche');
- if (audioPlayer) {
- audioPlayer.pause();
- // Aggiorna UI
- if (playIcon && pauseIcon) {
- playIcon.style.display = 'block';
- pauseIcon.style.display = 'none';
- }
- }
- });
-
- // Opzionale: gestisci skip (per radio potrebbe non servire)
- navigator.mediaSession.setActionHandler('seekbackward', null);
- navigator.mediaSession.setActionHandler('seekforward', null);
- navigator.mediaSession.setActionHandler('previoustrack', null);
- navigator.mediaSession.setActionHandler('nexttrack', null);
- }
-
- // NUOVA FUNZIONE: Aggiorna playback state
- function updateMediaSessionPlaybackState(state) {
- if ('mediaSession' in navigator) {
- navigator.mediaSession.playbackState = state;
- console.log('Media Session playback state:', state);
- }
- }
-
- function initializePlayer() {
- console.log('Inizializzazione player...');
-
- const playPauseBtn = document.getElementById('playPauseBtn');
- if (!playPauseBtn) return;
-
- const playIcon = document.querySelector('.play-icon');
- const pauseIcon = document.querySelector('.pause-icon');
- const hlsUrl = playPauseBtn.getAttribute('data-stream-hls');
- const fallbackUrl = playPauseBtn.getAttribute('data-stream-fallback');
- const stationName = playPauseBtn.getAttribute('data-station-name');
- const stationSlogan = playPauseBtn.getAttribute('data-station-slogan');
- const stationLogo = playPauseBtn.getAttribute('data-station-logo');
- const statusElement = document.getElementById('playerStatus');
-
- console.log('Station:', stationName);
- console.log('HLS URL:', hlsUrl);
- console.log('Fallback URL:', fallbackUrl);
-
- // Setup Media Session all'inizio passando anche le icone
- setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon);
-
- if (hlsInstance) {
- hlsInstance.destroy();
- hlsInstance = null;
- }
-
- if (audioPlayer) {
- audioPlayer.pause();
- audioPlayer = null;
- }
-
- audioPlayer = document.getElementById('hlsAudioPlayer');
- if (!audioPlayer) {
- audioPlayer = new Audio();
- }
- audioPlayer.preload = 'none';
-
- function updateStatus(msg, isError = false) {
- console.log('Status:', msg);
- if (statusElement) {
- statusElement.textContent = msg;
- statusElement.className = 'station-status' + (isError ? ' error' : '');
- }
- }
-
- function canPlayHLS() {
- const video = document.createElement('video');
- return video.canPlayType('application/vnd.apple.mpegurl') !== '';
- }
-
- function setupHLS() {
- if (!window.Hls || !Hls.isSupported()) {
- console.log('HLS.js non disponibile');
- return false;
- }
-
- console.log('Uso HLS.js');
- updateStatus('Caricamento...');
-
- hlsInstance = new Hls({
- enableWorker: true,
- lowLatencyMode: true
- });
-
- hlsInstance.loadSource(hlsUrl);
- hlsInstance.attachMedia(audioPlayer);
-
- hlsInstance.on(Hls.Events.MANIFEST_PARSED, function() {
- console.log('HLS manifest caricato');
- updateStatus('Pronto');
- });
-
- hlsInstance.on(Hls.Events.ERROR, function(event, data) {
- console.error('Errore HLS:', data.type, data.details);
-
- if (data.fatal) {
- switch(data.type) {
- case Hls.ErrorTypes.NETWORK_ERROR:
- console.log('Errore rete, riprovo...');
- hlsInstance.startLoad();
- break;
- case Hls.ErrorTypes.MEDIA_ERROR:
- console.log('Errore media, riprovo...');
- hlsInstance.recoverMediaError();
- break;
- default:
- console.error('Errore fatale, uso fallback');
- hlsInstance.destroy();
- hlsInstance = null;
- useFallback();
- break;
- }
- }
- });
-
- return true;
- }
-
- function useFallback() {
- console.log('Uso fallback:', fallbackUrl);
- if (fallbackUrl) {
- audioPlayer.src = fallbackUrl;
- audioPlayer.load();
- updateStatus('Pronto (MP3)');
- } else {
- updateStatus('Stream non disponibile', true);
- }
- }
-
- if (hlsUrl) {
- if (canPlayHLS()) {
- console.log('Uso HLS nativo (Safari)');
- audioPlayer.src = hlsUrl;
- updateStatus('Pronto');
- } else if (!setupHLS()) {
- useFallback();
- }
- } else {
- useFallback();
- }
-
- // Gestore pulsante play/pause
- playPauseBtn.addEventListener('click', function() {
- if (audioPlayer.paused) {
- updateStatus('Connessione...');
-
- audioPlayer.play()
- .then(() => {
- playIcon.style.display = 'none';
- pauseIcon.style.display = 'block';
- updateStatus('In riproduzione');
- updateMediaSessionPlaybackState('playing');
- })
- .catch(error => {
- console.error('Errore nella riproduzione:', error);
- updateStatus('Errore riproduzione', true);
- updateMediaSessionPlaybackState('paused');
-
- if (hlsInstance && fallbackUrl) {
- console.log('Tentativo con fallback...');
- hlsInstance.destroy();
- hlsInstance = null;
- useFallback();
- setTimeout(() => {
- audioPlayer.play().catch(e => {
- console.error('Fallback fallito:', e);
- alert('Impossibile riprodurre lo stream audio. Riprova più tardi.');
- });
- }, 500);
- } else {
- alert('Impossibile riprodurre lo stream audio. Riprova più tardi.');
- }
- });
- } else {
- audioPlayer.pause();
- playIcon.style.display = 'block';
- pauseIcon.style.display = 'none';
- updateStatus('In pausa');
- updateMediaSessionPlaybackState('paused');
- }
- });
-
- // Event listeners con Media Session updates e sincronizzazione UI
- audioPlayer.addEventListener('play', function() {
- updateMediaSessionPlaybackState('playing');
- updateStatus('In riproduzione');
- // Sincronizza UI quando l'audio parte (da qualsiasi fonte)
- playIcon.style.display = 'none';
- pauseIcon.style.display = 'block';
- });
-
- audioPlayer.addEventListener('pause', function() {
- updateMediaSessionPlaybackState('paused');
- updateStatus('In pausa');
- // Sincronizza UI quando l'audio va in pausa (da qualsiasi fonte)
- playIcon.style.display = 'block';
- pauseIcon.style.display = 'none';
- });
-
- audioPlayer.addEventListener('ended', function() {
- playIcon.style.display = 'block';
- pauseIcon.style.display = 'none';
- updateStatus('Terminato');
- updateMediaSessionPlaybackState('paused');
- });
-
- audioPlayer.addEventListener('error', function() {
- playIcon.style.display = 'block';
- pauseIcon.style.display = 'none';
- updateStatus('Errore stream', true);
- updateMediaSessionPlaybackState('paused');
- alert('Si è verificato un errore durante la riproduzione. Riprova più tardi.');
- });
-
- audioPlayer.addEventListener('waiting', () => updateStatus('Buffering...'));
- audioPlayer.addEventListener('playing', () => updateStatus('In riproduzione'));
-
- console.log('Player inizializzato con Media Session');
- }
-
- function initializeTVPlayer() {
- console.log('TV Player inizializzato');
- }
-
- function initializeContactForm() {
- const contactForm = document.getElementById('contactForm');
- if (!contactForm) return;
-
- contactForm.addEventListener('submit', function(e) {
- e.preventDefault();
-
- const formData = new FormData(contactForm);
- const formResponse = document.getElementById('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) {
- formResponse.innerHTML = 'Messaggio inviato con successo! Ti risponderemo presto.';
- formResponse.className = 'form-response success';
- contactForm.reset();
- } else {
- formResponse.innerHTML = 'Errore: ' + (response.message || 'Si è verificato un errore durante l\'invio del messaggio.');
- formResponse.className = 'form-response error';
- }
- } catch (e) {
- formResponse.innerHTML = 'Si è verificato un errore durante l\'elaborazione della risposta.';
- formResponse.className = 'form-response error';
- }
- } else {
- formResponse.innerHTML = 'Si è verificato un errore durante l\'invio del messaggio.';
- formResponse.className = 'form-response error';
- }
- };
- xhr.onerror = function() {
- formResponse.innerHTML = 'Errore di connessione. Controlla la tua connessione internet.';
- formResponse.className = 'form-response error';
- };
- xhr.send(formData);
- });
- }
-
- function attachLinkListeners() {
- const navLinks = document.querySelectorAll('.navLink, .nav-link, .station-link, .linkBox');
-
- navLinks.forEach(link => {
- const newLink = link.cloneNode(true);
- link.parentNode.replaceChild(newLink, link);
-
- newLink.addEventListener('click', function(e) {
- const href = this.getAttribute('href');
- const target = this.getAttribute('target');
- const page = this.getAttribute('data-page');
-
- const isExternal = href && (
- href.startsWith('http://') ||
- href.startsWith('https://') ||
- href.startsWith('//') ||
- target === '_blank'
- );
-
- if (isExternal) {
- console.log('Link esterno rilevato:', href);
- e.preventDefault();
- window.location.href = href;
- return;
- }
-
- e.preventDefault();
- e.stopPropagation();
-
- console.log('Link cliccato, pagina:', page, 'currentPage:', currentPage);
-
- if (page && page !== currentPage) {
- if (audioPlayer && !audioPlayer.paused) {
- audioPlayer.pause();
- }
- loadPage(page);
- }
- });
- });
- }
-
- 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);
- });
- }
- }
-
- 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);
- }
-
- function setupStandaloneMode() {
- const appContainer = document.querySelector('.container');
- if (!appContainer) return;
-
- if (window.opener && !window.opener.closed) {
- appContainer.classList.add('standalone-app');
- }
-
- if (window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches) {
- appContainer.classList.add('standalone-app');
- }
- }
-
- function init() {
- setupOpenAppButton();
-
- if (document.querySelector('.container')) {
- setupStandaloneMode();
-
- attachLinkListeners();
-
- setupHistoryNavigation();
-
- initializeCurrentPage();
- }
- }
-
- function initializeCurrentPage() {
- console.log('Inizializzazione pagina corrente:', currentPage);
-
- updateActiveNavigation(currentPage);
-
- if (currentPage.startsWith('play/')) {
- setTimeout(function() {
- initializePlayer();
- }, 100);
- }
-
- if (currentPage.startsWith('playtv/')) {
- setTimeout(function() {
- initializeTVPlayer();
- }, 100);
- }
-
- if (currentPage === 'page/contact') {
- setTimeout(function() {
- initializeContactForm();
- }, 100);
- }
- }
-
- init();
-});
-
-
-// Funzione per bloccare l'orientamento (funziona solo in PWA/fullscreen)
-function lockScreenOrientation() {
- // Prova a bloccare l'orientamento con 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);
- // Non è un errore grave, il CSS gestirà la situazione
- });
- }
-
- // Alternativa per browser più vecchi
- if (screen.lockOrientation) {
- screen.lockOrientation('portrait');
- } else if (screen.mozLockOrientation) {
- screen.mozLockOrientation('portrait');
- } else if (screen.msLockOrientation) {
- screen.msLockOrientation('portrait');
- }
-}
-
-// Prova a bloccare quando l'app è in fullscreen/standalone
-function tryLockOrientation() {
- // Controlla se siamo in modalità standalone (PWA)
- const isStandalone = window.navigator.standalone ||
- window.matchMedia('(display-mode: standalone)').matches;
-
- if (isStandalone) {
- lockScreenOrientation();
- }
-
- // Prova anche quando entriamo in fullscreen
- 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();
-}
-
+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;
+
+ // 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';
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ // 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;
+
+ // 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 {
+ contentContainer.innerHTML = 'Errore
Impossibile caricare la pagina. Codice errore: ' + this.status + '
';
+ contentContainer.classList.remove('fade-out');
+ contentContainer.classList.add('fade-in');
+ console.error('Errore caricamento pagina:', this.status);
+ }
+ };
+
+ currentXHR.onerror = function () {
+ isLoading = false;
+ currentXHR = null;
+ contentContainer.innerHTML = 'Errore di connessione
Controlla la tua connessione internet e riprova.
';
+ 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;
+ 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...');
+
+ const playPauseBtn = document.getElementById('playPauseBtn');
+ const playIcon = document.querySelector('.play-icon');
+ const pauseIcon = document.querySelector('.pause-icon');
+ const playerStatus = document.getElementById('playerStatus');
+ const artistElement = document.getElementById('artist');
+
+ if (!playPauseBtn) {
+ console.error('Pulsante play/pause non trovato');
+ return;
+ }
+
+ // Pulisci il player precedente
+ cleanupPlayer();
+
+ // Ottieni il nuovo audio player dal DOM
+ audioPlayer = document.getElementById('hlsAudioPlayer');
+
+ if (!audioPlayer) {
+ console.error('Elemento audio non trovato');
+ return;
+ }
+
+ const streamHLS = playPauseBtn.getAttribute('data-stream-hls');
+ const streamFallback = playPauseBtn.getAttribute('data-stream-fallback');
+ const stationName = playPauseBtn.getAttribute('data-station-name');
+ const stationSlogan = playPauseBtn.getAttribute('data-station-slogan');
+ const stationLogo = playPauseBtn.getAttribute('data-station-logo');
+
+ console.log('Stream HLS:', streamHLS);
+ console.log('Stream Fallback:', streamFallback);
+ console.log('Stazione:', stationName);
+
+ let isPlaying = false;
+ let streamType = 'hls'; // 'hls' o 'direct'
+
+ /**
+ * Inizializza HLS stream
+ */
+ function initHLSStream() {
+ if (!window.Hls) {
+ console.error('HLS.js non caricato');
+ return false;
+ }
+
+ if (Hls.isSupported()) {
+ console.log('HLS supportato - uso HLS.js');
+
+ hlsInstance = new Hls({
+ debug: false,
+ enableWorker: true,
+ lowLatencyMode: true,
+ backBufferLength: 90
+ });
+
+ hlsInstance.loadSource(streamHLS);
+ hlsInstance.attachMedia(audioPlayer);
+
+ hlsInstance.on(Hls.Events.MANIFEST_PARSED, function () {
+ console.log('Manifest HLS caricato');
+ audioPlayer.play().then(() => {
+ console.log('Riproduzione HLS avviata');
+ updatePlayState(true);
+ }).catch(err => {
+ console.error('Errore avvio riproduzione HLS:', err);
+ tryFallbackStream();
+ });
+ });
+
+ hlsInstance.on(Hls.Events.ERROR, function (event, data) {
+ console.error('Errore HLS:', data.type, data.details);
+ if (data.fatal) {
+ switch (data.type) {
+ case Hls.ErrorTypes.NETWORK_ERROR:
+ console.log('Errore di rete, tentativo fallback...');
+ tryFallbackStream();
+ break;
+ case Hls.ErrorTypes.MEDIA_ERROR:
+ console.log('Errore media, tentativo recupero...');
+ hlsInstance.recoverMediaError();
+ break;
+ default:
+ console.log('Errore fatale, tentativo fallback...');
+ tryFallbackStream();
+ break;
+ }
+ }
+ });
+
+ return true;
+ } else if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
+ console.log('HLS nativo supportato');
+ audioPlayer.src = streamHLS;
+ audioPlayer.play().then(() => {
+ console.log('Riproduzione HLS nativa avviata');
+ updatePlayState(true);
+ }).catch(err => {
+ console.error('Errore HLS nativo:', err);
+ tryFallbackStream();
+ });
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Usa stream diretto come fallback
+ */
+ function tryFallbackStream() {
+ console.log('Tentativo stream fallback:', streamFallback);
+
+ if (hlsInstance) {
+ hlsInstance.destroy();
+ hlsInstance = null;
+ }
+
+ streamType = 'direct';
+ audioPlayer.src = streamFallback;
+ audioPlayer.load();
+
+ audioPlayer.play().then(() => {
+ console.log('Riproduzione stream diretto avviata');
+ updatePlayState(true);
+ }).catch(err => {
+ console.error('Errore stream diretto:', err);
+ updatePlayState(false);
+ if (playerStatus) {
+ playerStatus.textContent = 'Errore di riproduzione';
+ }
+ });
+ }
+
+ /**
+ * Aggiorna stato visuale play/pause
+ */
+ function updatePlayState(playing) {
+ isPlaying = playing;
+
+ if (playing) {
+ playIcon.style.display = 'none';
+ pauseIcon.style.display = 'inline-block';
+ if (playerStatus) {
+ playerStatus.textContent = 'In riproduzione...';
+ }
+ } else {
+ playIcon.style.display = 'inline-block';
+ pauseIcon.style.display = 'none';
+ if (playerStatus) {
+ playerStatus.textContent = stationSlogan || 'In pausa';
+ }
+ }
+ }
+
+ /**
+ * Gestione click play/pause
+ */
+ playPauseBtn.addEventListener('click', function () {
+ if (!audioPlayer) return;
+
+ if (isPlaying) {
+ // Pausa
+ audioPlayer.pause();
+ updatePlayState(false);
+ console.log('Riproduzione in pausa');
+ } else {
+ // Play
+ if (!audioPlayer.src && !hlsInstance) {
+ // Prima riproduzione - inizializza lo stream
+ if (streamHLS && !initHLSStream()) {
+ // Se HLS fallisce, usa direttamente il fallback
+ tryFallbackStream();
+ }
+ } else {
+ // Riprendi riproduzione
+ audioPlayer.play().then(() => {
+ updatePlayState(true);
+ console.log('Riproduzione ripresa');
+ }).catch(err => {
+ console.error('Errore ripresa riproduzione:', err);
+ updatePlayState(false);
+ });
+ }
+ }
+ });
+
+ /**
+ * Event listeners audio player
+ */
+ audioPlayer.addEventListener('play', function () {
+ updatePlayState(true);
+ });
+
+ audioPlayer.addEventListener('pause', function () {
+ updatePlayState(false);
+ });
+
+ audioPlayer.addEventListener('error', function (e) {
+ console.error('Errore audio player:', e);
+ updatePlayState(false);
+ if (playerStatus) {
+ playerStatus.textContent = 'Errore di riproduzione';
+ }
+ });
+
+ audioPlayer.addEventListener('waiting', function () {
+ if (playerStatus) {
+ playerStatus.textContent = 'Buffering...';
+ }
+ });
+
+ audioPlayer.addEventListener('playing', function () {
+ if (playerStatus) {
+ playerStatus.textContent = 'In riproduzione...';
+ }
+ });
+
+ // Setup Media Session
+ setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon);
+
+ console.log('Player audio inizializzato');
+ }
+
+ /**
+ * Inizializza player TV
+ */
+ function initializeTVPlayer() {
+ console.log('TV Player inizializzato');
+ // Implementare logica specifica per TV se necessario
+ }
+
+ /**
+ * 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');
+ 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')) {
+ 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();
+}
+
diff --git a/js/app.js:Zone.Identifier b/js/app.js:Zone.Identifier
new file mode 100644
index 0000000..d6c1ec6
Binary files /dev/null and b/js/app.js:Zone.Identifier differ
diff --git a/manifest.json:Zone.Identifier b/manifest.json:Zone.Identifier
new file mode 100644
index 0000000..d6c1ec6
Binary files /dev/null and b/manifest.json:Zone.Identifier differ
diff --git a/pages/desktop.php b/pages/desktop.php
index 8a14228..80a8438 100644
--- a/pages/desktop.php
+++ b/pages/desktop.php
@@ -1,91 +1,94 @@
-
-
-
-
-