valoracion-mirrors
This commit is contained in:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user