aggiunto player hls v.2
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<changelog>
|
<changelog>
|
||||||
|
|
||||||
<version>
|
<version>
|
||||||
<number>2.1.2 (c)</number>
|
<number>2.1.2 (d)</number>
|
||||||
<logs>
|
<logs>
|
||||||
<log>Correzione e bugfix di problematiche varie.</log>
|
<log>Correzione e bugfix di problematiche varie.</log>
|
||||||
</logs>
|
</logs>
|
||||||
|
|||||||
303
js/app.js
303
js/app.js
@@ -309,282 +309,173 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function initializePlayer() {
|
function initializePlayer() {
|
||||||
console.log('Inizializzazione player audio...');
|
console.log('Inizializzazione player audio (v2.0 - Hybrid Strategy)...');
|
||||||
|
|
||||||
const playPauseBtn = document.getElementById('playPauseBtn');
|
const playPauseBtn = document.getElementById('playPauseBtn');
|
||||||
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 playerStatus = document.getElementById('playerStatus');
|
const playerStatus = document.getElementById('playerStatus');
|
||||||
const artistElement = document.getElementById('artist');
|
|
||||||
|
|
||||||
if (!playPauseBtn) {
|
if (!playPauseBtn) return;
|
||||||
console.error('Pulsante play/pause non trovato');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pulisci il player precedente
|
// 1. Cleanup Preventivo
|
||||||
cleanupPlayer();
|
cleanupPlayer();
|
||||||
|
|
||||||
// Ottieni il nuovo audio player dal DOM
|
// 2. Setup Elementi
|
||||||
audioPlayer = document.getElementById('hlsAudioPlayer');
|
audioPlayer = document.getElementById('hlsAudioPlayer');
|
||||||
|
if (!audioPlayer) return;
|
||||||
if (!audioPlayer) {
|
|
||||||
console.error('Elemento audio non trovato');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const streamHLS = playPauseBtn.getAttribute('data-stream-hls');
|
const streamHLS = playPauseBtn.getAttribute('data-stream-hls');
|
||||||
const streamFallback = playPauseBtn.getAttribute('data-stream-fallback');
|
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 stationLogo = playPauseBtn.getAttribute('data-station-logo');
|
|
||||||
|
|
||||||
console.log('Stream HLS:', streamHLS);
|
|
||||||
console.log('Stream Fallback:', streamFallback);
|
|
||||||
console.log('Stazione:', stationName);
|
|
||||||
|
|
||||||
let isPlaying = false;
|
let isPlaying = false;
|
||||||
let streamType = 'hls'; // 'hls' o 'direct'
|
|
||||||
|
|
||||||
/**
|
// Configurazione HLS.js Ottimizzata per Qualità
|
||||||
* Inizializza HLS stream
|
const hlsConfig = {
|
||||||
*/
|
|
||||||
function initHLSStream() {
|
|
||||||
if (!window.Hls) {
|
|
||||||
console.error('HLS.js non caricato');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Hls.isSupported()) {
|
|
||||||
console.log('HLS supportato - uso HLS.js');
|
|
||||||
|
|
||||||
hlsInstance = new Hls({
|
|
||||||
debug: false,
|
debug: false,
|
||||||
enableWorker: true,
|
enableWorker: true,
|
||||||
lowLatencyMode: false, // Disabilitato LL per migliorare stabilità ABR su flussi standard
|
lowLatencyMode: false, // Disabilitato per stabilità e buffer migliore
|
||||||
backBufferLength: 90
|
backBufferLength: 90,
|
||||||
});
|
maxBufferLength: 30, // Aumentato buffer (default 30s)
|
||||||
|
startLevel: -1, // Auto start
|
||||||
|
xhrSetup: function (xhr, url) {
|
||||||
|
xhr.withCredentials = false; // Fix CORS issues sometimes
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logica di Selezione Player
|
||||||
|
* Priorità: HLS.js (PC/Android) > Nativo (iOS) > Fallback (Direct Stream)
|
||||||
|
*/
|
||||||
|
function initStream() {
|
||||||
|
// STRATEGIA 1: HLS.js (Preferita per PC e Android)
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
console.log('Strategy: HLS.js (High Quality Control)');
|
||||||
|
|
||||||
|
hlsInstance = new Hls(hlsConfig);
|
||||||
hlsInstance.loadSource(streamHLS);
|
hlsInstance.loadSource(streamHLS);
|
||||||
hlsInstance.attachMedia(audioPlayer);
|
hlsInstance.attachMedia(audioPlayer);
|
||||||
|
|
||||||
hlsInstance.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
hlsInstance.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
||||||
console.log('Manifest HLS caricato');
|
console.log(`HLS Manifest Loaded. Levels: ${data.levels.length}`);
|
||||||
console.log('Livelli trovati:', data.levels.length, data.levels);
|
|
||||||
|
|
||||||
|
// Se ci sono più livelli, prova a partire aggressivo se non specificato
|
||||||
if (data.levels.length > 1) {
|
if (data.levels.length > 1) {
|
||||||
console.log('Multi-bitrate disponibile. Auto-switch abilitato.');
|
// Opzionale: Logica custom startLevel se necessario
|
||||||
|
// hlsInstance.startLevel = data.levels.length - 1;
|
||||||
// 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.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPlayer.play().then(() => {
|
attemptPlay();
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
hlsInstance.on(Hls.Events.ERROR, function (event, data) {
|
||||||
console.error('Errore HLS:', data.type, data.details);
|
|
||||||
if (data.fatal) {
|
if (data.fatal) {
|
||||||
|
console.warn('HLS Fatal Error:', data.type);
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||||
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, tentativo recupero...');
|
|
||||||
hlsInstance.recoverMediaError();
|
hlsInstance.recoverMediaError();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Errore fatale, tentativo fallback...');
|
fallbackToDirectStream();
|
||||||
tryFallbackStream();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return;
|
||||||
} else if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
|
|
||||||
console.log('HLS nativo supportato');
|
|
||||||
|
|
||||||
// Visual Debugging per Mobile
|
|
||||||
if (playerStatus) {
|
|
||||||
playerStatus.textContent = 'Caricamento HLS Nativo...';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STRATEGIA 2: Nativo (Obbligatorio per iOS)
|
||||||
|
if (audioPlayer.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
|
console.log('Strategy: Native HLS (iOS/Safari)');
|
||||||
audioPlayer.src = streamHLS;
|
audioPlayer.src = streamHLS;
|
||||||
audioPlayer.play().then(() => {
|
audioPlayer.preload = 'auto'; // Suggerisce al browser di caricare dati
|
||||||
console.log('Riproduzione HLS nativa avviata');
|
|
||||||
updatePlayState(true);
|
// Hack per iOS: Alcune volte serve un tocco utente, gestito dal click play
|
||||||
if (playerStatus) playerStatus.textContent += ' (Native)'; // Debug visivo
|
attemptPlay();
|
||||||
}).catch(err => {
|
return;
|
||||||
console.error('Errore HLS nativo:', err);
|
|
||||||
tryFallbackStream();
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// STRATEGIA 3: Fallback (Stream MP3 diretto)
|
||||||
|
console.log('Strategy: Direct Fallback');
|
||||||
|
fallbackToDirectStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function fallbackToDirectStream() {
|
||||||
* Usa stream diretto come fallback
|
|
||||||
*/
|
|
||||||
function tryFallbackStream() {
|
|
||||||
console.log('Tentativo stream fallback:', streamFallback);
|
|
||||||
|
|
||||||
if (hlsInstance) {
|
if (hlsInstance) {
|
||||||
hlsInstance.destroy();
|
hlsInstance.destroy();
|
||||||
hlsInstance = null;
|
hlsInstance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
streamType = 'direct';
|
|
||||||
audioPlayer.src = streamFallback;
|
audioPlayer.src = streamFallback;
|
||||||
audioPlayer.load();
|
attemptPlay();
|
||||||
|
}
|
||||||
|
|
||||||
audioPlayer.play().then(() => {
|
function attemptPlay() {
|
||||||
console.log('Riproduzione stream diretto avviata');
|
// Nota: chiamare .play() senza interazione utente fallirà.
|
||||||
updatePlayState(true);
|
// Questa funzione prepara lo stato. Il vero play avviene al click.
|
||||||
if (playerStatus) playerStatus.textContent += ' (Fallback)'; // Debug visivo
|
if (playerStatus) playerStatus.textContent = 'Pronto alla riproduzione';
|
||||||
}).catch(err => {
|
}
|
||||||
console.error('Errore stream diretto:', err);
|
|
||||||
updatePlayState(false);
|
function togglePlay() {
|
||||||
if (playerStatus) {
|
if (audioPlayer.paused) {
|
||||||
playerStatus.textContent = 'Errore di riproduzione';
|
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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
||||||
playIcon.style.display = 'inline-block';
|
|
||||||
pauseIcon.style.display = 'none';
|
|
||||||
if (playerStatus) {
|
|
||||||
playerStatus.textContent = stationSlogan || 'In pausa';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gestione click play/pause
|
|
||||||
*/
|
|
||||||
playPauseBtn.addEventListener('click', function () {
|
|
||||||
if (!audioPlayer) return;
|
|
||||||
|
|
||||||
if (isPlaying) {
|
|
||||||
// Pausa
|
|
||||||
audioPlayer.pause();
|
audioPlayer.pause();
|
||||||
updatePlayState(false);
|
updateUI(false);
|
||||||
console.log('Riproduzione in pausa');
|
}
|
||||||
} else {
|
}
|
||||||
// Play
|
|
||||||
|
function updateUI(playing) {
|
||||||
|
isPlaying = playing;
|
||||||
|
playIcon.style.display = playing ? 'none' : 'block';
|
||||||
|
pauseIcon.style.display = playing ? 'block' : 'none';
|
||||||
|
if (playerStatus) {
|
||||||
|
playerStatus.textContent = playing ? 'In riproduzione' : 'In pausa';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listeners
|
||||||
|
playPauseBtn.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Se non inizializzato (primo click), avvia stream
|
||||||
if (!audioPlayer.src && !hlsInstance) {
|
if (!audioPlayer.src && !hlsInstance) {
|
||||||
// Prima riproduzione - inizializza lo stream
|
initStream();
|
||||||
if (streamHLS && !initHLSStream()) {
|
// Piccolo timeout per dare tempo al setup prima del play effettivo
|
||||||
// Se HLS fallisce, usa direttamente il fallback
|
setTimeout(togglePlay, 50);
|
||||||
tryFallbackStream();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Riprendi riproduzione
|
togglePlay();
|
||||||
audioPlayer.play().then(() => {
|
|
||||||
updatePlayState(true);
|
|
||||||
console.log('Riproduzione ripresa');
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Errore ripresa riproduzione:', err);
|
|
||||||
updatePlayState(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
audioPlayer.onplay = () => updateUI(true);
|
||||||
* Event listeners audio player
|
audioPlayer.onpause = () => updateUI(false);
|
||||||
*/
|
|
||||||
audioPlayer.addEventListener('play', function () {
|
|
||||||
updatePlayState(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
audioPlayer.addEventListener('pause', function () {
|
audioPlayer.onerror = (e) => {
|
||||||
updatePlayState(false);
|
console.error('Audio Error:', audioPlayer.error);
|
||||||
});
|
if (playerStatus) playerStatus.textContent = 'Errore riproduzione';
|
||||||
|
updateUI(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.addEventListener('waiting', function () {
|
|
||||||
if (playerStatus) {
|
|
||||||
playerStatus.textContent = 'Buffering...';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
audioPlayer.addEventListener('playing', function () {
|
|
||||||
if (playerStatus) {
|
|
||||||
playerStatus.textContent = 'In riproduzione...';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup Media Session
|
// 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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user