diff --git a/src/assets/img/devices/firetv.svg b/src/assets/img/devices/firetv.svg new file mode 100644 index 0000000000..b7348a3bd7 --- /dev/null +++ b/src/assets/img/devices/firetv.svg @@ -0,0 +1 @@ +Amazon Fire TV diff --git a/src/components/apphost.js b/src/components/apphost.js index ce88435a50..0693663f3c 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -13,6 +13,7 @@ const BrowserName = { tizen: 'Samsung Smart TV', web0s: 'LG Smart TV', titanos: 'Titan OS', + vega: 'Vega OS', operaTv: 'Opera TV', xboxOne: 'Xbox One', ps4: 'Sony PS4', diff --git a/src/scripts/browser.d.ts b/src/scripts/browser.d.ts index 3d211441ce..fc24fae03a 100644 --- a/src/scripts/browser.d.ts +++ b/src/scripts/browser.d.ts @@ -21,6 +21,7 @@ declare namespace browser { export let animate: boolean; export let hisense: boolean; export let tizen: boolean; + export let vega: boolean; export let vidaa: boolean; export let web0s: boolean; export let titanos: boolean; diff --git a/src/scripts/browser.js b/src/scripts/browser.js index 0539cd4371..b930f29706 100644 --- a/src/scripts/browser.js +++ b/src/scripts/browser.js @@ -235,10 +235,10 @@ const uaMatch = function (ua) { } return { - browser: browser, - version: version, + browser, + version, platform: platformMatch[0] || '', - versionMajor: versionMajor + versionMajor }; }; @@ -283,10 +283,11 @@ export const detectBrowser = (userAgent = navigator.userAgent) => { browser.animate = typeof document !== 'undefined' && document.documentElement.animate != null; browser.hisense = normalizedUA.includes('hisense'); browser.tizen = normalizedUA.includes('tizen') || window.tizen != null; + browser.vega = normalizedUA.includes('kepler'); browser.vidaa = normalizedUA.includes('vidaa'); browser.web0s = isWeb0s(normalizedUA); - browser.tv = browser.ps4 || browser.xboxOne || isTv(normalizedUA); + browser.tv = browser.ps4 || browser.vega || browser.xboxOne || isTv(normalizedUA); browser.operaTv = browser.tv && normalizedUA.includes('opr/'); browser.edgeUwp = (browser.edge || browser.edgeChromium) && (normalizedUA.includes('msapphost') || normalizedUA.includes('webview')); @@ -305,9 +306,15 @@ export const detectBrowser = (userAgent = navigator.userAgent) => { delete browser.chrome; delete browser.safari; } else if (browser.titanos) { - // UserAgent string contains 'Opr' and 'Safari', but we only want 'titanos' to be true + // UserAgent string contains 'Opr' and 'Safari', but we only want 'titanos' to be true delete browser.operaTv; delete browser.safari; + } else if (browser.vega) { + // UserAgent string contains 'Chrome' and 'Safari', but we only want 'vega' to be true + delete browser.chrome; + delete browser.safari; + // UserAgent string contains 'Mobile Chrome', but it is a TV + delete browser.mobile; } else { browser.orsay = normalizedUA.includes('smarthub'); } diff --git a/src/scripts/browser.test.ts b/src/scripts/browser.test.ts index 96b9fe8d54..a86f473618 100644 --- a/src/scripts/browser.test.ts +++ b/src/scripts/browser.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { detectBrowser } from './browser'; describe('Browser', () => { - it('should identify TitanOS devices', async () => { + it('should identify TitanOS devices', () => { // Ref: https://docs.titanos.tv/user-agents-specifications // Philips example let browser = detectBrowser('Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.4147.62 Safari/537.36 OPR/46.0.2207.0 OMI/4.24, TV_NT72690_2025_4K / (Philips, , wired) CE-HTML/1.0 NETTV/4.6.0.8 SignOn/2.0 SmartTvA/5.0.0 TitanOS/3.0 en Ginga'); @@ -20,7 +20,17 @@ describe('Browser', () => { expect(browser.tv).toBe(true); }); - it('should identify Xbox devices', async () => { + it('should identify Vega devices', () => { + // Ref: https://developer.amazon.com/docs/vega/0.21/webview-development-best-practices-tv.html#avoid-relying-on-the-useragent + const browser = detectBrowser('Mozilla/5.0 (Linux; Kepler 1.1; AFTCA002 user/1234; wv) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Chrome/130.0.6723.192 Safari/537.36'); + expect(browser.vega).toBe(true); + expect(browser.chrome).toBeFalsy(); + expect(browser.safari).toBeFalsy(); + expect(browser.mobile).toBeFalsy(); + expect(browser.tv).toBe(true); + }); + + it('should identify Xbox devices', () => { const browser = detectBrowser('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0 WebView2 Xbox'); expect(browser.xboxOne).toBe(true); expect(browser.tv).toBe(true); diff --git a/src/utils/image.ts b/src/utils/image.ts index 60d10798e2..cf5edc60f6 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -33,6 +33,8 @@ function getWebDeviceIcon(browser: string | null | undefined) { return BASE_DEVICE_IMAGE_URL + 'msie.svg'; case 'Titan OS': return BASE_DEVICE_IMAGE_URL + 'titanos.svg'; + case 'Vega OS': + return BASE_DEVICE_IMAGE_URL + 'firetv.svg'; default: return BASE_DEVICE_IMAGE_URL + 'html5.svg'; }