Compare commits

...

3 Commits

Author SHA1 Message Date
David Inostroza 152a974533 apunta env_file a ruta absoluta del archivo de configuración
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:51:43 -04:00
David Inostroza 22ee2b58ad mueve selector de dispositivo al panel de admin
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:51:41 -04:00
David Inostroza d88547e310 reinicia votos al activar ventana de votación y al reproducir ganador
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:51:38 -04:00
4 changed files with 71 additions and 43 deletions
+16 -2
View File
@@ -299,12 +299,24 @@ def update_voting_config(
status_code=400,
)
was_active = config.is_active
new_active = is_active == "on"
config.start_time = start_time
config.end_time = end_time
config.is_active = is_active == "on"
config.is_active = new_active
db.commit()
votes_reset = False
if new_active and not was_active:
db.query(Vote).delete()
db.commit()
votes_reset = True
results = _vote_results(db)
success_msg = "Configuración guardada"
if votes_reset:
success_msg = "Configuración guardada · Votos reiniciados para la nueva ventana"
return templates.TemplateResponse(
"admin/voting.html",
{
@@ -314,7 +326,7 @@ def update_voting_config(
"total_votes": sum(r["votes"] for r in results),
"server_time": now_str,
"error": None,
"success": "Configuración guardada",
"success": success_msg,
},
)
@@ -341,6 +353,8 @@ def play_winner(request: Request, db: Session = Depends(get_db)):
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
db.query(Vote).delete()
db.commit()
return RedirectResponse(url="/admin/voting", status_code=303)
+53
View File
@@ -66,6 +66,18 @@
</div>
</div>
<!-- Selector de dispositivo -->
<div class="card">
<h2>Dispositivo de reproducción</h2>
<div class="device-row">
<select id="device-select" class="input" onchange="setDevice(this.value)" style="flex:1">
<option value="">Cargando dispositivos...</option>
</select>
<button class="btn-sm" onclick="loadDevices()">↻ Actualizar</button>
</div>
<div id="device-msg" style="font-size:.8rem;color:var(--text-muted);margin-top:.4rem"></div>
</div>
<!-- Resultados -->
<div class="card">
<div class="results-header">
@@ -121,7 +133,48 @@
</div>
<script>
async function loadDevices() {
const sel = document.getElementById('device-select');
const msg = document.getElementById('device-msg');
sel.innerHTML = '<option value="">Cargando...</option>';
msg.textContent = '';
try {
const res = await fetch('/player/devices');
const devices = await res.json();
if (!devices.length) {
sel.innerHTML = '<option value="">Sin dispositivos activos</option>';
return;
}
sel.innerHTML = devices.map(d =>
`<option value="${d.id}"${d.is_active ? ' selected' : ''}>${d.name} (${d.type})</option>`
).join('');
} catch (_) {
sel.innerHTML = '<option value="">Error al cargar dispositivos</option>';
}
}
async function setDevice(deviceId) {
if (!deviceId) return;
const msg = document.getElementById('device-msg');
try {
await fetch('/player/device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device_id: deviceId }),
});
msg.textContent = 'Dispositivo actualizado.';
setTimeout(() => msg.textContent = '', 2500);
} catch (_) {
msg.textContent = 'Error al cambiar dispositivo.';
}
}
loadDevices();
</script>
<style>
.device-row { display: flex; align-items: center; gap: .5rem; }
.voting-form { display: flex; flex-direction: column; gap: 1rem; }
.time-row { display: flex; align-items: flex-end; gap: 1rem; }
.time-field { display: flex; flex-direction: column; gap: .3rem; flex: 1; }
+1 -40
View File
@@ -44,17 +44,6 @@
<span class="volume-icon">🔊</span>
</div>
<!-- Selector de dispositivo -->
<div class="device-section">
<label class="section-label">Dispositivo</label>
<div class="device-row">
<select id="device-select" class="select" onchange="setDevice(this.value)">
<option value="">Cargando dispositivos...</option>
</select>
<button class="btn-sm" onclick="loadDevices()"></button>
</div>
</div>
<!-- Playlists / Votación -->
<div class="playlist-section">
<div class="playlist-section-header">
@@ -88,7 +77,6 @@
<script>
let isPlaying = false;
let currentDeviceId = null;
let votingOpen = false;
let myVotedPlaylistId = null;
let cooldownRemaining = 0;
@@ -128,8 +116,7 @@ async function fetchCurrent() {
async function togglePlay() {
const endpoint = isPlaying ? '/player/pause' : '/player/play';
const body = (!isPlaying && currentDeviceId) ? JSON.stringify({ device_id: currentDeviceId }) : '{}';
await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body });
await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
setTimeout(fetchCurrent, 300);
}
@@ -155,29 +142,6 @@ function setVolume(val) {
}, 200);
}
async function loadDevices() {
const res = await fetch('/player/devices');
const devices = await res.json();
const sel = document.getElementById('device-select');
sel.innerHTML = devices.length === 0
? '<option value="">Sin dispositivos activos</option>'
: devices.map(d => `<option value="${d.id}">${d.name} (${d.type})</option>`).join('');
if (devices.length > 0) {
currentDeviceId = devices[0].id;
sel.value = currentDeviceId;
}
}
function setDevice(deviceId) {
if (!deviceId) return;
currentDeviceId = deviceId;
fetch('/player/device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device_id: deviceId }),
});
}
async function playItem(spotifyId, spotifyType) {
const uri = `spotify:${spotifyType}:${spotifyId}`;
const body = {};
@@ -186,7 +150,6 @@ async function playItem(spotifyId, spotifyType) {
} else {
body.context_uri = uri;
}
if (currentDeviceId) body.device_id = currentDeviceId;
await fetch('/player/play', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -395,7 +358,6 @@ function closeTracksModal(event) {
async function playTrackFromModal(trackId) {
const body = { uris: [`spotify:track:${trackId}`] };
if (currentDeviceId) body.device_id = currentDeviceId;
await fetch('/player/play', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -410,7 +372,6 @@ document.addEventListener('keydown', e => {
if (e.key === 'Escape') closeTracksModal();
});
loadDevices();
fetchCurrent();
fetchVotingStatus();
+1 -1
View File
@@ -3,7 +3,7 @@ services:
build: .
ports:
- "8000:8000"
env_file: .env
env_file: /Users/deivid/env_cantina
environment:
TZ: America/Santiago
volumes: