From bea7b6b045098d6b6ee56489dcc6459acb259cc4 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sun, 31 May 2026 21:41:14 -0400 Subject: [PATCH] ui: show active device type counts in streams header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parses each session's client string (e.g. "Emby for Roku / Bedroom TV") into a device category using priority-ordered regex matching: Android TV, Fire TV, Apple TV, Android, iOS, Roku, LG, Samsung, Google TV, Kodi, Browser, Desktop. Counts are grouped and sorted by frequency, displayed as compact chips to the right of the server badges: Emby 7 Jellyfin 2 · 3 Android 2 Roku 1 Fire TV 1 iOS Unknown client strings are silently skipped (not shown as "Other"). Co-Authored-By: Claude Sonnet 4.6 --- Plugin/unraid/css/varaverk.css | 7 ++++++- Plugin/unraid/pages/monitor.php | 36 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) 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 = `
${badges}
` + '

Nothing playing

'; @@ -1486,7 +1520,7 @@ function vvRenderStreams() { ? `
+${sessions.length - 12} more not shown
` : ''; - el.innerHTML = `
${badges}
+ el.innerHTML = `
${badges}${deviceSection}
${sCols.map(vvStreamCol).join('')}
${overflow}`; }