aggiunto player hls v.2

This commit is contained in:
2026-01-28 23:13:46 +01:00
parent 008df364fe
commit 3c350b17ae
2 changed files with 97 additions and 206 deletions

301
js/app.js
View File

@@ -309,282 +309,173 @@ document.addEventListener('DOMContentLoaded', function () {
*/
function initializePlayer() {
console.log('Inizializzazione player audio...');
console.log('Inizializzazione player audio (v2.0 - Hybrid Strategy)...');
const playPauseBtn = document.getElementById('playPauseBtn');
const playIcon = document.querySelector('.play-icon');
const pauseIcon = document.querySelector('.pause-icon');
const playerStatus = document.getElementById('playerStatus');
const artistElement = document.getElementById('artist');
if (!playPauseBtn) {
console.error('Pulsante play/pause non trovato');
return;
}
if (!playPauseBtn) return;
// Pulisci il player precedente
// 1. Cleanup Preventivo
cleanupPlayer();
// Ottieni il nuovo audio player dal DOM
// 2. Setup Elementi
audioPlayer = document.getElementById('hlsAudioPlayer');
if (!audioPlayer) {
console.error('Elemento audio non trovato');
return;
}
if (!audioPlayer) return;
const streamHLS = playPauseBtn.getAttribute('data-stream-hls');
const streamFallback = playPauseBtn.getAttribute('data-stream-fallback');
const stationName = playPauseBtn.getAttribute('data-station-name');
const stationSlogan = playPauseBtn.getAttribute('data-station-slogan');
const stationLogo = playPauseBtn.getAttribute('data-station-logo');
console.log('Stream HLS:', streamHLS);
console.log('Stream Fallback:', streamFallback);
console.log('Stazione:', stationName);
let isPlaying = false;
let streamType = 'hls'; // 'hls' o 'direct'
// Configurazione HLS.js Ottimizzata per Qualità
const hlsConfig = {
debug: false,
enableWorker: true,
lowLatencyMode: false, // Disabilitato per stabilità e buffer migliore
backBufferLength: 90,
maxBufferLength: 30, // Aumentato buffer (default 30s)
startLevel: -1, // Auto start
xhrSetup: function (xhr, url) {
xhr.withCredentials = false; // Fix CORS issues sometimes
}
};
/**
* Inizializza HLS stream
* Logica di Selezione Player
* Priorità: HLS.js (PC/Android) > Nativo (iOS) > Fallback (Direct Stream)
*/
function initHLSStream() {
if (!window.Hls) {
console.error('HLS.js non caricato');
return false;
}
function initStream() {
// STRATEGIA 1: HLS.js (Preferita per PC e Android)
if (Hls.isSupported()) {
console.log('HLS supportato - uso HLS.js');
hlsInstance = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: false, // Disabilitato LL per migliorare stabilità ABR su flussi standard
backBufferLength: 90
});
console.log('Strategy: HLS.js (High Quality Control)');
hlsInstance = new Hls(hlsConfig);
hlsInstance.loadSource(streamHLS);
hlsInstance.attachMedia(audioPlayer);
hlsInstance.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log('Manifest HLS caricato');
console.log('Livelli trovati:', data.levels.length, data.levels);
console.log(`HLS Manifest Loaded. Levels: ${data.levels.length}`);
// Se ci sono più livelli, prova a partire aggressivo se non specificato
if (data.levels.length > 1) {
console.log('Multi-bitrate disponibile. Auto-switch abilitato.');
// TENTATIVO FIX QUALITY: Forza il livello massimo all'avvio
// Questo bypassa la stima iniziale conservativa della banda
const maxLevel = data.levels.length - 1;
console.log(`Forcing start level to max: ${maxLevel} (${data.levels[maxLevel].height}p / ${data.levels[maxLevel].bitrate})`);
hlsInstance.nextLevel = maxLevel;
// Nota: startLevel funziona solo prima di startLoad, ma nextLevel forza il prossimo frammento
} else {
console.warn('ATTENZIONE: Trovato solo 1 livello di qualità. ABR non possibile.');
// Opzionale: Logica custom startLevel se necessario
// hlsInstance.startLevel = data.levels.length - 1;
}
audioPlayer.play().then(() => {
console.log('Riproduzione HLS avviata');
updatePlayState(true);
}).catch(err => {
console.error('Errore avvio riproduzione HLS:', err);
tryFallbackStream();
});
});
hlsInstance.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
console.log('HLS Quality: Switched to level', data.level);
attemptPlay();
});
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
console.error('Errore HLS:', data.type, data.details);
if (data.fatal) {
console.warn('HLS Fatal Error:', data.type);
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
console.log('Errore di rete, tentativo fallback...');
tryFallbackStream();
hlsInstance.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log('Errore media, tentativo recupero...');
hlsInstance.recoverMediaError();
break;
default:
console.log('Errore fatale, tentativo fallback...');
tryFallbackStream();
fallbackToDirectStream();
break;
}
}
});
return true;
} else if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
console.log('HLS nativo supportato');
// Visual Debugging per Mobile
if (playerStatus) {
playerStatus.textContent = 'Caricamento HLS Nativo...';
}
audioPlayer.src = streamHLS;
audioPlayer.play().then(() => {
console.log('Riproduzione HLS nativa avviata');
updatePlayState(true);
if (playerStatus) playerStatus.textContent += ' (Native)'; // Debug visivo
}).catch(err => {
console.error('Errore HLS nativo:', err);
tryFallbackStream();
});
return true;
return;
}
return false;
// STRATEGIA 2: Nativo (Obbligatorio per iOS)
if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
console.log('Strategy: Native HLS (iOS/Safari)');
audioPlayer.src = streamHLS;
audioPlayer.preload = 'auto'; // Suggerisce al browser di caricare dati
// Hack per iOS: Alcune volte serve un tocco utente, gestito dal click play
attemptPlay();
return;
}
// STRATEGIA 3: Fallback (Stream MP3 diretto)
console.log('Strategy: Direct Fallback');
fallbackToDirectStream();
}
/**
* Usa stream diretto come fallback
*/
function tryFallbackStream() {
console.log('Tentativo stream fallback:', streamFallback);
function fallbackToDirectStream() {
if (hlsInstance) {
hlsInstance.destroy();
hlsInstance = null;
}
streamType = 'direct';
audioPlayer.src = streamFallback;
audioPlayer.load();
audioPlayer.play().then(() => {
console.log('Riproduzione stream diretto avviata');
updatePlayState(true);
if (playerStatus) playerStatus.textContent += ' (Fallback)'; // Debug visivo
}).catch(err => {
console.error('Errore stream diretto:', err);
updatePlayState(false);
if (playerStatus) {
playerStatus.textContent = 'Errore di riproduzione';
}
});
attemptPlay();
}
/**
* Aggiorna stato visuale play/pause
*/
function updatePlayState(playing) {
isPlaying = playing;
if (playing) {
playIcon.style.display = 'none';
pauseIcon.style.display = 'inline-block';
if (playerStatus) {
playerStatus.textContent = 'In riproduzione...';
}
} else {
playIcon.style.display = 'inline-block';
pauseIcon.style.display = 'none';
if (playerStatus) {
playerStatus.textContent = stationSlogan || 'In pausa';
}
}
function attemptPlay() {
// Nota: chiamare .play() senza interazione utente fallirà.
// Questa funzione prepara lo stato. Il vero play avviene al click.
if (playerStatus) playerStatus.textContent = 'Pronto alla riproduzione';
}
/**
* 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);
function togglePlay() {
if (audioPlayer.paused) {
const playPromise = audioPlayer.play();
if (playPromise !== undefined) {
playPromise.then(() => {
updateUI(true);
}).catch(error => {
console.error('Play prevented:', error);
// Se HLS fallisce al play (es. stale manifest), prova fallback
if (!audioPlayer.src.includes(streamFallback) && !hlsInstance) {
fallbackToDirectStream();
audioPlayer.play().catch(e => console.error('Fallback failed:', e));
}
});
}
}
});
/**
* Event listeners audio player
*/
audioPlayer.addEventListener('play', function () {
updatePlayState(true);
});
audioPlayer.addEventListener('pause', function () {
updatePlayState(false);
});
audioPlayer.addEventListener('error', function (e) {
console.error('Errore audio player (Event):', e);
if (audioPlayer.error) {
const err = audioPlayer.error;
console.error('Dettagli MediaError:', {
code: err.code,
message: err.message,
readableCode: getMediaErrorMessage(err.code)
});
// Mostra errore specifico all'utente se possibile
if (playerStatus) {
playerStatus.textContent = 'Errore: ' + getMediaErrorMessage(err.code);
}
} else {
if (playerStatus) {
playerStatus.textContent = 'Errore sconosciuto';
}
}
updatePlayState(false);
});
// Helper per decodificare errori media
function getMediaErrorMessage(code) {
switch (code) {
case 1: return 'MEDIA_ERR_ABORTED (Aborted by user)';
case 2: return 'MEDIA_ERR_NETWORK (Network error)';
case 3: return 'MEDIA_ERR_DECODE (Decoding error)';
case 4: return 'MEDIA_ERR_SRC_NOT_SUPPORTED (Format not supported)';
default: return 'Unknown error code ' + code;
audioPlayer.pause();
updateUI(false);
}
}
audioPlayer.addEventListener('waiting', function () {
function updateUI(playing) {
isPlaying = playing;
playIcon.style.display = playing ? 'none' : 'block';
pauseIcon.style.display = playing ? 'block' : 'none';
if (playerStatus) {
playerStatus.textContent = 'Buffering...';
playerStatus.textContent = playing ? 'In riproduzione' : 'In pausa';
}
});
}
audioPlayer.addEventListener('playing', function () {
if (playerStatus) {
playerStatus.textContent = 'In riproduzione...';
// Listeners
playPauseBtn.onclick = (e) => {
e.preventDefault();
// Se non inizializzato (primo click), avvia stream
if (!audioPlayer.src && !hlsInstance) {
initStream();
// Piccolo timeout per dare tempo al setup prima del play effettivo
setTimeout(togglePlay, 50);
} else {
togglePlay();
}
});
};
audioPlayer.onplay = () => updateUI(true);
audioPlayer.onpause = () => updateUI(false);
audioPlayer.onerror = (e) => {
console.error('Audio Error:', audioPlayer.error);
if (playerStatus) playerStatus.textContent = 'Errore riproduzione';
updateUI(false);
};
// Setup Media Session
setupMediaSession(stationName, stationSlogan, stationLogo, playIcon, pauseIcon);
setupMediaSession(stationName, playPauseBtn.getAttribute('data-station-slogan'), playPauseBtn.getAttribute('data-station-logo'), playIcon, pauseIcon);
console.log('Player audio inizializzato');
console.log('Player System Ready.');
}
/**