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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.date-button {
|
|
|
|
|
background-color: #4169E1 !important;
|
2025-09-13 22:30:33 +02:00
|
|
|
font-weight: bold;
|
2025-09-14 20:40:42 +02:00
|
|
|
padding: 8px 20px !important;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.date-button:hover {
|
|
|
|
|
background-color: #1E90FF !important;
|
|
|
|
|
transform: scale(1.05);
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.episode-info {
|
|
|
|
|
background-color: #e8f4f8;
|
|
|
|
|
color: #2c5282;
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-align: center;
|
|
|
|
|
border: 1px solid #bee3f8;
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 700px;
|
|
|
|
|
min-width: 400px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
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 {
|
2025-09-14 20:40:42 +02:00
|
|
|
width: 100%;
|
2025-09-13 22:30:33 +02:00
|
|
|
max-width: 800px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header {
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-group {
|
|
|
|
|
margin: 20px 0;
|
2025-09-14 20:40:42 +02:00
|
|
|
min-width: 600px;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.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;
|
2025-09-14 20:40:42 +02:00
|
|
|
max-width: 800px;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
margin-right: auto;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.success {
|
|
|
|
|
color: navy;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.error {
|
|
|
|
|
color: red;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.download {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* PLAYER AUDIO CUSTOM SEMPLICE */
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
/* 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;
|
2025-09-14 20:40:42 +02:00
|
|
|
width: 100%;
|
2025-09-13 22:30:33 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
/* Controlli principali con navigazione */
|
|
|
|
|
.main-controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pulsanti di navigazione */
|
|
|
|
|
.nav-btn {
|
|
|
|
|
width: 50px;
|
|
|
|
|
height: 50px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background-color: #6c757d;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn:hover:not(:disabled) {
|
|
|
|
|
background-color: #5a6268;
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn:disabled {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn svg {
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
fill: white;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
/* Container dei tempi con data */
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
/* Pulsante Share inline */
|
|
|
|
|
.share-btn-inline {
|
|
|
|
|
background-color: #6c757d;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
margin: 0 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-btn-inline:hover {
|
|
|
|
|
background-color: #5a6268;
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-btn-inline:active {
|
|
|
|
|
transform: scale(0.95);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-btn-inline svg {
|
|
|
|
|
fill: currentColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Messaggio di conferma */
|
|
|
|
|
.share-success {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
background-color: #333;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 15px 30px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 0.3s ease;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.share-success.show {
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
/* 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 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;
|
2025-09-14 20:40:42 +02:00
|
|
|
min-width: auto; /* Rimuovo min-width su mobile */
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.form-group button {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.random-button {
|
|
|
|
|
order: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.date-button {
|
2025-09-13 22:30:33 +02:00
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.form-group button[type="button"]:not(.date-button) {
|
2025-09-13 22:30:33 +02:00
|
|
|
order: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.episode-info {
|
|
|
|
|
padding: 10px 15px;
|
2025-09-13 22:30:33 +02:00
|
|
|
font-size: 14px;
|
2025-09-14 20:40:42 +02:00
|
|
|
min-width: auto; /* Rimuovo min-width su mobile */
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.share-btn-inline {
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
margin: 0 10px;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.main-controls {
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn {
|
|
|
|
|
width: 45px;
|
|
|
|
|
height: 45px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn svg {
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
.play-pause-btn {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.play-pause-btn svg {
|
|
|
|
|
width: 26px;
|
|
|
|
|
height: 26px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.progress-bar {
|
2025-09-14 20:40:42 +02:00
|
|
|
height: 36px;
|
2025-09-13 22:30:33 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
.nav-btn {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
.play-pause-btn {
|
2025-09-14 20:40:42 +02:00
|
|
|
width: 50px;
|
|
|
|
|
height: 50px;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.play-pause-btn svg {
|
2025-09-14 20:40:42 +02:00
|
|
|
width: 22px;
|
|
|
|
|
height: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-btn svg {
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.time-display {
|
|
|
|
|
font-size: 14px;
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<div class="container">
|
|
|
|
|
<header>
|
|
|
|
|
<h1>- - - Crux mea Lux - - -</h1>
|
|
|
|
|
<h2>Unofficial LaZanzara Repository</h2>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main>
|
|
|
|
|
<?php
|
2025-09-14 20:40:42 +02:00
|
|
|
// Gestione link condiviso - se c'è un parametro date nel GET, lo uso
|
|
|
|
|
if (isset($_GET['date']) && !empty($_GET['date'])) {
|
|
|
|
|
$_POST['datepicker'] = $_GET['date'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Salvo il timestamp se presente per usarlo nel player
|
|
|
|
|
$startTime = 0;
|
|
|
|
|
if (isset($_GET['t']) && is_numeric($_GET['t'])) {
|
|
|
|
|
$startTime = intval($_GET['t']);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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">
|
2025-09-14 20:40:42 +02:00
|
|
|
<button type="submit" name="random" value="1" class="random-button" title="Play a random episode">
|
2025-09-13 22:30:33 +02:00
|
|
|
🎲 Random Play
|
|
|
|
|
</button>
|
2025-09-14 20:40:42 +02:00
|
|
|
<button type="button" id="selectDateBtn" class="date-button" title="Select a date">
|
|
|
|
|
📅 Select Date
|
|
|
|
|
</button>
|
|
|
|
|
<input type="hidden"
|
2025-09-13 22:30:33 +02:00
|
|
|
id="datepicker"
|
|
|
|
|
name="datepicker"
|
2025-09-14 20:40:42 +02:00
|
|
|
value="<?php echo htmlspecialchars($inputValue); ?>">
|
|
|
|
|
<button type="button" onclick="window.location.href=window.location.pathname" title="Clear all and start fresh">Reset</button>
|
2025-09-13 22:30:33 +02:00
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<div id="loading" style="display: none;">
|
2025-09-14 20:40:42 +02:00
|
|
|
<p>Loading available dates...</p>
|
2025-09-13 22:30:33 +02:00
|
|
|
</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') {
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<p class="episode-info">🎲 Random episode • <strong>' . htmlspecialchars($formattedDate) . '</strong> • ' . $fileSizeMB . ' MB</p>';
|
|
|
|
|
} else {
|
|
|
|
|
echo '<p class="episode-info">Selected episode • <strong>' . htmlspecialchars($formattedDate) . '</strong> • ' . $fileSizeMB . ' MB</p>';
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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>';
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Layout principale
|
|
|
|
|
echo '<div class="player-layout">';
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Controlli principali con navigazione
|
|
|
|
|
echo '<div class="main-controls">';
|
|
|
|
|
|
|
|
|
|
// Pulsante precedente
|
|
|
|
|
if ($prevDate) {
|
|
|
|
|
echo '<button class="nav-btn prev-btn" id="prevBtn" data-date="' . $prevDate . '" title="Previous day">
|
|
|
|
|
<svg viewBox="0 0 24 24">
|
|
|
|
|
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>';
|
|
|
|
|
} else {
|
|
|
|
|
echo '<button class="nav-btn prev-btn" disabled>
|
|
|
|
|
<svg viewBox="0 0 24 24">
|
|
|
|
|
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>';
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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>';
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Pulsante successivo
|
|
|
|
|
if ($nextDate) {
|
|
|
|
|
echo '<button class="nav-btn next-btn" id="nextBtn" data-date="' . $nextDate . '" title="Next day">
|
|
|
|
|
<svg viewBox="0 0 24 24">
|
|
|
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>';
|
|
|
|
|
} else {
|
|
|
|
|
echo '<button class="nav-btn next-btn" disabled>
|
|
|
|
|
<svg viewBox="0 0 24 24">
|
|
|
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo '</div>'; // Chiude main-controls
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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>';
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<button class="share-btn-inline" id="shareBtn" title="Share episode link">
|
|
|
|
|
<svg viewBox="0 0 24 24" width="18" height="18">
|
|
|
|
|
<path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>';
|
2025-09-13 22:30:33 +02:00
|
|
|
echo '<span class="time-display" id="duration">0:00</span>';
|
|
|
|
|
echo '</div>';
|
|
|
|
|
echo '</div>';
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
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");
|
2025-09-14 20:40:42 +02:00
|
|
|
const prevBtn = document.getElementById("prevBtn");
|
|
|
|
|
const nextBtn = document.getElementById("nextBtn");
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
let isPlaying = false;
|
|
|
|
|
let isDragging = false;
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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;
|
|
|
|
|
}
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Funzione per aggiornare la posizione
|
|
|
|
|
function updateProgress(e) {
|
|
|
|
|
const rect = progressBar.getBoundingClientRect();
|
|
|
|
|
let x;
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Supporto per touch e mouse
|
|
|
|
|
if (e.touches) {
|
|
|
|
|
x = e.touches[0].clientX - rect.left;
|
|
|
|
|
} else {
|
|
|
|
|
x = e.clientX - rect.left;
|
|
|
|
|
}
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
const clickPercent = Math.max(0, Math.min(1, x / rect.width));
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
if (audio.duration) {
|
|
|
|
|
audio.currentTime = clickPercent * audio.duration;
|
|
|
|
|
progressFill.style.width = (clickPercent * 100) + "%";
|
|
|
|
|
currentTimeDisplay.textContent = formatTime(audio.currentTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Play/Pause
|
|
|
|
|
playPauseBtn.addEventListener("click", function() {
|
|
|
|
|
if (isPlaying) {
|
|
|
|
|
audio.pause();
|
|
|
|
|
} else {
|
|
|
|
|
audio.play();
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Navigazione giorni
|
|
|
|
|
if (prevBtn && !prevBtn.disabled) {
|
|
|
|
|
prevBtn.addEventListener("click", function() {
|
|
|
|
|
const form = document.createElement("form");
|
|
|
|
|
form.method = "POST";
|
|
|
|
|
form.action = "";
|
|
|
|
|
const input = document.createElement("input");
|
|
|
|
|
input.type = "hidden";
|
|
|
|
|
input.name = "datepicker";
|
|
|
|
|
input.value = this.getAttribute("data-date");
|
|
|
|
|
form.appendChild(input);
|
|
|
|
|
const navInput = document.createElement("input");
|
|
|
|
|
navInput.type = "hidden";
|
|
|
|
|
navInput.name = "navigation";
|
|
|
|
|
navInput.value = "1";
|
|
|
|
|
form.appendChild(navInput);
|
|
|
|
|
document.body.appendChild(form);
|
|
|
|
|
form.submit();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nextBtn && !nextBtn.disabled) {
|
|
|
|
|
nextBtn.addEventListener("click", function() {
|
|
|
|
|
const form = document.createElement("form");
|
|
|
|
|
form.method = "POST";
|
|
|
|
|
form.action = "";
|
|
|
|
|
const input = document.createElement("input");
|
|
|
|
|
input.type = "hidden";
|
|
|
|
|
input.name = "datepicker";
|
|
|
|
|
input.value = this.getAttribute("data-date");
|
|
|
|
|
form.appendChild(input);
|
|
|
|
|
const navInput = document.createElement("input");
|
|
|
|
|
navInput.type = "hidden";
|
|
|
|
|
navInput.name = "navigation";
|
|
|
|
|
navInput.value = "1";
|
|
|
|
|
form.appendChild(navInput);
|
|
|
|
|
document.body.appendChild(form);
|
|
|
|
|
form.submit();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
audio.addEventListener("play", function() {
|
|
|
|
|
isPlaying = true;
|
|
|
|
|
audioPlayer.classList.add("playing");
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
audio.addEventListener("pause", function() {
|
|
|
|
|
isPlaying = false;
|
|
|
|
|
audioPlayer.classList.remove("playing");
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Aggiorna durata quando disponibile
|
|
|
|
|
audio.addEventListener("loadedmetadata", function() {
|
|
|
|
|
durationDisplay.textContent = formatTime(audio.duration);
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Se c\'è un timestamp di inizio, posiziona il player
|
|
|
|
|
' . (isset($startTime) && $startTime > 0 ? '
|
|
|
|
|
audio.currentTime = ' . $startTime . ';
|
|
|
|
|
currentTimeDisplay.textContent = formatTime(' . $startTime . ');
|
|
|
|
|
const startProgress = (' . $startTime . ' / audio.duration) * 100;
|
|
|
|
|
progressFill.style.width = startProgress + "%";
|
|
|
|
|
' : '') . '
|
2025-09-13 22:30:33 +02:00
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Eventi Mouse
|
|
|
|
|
progressBar.addEventListener("mousedown", function(e) {
|
|
|
|
|
isDragging = true;
|
|
|
|
|
audioPlayer.classList.add("dragging");
|
|
|
|
|
updateProgress(e);
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
document.addEventListener("mousemove", function(e) {
|
|
|
|
|
if (isDragging) {
|
|
|
|
|
updateProgress(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
document.addEventListener("mouseup", function() {
|
|
|
|
|
if (isDragging) {
|
|
|
|
|
isDragging = false;
|
|
|
|
|
audioPlayer.classList.remove("dragging");
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Eventi Touch
|
|
|
|
|
progressBar.addEventListener("touchstart", function(e) {
|
|
|
|
|
isDragging = true;
|
|
|
|
|
audioPlayer.classList.add("dragging");
|
|
|
|
|
updateProgress(e);
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
document.addEventListener("touchmove", function(e) {
|
|
|
|
|
if (isDragging) {
|
|
|
|
|
updateProgress(e);
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
document.addEventListener("touchend", function() {
|
|
|
|
|
if (isDragging) {
|
|
|
|
|
isDragging = false;
|
|
|
|
|
audioPlayer.classList.remove("dragging");
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Click semplice sulla barra
|
|
|
|
|
progressBar.addEventListener("click", function(e) {
|
|
|
|
|
if (!isDragging) {
|
|
|
|
|
updateProgress(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Auto-play solo se NON è una navigazione (prev/next)
|
|
|
|
|
' . (!isset($_POST['navigation']) ? '
|
2025-09-13 22:30:33 +02:00
|
|
|
audio.play().catch(function(error) {
|
2025-09-14 20:40:42 +02:00
|
|
|
console.log("Autoplay blocked:", error);
|
|
|
|
|
});' : '') . '
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Gestione buffering
|
|
|
|
|
audio.addEventListener("waiting", function() {
|
|
|
|
|
audioPlayer.classList.add("buffering");
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
audio.addEventListener("canplay", function() {
|
|
|
|
|
audioPlayer.classList.remove("buffering");
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// Fine del file
|
|
|
|
|
audio.addEventListener("ended", function() {
|
|
|
|
|
isPlaying = false;
|
|
|
|
|
audioPlayer.classList.remove("playing");
|
|
|
|
|
progressFill.style.width = "100%";
|
|
|
|
|
});
|
|
|
|
|
})();
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Gestione pulsante Share - solo quando c\'è una puntata caricata
|
|
|
|
|
(function() {
|
|
|
|
|
const shareBtn = document.getElementById("shareBtn");
|
|
|
|
|
if (shareBtn) {
|
|
|
|
|
shareBtn.addEventListener("click", function() {
|
|
|
|
|
const audio = document.getElementById("audio");
|
|
|
|
|
const baseUrl = window.location.origin + window.location.pathname;
|
|
|
|
|
const dateValue = "' . htmlspecialchars($selectedDate) . '";
|
|
|
|
|
const currentTime = Math.floor(audio.currentTime);
|
|
|
|
|
let shareUrl = baseUrl + "?date=" + dateValue;
|
|
|
|
|
|
|
|
|
|
if (currentTime > 0) {
|
|
|
|
|
shareUrl += "&t=" + currentTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (navigator.share) {
|
|
|
|
|
const shareTime = currentTime > 0 ? " at " + formatTime(currentTime) : "";
|
|
|
|
|
navigator.share({
|
|
|
|
|
title: "LaZanzara Episode - ' . htmlspecialchars($formattedDate) . '",
|
|
|
|
|
text: "Listen to LaZanzara episode from ' . htmlspecialchars($formattedDate) . '" + shareTime,
|
|
|
|
|
url: shareUrl
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
copyToClipboard(shareUrl);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
copyToClipboard(shareUrl);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatTime(seconds) {
|
|
|
|
|
const mins = Math.floor(seconds / 60);
|
|
|
|
|
const secs = Math.floor(seconds % 60);
|
|
|
|
|
return mins + ":" + (secs < 10 ? "0" : "") + secs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function copyToClipboard(text) {
|
|
|
|
|
if (navigator.clipboard) {
|
|
|
|
|
navigator.clipboard.writeText(text).then(function() {
|
|
|
|
|
showShareSuccess("Link copied to clipboard!");
|
|
|
|
|
}, function() {
|
|
|
|
|
fallbackCopyToClipboard(text);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
fallbackCopyToClipboard(text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fallbackCopyToClipboard(text) {
|
|
|
|
|
const textArea = document.createElement("textarea");
|
|
|
|
|
textArea.value = text;
|
|
|
|
|
textArea.style.position = "fixed";
|
|
|
|
|
textArea.style.left = "-999999px";
|
|
|
|
|
document.body.appendChild(textArea);
|
|
|
|
|
textArea.select();
|
|
|
|
|
try {
|
|
|
|
|
document.execCommand("copy");
|
|
|
|
|
showShareSuccess("Link copied to clipboard!");
|
|
|
|
|
} catch (err) {
|
|
|
|
|
showShareSuccess("Press Ctrl+C to copy: " + text);
|
|
|
|
|
}
|
|
|
|
|
document.body.removeChild(textArea);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function showShareSuccess(message) {
|
|
|
|
|
let successMsg = document.getElementById("shareSuccess");
|
|
|
|
|
if (!successMsg) {
|
|
|
|
|
successMsg = document.createElement("div");
|
|
|
|
|
successMsg.id = "shareSuccess";
|
|
|
|
|
successMsg.className = "share-success";
|
|
|
|
|
document.body.appendChild(successMsg);
|
|
|
|
|
}
|
|
|
|
|
successMsg.textContent = message;
|
|
|
|
|
successMsg.classList.add("show");
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
successMsg.classList.remove("show");
|
|
|
|
|
}, 2000);
|
|
|
|
|
}
|
|
|
|
|
})();
|
2025-09-13 22:30:33 +02:00
|
|
|
</script>';
|
|
|
|
|
|
|
|
|
|
// Mostra link download solo se configurato
|
|
|
|
|
if ($config['show_download_link']) {
|
|
|
|
|
echo '<p class="download">
|
2025-09-14 20:40:42 +02:00
|
|
|
Or download directly:
|
2025-09-13 22:30:33 +02:00
|
|
|
<a href="' . htmlspecialchars($paths['url']) . '" download>' .
|
|
|
|
|
htmlspecialchars($paths['filename']) . '</a>
|
|
|
|
|
</p>';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// File non trovato
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<p class="error">File not found for date: ' .
|
2025-09-13 22:30:33 +02:00
|
|
|
htmlspecialchars($selectedDate) . '</p>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo '</div>';
|
|
|
|
|
} else {
|
|
|
|
|
echo '<div class="result">
|
2025-09-14 20:40:42 +02:00
|
|
|
<p class="error">Invalid date. Use format YYYYMMDD</p>
|
2025-09-13 22:30:33 +02:00
|
|
|
</div>';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mostra statistiche
|
|
|
|
|
$validDates = getValidDates($config);
|
|
|
|
|
$totalFiles = count($validDates);
|
|
|
|
|
if ($totalFiles > 0) {
|
|
|
|
|
$firstDate = $validDates[0];
|
|
|
|
|
$lastDate = $validDates[$totalFiles - 1];
|
|
|
|
|
|
|
|
|
|
echo '<div class="stats">';
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<h3>Archive Statistics</h3>';
|
|
|
|
|
echo '<p>Available files: <strong>' . $totalFiles . '</strong></p>';
|
|
|
|
|
echo '<p>First episode: <strong>' . substr($firstDate, 6, 2) . '/' . substr($firstDate, 4, 2) . '/' . substr($firstDate, 0, 4) . '</strong></p>';
|
|
|
|
|
echo '<p>Last episode: <strong>' . substr($lastDate, 6, 2) . '/' . substr($lastDate, 4, 2) . '/' . substr($lastDate, 0, 4) . '</strong></p>';
|
2025-09-13 22:30:33 +02:00
|
|
|
|
|
|
|
|
// 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>';
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<summary>Files per year</summary>';
|
2025-09-13 22:30:33 +02:00
|
|
|
echo '<ul>';
|
|
|
|
|
foreach ($yearCount as $year => $count) {
|
2025-09-14 20:40:42 +02:00
|
|
|
echo '<li>' . $year . ': ' . $count . ' episodes</li>';
|
2025-09-13 22:30:33 +02:00
|
|
|
}
|
|
|
|
|
echo '</ul>';
|
|
|
|
|
echo '</details>';
|
|
|
|
|
echo '</div>';
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-14 20:40:42 +02:00
|
|
|
<!-- Keyboard shortcuts indicator -->
|
2025-09-13 22:30:33 +02:00
|
|
|
<div class="shortcuts-info">
|
2025-09-14 20:40:42 +02:00
|
|
|
<strong>Shortcuts:</strong> ← → to navigate | R for random | Space for play/pause
|
2025-09-13 22:30:33 +02:00
|
|
|
</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',
|
2025-09-14 20:40:42 +02:00
|
|
|
isAvailable ? 'Available' : 'Not available'];
|
2025-09-13 22:30:33 +02:00
|
|
|
},
|
|
|
|
|
onSelect: function() {
|
|
|
|
|
$('#dateForm').submit();
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-14 20:40:42 +02:00
|
|
|
|
|
|
|
|
// Gestione click sul pulsante di selezione data
|
|
|
|
|
$('#selectDateBtn').on('click', function(e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
$('#datepicker').datepicker('show');
|
2025-09-14 12:01:53 +02:00
|
|
|
});
|
|
|
|
|
|
2025-09-13 22:30:33 +02:00
|
|
|
// 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;">
|
2025-09-14 20:40:42 +02:00
|
|
|
🔗 Official LaZanzara page
|
2025-09-13 22:30:33 +02:00
|
|
|
</a>
|
|
|
|
|
</p>
|
2025-09-14 20:40:42 +02:00
|
|
|
<p>- - - Powered by FreeBSD. Developed by Gabry, Claude and Gianmarco - v1.1.1 (14/09/2025) - - -</p>
|
2025-09-13 22:30:33 +02:00
|
|
|
</footer>
|
|
|
|
|
</body>
|
2025-09-14 20:40:42 +02:00
|
|
|
</html>
|