|
|
|
@ -4,10 +4,11 @@ |
|
|
|
<header class="topbar reveal"> |
|
|
|
<header class="topbar reveal"> |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
<h1>Tableau de bord</h1> |
|
|
|
<h1>Tableau de bord</h1> |
|
|
|
|
|
|
|
<p class="muted">Backend: {{ backend_url }} | Utilisateur: {{ username }}</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="topbar-actions"> |
|
|
|
<div class="topbar-actions"> |
|
|
|
<button type="button" class="danger" id="btn-stop-audio">Arrêter l'audio</button> |
|
|
|
<button type="button" class="danger" id="btn-stop-audio">Arreter l'audio</button> |
|
|
|
<a href="{{ url_for('ui.logout') }}" class="ghost-link">Déconnexion</a> |
|
|
|
<a href="{{ url_for('ui.logout') }}" class="ghost-link">Deconnexion</a> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</header> |
|
|
|
</header> |
|
|
|
|
|
|
|
|
|
|
|
@ -25,28 +26,20 @@ |
|
|
|
</label> |
|
|
|
</label> |
|
|
|
<label> |
|
|
|
<label> |
|
|
|
Nom |
|
|
|
Nom |
|
|
|
<input type="text" name="name" id="trigger_name" placeholder="Bouton entrée" required /> |
|
|
|
<input type="text" name="name" id="trigger_name" placeholder="Bouton entree" required /> |
|
|
|
</label> |
|
|
|
</label> |
|
|
|
<label> |
|
|
|
<label> |
|
|
|
Fichier audio |
|
|
|
Fichier audio |
|
|
|
<div class="audio-select-row"> |
|
|
|
<input list="audio-files" name="music_file" id="music_file" placeholder="bell.mp3" required /> |
|
|
|
<select name="music_file" id="music_file" required |
|
|
|
<datalist id="audio-files"> |
|
|
|
data-stream-url="{{ url_for('ui.stream_audio', filename='__FILE__') }}"> |
|
|
|
|
|
|
|
<option value="" disabled {% if not audio_files %}selected{% endif %}> |
|
|
|
|
|
|
|
{% if audio_files %}Choisir un fichier…{% else %}Aucun fichier disponible{% endif %} |
|
|
|
|
|
|
|
</option> |
|
|
|
|
|
|
|
{% for f in audio_files %} |
|
|
|
{% for f in audio_files %} |
|
|
|
<option value="{{ f }}">{{ f }}</option> |
|
|
|
<option value="{{ f }}"></option> |
|
|
|
{% endfor %} |
|
|
|
{% endfor %} |
|
|
|
</select> |
|
|
|
</datalist> |
|
|
|
<button type="button" class="small btn-preview" id="btn-preview-trigger-audio" |
|
|
|
|
|
|
|
title="Prévisualiser le fichier sélectionné" |
|
|
|
|
|
|
|
{% if not audio_files %}disabled{% endif %}>▶</button> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</label> |
|
|
|
</label> |
|
|
|
<div class="time-row"> |
|
|
|
<div class="time-row"> |
|
|
|
<label> |
|
|
|
<label> |
|
|
|
Début (s) |
|
|
|
Debut (s) |
|
|
|
<input type="number" step="0.1" min="0" name="start_seconds" id="start_seconds" value="0" /> |
|
|
|
<input type="number" step="0.1" min="0" name="start_seconds" id="start_seconds" value="0" /> |
|
|
|
</label> |
|
|
|
</label> |
|
|
|
<label> |
|
|
|
<label> |
|
|
|
@ -64,7 +57,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
<section class="panel reveal delay-2"> |
|
|
|
<section class="panel reveal delay-2"> |
|
|
|
<h2>Liste des triggers</h2> |
|
|
|
<h2>Liste des triggers</h2> |
|
|
|
<p class="muted" id="no-triggers-msg"{% if triggers %} hidden{% endif %}>Aucun trigger configuré.</p> |
|
|
|
<p class="muted" id="no-triggers-msg"{% if triggers %} hidden{% endif %}>Aucun trigger configure.</p> |
|
|
|
<div class="table-wrap"{% if not triggers %} hidden{% endif %} id="triggers-table-wrap"> |
|
|
|
<div class="table-wrap"{% if not triggers %} hidden{% endif %} id="triggers-table-wrap"> |
|
|
|
<table |
|
|
|
<table |
|
|
|
id="triggers-table" |
|
|
|
id="triggers-table" |
|
|
|
@ -77,7 +70,7 @@ |
|
|
|
<th>Nom</th> |
|
|
|
<th>Nom</th> |
|
|
|
<th>Type</th> |
|
|
|
<th>Type</th> |
|
|
|
<th>Audio</th> |
|
|
|
<th>Audio</th> |
|
|
|
<th>Début</th> |
|
|
|
<th>Debut</th> |
|
|
|
<th>Fin</th> |
|
|
|
<th>Fin</th> |
|
|
|
<th>Volume</th> |
|
|
|
<th>Volume</th> |
|
|
|
<th>Actions</th> |
|
|
|
<th>Actions</th> |
|
|
|
@ -133,7 +126,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
<form method="post" action="{{ url_for('ui.upload_audio') }}" enctype="multipart/form-data" class="upload-row" id="upload-audio-form"> |
|
|
|
<form method="post" action="{{ url_for('ui.upload_audio') }}" enctype="multipart/form-data" class="upload-row" id="upload-audio-form"> |
|
|
|
<input type="file" name="audio_file" id="audio_file_input" accept=".mp3,.wav,.ogg,.flac,.aac,.m4a" required /> |
|
|
|
<input type="file" name="audio_file" id="audio_file_input" accept=".mp3,.wav,.ogg,.flac,.aac,.m4a" required /> |
|
|
|
<button type="submit">Téléverser</button> |
|
|
|
<button type="submit">Televerser</button> |
|
|
|
</form> |
|
|
|
</form> |
|
|
|
|
|
|
|
|
|
|
|
{% if audio_files %} |
|
|
|
{% if audio_files %} |
|
|
|
@ -150,7 +143,7 @@ |
|
|
|
> |
|
|
|
> |
|
|
|
Lire |
|
|
|
Lire |
|
|
|
</button> |
|
|
|
</button> |
|
|
|
<a href="{{ url_for('ui.download_audio', filename=filename) }}" class="small-button">Télécharger</a> |
|
|
|
<a href="{{ url_for('ui.download_audio', filename=filename) }}" class="small-button">Telecharger</a> |
|
|
|
<form method="post" action="{{ url_for('ui.delete_audio') }}" class="inline-form js-delete-audio-form" data-filename="{{ filename }}"> |
|
|
|
<form method="post" action="{{ url_for('ui.delete_audio') }}" class="inline-form js-delete-audio-form" data-filename="{{ filename }}"> |
|
|
|
<input type="hidden" name="filename" value="{{ filename }}" /> |
|
|
|
<input type="hidden" name="filename" value="{{ filename }}" /> |
|
|
|
<button type="submit" class="small danger">Supprimer</button> |
|
|
|
<button type="submit" class="small danger">Supprimer</button> |
|
|
|
@ -189,8 +182,8 @@ |
|
|
|
|
|
|
|
|
|
|
|
<div class="modal-backdrop" id="duplicate-name-modal" aria-hidden="true"> |
|
|
|
<div class="modal-backdrop" id="duplicate-name-modal" aria-hidden="true"> |
|
|
|
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="duplicate-name-title"> |
|
|
|
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="duplicate-name-title"> |
|
|
|
<h3 id="duplicate-name-title">Nom déjà utilisé</h3> |
|
|
|
<h3 id="duplicate-name-title">Nom deja utilise</h3> |
|
|
|
<p id="duplicate-name-message">Ce nom existe déjà dans les fichiers enregistrés.</p> |
|
|
|
<p id="duplicate-name-message">Ce nom existe deja dans les fichiers enregistres.</p> |
|
|
|
<div class="modal-actions"> |
|
|
|
<div class="modal-actions"> |
|
|
|
<button type="button" class="ghost-link" id="duplicate-name-close">Fermer</button> |
|
|
|
<button type="button" class="ghost-link" id="duplicate-name-close">Fermer</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -302,8 +295,8 @@ |
|
|
|
body: body.toString(), |
|
|
|
body: body.toString(), |
|
|
|
}) |
|
|
|
}) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((d) => { showFlash(d.ok ? (d.message || "Trigger démarré.") : (d.error || "Échec."), d.ok ? "success" : "error"); }) |
|
|
|
.then((d) => { showFlash(d.ok ? (d.message || "Trigger demarre.") : (d.error || "Echec."), d.ok ? "success" : "error"); }) |
|
|
|
.catch(() => { showFlash("Erreur réseau lors du lancement.", "error"); }) |
|
|
|
.catch(() => { showFlash("Erreur reseau lors du lancement.", "error"); }) |
|
|
|
.finally(() => { playBtn.disabled = false; }); |
|
|
|
.finally(() => { playBtn.disabled = false; }); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -326,13 +319,13 @@ |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((data) => { |
|
|
|
.then((data) => { |
|
|
|
if (data.ok) { |
|
|
|
if (data.ok) { |
|
|
|
showFlash(data.message || "Audio arrêté.", "info"); |
|
|
|
showFlash(data.message || "Audio arrete.", "info"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
showFlash(data.error || "Échec de l'arrêt audio.", "error"); |
|
|
|
showFlash(data.error || "Echec de l'arret audio.", "error"); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch(() => { |
|
|
|
.catch(() => { |
|
|
|
showFlash("Erreur réseau lors de l'arrêt audio.", "error"); |
|
|
|
showFlash("Erreur reseau lors de l'arret audio.", "error"); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
.finally(() => { |
|
|
|
btn.disabled = false; |
|
|
|
btn.disabled = false; |
|
|
|
@ -357,13 +350,13 @@ |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((data) => { |
|
|
|
.then((data) => { |
|
|
|
if (data.ok) { |
|
|
|
if (data.ok) { |
|
|
|
showFlash(data.message || "Trigger démarré.", "success"); |
|
|
|
showFlash(data.message || "Trigger demarre.", "success"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
showFlash(data.error || "Échec du lancement.", "error"); |
|
|
|
showFlash(data.error || "Echec du lancement.", "error"); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch(() => { |
|
|
|
.catch(() => { |
|
|
|
showFlash("Erreur réseau lors du lancement.", "error"); |
|
|
|
showFlash("Erreur reseau lors du lancement.", "error"); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
.finally(() => { |
|
|
|
btn.disabled = false; |
|
|
|
btn.disabled = false; |
|
|
|
@ -402,14 +395,14 @@ |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((res) => res.json()) |
|
|
|
.then((data) => { |
|
|
|
.then((data) => { |
|
|
|
if (data.ok) { |
|
|
|
if (data.ok) { |
|
|
|
showFlash(data.message || "Trigger enregistré.", "success"); |
|
|
|
showFlash(data.message || "Trigger enregistre.", "success"); |
|
|
|
upsertTriggerRow(data); |
|
|
|
upsertTriggerRow(data); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
showFlash(data.error || "Échec de l'enregistrement.", "error"); |
|
|
|
showFlash(data.error || "Echec de l'enregistrement.", "error"); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch(() => { |
|
|
|
.catch(() => { |
|
|
|
showFlash("Erreur réseau lors de l'enregistrement.", "error"); |
|
|
|
showFlash("Erreur reseau lors de l'enregistrement.", "error"); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
.finally(() => { |
|
|
|
if (submitBtn) submitBtn.disabled = false; |
|
|
|
if (submitBtn) submitBtn.disabled = false; |
|
|
|
@ -642,37 +635,18 @@ |
|
|
|
if (tableWrap) tableWrap.hidden = true; |
|
|
|
if (tableWrap) tableWrap.hidden = true; |
|
|
|
if (noMsg) noMsg.hidden = false; |
|
|
|
if (noMsg) noMsg.hidden = false; |
|
|
|
} |
|
|
|
} |
|
|
|
showFlash(data.message || "Trigger supprimé.", "success"); |
|
|
|
showFlash(data.message || "Trigger supprime.", "success"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
showFlash(data.error || "Échec de la suppression.", "error"); |
|
|
|
showFlash(data.error || "Echec de la suppression.", "error"); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch(() => { |
|
|
|
.catch(() => { |
|
|
|
showFlash("Erreur réseau lors de la suppression.", "error"); |
|
|
|
showFlash("Erreur reseau lors de la suppression.", "error"); |
|
|
|
}) |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
.finally(() => { |
|
|
|
confirmBtn.disabled = false; |
|
|
|
confirmBtn.disabled = false; |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
(function () { |
|
|
|
|
|
|
|
const select = document.getElementById("music_file"); |
|
|
|
|
|
|
|
const btn = document.getElementById("btn-preview-trigger-audio"); |
|
|
|
|
|
|
|
if (!select || !btn) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
btn.addEventListener("click", () => { |
|
|
|
|
|
|
|
const filename = select.value; |
|
|
|
|
|
|
|
if (!filename) return; |
|
|
|
|
|
|
|
const streamBase = select.dataset.streamUrl || ""; |
|
|
|
|
|
|
|
const url = streamBase.replace("__FILE__", encodeURIComponent(filename)); |
|
|
|
|
|
|
|
const player = document.getElementById("browser-audio-player"); |
|
|
|
|
|
|
|
const nowPlaying = document.getElementById("audio-now-playing"); |
|
|
|
|
|
|
|
if (!player) return; |
|
|
|
|
|
|
|
player.src = url; |
|
|
|
|
|
|
|
player.play().catch(() => {}); |
|
|
|
|
|
|
|
if (nowPlaying) nowPlaying.textContent = "Lecture: " + filename; |
|
|
|
|
|
|
|
player.closest("section")?.scrollIntoView({ behavior: "smooth", block: "nearest" }); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
})(); |
|
|
|
|
|
|
|
</script> |
|
|
|
</script> |
|
|
|
{% endblock %} |
|
|
|
{% endblock %} |
|
|
|
|