diff --git a/Plugin/unraid/css/varaverk.css b/Plugin/unraid/css/varaverk.css index 774ccff..8a3b3ee 100644 --- a/Plugin/unraid/css/varaverk.css +++ b/Plugin/unraid/css/varaverk.css @@ -749,10 +749,15 @@ mark { background: #5d4037; color: #ffcc80; border-radius: 2px; } .vv-doc-hint { font-size: 12px; color: #666; margin-top: 8px; } /* Media stream sessions */ -.vv-stream-servers { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; } +.vv-stream-servers { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; align-items: center; } .vv-server-badge { background: #333; border: 1px solid #555; color: #aaa; font-size: 10px; padding: 1px 7px; border-radius: 10px; } .vv-server-badge-sm { font-size: 9px; padding: 1px 6px; flex-shrink: 0; } +/* Device type chips — shown right of server badges when sessions are active */ +.vv-device-sep { color: #2a2a2a; font-size: 12px; margin: 0 2px; user-select: none; } +.vv-device-chip { font-size: 10px; color: #666; padding: 1px 7px; + border-radius: 10px; background: #181818; border: 1px solid #252525; + white-space: nowrap; } .vv-stream-empty { color: #555; font-style: italic; font-size: 12px; margin: 4px 0; } .vv-stream-empty span { font-size: 11px; color: #444; } .vv-stream-row { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #282828; } diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index ef2ab91..0190676 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -1401,6 +1401,26 @@ requestAnimationFrame(vvSyncCardWidths); // ── Media streams (slower poll — media server API calls) ────────────────────── +// Map a session's client string to a human-readable device category +function vvDeviceType(client) { + const c = (client || '').toLowerCase(); + // Order matters: more specific patterns first + if (/android\s*tv|androidtv|shield|nvidia\s*shield|android.*tv|tv.*android/.test(c)) return 'Android TV'; + if (/amazon|fire\s*tv|firetv|fire\s*stick|firestick/.test(c)) return 'Fire TV'; + if (/tvos|apple\s*tv/.test(c)) return 'Apple TV'; + if (/infuse/.test(c)) return 'Apple TV'; + if (/android|mobile/.test(c)) return 'Android'; + if (/ios|iphone|ipad/.test(c)) return 'iOS'; + if (/roku/.test(c)) return 'Roku'; + if (/webos|lg\s*tv|lgtv|lg /.test(c)) return 'LG'; + if (/samsung/.test(c)) return 'Samsung'; + if (/google\s*tv|googletv|chromecast/.test(c)) return 'Google TV'; + if (/kodi/.test(c)) return 'Kodi'; + if (/web|chrome|firefox|safari|browser|edge/.test(c)) return 'Browser'; + if (/desktop|theater|media\s*player|windows|linux|mac\s*os|macos/.test(c)) return 'Desktop'; + return null; // unknown — don't show +} + function vvFmtSec(sec) { const h = Math.floor(sec / 3600); const m = Math.floor((sec % 3600) / 60); @@ -1427,6 +1447,20 @@ function vvRenderStreams() { : `${n}`; }).join(''); + // Device type summary — e.g. "3 Android 1 iOS 2 Roku" + const devCounts = {}; + sessions.forEach(s => { + const t = vvDeviceType(s.client); + if (t) devCounts[t] = (devCounts[t] || 0) + 1; + }); + const deviceBar = Object.entries(devCounts) + .sort((a, b) => b[1] - a[1]) + .map(([t, n]) => `${n} ${t}`) + .join(''); + const deviceSection = deviceBar + ? `·${deviceBar}` + : ''; + if (sessions.length === 0) { el.innerHTML = `
Nothing playing
'; @@ -1486,7 +1520,7 @@ function vvRenderStreams() { ? `