valoracion-mirrors

This commit is contained in:
2026-03-10 11:04:07 +01:00
parent 0b8fe7bbec
commit 218983660c
4 changed files with 187 additions and 71 deletions

View File

@@ -452,6 +452,22 @@
return null;
}
// ── Mirror status ────────────────────────────────────────────────────────────
const STATUS_CONFIG = {
unknown: { emoji: '❔', label: 'Por comprobar', bg: 'transparent' },
ok: { emoji: '✅', label: 'Funciona', bg: 'rgba(34,197,94,0.08)' },
issues: { emoji: '⚠️', label: 'Problemas', bg: 'rgba(245,158,11,0.10)' },
broken: { emoji: '⛔️', label: 'No funciona', bg: 'rgba(239,68,68,0.10)' },
};
const STATUS_CYCLE = ['unknown', 'ok', 'issues', 'broken'];
function getMirrorStatus(hash) {
return localStorage.getItem(`ms_${hash}`) || 'unknown';
}
function setMirrorStatus(hash, status) {
localStorage.setItem(`ms_${hash}`, status);
}
// ── Flag helper ──────────────────────────────────────────────────────────────
const TV_ICON = `<svg class="w-7 h-7 opacity-30" fill="none" viewBox="0 0 24 24" stroke="white" stroke-width="1.2">
<path stroke-linecap="round" stroke-linejoin="round"
@@ -461,7 +477,7 @@
function flagHTML(code) {
if (!code) return '';
return `<img src="/flags/${escAttr(code)}.svg" alt="${escAttr(code)}"
return `<img src="flags/${escAttr(code)}.svg" alt="${escAttr(code)}"
class="h-3.5 w-auto rounded-sm opacity-90"
onerror="this.style.display='none'" loading="lazy">`;
}
@@ -685,18 +701,30 @@
// Mirrors
$('modalMirrors').innerHTML = sorted.map((m, i) => {
const safeUrl = escAttr(`acestream://${m.acestream_hash}`);
const hashFull = escHTML(m.acestream_hash);
const hashShort = m.acestream_hash.slice(0, 16) + '…';
const safeUrl = escAttr(`acestream://${m.acestream_hash}`);
const hashFull = escHTML(m.acestream_hash);
const hashShort = m.acestream_hash.slice(0, 16) + '…';
const status = getMirrorStatus(m.acestream_hash);
const { emoji, label, bg } = STATUS_CONFIG[status] || STATUS_CONFIG.unknown;
const safeHash = escAttr(m.acestream_hash);
const safeChId = escAttr(ch.id);
return `
<div class="mirror-row flex items-center gap-3 px-4 py-2.5 rounded-lg mx-2 mb-0.5">
<div class="mirror-row flex items-center gap-3 px-4 py-2.5 rounded-lg mx-2 mb-0.5"
style="background:${bg}">
<span class="text-xs text-gray-700 font-mono w-5 flex-shrink-0 text-right">${i + 1}</span>
<div class="flex-shrink-0 w-20">
${tierBadge(m.resolution) || '<span class="text-xs text-gray-700">—</span>'}
</div>
<code class="flex-1 text-xs font-mono text-gray-600 truncate hidden sm:block"
title="${hashFull}">${hashShort}</code>
<button class="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg
text-base transition-all cursor-pointer"
title="${escAttr(label)}"
style="background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.08)"
data-status-hash="${safeHash}"
data-channel-id="${safeChId}"
data-mirror-idx="${i}">${emoji}</button>
<button class="flex-shrink-0 flex items-center gap-1.5 px-3 py-1.5 rounded-lg
text-xs font-semibold transition-all w-20 justify-center"
style="background:rgba(124,58,237,0.2);color:var(--accent-light);
@@ -768,8 +796,27 @@
renderAll();
});
// Mirror open button
// Mirror open button + status selector
$('modalMirrors').addEventListener('click', e => {
const statusBtn = e.target.closest('[data-status-hash]');
if (statusBtn) {
const hash = statusBtn.dataset.statusHash;
const cur = getMirrorStatus(hash);
const next = STATUS_CYCLE[(STATUS_CYCLE.indexOf(cur) + 1) % STATUS_CYCLE.length];
setMirrorStatus(hash, next);
statusBtn.textContent = STATUS_CONFIG[next].emoji;
statusBtn.title = STATUS_CONFIG[next].label;
fetch('/api/mirror/status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
channel_id: statusBtn.dataset.channelId,
mirror_idx: parseInt(statusBtn.dataset.mirrorIdx),
status: next,
}),
}).catch(() => {});
return;
}
const btn = e.target.closest('[data-url]');
if (btn) window.location.href = btn.dataset.url;
});
@@ -882,6 +929,13 @@
(data.groups || []).filter(g => typeof g !== 'string').map(g => [g.name, g.logo || ''])
);
$('sourceLabel').textContent = data.source || '';
// Superponer estados guardados en localStorage
App.channels.forEach(ch =>
ch.mirrors.forEach(m => {
const saved = localStorage.getItem(`ms_${m.acestream_hash}`);
if (saved) m.status = saved;
})
);
renderAll();
} catch (err) {
$('stateLoading').classList.add('hidden');