reproduce ganador en dispositivo seleccionado si lo permite
- play_winner acepta device_id del formulario y verifica que el dispositivo exista y no sea restringido antes de reproducir - corrige construcción del URI según spotify_type del ganador (antes siempre usaba spotify:playlist: independiente del tipo) - errores se muestran en la página admin en lugar de HTTPException cruda - template sincroniza device_id activo al campo oculto del formulario - dispositivos restringidos se marcan en el selector del admin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+46
-4
@@ -340,18 +340,60 @@ def reset_votes(request: Request, db: Session = Depends(get_db)):
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/voting/play-winner")
|
@router.post("/voting/play-winner")
|
||||||
def play_winner(request: Request, db: Session = Depends(get_db)):
|
def play_winner(
|
||||||
|
request: Request,
|
||||||
|
device_id: str = Form(default=""),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
_require_admin(request)
|
_require_admin(request)
|
||||||
results = _vote_results(db)
|
results = _vote_results(db)
|
||||||
|
config = _get_or_create_config(db)
|
||||||
|
now_str = datetime.now().strftime("%H:%M:%S")
|
||||||
|
|
||||||
|
def _error(msg: str):
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"admin/voting.html",
|
||||||
|
{
|
||||||
|
"request": request,
|
||||||
|
"config": config,
|
||||||
|
"results": results,
|
||||||
|
"total_votes": sum(r["votes"] for r in results),
|
||||||
|
"server_time": now_str,
|
||||||
|
"error": msg,
|
||||||
|
"success": None,
|
||||||
|
},
|
||||||
|
status_code=400,
|
||||||
|
)
|
||||||
|
|
||||||
if not results or results[0]["votes"] == 0:
|
if not results or results[0]["votes"] == 0:
|
||||||
raise HTTPException(status_code=400, detail="No hay votos registrados")
|
return _error("No hay votos registrados")
|
||||||
|
|
||||||
winner = results[0]["playlist"]
|
winner = results[0]["playlist"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sp = spotify.get_client()
|
sp = spotify.get_client()
|
||||||
sp.start_playback(context_uri=f"spotify:playlist:{winner.spotify_id}")
|
|
||||||
|
if device_id:
|
||||||
|
devices = sp.devices().get("devices", [])
|
||||||
|
device = next((d for d in devices if d["id"] == device_id), None)
|
||||||
|
if not device:
|
||||||
|
return _error("El dispositivo seleccionado ya no está disponible")
|
||||||
|
if device.get("is_restricted"):
|
||||||
|
return _error(
|
||||||
|
f"El dispositivo «{device['name']}» no permite reproducción remota"
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs: dict = {}
|
||||||
|
if device_id:
|
||||||
|
kwargs["device_id"] = device_id
|
||||||
|
if winner.spotify_type == "track":
|
||||||
|
kwargs["uris"] = [f"spotify:track:{winner.spotify_id}"]
|
||||||
|
else:
|
||||||
|
kwargs["context_uri"] = f"spotify:{winner.spotify_type}:{winner.spotify_id}"
|
||||||
|
|
||||||
|
sp.start_playback(**kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
return _error(f"Error al reproducir: {e}")
|
||||||
|
|
||||||
db.query(Vote).delete()
|
db.query(Vote).delete()
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
<div style="display:flex;gap:.5rem;align-items:center">
|
<div style="display:flex;gap:.5rem;align-items:center">
|
||||||
{% if results and results[0].votes > 0 %}
|
{% if results and results[0].votes > 0 %}
|
||||||
<form method="post" action="/admin/voting/play-winner">
|
<form method="post" action="/admin/voting/play-winner">
|
||||||
|
<input type="hidden" id="play-winner-device-id" name="device_id" value="">
|
||||||
<button type="submit" class="btn-primary btn-sm">
|
<button type="submit" class="btn-primary btn-sm">
|
||||||
▶ Reproducir ganador
|
▶ Reproducir ganador
|
||||||
</button>
|
</button>
|
||||||
@@ -134,6 +135,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function _syncWinnerDevice(deviceId) {
|
||||||
|
const input = document.getElementById('play-winner-device-id');
|
||||||
|
if (input) input.value = deviceId || '';
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDevices() {
|
async function loadDevices() {
|
||||||
const sel = document.getElementById('device-select');
|
const sel = document.getElementById('device-select');
|
||||||
const msg = document.getElementById('device-msg');
|
const msg = document.getElementById('device-msg');
|
||||||
@@ -144,19 +150,24 @@ async function loadDevices() {
|
|||||||
const devices = await res.json();
|
const devices = await res.json();
|
||||||
if (!devices.length) {
|
if (!devices.length) {
|
||||||
sel.innerHTML = '<option value="">Sin dispositivos activos</option>';
|
sel.innerHTML = '<option value="">Sin dispositivos activos</option>';
|
||||||
|
_syncWinnerDevice('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sel.innerHTML = devices.map(d =>
|
sel.innerHTML = devices.map(d =>
|
||||||
`<option value="${d.id}"${d.is_active ? ' selected' : ''}>${d.name} (${d.type})</option>`
|
`<option value="${d.id}"${d.is_active ? ' selected' : ''}>${d.name} (${d.type})${d.is_restricted ? ' — sin control remoto' : ''}</option>`
|
||||||
).join('');
|
).join('');
|
||||||
|
const active = devices.find(d => d.is_active);
|
||||||
|
_syncWinnerDevice(active ? active.id : devices[0].id);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
sel.innerHTML = '<option value="">Error al cargar dispositivos</option>';
|
sel.innerHTML = '<option value="">Error al cargar dispositivos</option>';
|
||||||
|
_syncWinnerDevice('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setDevice(deviceId) {
|
async function setDevice(deviceId) {
|
||||||
if (!deviceId) return;
|
if (!deviceId) return;
|
||||||
const msg = document.getElementById('device-msg');
|
const msg = document.getElementById('device-msg');
|
||||||
|
_syncWinnerDevice(deviceId);
|
||||||
try {
|
try {
|
||||||
await fetch('/player/device', {
|
await fetch('/player/device', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
Reference in New Issue
Block a user