feat: quality badges in player controls (4K, Dolby Vision, Atmos, IMAX…)#410
Conversation
Vanilla JS + Supabase single-page app served alongside the existing arvio.tv marketing site. No build step required — just static files. Features: - Google OAuth login (via Supabase GoTrue) - Dashboard with watch stats and recent activity - Profiles view — shows all ARVIO sub-profiles with active/kids badges - Addons manager — lists all Stremio + Telegram addons with enabled status - Watch History — paginated by movie/tv/all, delete individual entries - Watchlist — view and remove items - AI Subtitle Translation — toggle on/off, select model (Groq Llama 70B / Gemini Flash 2.5), configure auto-select and hearing-impaired removal; settings saved directly to cloud sync payload - Settings — card layout, language, OLED mode, skip profile selection Data layer: reads/writes the cloud sync payload stored in profiles.addons.__arvioAccountSyncPayload (same format the TV app uses), and queries watch_history/watchlist tables directly via Supabase REST. Co-Authored-By: koby455 <koby455@gmail.com>
Android TV app (CloudSyncRepository.kt): - Inject PluginDataStore into CloudSyncRepository - buildCloudPayload: export pluginRepositories, pluginScrapers, pluginsEnabled into __arvioAccountSyncPayload so plugins survive device wipes / multi-device - applyCloudPayload: restore repositories + scrapers from cloud on first launch (scraper JS code stays local-only for security; only metadata is synced) Companion web app (app.js): - Add IPTV section: shows all M3U playlists per profile with M3U/EPG URLs, enabled status, favourite groups and favourite channels - Add Plugins section: shows all plugin repositories (name, URL, scraper count, last updated) and individual scrapers (name, version, supported types, content languages, enabled status) with global plugins toggle - Add both to sidebar navigation Co-Authored-By: koby455 <koby455@gmail.com>
- All navigation labels, section titles, badges, toasts, and helper text translated to English in app.js and index.html - Auth screen subtitle and Google button label now in English - timeAgo() helper uses English relative-time strings - Hebrew remains fully supported as a selectable app language (setting stored in cloud sync payload as before) - Add mock-preview.html: standalone demo page with generic sample data for screenshots / PR previews (no Supabase dependency) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cPayload - Add escapeHtml() helper and apply it to every cloud value injected into innerHTML: user display name/email/avatar initial, profile names, addon name/description/logo, IPTV playlist name/URL/EPG/profile label/fav groups+channels, plugin repo name/URL, scraper name/description/version/logo/types/languages, history title/poster, watchlist tmdb_id, settings user ID - Add safeUrl() helper (https/http only) and use it for all image src attributes that come from cloud data (addon logos, scraper logos, user avatar) - saveSyncPayload now reads the existing wrapper first and only updates __arvioAccountSyncPayload and __arvioAccountSyncUpdatedAt, preserving any other fields the TV app may have written Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Normalizes the URL via the browser's URL parser before use and HTML-escapes it, preventing quote/attribute injection from synced logo or avatar URLs in innerHTML img src attributes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a horizontal row of small white logo badges above the seekbar,
visible whenever the player controls overlay is shown.
Badges displayed (up to 3-4 icons per stream):
Resolution — 4K · 1080p · 720p (image assets)
Video fmt — Dolby Vision · HDR10+ · HDR10 · HDR · IMAX
Audio fmt — Dolby Atmos · TrueHD · DTS:X · DTS-HD MA ·
DTS-HD · DTS · Dolby Digital+ · Dolby Digital
New file: PlaybackQualityBadges.kt (ui/components)
• PlaybackBadge data class — text label + optional image URL
• BadgeImages — same white-on-transparent PNG URLs already used in
the source picker (SourceBadgeImages)
• BadgeRegex — same regex corpus as StreamRegexes in StreamSelector
• buildPlaybackBadges(StreamSource): List<PlaybackBadge> — scans
quality, source, description and filename fields; returns at most
one resolution + one video-format + one audio-format badge
• PlaybackQualityBadgeRow composable — Row of AsyncImage (for logo
badges) or Text pills (for text-only badges like 480p)
PlayerScreen.kt
• Imports PlaybackQualityBadgeRow
• Calls it directly above the seekbar Row inside the bottom controls
Column, so it appears and disappears with the controls overlay
|
Looks good overall and both Play/Sideload Kotlin compiles pass. One small cleanup: After that I think this is safe to merge. Nice feature, it makes the player more informative without needing to open the source picker. |
…hange The reviewer noted that remember(quality, source, description) would miss badge-relevant data embedded only in behaviorHints.filename (e.g. a DV or Atmos tag only present in the filename). Keying on the full StreamSource object ensures badges are recomputed whenever any stream field changes.
|
Good catch — fixed in the latest push.
|
What this adds
Small white quality-logo badges appear above the seekbar whenever the player controls overlay is visible — so you always know exactly what you're watching without opening the source picker.
Badges shown (up to 3–4 icons)
Each badge uses the same white-on-transparent PNG assets already loaded by the source picker, so no new network dependencies are added. Text-only fallback (e.g. "480p") is used when no image asset exists.
Position
Implementation — 2 files, 185 insertions
PlaybackQualityBadges.kt(new,ui/components)Self-contained module. Re-uses the same regex patterns and image URLs as
StreamSelector.ktfor consistent labelling across the app.PlaybackBadgedata class— label text + optional image URLBadgeImagesSourceBadgeImagesBadgeRegexStreamRegexesbuildPlaybackBadges(StreamSource)quality,source,description,filename; returns ≤ 1 resolution + ≤ 1 video-format + ≤ 1 audio-format badgePlaybackQualityBadgeRow@Composable— horizontalRowofAsyncImage(logos) orTextpillsPlayerScreen.ktTwo-line change: import
PlaybackQualityBadgeRow, call it above the seekbar inside the existing bottom-controlsColumn.🤖 Generated with Claude Code