zanzara/zanzara.php

1282 lines
40 KiB
PHP
Raw Normal View History

2025-09-13 22:30:33 +02:00
<?php
// Configurazione
$config = [
'urlname' => 'lazanzara.greenpirates.org',
'dirfiles' => 'lazanzara',
'base_dir' => __DIR__,
'file_pattern' => '/^(\d{8})-lazanzara\.mp3$/i',
'cache_file' => __DIR__ . '/cache/valid_dates.json',
'cache_timeout' => 3600, // 1 ora
'show_download_link' => false // Mostra/nascondi link download diretto
];
/**
* Scansiona ricorsivamente le directory per trovare i file mp3
*
* @param string $dir Directory da scansionare
* @param array $config Configurazione
* @return array Array di date valide (formato YYYYMMDD)
*/
function findValidDates($dir, $config) {
$validDates = [];
// Costruisci il percorso completo
$fullPath = $config['base_dir'] . DIRECTORY_SEPARATOR . $dir;
if (!is_dir($fullPath)) {
return $validDates;
}
// Scansiona tutte le sottodirectory (anni)
$years = scandir($fullPath);
foreach ($years as $year) {
// Salta . e .. e verifica che sia una directory anno valida
if ($year === '.' || $year === '..' || !preg_match('/^\d{4}$/', $year)) {
continue;
}
$yearPath = $fullPath . DIRECTORY_SEPARATOR . $year;
if (!is_dir($yearPath)) {
continue;
}
// Scansiona i file nella directory dell'anno
$files = scandir($yearPath);
foreach ($files as $file) {
// Verifica se il file corrisponde al pattern
if (preg_match($config['file_pattern'], $file, $matches)) {
$validDates[] = $matches[1];
}
}
}
// Ordina le date in ordine cronologico
sort($validDates);
return $validDates;
}
/**
* Ottiene le date valide con cache
*/
function getValidDates($config) {
// Verifica se usare la cache
$useCache = false;
if (file_exists($config['cache_file'])) {
$cacheAge = time() - filemtime($config['cache_file']);
if ($cacheAge < $config['cache_timeout']) {
$useCache = true;
}
}
if ($useCache) {
// Usa i dati dalla cache
return json_decode(file_get_contents($config['cache_file']), true);
} else {
// Scansiona le directory
$validDates = findValidDates($config['dirfiles'], $config);
// Salva nella cache (se la directory cache esiste)
$cacheDir = dirname($config['cache_file']);
if (is_dir($cacheDir) || @mkdir($cacheDir, 0755, true)) {
file_put_contents($config['cache_file'], json_encode($validDates));
}
return $validDates;
}
}
// Funzione per validare e formattare la data
function validateDate($date) {
// Verifica se la data è nel formato YYYYMMDD
if (!preg_match('/^\d{8}$/', $date)) {
return false;
}
$year = substr($date, 0, 4);
$month = substr($date, 4, 2);
$day = substr($date, 6, 2);
// Verifica se è una data valida
return checkdate($month, $day, $year) ? $date : false;
}
// Funzione per generare il percorso del file
function generateFilePath($date, $config) {
$year = substr($date, 0, 4);
return [
'local_path' => sprintf("%s/%s/%s/%s-lazanzara.mp3",
$config['base_dir'],
$config['dirfiles'],
$year,
$date
),
'url' => sprintf("https://%s/%s/%s/%s-lazanzara.mp3",
$config['urlname'],
$config['dirfiles'],
$year,
$date
),
'filename' => sprintf("%s-lazanzara.mp3", $date)
];
}
// Gestione richiesta AJAX per date valide
if (isset($_GET['action']) && $_GET['action'] === 'get_valid_dates') {
header('Content-Type: application/json');
header('Cache-Control: max-age=3600');
echo json_encode(getValidDates($config));
exit;
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
<meta name="description" content="LaZanzara - Repository non ufficiale dei podcast">
<meta name="theme-color" content="#000080">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>Crux mea lux - LaZanzara Repository</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Dosis:wght@400;600;700&display=swap" rel="stylesheet">
<!-- jQuery UI CSS -->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.3/themes/base/jquery-ui.css">
<!-- Stili -->
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #ffffff;
font-family: 'Dosis', sans-serif;
color: #000080;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
/* Disabilita selezione testo per sicurezza aggiuntiva */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Permetti selezione solo negli input */
input[type="text"] {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
main {
flex: 1;
}
h1 {
color: #000080;
margin: 20px 0;
font-weight: 700;
}
h2 {
color: #4169E1;
margin: 15px 0;
font-weight: 600;
}
a {
color: #006400;
text-decoration: none;
transition: color 0.3s ease;
}
a:hover {
color: #ff0000;
text-decoration: underline;
}
button {
background-color: #4169E1;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: 14px;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #1E90FF;
}
button[type="button"] {
background-color: #6c757d;
}
button[type="button"]:hover {
background-color: #5a6268;
}
.random-button {
background-color: #ff6b6b !important;
font-weight: bold;
padding: 8px 20px !important;
}
.random-button:hover {
background-color: #ff5252 !important;
transform: scale(1.05);
}
.random-info {
background-color: #fff3cd;
color: #856404;
padding: 10px;
border-radius: 5px;
margin-bottom: 15px;
font-weight: bold;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0% { opacity: 0.8; }
50% { opacity: 1; }
100% { opacity: 0.8; }
}
input[type="text"] {
border: 1px solid #ccc;
border-radius: 4px;
padding: 6px 10px;
font-family: inherit;
font-size: 14px;
}
input[type="text"]:focus {
outline: none;
border-color: #4169E1;
box-shadow: 0 0 0 2px rgba(65, 105, 225, 0.1);
}
.footer {
text-align: center;
padding: 20px;
color: #666;
font-size: 14px;
border-top: 1px solid #eee;
margin-top: 40px;
}
/* Stili specifici dell'applicazione */
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
header {
margin-bottom: 30px;
}
.form-group {
margin: 20px 0;
}
.form-group label {
margin-right: 10px;
}
.form-group input {
margin-right: 10px;
padding: 5px;
}
.form-group button {
padding: 5px 15px;
margin: 0 5px;
cursor: pointer;
}
.result {
margin-top: 30px;
padding: 20px;
border-radius: 5px;
background-color: #f5f5f5;
}
.success {
color: navy;
font-weight: bold;
}
.error {
color: red;
font-weight: bold;
}
.download {
margin-top: 20px;
}
/* PLAYER AUDIO CUSTOM SEMPLICE */
/* Nascondi il player nativo */
audio {
display: none;
}
/* Container del player custom */
.custom-audio-player {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 10px;
padding: 25px;
margin: 20px auto;
max-width: 700px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Layout principale centrato */
.player-layout {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
/* Pulsante Play/Pause */
.play-pause-btn {
width: 70px;
height: 70px;
border-radius: 50%;
background-color: #4169E1;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
flex-shrink: 0;
}
.play-pause-btn:hover {
background-color: #1E90FF;
transform: scale(1.05);
}
.play-pause-btn:active {
transform: scale(0.95);
}
.play-pause-btn svg {
width: 30px;
height: 30px;
fill: white;
pointer-events: none;
}
/* Container per barra del progresso e tempi */
.progress-container {
width: 100%;
display: flex;
flex-direction: column;
gap: 10px;
}
/* Barra del progresso più alta */
.progress-bar {
width: 100%;
height: 40px; /* Altezza totale con padding */
cursor: pointer;
position: relative;
overflow: visible;
/* Padding per area cliccabile più grande */
padding: 10px 0;
/* Rimuovo il background dal contenitore principale */
}
/* Sfondo grigio della barra */
.progress-bar::before {
content: '';
position: absolute;
top: 10px;
left: 0;
right: 0;
height: 20px;
background-color: #e9ecef;
border-radius: 10px;
}
.progress-bar:hover .progress-fill {
background-color: #1E90FF;
}
.progress-bar:hover .progress-fill::after {
transform: translateY(-50%) scale(1.2);
}
.progress-fill {
height: 20px;
background-color: #4169E1;
border-radius: 10px;
width: 0%;
transition: background-color 0.3s ease;
position: absolute;
top: 10px; /* Allineato perfettamente con ::before */
left: 0;
pointer-events: none;
}
/* Indicatore sulla barra più grande */
.progress-fill::after {
content: '';
position: absolute;
right: -12px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 24px;
background-color: white;
border: 3px solid #4169E1;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
transition: transform 0.2s ease;
pointer-events: none;
}
/* Stato durante il dragging */
.dragging .progress-fill::after {
transform: translateY(-50%) scale(1.3);
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
}
/* Container dei tempi */
.time-info {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
/* Display del tempo */
.time-display {
font-size: 18px;
font-weight: 600;
color: #333;
font-variant-numeric: tabular-nums;
}
/* Stato buffering */
.buffering .play-pause-btn {
animation: pulse 1.5s ease-in-out infinite;
}
/* Icone play/pause */
.play-icon {
display: block;
}
.pause-icon {
display: none;
}
.playing .play-icon {
display: none;
}
.playing .pause-icon {
display: block;
}
/* Stili per la navigazione */
.navigation {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20px;
padding: 15px;
background-color: #e9ecef;
border-radius: 5px;
}
.nav-button {
background-color: #28a745;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
min-width: 120px;
}
.nav-button:hover:not(:disabled) {
background-color: #218838;
transform: translateY(-2px);
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.nav-button:disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
.current-date {
font-weight: bold;
color: #333;
padding: 0 20px;
}
/* Stili per il calendario */
.ui-datepicker td.available a {
background-color: #90EE90 !important;
color: #000 !important;
}
.ui-datepicker td.unavailable span {
background-color: #FFB6C1 !important;
color: #999 !important;
}
/* Stili per le statistiche */
.stats {
margin-top: 40px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 5px;
text-align: left;
max-width: 400px;
margin-left: auto;
margin-right: auto;
}
.stats h3 {
margin-bottom: 15px;
color: #333;
}
.stats p {
margin: 5px 0;
}
.stats details {
margin-top: 15px;
}
.stats summary {
cursor: pointer;
font-weight: bold;
color: #4169E1;
}
.stats ul {
margin-top: 10px;
list-style: none;
padding-left: 20px;
}
.stats li {
margin: 3px 0;
}
#loading {
margin: 20px 0;
color: #666;
}
/* Tooltip per scorciatoie */
.shortcuts-info {
position: fixed;
bottom: 20px;
right: 20px;
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 15px;
border-radius: 5px;
font-size: 12px;
opacity: 0.7;
transition: opacity 0.3s;
}
.shortcuts-info:hover {
opacity: 1;
}
/* Media queries per dispositivi mobili */
@media (max-width: 768px) {
body {
font-size: 16px;
}
.container {
padding: 10px;
}
h1 {
font-size: 1.5rem;
margin: 15px 0;
}
h2 {
font-size: 1.2rem;
margin: 10px 0;
}
.form-group {
display: flex;
flex-direction: column;
gap: 10px;
align-items: stretch;
}
.form-group label {
margin: 0 10px;
}
.form-group input {
width: 100%;
margin-right: 0;
padding: 12px;
font-size: 16px;
text-align: center;
}
.form-group button {
width: 100%;
padding: 12px;
font-size: 16px;
margin: 0;
}
.random-button {
order: 0;
}
.form-group label {
order: 1;
margin-bottom: 5px;
}
.form-group input {
order: 2;
}
.form-group button[type="submit"]:not(.random-button) {
order: 3;
}
.form-group button[type="button"] {
order: 4;
}
.navigation {
flex-direction: column;
gap: 10px;
padding: 10px;
}
.nav-button {
width: 100%;
padding: 12px;
font-size: 14px;
}
.current-date {
margin: 10px 0;
padding: 10px;
background-color: #fff;
border-radius: 5px;
}
.stats {
margin-top: 20px;
padding: 15px;
font-size: 14px;
min-height: 140px;
}
.stats h3 {
font-size: 1.1rem;
}
.stats p {
min-height: 22px;
line-height: 22px;
}
/* Player custom su mobile */
.custom-audio-player {
padding: 20px;
margin: 15px 0;
}
.player-layout {
gap: 15px;
}
.play-pause-btn {
width: 60px;
height: 60px;
}
.play-pause-btn svg {
width: 26px;
height: 26px;
}
.progress-bar {
height: 36px; /* Ridotto per mobile */
padding: 8px 0;
}
.progress-bar::before {
top: 8px;
height: 18px;
}
.progress-fill {
height: 18px;
top: 8px;
}
.progress-fill::after {
width: 20px;
height: 20px;
right: -10px;
}
.time-display {
font-size: 16px;
}
.result {
padding: 15px;
margin-top: 20px;
min-height: 220px;
}
.shortcuts-info {
display: none;
}
.footer {
padding: 15px;
font-size: 12px;
}
input[type="text"],
select,
textarea {
font-size: 16px !important;
}
}
/* Media query per schermi molto piccoli */
@media (max-width: 380px) {
h1 {
font-size: 1.3rem;
}
h2 {
font-size: 1.1rem;
}
.form-group button {
padding: 10px;
font-size: 14px;
}
.nav-button {
font-size: 12px;
padding: 10px;
}
.play-pause-btn {
width: 45px;
height: 45px;
}
.play-pause-btn svg {
width: 20px;
height: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>- - - Crux mea Lux - - -</h1>
<h2>Unofficial LaZanzara Repository</h2>
</header>
<main>
<?php
// Gestione puntata random
if (isset($_POST['random']) && $_POST['random'] == '1') {
$validDates = getValidDates($config);
if (count($validDates) > 0) {
$randomIndex = array_rand($validDates);
$_POST['datepicker'] = $validDates[$randomIndex];
}
}
// Determina il valore da mostrare nell'input
$inputValue = '';
if (isset($_POST['datepicker']) && !empty($_POST['datepicker'])) {
$inputValue = $_POST['datepicker'];
}
?>
<div class="form-container">
<form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="POST" id="dateForm">
<div class="form-group">
<button type="submit" name="random" value="1" class="random-button" title="Riproduci una puntata casuale">
🎲 Random Play
</button>
<label for="datepicker">Data selezionata:</label>
<input type="text"
size="10"
maxlength="8"
id="datepicker"
name="datepicker"
placeholder="YYYYMMDD"
value="<?php echo htmlspecialchars($inputValue); ?>"
autocomplete="off"
inputmode="numeric">
<button type="submit">Procedi</button>
<button type="button" onclick="window.location.href=window.location.href">Reset</button>
</div>
</form>
<div id="loading" style="display: none;">
<p>Caricamento date disponibili...</p>
</div>
</div>
<?php
// Gestione della richiesta
if (isset($_POST['datepicker']) && !empty($_POST['datepicker'])) {
$selectedDate = validateDate($_POST['datepicker']);
if ($selectedDate) {
$paths = generateFilePath($selectedDate, $config);
echo '<div class="result">';
if (file_exists($paths['local_path'])) {
// File trovato
$year = substr($selectedDate, 0, 4);
$month = substr($selectedDate, 4, 2);
$day = substr($selectedDate, 6, 2);
$formattedDate = sprintf("%s/%s/%s", $day, $month, $year);
// Ottieni dimensione file
$fileSize = filesize($paths['local_path']);
$fileSizeMB = round($fileSize / 1024 / 1024, 1);
// Trova puntata precedente e successiva
$validDates = getValidDates($config);
$currentIndex = array_search($selectedDate, $validDates);
$prevDate = ($currentIndex > 0) ? $validDates[$currentIndex - 1] : null;
$nextDate = ($currentIndex < count($validDates) - 1) ? $validDates[$currentIndex + 1] : null;
// Mostra se è una puntata random
if (isset($_POST['random']) && $_POST['random'] == '1') {
echo '<p class="random-info">🎲 Puntata casuale in riproduzione!</p>';
}
// Audio player custom
echo '<div class="custom-audio-player" id="audioPlayer">';
echo '<audio id="audio" preload="metadata">
<source src="' . htmlspecialchars($paths['url']) . '" type="audio/mp3">
</audio>';
// Layout principale
echo '<div class="player-layout">';
// Pulsante Play/Pause
echo '<button class="play-pause-btn" id="playPauseBtn" title="Play/Pause">
<svg class="play-icon" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z"/>
</svg>
<svg class="pause-icon" viewBox="0 0 24 24">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
</svg>
</button>';
// Container progresso
echo '<div class="progress-container">';
echo '<div class="progress-bar" id="progressBar">
<div class="progress-fill" id="progressFill"></div>
</div>';
echo '<div class="time-info">';
echo '<span class="time-display" id="currentTime">0:00</span>';
echo '<span class="time-display" id="duration">0:00</span>';
echo '</div>';
echo '</div>';
echo '</div>'; // Chiude player-layout
echo '</div>'; // Chiude custom-audio-player
// JavaScript per il player
echo '<script>
(function() {
const audio = document.getElementById("audio");
const playPauseBtn = document.getElementById("playPauseBtn");
const progressBar = document.getElementById("progressBar");
const progressFill = document.getElementById("progressFill");
const currentTimeDisplay = document.getElementById("currentTime");
const durationDisplay = document.getElementById("duration");
const audioPlayer = document.getElementById("audioPlayer");
let isPlaying = false;
let isDragging = false;
// Formatta il tempo
function formatTime(seconds) {
if (isNaN(seconds)) return "0:00";
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return mins + ":" + (secs < 10 ? "0" : "") + secs;
}
// Funzione per aggiornare la posizione
function updateProgress(e) {
const rect = progressBar.getBoundingClientRect();
let x;
// Supporto per touch e mouse
if (e.touches) {
x = e.touches[0].clientX - rect.left;
} else {
x = e.clientX - rect.left;
}
const clickPercent = Math.max(0, Math.min(1, x / rect.width));
if (audio.duration) {
audio.currentTime = clickPercent * audio.duration;
progressFill.style.width = (clickPercent * 100) + "%";
currentTimeDisplay.textContent = formatTime(audio.currentTime);
}
}
// Play/Pause
playPauseBtn.addEventListener("click", function() {
if (isPlaying) {
audio.pause();
} else {
audio.play();
}
});
audio.addEventListener("play", function() {
isPlaying = true;
audioPlayer.classList.add("playing");
});
audio.addEventListener("pause", function() {
isPlaying = false;
audioPlayer.classList.remove("playing");
});
// Aggiorna durata quando disponibile
audio.addEventListener("loadedmetadata", function() {
durationDisplay.textContent = formatTime(audio.duration);
});
// Aggiorna progresso durante la riproduzione
audio.addEventListener("timeupdate", function() {
if (!isDragging && audio.duration) {
const progress = (audio.currentTime / audio.duration) * 100;
progressFill.style.width = progress + "%";
currentTimeDisplay.textContent = formatTime(audio.currentTime);
}
});
// Eventi Mouse
progressBar.addEventListener("mousedown", function(e) {
isDragging = true;
audioPlayer.classList.add("dragging");
updateProgress(e);
e.preventDefault();
});
document.addEventListener("mousemove", function(e) {
if (isDragging) {
updateProgress(e);
}
});
document.addEventListener("mouseup", function() {
if (isDragging) {
isDragging = false;
audioPlayer.classList.remove("dragging");
}
});
// Eventi Touch
progressBar.addEventListener("touchstart", function(e) {
isDragging = true;
audioPlayer.classList.add("dragging");
updateProgress(e);
e.preventDefault();
});
document.addEventListener("touchmove", function(e) {
if (isDragging) {
updateProgress(e);
e.preventDefault();
}
});
document.addEventListener("touchend", function() {
if (isDragging) {
isDragging = false;
audioPlayer.classList.remove("dragging");
}
});
// Click semplice sulla barra
progressBar.addEventListener("click", function(e) {
if (!isDragging) {
updateProgress(e);
}
});
// Auto-play
audio.play().catch(function(error) {
console.log("Autoplay bloccato:", error);
});
// Gestione buffering
audio.addEventListener("waiting", function() {
audioPlayer.classList.add("buffering");
});
audio.addEventListener("canplay", function() {
audioPlayer.classList.remove("buffering");
});
// Fine del file
audio.addEventListener("ended", function() {
isPlaying = false;
audioPlayer.classList.remove("playing");
progressFill.style.width = "100%";
});
})();
</script>';
// Navigazione dopo il player
echo '<div class="navigation">';
if ($prevDate) {
echo '<form method="POST" style="display: inline;">
<input type="hidden" name="datepicker" value="' . $prevDate . '">
<button type="submit" class="nav-button" title="Puntata precedente">
' . substr($prevDate, 6, 2) . '/' . substr($prevDate, 4, 2) . '/' . substr($prevDate, 0, 4) . '
</button>
</form>';
} else {
echo '<button class="nav-button" disabled>← Prima puntata</button>';
}
echo '<span class="current-date">Data: ' . htmlspecialchars($formattedDate) . ' - ' . $fileSizeMB . ' MB</span>';
if ($nextDate) {
echo '<form method="POST" style="display: inline;">
<input type="hidden" name="datepicker" value="' . $nextDate . '">
<button type="submit" class="nav-button" title="Puntata successiva">
' . substr($nextDate, 6, 2) . '/' . substr($nextDate, 4, 2) . '/' . substr($nextDate, 0, 4) . '
</button>
</form>';
} else {
echo '<button class="nav-button" disabled>Ultima puntata →</button>';
}
echo '</div>';
// Mostra link download solo se configurato
if ($config['show_download_link']) {
echo '<p class="download">
Oppure scarica direttamente:
<a href="' . htmlspecialchars($paths['url']) . '" download>' .
htmlspecialchars($paths['filename']) . '</a>
</p>';
}
} else {
// File non trovato
echo '<p class="error">File non trovato per la data: ' .
htmlspecialchars($selectedDate) . '</p>';
}
echo '</div>';
} else {
echo '<div class="result">
<p class="error">Data non valida. Usa il formato YYYYMMDD</p>
</div>';
}
}
// Mostra statistiche
$validDates = getValidDates($config);
$totalFiles = count($validDates);
if ($totalFiles > 0) {
$firstDate = $validDates[0];
$lastDate = $validDates[$totalFiles - 1];
echo '<div class="stats">';
echo '<h3>Statistiche archivio</h3>';
echo '<p>File disponibili: <strong>' . $totalFiles . '</strong></p>';
echo '<p>Prima puntata: <strong>' . substr($firstDate, 6, 2) . '/' . substr($firstDate, 4, 2) . '/' . substr($firstDate, 0, 4) . '</strong></p>';
echo '<p>Ultima puntata: <strong>' . substr($lastDate, 6, 2) . '/' . substr($lastDate, 4, 2) . '/' . substr($lastDate, 0, 4) . '</strong></p>';
// Conta file per anno
$yearCount = [];
foreach ($validDates as $date) {
$year = substr($date, 0, 4);
if (!isset($yearCount[$year])) {
$yearCount[$year] = 0;
}
$yearCount[$year]++;
}
echo '<details>';
echo '<summary>File per anno</summary>';
echo '<ul>';
foreach ($yearCount as $year => $count) {
echo '<li>' . $year . ': ' . $count . ' puntate</li>';
}
echo '</ul>';
echo '</details>';
echo '</div>';
}
?>
</main>
</div>
<!-- Indicatore scorciatoie da tastiera -->
<div class="shortcuts-info">
<strong>Scorciatoie:</strong> per navigare | R per random | Spazio per play/pause
</div>
<!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>
<script>
$(document).ready(function() {
// Disabilita il tasto destro del mouse
$(document).on('contextmenu', function(e) {
e.preventDefault();
return false;
});
// Disabilita il tasto destro specificamente sull'audio player
$(document).on('contextmenu', 'audio', function(e) {
e.preventDefault();
return false;
});
// Array per memorizzare le date valide
var validDates = [];
// Funzione per verificare se una data è disponibile
function isDateAvailable(date) {
var year = date.getFullYear();
var month = (date.getMonth() + 1).toString().padStart(2, '0');
var day = date.getDate().toString().padStart(2, '0');
var dateString = year + month + day;
return validDates.includes(dateString);
}
// Mostra indicatore di caricamento
$('#loading').show();
// Carica le date valide via AJAX
$.getJSON('<?php echo $_SERVER['PHP_SELF']; ?>?action=get_valid_dates', function(data) {
validDates = data;
$('#loading').hide();
// Inizializza il datepicker dopo aver caricato le date
$('#datepicker').datepicker({
dateFormat: 'yymmdd',
changeMonth: true,
changeYear: true,
yearRange: '2008:2025',
maxDate: '0',
beforeShowDay: function(date) {
var isAvailable = isDateAvailable(date);
return [isAvailable, isAvailable ? 'available' : 'unavailable',
isAvailable ? 'Disponibile' : 'Non disponibile'];
},
onSelect: function() {
$('#dateForm').submit();
}
});
// Aggiungi scorciatoie da tastiera per navigazione
$(document).keydown(function(e) {
if ($('input:focus').length === 0) { // Solo se non si sta digitando in un input
if (e.key === 'ArrowLeft' && $('.nav-button:first:not(:disabled)').length) {
$('.nav-button:first').closest('form').submit();
} else if (e.key === 'ArrowRight' && $('.nav-button:last:not(:disabled)').length) {
$('.nav-button:last').closest('form').submit();
} else if (e.key.toLowerCase() === 'r') {
// Scorciatoia per puntata random
$('.random-button').click();
} else if (e.key === ' ' && $('#playPauseBtn').length) {
// Spazio per play/pause
e.preventDefault();
$('#playPauseBtn').click();
}
}
});
}).fail(function() {
$('#loading').hide();
console.error('Errore nel caricamento delle date valide');
// Fallback: datepicker senza validazione
$('#datepicker').datepicker({
dateFormat: 'yymmdd',
changeMonth: true,
changeYear: true,
yearRange: '2008:2025',
maxDate: '0'
});
});
});
</script>
<footer class="footer">
<p style="margin-bottom: 10px;">
<a href="https://www.radio24.ilsole24ore.com/programmi/lazanzara"
target="_blank"
rel="noopener noreferrer"
style="color: #006400;">
🔗 Pagina ufficiale LaZanzara
</a>
</p>
<p>- - - powered by Gabry, Claude and me - v1.0.0 (01/08/2025) - - -</p>
</footer>
</body>
</html>