vers. 2.1.1

This commit is contained in:
2026-01-28 18:59:25 +01:00
parent 719d750a7a
commit 6b15afb9da
111 changed files with 14657 additions and 14012 deletions

77
.htaccess Normal file
View File

@@ -0,0 +1,77 @@
# Abilita il rewrite engine
RewriteEngine On
# Imposta la directory base (modifica se necessario)
# Se l'app è in una sottocartella, usa: RewriteBase /nome_cartella/
RewriteBase /newapp.rpigroup.it/
# Reindirizza richieste HTTP a HTTPS (opzionale, decommentare se necessario)
# RewriteCond %{HTTPS} off
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Non reindirizzare file e cartelle esistenti
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Escludi file statici dal rewriting
RewriteCond %{REQUEST_URI} !\.(css|js|jpg|jpeg|png|gif|svg|ico|xml|json|woff|woff2|ttf|eot)$ [NC]
# Reindirizza tutto a index.php mantenendo il path
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
# Impedisci l'accesso diretto a file sensibili
<FilesMatch "^(config|\.htaccess|\.env)">
Order Allow,Deny
Deny from all
</FilesMatch>
# Gestione MIME types
<IfModule mod_mime.c>
AddType application/javascript js
AddType text/css css
AddType image/svg+xml svg
AddType application/vnd.ms-fontobject eot
AddType font/ttf ttf
AddType font/otf otf
AddType font/woff woff
AddType font/woff2 woff2
</IfModule>
# Abilita compressione GZIP (opzionale)
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>
# Cache control (opzionale - commentato perché hai lo script no-cache)
# <IfModule mod_expires.c>
# ExpiresActive On
# ExpiresByType image/jpg "access plus 1 month"
# ExpiresByType image/jpeg "access plus 1 month"
# ExpiresByType image/gif "access plus 1 month"
# ExpiresByType image/png "access plus 1 month"
# ExpiresByType image/svg+xml "access plus 1 month"
# ExpiresByType text/css "access plus 1 week"
# ExpiresByType application/javascript "access plus 1 week"
# </IfModule>
# Header no-cache per sviluppo (rimuovi in produzione se usi la cache)
<IfModule mod_headers.c>
<FilesMatch "\.(html|php)$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</FilesMatch>
</IfModule>
# Sicurezza aggiuntiva
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
# Disabilita directory listing
Options -Indexes
# Abilita follow symlinks (necessario per RewriteRule)
Options +FollowSymLinks

BIN
.htaccess:Zone.Identifier Normal file

Binary file not shown.

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';

Binary file not shown.

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";

Binary file not shown.

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);

Binary file not shown.

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");

Binary file not shown.

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");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

BIN
data/tv.xml:Zone.Identifier Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
img/tv.png:Zone.Identifier Normal file

Binary file not shown.

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';

BIN
index.php:Zone.Identifier Normal file

Binary file not shown.

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();

BIN
js/app.js:Zone.Identifier Normal file

Binary file not shown.

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

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>";
} }
?> ?>

Binary file not shown.

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">

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

Binary file not shown.

Binary file not shown.

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>

Binary file not shown.

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();

Binary file not shown.

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>

Binary file not shown.

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>

Binary file not shown.

59
process_contact.php.old Normal file
View File

@@ -0,0 +1,59 @@
<?php
// File: process_contact.php
// Imposta header per JSON
header('Content-Type: application/json');
// Verifica se il modulo è stato inviato
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Raccolta e pulizia dei dati
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$subject = filter_input(INPUT_POST, 'subject', FILTER_SANITIZE_STRING);
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);
// Validazione dei dati
$errors = [];
if (empty($name)) {
$errors[] = "Il nome è richiesto";
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Email non valida";
}
if (empty($subject)) {
$errors[] = "L'oggetto è richiesto";
}
if (empty($message)) {
$errors[] = "Il messaggio è richiesto";
}
// Se non ci sono errori, procedi con l'invio della mail
if (empty($errors)) {
// Destinatario
$to = "info@tuoaggregatore.it";
// Intestazioni
$headers = "From: $name <$email>" . "\r\n";
$headers .= "Reply-To: $email" . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
// Prova a inviare l'email
$success = mail($to, $subject, $message, $headers);
if ($success) {
echo json_encode(['success' => true, 'message' => 'Messaggio inviato con successo']);
} else {
echo json_encode(['success' => false, 'message' => 'Impossibile inviare il messaggio']);
}
} else {
// Restituisci errori di validazione
echo json_encode(['success' => false, 'message' => implode(', ', $errors)]);
}
} else {
// Se qualcuno prova ad accedere direttamente a questa pagina
echo json_encode(['success' => false, 'message' => 'Metodo non consentito']);
}

Binary file not shown.

2
readme.md Normal file
View File

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

BIN
readme.md:Zone.Identifier Normal file

Binary file not shown.

BIN
robots.txt:Zone.Identifier Normal file

Binary file not shown.

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>

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More