Source: lib/device/webkit_stb.js

/*! @license
 * Shaka Player
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.device.WebKitSTB');

goog.require('shaka.device.AbstractDevice');
goog.require('shaka.device.DeviceFactory');
goog.require('shaka.device.IDevice');
goog.require('shaka.util.Lazy');


/**
 * @final
 */
shaka.device.WebKitSTB = class extends shaka.device.AbstractDevice {
  constructor() {
    super();

    /**
     * SkyQ STB
     *
     * @private {!shaka.util.Lazy<boolean>}
     */
    this.isSkyQ_ = new shaka.util.Lazy(() => {
      return navigator.userAgent.includes('DT_STB_BCM');
    });

    /** @private {!shaka.util.Lazy<?number>} */
    this.version_ = new shaka.util.Lazy(() => {
      if (navigator.userAgent.includes('DT_STB_BCM')) {
        return 11;
      }
      // This works for iOS Safari and desktop Safari, which contain something
      // like "Version/13.0" indicating the major Safari or iOS version.
      let match = navigator.userAgent.match(/Version\/(\d+)/);
      if (match) {
        return parseInt(match[1], /* base= */ 10);
      }

      // This works for all other browsers on iOS, which contain something like
      // "OS 13_3" indicating the major & minor iOS version.
      match = navigator.userAgent.match(/OS (\d+)(?:_\d+)?/);
      if (match) {
        return parseInt(match[1], /* base= */ 10);
      }

      return null;
    });
  }

  /**
   * @override
   */
  getVersion() {
    return this.version_.value();
  }

  /**
   * @override
   */
  getDeviceName() {
    return 'WebKit STB';
  }

  /**
   * @override
   */
  getDeviceType() {
    return shaka.device.IDevice.DeviceType.TV;
  }

  /**
   * @override
   */
  getBrowserEngine() {
    return shaka.device.IDevice.BrowserEngine.WEBKIT;
  }

  /**
   * @override
   */
  supportsMediaCapabilities() {
    return false;
  }

  /**
   * @override
   */
  supportsSequenceMode() {
    // See: https://bugs.webkit.org/show_bug.cgi?id=210341
    const version = this.version_.value();
    return version !== null ? version >= 15 : true;
  }

  /**
   * @override
   */
  detectMaxHardwareResolution() {
    const maxResolution = {
      width: window.screen.width * window.devicePixelRatio,
      height: window.screen.height * window.devicePixelRatio,
    };
    return Promise.resolve(maxResolution);
  }

  /**
   * @override
   */
  supportsEncryptionSchemePolyfill() {
    return !this.isSkyQ_.value();
  }

  /**
   * @return {boolean}
   * @private
   */
  static isWebkitSTB_() {
    if (navigator.userAgent.includes('DT_STB_BCM') ||
        navigator.userAgent.includes('DT_STB_BCM')) {
      return true;
    }
    if (!(navigator.vendor || '').includes('Apple')) {
      return false;
    }
    if (/(?:iPhone|iPad|iPod)/.test(navigator.userAgent) ||
        navigator.maxTouchPoints > 1) {
      return false;
    }
    if (navigator.userAgentData && navigator.userAgentData.platform &&
        navigator.userAgentData.platform.toLowerCase() == 'macos') {
      return false;
    } else if (navigator.platform &&
        navigator.platform.toLowerCase().includes('mac')) {
      return false;
    }
    return true;
  }
};

if (shaka.device.WebKitSTB.isWebkitSTB_()) {
  shaka.device.DeviceFactory.registerDeviceFactory(
      () => new shaka.device.WebKitSTB());
}