vers. 2.1.1

This commit is contained in:
2026-01-28 19:05:44 +01:00
parent 719d750a7a
commit 52e40799d6
35 changed files with 14523 additions and 14012 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*:Zone.Identifier
*.old

View File

@@ -1,5 +1,7 @@
<?php <?php
header('Content-Type: text/html; charset=UTF-8');
// Verifica se è una richiesta AJAX // Verifica se è una richiesta AJAX
$is_ajax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; $is_ajax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';

View File

@@ -1,5 +1,8 @@
<?php <?php
header('Content-Type: text/html; charset=UTF-8');
// Information App // Information App
$title_site = "RPIGroup Play"; $title_site = "RPIGroup Play";
$description_site = "Ascolta le radio del gruppo RPIGroup"; $description_site = "Ascolta le radio del gruppo RPIGroup";

View File

@@ -1,5 +1,7 @@
<?php <?php
header('Content-Type: text/html; charset=UTF-8');
// Determina il percorso base dell'applicazione // Determina il percorso base dell'applicazione
$script_name = $_SERVER['SCRIPT_NAME']; $script_name = $_SERVER['SCRIPT_NAME'];
$script_path = dirname($script_name); $script_path = dirname($script_name);

View File

@@ -1,10 +1,27 @@
<?php <?php
header('Content-Type: text/html; charset=UTF-8');
// File: config/getPage.inc.php
// Whitelist delle pagine valide
$validPages = ['home', 'radio', 'tv', 'play', 'playtv', 'page'];
$validSubPages = ['about', 'contact', 'copyright', 'addradio', 'termini-condizioni', 'policy-privacy', 'changelog'];
// Rileva se l'utente sta usando un dispositivo mobile // Rileva se l'utente sta usando un dispositivo mobile
function isMobile() { function isMobile() {
return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]); return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]);
} }
// Funzione per sanitizzare l'input
function sanitizePageInput($input) {
// Rimuovi caratteri pericolosi
$input = preg_replace('/[^a-zA-Z0-9\-_]/', '', $input);
// Previeni path traversal
$input = str_replace(['..', '/', '\\'], '', $input);
return $input;
}
// Recupera l'URL richiesto // Recupera l'URL richiesto
$request_uri = $_SERVER['REQUEST_URI']; $request_uri = $_SERVER['REQUEST_URI'];
$path = substr(urldecode($request_uri), strlen($base_path)); $path = substr(urldecode($request_uri), strlen($base_path));
@@ -23,9 +40,45 @@ if (isset($path_parts[0]) && $path_parts[0] == 'index.php') {
array_shift($path_parts); array_shift($path_parts);
} }
// Determina la pagina da mostrare in base all'URL // Determina la pagina da mostrare in base all'URL con validazione
$page = isset($path_parts[0]) && !empty($path_parts[0]) ? $path_parts[0] : 'home'; $page = 'home'; // Default sicuro
$param = isset($path_parts[1]) && !empty($path_parts[1]) ? $path_parts[1] : ''; $param = '';
if (isset($path_parts[0]) && !empty($path_parts[0])) {
$requestedPage = sanitizePageInput($path_parts[0]);
// Verifica se la pagina è nella whitelist
if (in_array($requestedPage, $validPages)) {
$page = $requestedPage;
} else {
// Pagina non valida, redirect a 404
$page = 'home';
error_log("Tentativo di accesso a pagina non valida: " . $path_parts[0]);
}
}
if (isset($path_parts[1]) && !empty($path_parts[1])) {
$requestedParam = sanitizePageInput($path_parts[1]);
// Validazione specifica per tipo di pagina
if ($page === 'play' || $page === 'playtv') {
// Per play/playtv, il parametro deve essere un numero
if (ctype_digit($requestedParam)) {
$param = $requestedParam;
} else {
error_log("ID stazione non valido: " . $path_parts[1]);
$page = 'home';
}
} elseif ($page === 'page') {
// Per page, il parametro deve essere nella whitelist
if (in_array($requestedParam, $validSubPages)) {
$param = $requestedParam;
} else {
error_log("Sottopagina non valida: " . $path_parts[1]);
$page = 'home';
}
}
}
// Debug (rimuovi in produzione) // Debug (rimuovi in produzione)
error_log("Page: $page, Param: $param, Path: $path"); error_log("Page: $page, Param: $param, Path: $path");

View File

@@ -1,54 +1,202 @@
<?php <?php
// Funzione per caricare il file XML delle radio /**
* Cache XML in memoria per la durata della richiesta
* Previene caricamenti multipli dello stesso file
*/
class StationCache {
private static $radioXML = null;
private static $tvXML = null;
private static $changelogXML = null;
/**
* Carica e cachea il file XML delle radio
*/
public static function getRadioXML() {
if (self::$radioXML === null) {
$xmlPath = './data/radio.xml';
if (!file_exists($xmlPath)) {
error_log("File XML non trovato: $xmlPath");
return false;
}
if (!is_readable($xmlPath)) {
error_log("File XML non leggibile: $xmlPath");
return false;
}
libxml_use_internal_errors(true);
self::$radioXML = simplexml_load_file($xmlPath);
if (self::$radioXML === false) {
$errors = libxml_get_errors();
foreach ($errors as $error) {
error_log("Errore XML radio.xml: " . trim($error->message) . " (Linea: " . $error->line . ")");
}
libxml_clear_errors();
return false;
}
}
return self::$radioXML;
}
/**
* Carica e cachea il file XML delle TV
*/
public static function getTVXML() {
if (self::$tvXML === null) {
$xmlPath = './data/tv.xml';
if (!file_exists($xmlPath)) {
error_log("File XML non trovato: $xmlPath");
return false;
}
if (!is_readable($xmlPath)) {
error_log("File XML non leggibile: $xmlPath");
return false;
}
libxml_use_internal_errors(true);
self::$tvXML = simplexml_load_file($xmlPath);
if (self::$tvXML === false) {
$errors = libxml_get_errors();
foreach ($errors as $error) {
error_log("Errore XML tv.xml: " . trim($error->message) . " (Linea: " . $error->line . ")");
}
libxml_clear_errors();
return false;
}
}
return self::$tvXML;
}
/**
* Carica e cachea il file XML del changelog
*/
public static function getChangelogXML() {
if (self::$changelogXML === null) {
$xmlPath = './data/changelog.xml';
if (!file_exists($xmlPath)) {
error_log("File XML non trovato: $xmlPath");
return false;
}
if (!is_readable($xmlPath)) {
error_log("File XML non leggibile: $xmlPath");
return false;
}
libxml_use_internal_errors(true);
self::$changelogXML = simplexml_load_file($xmlPath);
if (self::$changelogXML === false) {
$errors = libxml_get_errors();
foreach ($errors as $error) {
error_log("Errore XML changelog.xml: " . trim($error->message) . " (Linea: " . $error->line . ")");
}
libxml_clear_errors();
return false;
}
}
return self::$changelogXML;
}
}
/**
* Funzione per caricare tutte le stazioni radio
* @return array|SimpleXMLElement Array di stazioni o array vuoto in caso di errore
*/
function loadRadioStations() { function loadRadioStations() {
$xml = simplexml_load_file('./data/radio.xml'); $xml = StationCache::getRadioXML();
if ($xml === false) { if ($xml === false || !isset($xml->station)) {
error_log("Errore nel caricamento del file XML: data/radio.xml"); error_log("Impossibile caricare le stazioni radio");
return []; return [];
} }
return $xml->station; return $xml->station;
} }
// Funzione per ottenere una singola stazione radio /**
* Funzione per ottenere una singola stazione radio
* @param int $id ID della stazione
* @return SimpleXMLElement|null Stazione o null se non trovata
*/
function getRadioStation($id) { function getRadioStation($id) {
$xml = simplexml_load_file('./data/radio.xml'); $xml = StationCache::getRadioXML();
if ($xml === false) { if ($xml === false) {
error_log("Errore nel caricamento del file XML: data/radio.xml"); error_log("Impossibile caricare XML radio per ID: $id");
return null;
}
foreach ($xml->station as $station) {
if ((int)$station->id === $id) {
return $station;
}
}
return null; return null;
} }
// Funzione per caricare il file XML delle TV if (!isset($xml->station)) {
error_log("Nessuna stazione trovata nel file XML");
return null;
}
foreach ($xml->station as $station) {
if ((int)$station->id === (int)$id) {
return $station;
}
}
error_log("Stazione radio non trovata con ID: $id");
return null;
}
/**
* Funzione per caricare tutte le stazioni TV
* @return array|SimpleXMLElement Array di stazioni o array vuoto in caso di errore
*/
function loadTVStations() { function loadTVStations() {
$xml = simplexml_load_file('./data/tv.xml'); $xml = StationCache::getTVXML();
if ($xml === false) { if ($xml === false || !isset($xml->station)) {
error_log("Errore nel caricamento del file XML: data/tv.xml"); error_log("Impossibile caricare le stazioni TV");
return []; return [];
} }
return $xml->station; return $xml->station;
} }
// Funzione per ottenere una singola stazione TV /**
* Funzione per ottenere una singola stazione TV
* @param int $id ID della stazione
* @return SimpleXMLElement|null Stazione o null se non trovata
*/
function getTVStation($id) { function getTVStation($id) {
$xml = simplexml_load_file('./data/tv.xml'); $xml = StationCache::getTVXML();
if ($xml === false) { if ($xml === false) {
error_log("Errore nel caricamento del file XML: data/tv.xml"); error_log("Impossibile caricare XML TV per ID: $id");
return null;
}
foreach ($xml->station as $station) {
if ((int)$station->id === $id) {
return $station;
}
}
return null; return null;
} }
$changelog = simplexml_load_file("./data/changelog.xml") or die("Errore: Impossibile accedere al file CHANGELOG"); if (!isset($xml->station)) {
$version_app = $changelog->version->number[0]; error_log("Nessuna stazione TV trovata nel file XML");
return null;
}
foreach ($xml->station as $station) {
if ((int)$station->id === (int)$id) {
return $station;
}
}
error_log("Stazione TV non trovata con ID: $id");
return null;
}
/**
* Carica il changelog e la versione dell'app
*/
$changelog = StationCache::getChangelogXML();
$version_app = "1.0.0"; // Versione di default
if ($changelog !== false && isset($changelog->version) && isset($changelog->version[0]->number)) {
$version_app = (string)$changelog->version[0]->number;
} else {
error_log("Impossibile leggere la versione dal changelog.xml, uso versione di default: $version_app");
}

View File

@@ -2,6 +2,23 @@
<changelog> <changelog>
<version>
<number>2.1.1</number>
<logs>
<log>Corretti alcuni bug che impedivano l'accesso al player dal link esterno</log>
<log>Correzione e bugfix di problematiche varie.</log>
</logs>
</version>
<version>
<number>2.1.0</number>
<logs>
<log>Risoluzione dei problemi minori presenti nel codice, che causava problemi nella navigazione in app</log>
<log>Risoluzione dei problemi minori presenti nel codice, che causava problemi di riproduzione audio al player</log>
<log>Correzione e bugfix di problematiche varie.</log>
</logs>
</version>
<version> <version>
<number>2.0.4</number> <number>2.0.4</number>
<logs> <logs>

View File

@@ -35,7 +35,7 @@
<contentplayer></contentplayer> <contentplayer></contentplayer>
</station> </station>
<station> <!-- <station>
<id>4</id> <id>4</id>
<name>RC105 Christmas - Eboli</name> <name>RC105 Christmas - Eboli</name>
<slogan>La musica di Natale, in giro per Eboli</slogan> <slogan>La musica di Natale, in giro per Eboli</slogan>
@@ -44,7 +44,7 @@
<stream>https://srvone.radio.asvhosting.com/listen/rc105_christmas/radio.aac</stream> <stream>https://srvone.radio.asvhosting.com/listen/rc105_christmas/radio.aac</stream>
<streamhls>https://srvone.radio.asvhosting.com/hls/rc105_christmas/live.m3u8</streamhls> <streamhls>https://srvone.radio.asvhosting.com/hls/rc105_christmas/live.m3u8</streamhls>
<contentplayer></contentplayer> <contentplayer></contentplayer>
</station> </station> -->
<station> <station>
<id>5</id> <id>5</id>

View File

@@ -11,6 +11,8 @@
# Version app: VEDERE IN CHANGELOG.XML # Version app: VEDERE IN CHANGELOG.XML
*/ */
header('Content-Type: text/html; charset=UTF-8');
# Import config file # Import config file
include_once './config/config.php'; include_once './config/config.php';

640
js/app.js
View File

@@ -4,6 +4,11 @@ document.addEventListener('DOMContentLoaded', function() {
let currentPage = getCurrentPageFromPath(); let currentPage = getCurrentPageFromPath();
let audioPlayer = null; let audioPlayer = null;
let hlsInstance = null; let hlsInstance = null;
let currentXHR = null;
let isLoading = false;
// WeakMap per tracciare i listener degli elementi
const linkListeners = new WeakMap();
loadHlsLibrary(); loadHlsLibrary();
@@ -12,116 +17,233 @@ document.addEventListener('DOMContentLoaded', function() {
const script = document.createElement('script'); const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest';
script.onload = () => console.log('HLS.js caricato'); script.onload = () => console.log('HLS.js caricato con successo');
script.onerror = () => console.error('Errore caricamento HLS.js'); script.onerror = () => console.error('Errore nel caricamento di HLS.js');
document.head.appendChild(script); document.head.appendChild(script);
} }
function getCurrentPageFromPath() { function getCurrentPageFromPath() {
let path = window.location.pathname; let path = window.location.pathname;
// Rimuovi il base path se presente
if (BASE_PATH && path.startsWith(BASE_PATH)) { if (BASE_PATH && path.startsWith(BASE_PATH)) {
path = path.substring(BASE_PATH.length); path = path.substring(BASE_PATH.length);
} }
// Rimuovi slash iniziali e finali
path = path.replace(/^\/|\/$/g, ''); 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'; return path || 'home';
} }
function loadPage(page) { /**
const contentContainer = document.getElementById('content'); * Pulizia completa del player audio
if (!contentContainer) return; * 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.remove('fade-in');
contentContainer.classList.add('fade-out'); contentContainer.classList.add('fade-out');
setTimeout(() => { setTimeout(() => {
const url = BASE_PATH + '/index.php/' + page; const url = BASE_PATH + '/index.php/' + page;
const xhr = new XMLHttpRequest(); currentXHR = new XMLHttpRequest();
xhr.open('GET', url, true); currentXHR.open('GET', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); currentXHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function() { currentXHR.onload = function () {
if (xhr.status === 200) { isLoading = false;
contentContainer.innerHTML = xhr.responseText; currentXHR = null;
if (this.status === 200) {
contentContainer.innerHTML = this.responseText;
// Animazione fade in
contentContainer.classList.remove('fade-out'); contentContainer.classList.remove('fade-out');
contentContainer.classList.add('fade-in'); contentContainer.classList.add('fade-in');
// Aggiorna URL nella history
const historyUrl = page === 'home' ? BASE_PATH + '/' : BASE_PATH + '/' + page; const historyUrl = page === 'home' ? BASE_PATH + '/' : BASE_PATH + '/' + page;
history.pushState({ page: page }, null, historyUrl); history.pushState({ page: page }, null, historyUrl);
currentPage = page; currentPage = page;
// Aggiorna navigazione attiva
updateActiveNavigation(page); updateActiveNavigation(page);
// Inizializza componenti specifici della pagina
if (page.startsWith('play/')) { if (page.startsWith('play/')) {
initializePlayer(); setTimeout(() => initializePlayer(), 100);
} }
if (page.startsWith('playtv/')) { if (page.startsWith('playtv/')) {
initializeTVPlayer(); setTimeout(() => initializeTVPlayer(), 100);
} }
// Riattacca i listener ai link
attachLinkListeners(); attachLinkListeners();
// Inizializza form contatti se presente
if (page === 'page/contact') { if (page === 'page/contact') {
initializeContactForm(); setTimeout(() => initializeContactForm(), 100);
} }
// Scroll to top
window.scrollTo({ window.scrollTo({
top: 0, top: 0,
behavior: 'smooth' behavior: 'smooth'
}); });
console.log('Pagina caricata:', page);
} else { } else {
contentContainer.innerHTML = '<div class="content-page"><h2>Errore</h2><p>Impossibile caricare la pagina. Codice errore: ' + xhr.status + '</p></div>'; 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.remove('fade-out');
contentContainer.classList.add('fade-in'); contentContainer.classList.add('fade-in');
console.error('Errore caricamento pagina:', this.status);
} }
}; };
xhr.onerror = function() { currentXHR.onerror = function () {
contentContainer.innerHTML = '<div class="content-page"><h2>Errore di connessione</h2><p>Controlla la tua connessione internet.</p></div>'; isLoading = false;
currentXHR = null;
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.remove('fade-out');
contentContainer.classList.add('fade-in'); contentContainer.classList.add('fade-in');
console.error('Errore di rete durante il caricamento della pagina');
}; };
xhr.send(); currentXHR.onabort = function () {
isLoading = false;
currentXHR = null;
console.log('Richiesta XHR annullata');
};
currentXHR.send();
}, 200); }, 200);
} }
/**
* Aggiorna la navigazione attiva
*/
function updateActiveNavigation(page) { function updateActiveNavigation(page) {
console.log('Aggiornamento navigazione per pagina:', page); // Debug
const navLinks = document.querySelectorAll('.navLink'); const navLinks = document.querySelectorAll('.navLink');
navLinks.forEach(link => { navLinks.forEach(link => {
link.classList.remove('active'); link.classList.remove('active');
const linkPage = link.getAttribute('data-page'); const linkPage = link.getAttribute('data-page');
// Log di debug per vedere i confronti
console.log('Confronto:', linkPage, 'con', page);
if (linkPage === page) { if (linkPage === page) {
link.classList.add('active'); link.classList.add('active');
console.log('✓ Match esatto:', linkPage);
} else if (page.startsWith('play/') && linkPage === 'radio') { } else if (page.startsWith('play/') && linkPage === 'radio') {
link.classList.add('active'); link.classList.add('active');
console.log('✓ Match play/ -> radio');
} else if (page.startsWith('playtv/') && linkPage === 'tv') { } else if (page.startsWith('playtv/') && linkPage === 'tv') {
link.classList.add('active'); link.classList.add('active');
} else if (page.startsWith('page/')) { console.log('✓ Match playtv/ -> tv');
if (linkPage === page) { } else if (page.startsWith('page/') && linkPage === page) {
link.classList.add('active'); link.classList.add('active');
} console.log('✓ Match page/');
} }
}); });
} }
// NUOVA FUNZIONE: Setup Media Session API /**
* Setup Media Session API per controlli sistema
*/
function setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon) { function setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon) {
if (!('mediaSession' in navigator)) { if (!('mediaSession' in navigator)) {
console.log('Media Session API non supportata'); console.log('Media Session API non supportata su questo browser');
return; return;
} }
console.log('Setup Media Session:', stationName, stationSlogan); console.log('Configurazione Media Session:', stationName);
// Imposta i metadati // Imposta i metadati della riproduzione
navigator.mediaSession.metadata = new MediaMetadata({ navigator.mediaSession.metadata = new MediaMetadata({
title: stationName, title: stationName,
artist: stationSlogan, artist: stationSlogan,
@@ -136,25 +258,26 @@ document.addEventListener('DOMContentLoaded', function() {
] ]
}); });
// Gestisci i controlli del centro notifiche // Handler per il comando Play dal sistema
navigator.mediaSession.setActionHandler('play', () => { navigator.mediaSession.setActionHandler('play', () => {
console.log('Media Session: Play richiesto dal centro notifiche'); console.log('Media Session: Play richiesto');
if (audioPlayer) { if (audioPlayer) {
audioPlayer.play().then(() => { audioPlayer.play().then(() => {
// Aggiorna UI
if (playIcon && pauseIcon) { if (playIcon && pauseIcon) {
playIcon.style.display = 'none'; playIcon.style.display = 'none';
pauseIcon.style.display = 'block'; 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', () => { navigator.mediaSession.setActionHandler('pause', () => {
console.log('Media Session: Pause richiesto dal centro notifiche'); console.log('Media Session: Pause richiesto');
if (audioPlayer) { if (audioPlayer) {
audioPlayer.pause(); audioPlayer.pause();
// Aggiorna UI
if (playIcon && pauseIcon) { if (playIcon && pauseIcon) {
playIcon.style.display = 'block'; playIcon.style.display = 'block';
pauseIcon.style.display = 'none'; pauseIcon.style.display = 'none';
@@ -162,14 +285,18 @@ document.addEventListener('DOMContentLoaded', function() {
} }
}); });
// Opzionale: gestisci skip (per radio potrebbe non servire) // Disabilita i controlli non utilizzati per radio streaming
navigator.mediaSession.setActionHandler('seekbackward', null); navigator.mediaSession.setActionHandler('seekbackward', null);
navigator.mediaSession.setActionHandler('seekforward', null); navigator.mediaSession.setActionHandler('seekforward', null);
navigator.mediaSession.setActionHandler('previoustrack', null); navigator.mediaSession.setActionHandler('previoustrack', null);
navigator.mediaSession.setActionHandler('nexttrack', null); navigator.mediaSession.setActionHandler('nexttrack', null);
console.log('Media Session configurata correttamente');
} }
// NUOVA FUNZIONE: Aggiorna playback state /**
* Aggiorna lo stato di riproduzione nella Media Session
*/
function updateMediaSessionPlaybackState(state) { function updateMediaSessionPlaybackState(state) {
if ('mediaSession' in navigator) { if ('mediaSession' in navigator) {
navigator.mediaSession.playbackState = state; navigator.mediaSession.playbackState = state;
@@ -177,214 +304,252 @@ document.addEventListener('DOMContentLoaded', function() {
} }
} }
/**
* Inizializza il player audio con gestione HLS migliorata
*/
function initializePlayer() { function initializePlayer() {
console.log('Inizializzazione player...'); console.log('Inizializzazione player audio...');
const playPauseBtn = document.getElementById('playPauseBtn'); const playPauseBtn = document.getElementById('playPauseBtn');
if (!playPauseBtn) return;
const playIcon = document.querySelector('.play-icon'); const playIcon = document.querySelector('.play-icon');
const pauseIcon = document.querySelector('.pause-icon'); const pauseIcon = document.querySelector('.pause-icon');
const hlsUrl = playPauseBtn.getAttribute('data-stream-hls'); const playerStatus = document.getElementById('playerStatus');
const fallbackUrl = playPauseBtn.getAttribute('data-stream-fallback'); 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 stationName = playPauseBtn.getAttribute('data-station-name');
const stationSlogan = playPauseBtn.getAttribute('data-station-slogan'); const stationSlogan = playPauseBtn.getAttribute('data-station-slogan');
const stationLogo = playPauseBtn.getAttribute('data-station-logo'); const stationLogo = playPauseBtn.getAttribute('data-station-logo');
const statusElement = document.getElementById('playerStatus');
console.log('Station:', stationName); console.log('Stream HLS:', streamHLS);
console.log('HLS URL:', hlsUrl); console.log('Stream Fallback:', streamFallback);
console.log('Fallback URL:', fallbackUrl); console.log('Stazione:', stationName);
// Setup Media Session all'inizio passando anche le icone let isPlaying = false;
setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon); let streamType = 'hls'; // 'hls' o 'direct'
if (hlsInstance) { /**
hlsInstance.destroy(); * Inizializza HLS stream
hlsInstance = null; */
} function initHLSStream() {
if (!window.Hls) {
if (audioPlayer) { console.error('HLS.js non caricato');
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; return false;
} }
console.log('Uso HLS.js'); if (Hls.isSupported()) {
updateStatus('Caricamento...'); console.log('HLS supportato - uso HLS.js');
hlsInstance = new Hls({ hlsInstance = new Hls({
debug: false,
enableWorker: true, enableWorker: true,
lowLatencyMode: true lowLatencyMode: true,
backBufferLength: 90
}); });
hlsInstance.loadSource(hlsUrl); hlsInstance.loadSource(streamHLS);
hlsInstance.attachMedia(audioPlayer); hlsInstance.attachMedia(audioPlayer);
hlsInstance.on(Hls.Events.MANIFEST_PARSED, function () { hlsInstance.on(Hls.Events.MANIFEST_PARSED, function () {
console.log('HLS manifest caricato'); console.log('Manifest HLS caricato');
updateStatus('Pronto'); 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) { hlsInstance.on(Hls.Events.ERROR, function (event, data) {
console.error('Errore HLS:', data.type, data.details); console.error('Errore HLS:', data.type, data.details);
if (data.fatal) { if (data.fatal) {
switch (data.type) { switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR: case Hls.ErrorTypes.NETWORK_ERROR:
console.log('Errore rete, riprovo...'); console.log('Errore di rete, tentativo fallback...');
hlsInstance.startLoad(); tryFallbackStream();
break; break;
case Hls.ErrorTypes.MEDIA_ERROR: case Hls.ErrorTypes.MEDIA_ERROR:
console.log('Errore media, riprovo...'); console.log('Errore media, tentativo recupero...');
hlsInstance.recoverMediaError(); hlsInstance.recoverMediaError();
break; break;
default: default:
console.error('Errore fatale, uso fallback'); console.log('Errore fatale, tentativo fallback...');
hlsInstance.destroy(); tryFallbackStream();
hlsInstance = null;
useFallback();
break; 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 true;
} }
function useFallback() { return false;
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()) { * Usa stream diretto come fallback
console.log('Uso HLS nativo (Safari)'); */
audioPlayer.src = hlsUrl; function tryFallbackStream() {
updateStatus('Pronto'); console.log('Tentativo stream fallback:', streamFallback);
} else if (!setupHLS()) {
useFallback();
}
} else {
useFallback();
}
// Gestore pulsante play/pause if (hlsInstance) {
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.destroy();
hlsInstance = null; hlsInstance = null;
useFallback(); }
setTimeout(() => {
audioPlayer.play().catch(e => { streamType = 'direct';
console.error('Fallback fallito:', e); audioPlayer.src = streamFallback;
alert('Impossibile riprodurre lo stream audio. Riprova più tardi.'); audioPlayer.load();
});
}, 500); audioPlayer.play().then(() => {
} else { console.log('Riproduzione stream diretto avviata');
alert('Impossibile riprodurre lo stream audio. Riprova più tardi.'); 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 { } else {
audioPlayer.pause(); playIcon.style.display = 'inline-block';
playIcon.style.display = 'block';
pauseIcon.style.display = 'none'; pauseIcon.style.display = 'none';
updateStatus('In pausa'); if (playerStatus) {
updateMediaSessionPlaybackState('paused'); 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 con Media Session updates e sincronizzazione UI /**
* Event listeners audio player
*/
audioPlayer.addEventListener('play', function () { audioPlayer.addEventListener('play', function () {
updateMediaSessionPlaybackState('playing'); updatePlayState(true);
updateStatus('In riproduzione');
// Sincronizza UI quando l'audio parte (da qualsiasi fonte)
playIcon.style.display = 'none';
pauseIcon.style.display = 'block';
}); });
audioPlayer.addEventListener('pause', function () { audioPlayer.addEventListener('pause', function () {
updateMediaSessionPlaybackState('paused'); updatePlayState(false);
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() { audioPlayer.addEventListener('error', function (e) {
playIcon.style.display = 'block'; console.error('Errore audio player:', e);
pauseIcon.style.display = 'none'; updatePlayState(false);
updateStatus('Terminato'); if (playerStatus) {
updateMediaSessionPlaybackState('paused'); playerStatus.textContent = 'Errore di riproduzione';
}
}); });
audioPlayer.addEventListener('error', function() { audioPlayer.addEventListener('waiting', function () {
playIcon.style.display = 'block'; if (playerStatus) {
pauseIcon.style.display = 'none'; playerStatus.textContent = 'Buffering...';
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', function () {
audioPlayer.addEventListener('playing', () => updateStatus('In riproduzione')); if (playerStatus) {
playerStatus.textContent = 'In riproduzione...';
}
});
console.log('Player inizializzato con Media Session'); // Setup Media Session
setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon);
console.log('Player audio inizializzato');
} }
/**
* Inizializza player TV
*/
function initializeTVPlayer() { function initializeTVPlayer() {
console.log('TV Player inizializzato'); console.log('TV Player inizializzato');
// Implementare logica specifica per TV se necessario
} }
/**
* Inizializza form contatti
*/
function initializeContactForm() { function initializeContactForm() {
const contactForm = document.getElementById('contactForm'); const contactForm = document.getElementById('contactForm');
if (!contactForm) return; if (!contactForm) {
console.log('Form contatti non trovato');
return;
}
contactForm.addEventListener('submit', function (e) { contactForm.addEventListener('submit', function (e) {
e.preventDefault(); e.preventDefault();
@@ -392,53 +557,78 @@ document.addEventListener('DOMContentLoaded', function() {
const formData = new FormData(contactForm); const formData = new FormData(contactForm);
const formResponse = document.getElementById('formResponse'); const formResponse = document.getElementById('formResponse');
if (formResponse) {
formResponse.innerHTML = 'Invio in corso...'; formResponse.innerHTML = 'Invio in corso...';
formResponse.className = 'form-response'; formResponse.className = 'form-response';
formResponse.style.display = 'block'; formResponse.style.display = 'block';
}
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', BASE_PATH + '/process_contact.php', true); xhr.open('POST', BASE_PATH + '/process_contact.php', true);
xhr.onload = function () { xhr.onload = function () {
if (xhr.status === 200) { if (xhr.status === 200) {
try { try {
const response = JSON.parse(xhr.responseText); const response = JSON.parse(xhr.responseText);
if (response.success) { if (response.success) {
if (formResponse) {
formResponse.innerHTML = 'Messaggio inviato con successo! Ti risponderemo presto.'; formResponse.innerHTML = 'Messaggio inviato con successo! Ti risponderemo presto.';
formResponse.className = 'form-response success'; formResponse.className = 'form-response success';
}
contactForm.reset(); contactForm.reset();
} else { } else {
if (formResponse) {
formResponse.innerHTML = 'Errore: ' + (response.message || 'Si è verificato un errore durante l\'invio del messaggio.'); formResponse.innerHTML = 'Errore: ' + (response.message || 'Si è verificato un errore durante l\'invio del messaggio.');
formResponse.className = 'form-response error'; formResponse.className = 'form-response error';
} }
}
} catch (e) { } catch (e) {
console.error('Errore parsing risposta:', e);
if (formResponse) {
formResponse.innerHTML = 'Si è verificato un errore durante l\'elaborazione della risposta.'; formResponse.innerHTML = 'Si è verificato un errore durante l\'elaborazione della risposta.';
formResponse.className = 'form-response error'; formResponse.className = 'form-response error';
} }
}
} else { } else {
if (formResponse) {
formResponse.innerHTML = 'Si è verificato un errore durante l\'invio del messaggio.'; formResponse.innerHTML = 'Si è verificato un errore durante l\'invio del messaggio.';
formResponse.className = 'form-response error'; formResponse.className = 'form-response error';
} }
}
}; };
xhr.onerror = function () { xhr.onerror = function () {
if (formResponse) {
formResponse.innerHTML = 'Errore di connessione. Controlla la tua connessione internet.'; formResponse.innerHTML = 'Errore di connessione. Controlla la tua connessione internet.';
formResponse.className = 'form-response error'; formResponse.className = 'form-response error';
}
}; };
xhr.send(formData); xhr.send(formData);
}); });
console.log('Form contatti inizializzato');
} }
/**
* Attacca event listener ai link con gestione migliorata
*/
function attachLinkListeners() { function attachLinkListeners() {
const navLinks = document.querySelectorAll('.navLink, .nav-link, .station-link, .linkBox'); const navLinks = document.querySelectorAll('.navLink, .nav-link, .station-link, .linkBox');
navLinks.forEach(link => { navLinks.forEach(link => {
const newLink = link.cloneNode(true); // Rimuovi listener precedente se esiste
link.parentNode.replaceChild(newLink, link); const oldListener = linkListeners.get(link);
if (oldListener) {
link.removeEventListener('click', oldListener);
}
newLink.addEventListener('click', function(e) { const clickHandler = function (e) {
const href = this.getAttribute('href'); const href = this.getAttribute('href');
const target = this.getAttribute('target'); const target = this.getAttribute('target');
const page = this.getAttribute('data-page'); const page = this.getAttribute('data-page');
// Verifica se è un link esterno
const isExternal = href && ( const isExternal = href && (
href.startsWith('http://') || href.startsWith('http://') ||
href.startsWith('https://') || href.startsWith('https://') ||
@@ -447,27 +637,35 @@ document.addEventListener('DOMContentLoaded', function() {
); );
if (isExternal) { if (isExternal) {
console.log('Link esterno rilevato:', href); console.log('Link esterno:', href);
e.preventDefault(); return; // Lascia che il browser gestisca normalmente
window.location.href = href;
return;
} }
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
console.log('Link cliccato, pagina:', page, 'currentPage:', currentPage); console.log('Navigazione a:', page);
if (page && page !== currentPage) { // 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) { if (audioPlayer && !audioPlayer.paused) {
audioPlayer.pause(); audioPlayer.pause();
} }
loadPage(page); 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() { function setupOpenAppButton() {
const openAppBtn = document.getElementById('openAppBtn'); const openAppBtn = document.getElementById('openAppBtn');
if (openAppBtn) { if (openAppBtn) {
@@ -486,9 +684,14 @@ document.addEventListener('DOMContentLoaded', function() {
const windowFeatures = 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',resizable=yes,scrollbars=yes,status=yes'; 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); window.open(BASE_PATH + '/?app=true' + redirectParam, 'RadioApp', windowFeatures);
}); });
console.log('Pulsante apertura app configurato');
} }
} }
/**
* Setup navigazione con history API
*/
function setupHistoryNavigation() { function setupHistoryNavigation() {
window.addEventListener('popstate', function (e) { window.addEventListener('popstate', function (e) {
if (e.state && e.state.page) { if (e.state && e.state.page) {
@@ -503,76 +706,89 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
history.replaceState({ page: currentPage }, null, window.location.pathname); history.replaceState({ page: currentPage }, null, window.location.pathname);
console.log('Navigazione history configurata');
} }
/**
* Setup modalità standalone (PWA)
*/
function setupStandaloneMode() { function setupStandaloneMode() {
const appContainer = document.querySelector('.container'); const appContainer = document.querySelector('.container');
if (!appContainer) return; if (!appContainer) return;
if (window.opener && !window.opener.closed) { if (window.opener && !window.opener.closed) {
appContainer.classList.add('standalone-app'); appContainer.classList.add('standalone-app');
console.log('App in modalità finestra popup');
} }
if (window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches) { if (window.navigator.standalone || window.matchMedia('(display-mode: standalone)').matches) {
appContainer.classList.add('standalone-app'); appContainer.classList.add('standalone-app');
console.log('App in modalità standalone (PWA)');
} }
} }
function init() { /**
setupOpenAppButton(); * Inizializza la pagina corrente
*/
if (document.querySelector('.container')) {
setupStandaloneMode();
attachLinkListeners();
setupHistoryNavigation();
initializeCurrentPage();
}
}
function initializeCurrentPage() { function initializeCurrentPage() {
console.log('Inizializzazione pagina corrente:', currentPage); console.log('Inizializzazione pagina corrente:', currentPage);
updateActiveNavigation(currentPage); updateActiveNavigation(currentPage);
if (currentPage.startsWith('play/')) { if (currentPage.startsWith('play/')) {
setTimeout(function() { setTimeout(() => initializePlayer(), 100);
initializePlayer();
}, 100);
} }
if (currentPage.startsWith('playtv/')) { if (currentPage.startsWith('playtv/')) {
setTimeout(function() { setTimeout(() => initializeTVPlayer(), 100);
initializeTVPlayer();
}, 100);
} }
if (currentPage === 'page/contact') { if (currentPage === 'page/contact') {
setTimeout(function() { setTimeout(() => initializeContactForm(), 100);
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(); init();
}); });
// Funzione per bloccare l'orientamento (funziona solo in PWA/fullscreen) /**
* GESTIONE ORIENTAMENTO SCHERMO
*/
/**
* Blocca l'orientamento in portrait (funziona solo in PWA/fullscreen)
*/
function lockScreenOrientation() { function lockScreenOrientation() {
// Prova a bloccare l'orientamento con Screen Orientation API // Screen Orientation API
if (screen.orientation && screen.orientation.lock) { if (screen.orientation && screen.orientation.lock) {
screen.orientation.lock('portrait').then(() => { screen.orientation.lock('portrait').then(() => {
console.log('Orientamento bloccato in portrait'); console.log('Orientamento bloccato in portrait');
}).catch((err) => { }).catch((err) => {
console.log('Impossibile bloccare orientamento:', err.message); console.log('Impossibile bloccare orientamento:', err.message);
// Non è un errore grave, il CSS gestirà la situazione
}); });
} }
// Alternativa per browser più vecchi // Fallback per browser più vecchi
if (screen.lockOrientation) { if (screen.lockOrientation) {
screen.lockOrientation('portrait'); screen.lockOrientation('portrait');
} else if (screen.mozLockOrientation) { } else if (screen.mozLockOrientation) {
@@ -582,9 +798,20 @@ function lockScreenOrientation() {
} }
} }
// Prova a bloccare quando l'app è in fullscreen/standalone /**
* 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() { function tryLockOrientation() {
// Controlla se siamo in modalità standalone (PWA)
const isStandalone = window.navigator.standalone || const isStandalone = window.navigator.standalone ||
window.matchMedia('(display-mode: standalone)').matches; window.matchMedia('(display-mode: standalone)').matches;
@@ -592,7 +819,6 @@ function tryLockOrientation() {
lockScreenOrientation(); lockScreenOrientation();
} }
// Prova anche quando entriamo in fullscreen
document.addEventListener('fullscreenchange', () => { document.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) { if (document.fullscreenElement) {
lockScreenOrientation(); lockScreenOrientation();

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<div id="proradio-secondary-header" class="proradio-secondaryhead proradio-primary" style="border-bottom: solid 3px #3849a8;"> <div id="proradio-secondary-header" class="proradio-secondaryhead proradio-primary" style="border-bottom: solid 3px #3849a8;">
<style> <style>
@@ -59,6 +61,7 @@
<a href="https://www.radiocitta105.it" target="_blank"><span class="link">Radio Città 105</span></a> <a href="https://www.radiocitta105.it" target="_blank"><span class="link">Radio Città 105</span></a>
<a href="https://www.radiodiffusionelibera.com" target="_blank"><span class="link">Radio DiffusioneLibera</span></a> <a href="https://www.radiodiffusionelibera.com" target="_blank"><span class="link">Radio DiffusioneLibera</span></a>
<a href="https://www.co-municare.it" target="_blank"><span class="link">Co-Municare.it</span></a> <a href="https://www.co-municare.it" target="_blank"><span class="link">Co-Municare.it</span></a>
<a href="https://www.ineboli.it" target="_blank"><span class="link">InEboli</span></a>
<a href="https://app.rpigroup.it" target="_blank"><span class="link active">RPIGroup Play</span></a> <a href="https://app.rpigroup.it" target="_blank"><span class="link active">RPIGroup Play</span></a>
</div> </div>
</div> </div>

View File

@@ -1,3 +1,8 @@
<?php
// Debug: verifica valori delle variabili (rimuovi dopo aver risolto)
// echo "<!-- DEBUG - Page: " . htmlspecialchars($page) . " | Param: " . htmlspecialchars($param) . " -->\n";
?>
<div class="header"> <div class="header">
<div class="logo-section"> <div class="logo-section">
<img src="<?=$base_path?>/img/RpiGroupPlayWHITE.png" alt="Logo"> <img src="<?=$base_path?>/img/RpiGroupPlayWHITE.png" alt="Logo">
@@ -94,5 +99,5 @@
<a href="<?php echo $base_path; ?>/page/policy-privacy" data-page="page/policy-privacy" class="navLink">Policy Privacy</a> • <a href="<?php echo $base_path; ?>/page/policy-privacy" data-page="page/policy-privacy" class="navLink">Policy Privacy</a> •
<a href="<?php echo $base_path; ?>/page/changelog" data-page="page/changelog" class="navLink">Changelog</a> <a href="<?php echo $base_path; ?>/page/changelog" data-page="page/changelog" class="navLink">Changelog</a>
</div> </div>
<div class="copyright-section" <?php if($is_mobile){ ?> style="padding: 10px 0 25px;" <? } ?>>&copy; 2025 RPIGroup • Versione: <?php echo $version_app; ?></div> <div class="copyright-section" <?php if($is_mobile){ ?> style="padding: 10px 0 25px;" <?php } ?>>© 2025 RPIGroup • Versione: <?php echo $version_app; ?></div>
</div> </div>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Pagina non trovata</h1> <h1 class="titlePage">Pagina non trovata</h1>
<p class="text-center">La pagina che hai provato a cercare non esiste o non è disponibile.</p> <p class="text-center">La pagina che hai provato a cercare non esiste o non è disponibile.</p>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Come funziona RPIGroup Play?</h1> <h1 class="titlePage">Come funziona RPIGroup Play?</h1>
<hr> <hr>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Aggiungi la tua radio</h1> <h1 class="titlePage">Aggiungi la tua radio</h1>
<hr> <hr>

View File

@@ -1,13 +1,31 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Changelog</h1> <h1 class="titlePage">Changelog</h1>
<p class="subtitlePage">Visualizza tutti gli ricevuti</p> <p class="subtitlePage">Visualizza tutti gli aggiornamenti ricevuti</p>
<?php <?php
// Verifica che $changelog sia stato caricato correttamente in getStation.inc.php
if (isset($changelog) && $changelog !== false && isset($changelog->version)) {
// Itera attraverso le versioni
foreach($changelog->version as $version) { foreach($changelog->version as $version) {
echo "<hr>"; echo "<hr>";
echo "<p class=\"changelogTitle\">Versione ".$version->number."</p>"; echo "<p class=\"changelogTitle\">Versione " . htmlspecialchars((string)$version->number) . "</p>";
// Verifica che esistano i log prima di iterare
if (isset($version->logs) && isset($version->logs->log)) {
foreach($version->logs->log as $log_print) { foreach($version->logs->log as $log_print) {
echo "<p class=\"changelogList\">• ".$log_print."</p>"; echo "<p class=\"changelogList\">• " . htmlspecialchars((string)$log_print) . "</p>";
} }
} else {
echo "<p class=\"changelogList\" style=\"font-style: italic; color: #999;\">Nessun dettaglio disponibile per questa versione.</p>";
}
}
} else {
// Messaggio di errore se il changelog non è disponibile
echo "<hr>";
echo "<div style=\"padding: 20px; background: #fff3cd; border: 1px solid #ffc107; border-radius: 5px; margin: 20px 0;\">";
echo "<p style=\"margin: 0; color: #856404;\"><strong>Attenzione:</strong> Impossibile caricare il changelog. Il file potrebbe essere mancante o corrotto.</p>";
echo "</div>";
} }
?> ?>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<!-- <div class="content-page"> <!-- <div class="content-page">
<h2>Contatti</h2> <h2>Contatti</h2>
<div class="content-box"> <div class="content-box">

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Diritti d'Autore</h1> <h1 class="titlePage">Diritti d'Autore</h1>
<p class="subtitlePage">Come funziona e chi lo gestisce</p> <p class="subtitlePage">Come funziona e chi lo gestisce</p>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Benvenuto</h1> <h1 class="titlePage">Benvenuto</h1>
<p class="subtitlePage">nella nuova RPIGroup Play</p> <p class="subtitlePage">nella nuova RPIGroup Play</p>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Policy Privacy</h1> <h1 class="titlePage">Policy Privacy</h1>
<p class="subtitlePage">Ultimo aggiornamento: 17/11/2025</p> <p class="subtitlePage">Ultimo aggiornamento: 17/11/2025</p>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<?php <?php
// views/home.php - Vista della pagina principale // views/home.php - Vista della pagina principale
$stations = loadRadioStations(); $stations = loadRadioStations();

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">Termini & Condizioni</h1> <h1 class="titlePage">Termini & Condizioni</h1>
<p class="subtitlePage">Ultimo aggiornamento: 17/11/2025</p> <p class="subtitlePage">Ultimo aggiornamento: 17/11/2025</p>

View File

@@ -1,3 +1,5 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<h1 class="titlePage">TV</h1> <h1 class="titlePage">TV</h1>
<p class="subtitlePage">Guarda in streaming <b>RC105 TV</b></p> <p class="subtitlePage">Guarda in streaming <b>RC105 TV</b></p>

2
readme.md Normal file
View File

@@ -0,0 +1,2 @@
# RPIGroup Play
Il player ufficiale del gruppo RPIGroup

View File

@@ -1,7 +1,4 @@
<div style="display:none">
<p><a href="https://www.jennymcnieceflowers.com/">777slot</a></p>
</div>
<script src="<?php echo $base_path; ?>/js/app.js?v=<?=time()?>"></script> <script src="<?php echo $base_path; ?>/js/app.js?v=<?=time()?>"></script>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,14 @@
<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<?php
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="it"> <html lang="it">
<head> <head>