// Device
export type DeviceType = 'chrome' | 'tv' | 'console';
export type DeviceDRM = 'playready' | 'widevine' | 'fairplay';

export interface WindowsDeviceFamily {
  MOBILE: string;
  DESKTOP: string;
  XBOX: string;
  HOLOGRAPHIC: string;
  IOT: string;
  TEAM: string;
}

export interface XboxUserCache {
  User: {
    IsValid: boolean;
    JwToken: string;
    ProfileId: string;
  };
}

export declare const WM_devSettings: {
  deviceIP: string;
  deviceName: string;
  memoryInfo: string;
  version: string;
  videoPlayerVersion: number;
};

export declare const WM_videoPlayer: {
  version: number;
  TTYLevel: number;
  systemClosedCaptionSetting: boolean;
  timedMetadata: boolean;
  closeCaption: boolean;
  bufferedRange: number;
  downloadBufferSize: number;
};

export interface DeviceKeycodes {
  KEY_0: number;
  KEY_1: number;
  KEY_2: number;
  KEY_3: number;
  KEY_4: number;
  KEY_5: number;
  KEY_6: number;
  KEY_7: number;
  KEY_8: number;
  KEY_9: number;

  KEY_R?: number;

  KEY_ARROW_LEFT: number;
  KEY_ARROW_RIGHT: number;
  KEY_ARROW_UP: number;
  KEY_ARROW_DOWN: number;

  KEY_F0_RED: number;
  KEY_F1_GREEN: number;
  KEY_F2_YELLOW: number;
  KEY_F3_BLUE: number;

  KEY_ENTER: number;
  KEY_ESC: number;
  KEY_BACKSPACE: number;
  KEY_TAB: number;

  KEY_BACK: number;
  KEY_EXIT: number;

  KEY_PREVIOUS_CHANNEL: number;
  KEY_LIST: number;

  KEY_QVIEW: number;
  KEY_FAV: number;

  KEY_INFO: number;
  KEY_SETTINGS: number;
  KEY_PROGRAM_UP: number;
  KEY_PROGRAM_DOWN: number;
  KEY_SOURCE: number;

  KEY_LANGUAGE: number;
  KEY_GUIDE: number;
  KEY_TEXT: number;
  KEY_SUBTITLES: number;

  KEY_LIVE_MENU: number;
  KEY_TELETEXT: number;
  KEY_TELETEXT_OPTIONS: number;

  KEY_AD: number;

  KEY_MEDIA_SAP: number;

  KEY_MEDIA_RECORD: number;
  KEY_MEDIA_REWIND: number;
  KEY_MEDIA_FASTFORWARD: number;
  KEY_MEDIA_PLAY: number;
  KEY_MEDIA_STOP: number;
  KEY_MEDIA_PAUSE: number;
  KEY_MEDIA_TRACKPREVIOUS: number;
  KEY_MEDIA_TRACKNEXT: number;
  KEY_MEDIA_PLAYPAUSE: number;
  KEY_MEDIA_REWIND_ALT: number;
  KEY_MEDIA_FASTFORWARD_ALT: number;
  KEY_MEDIA_TRACKPREVIOUS_ALT: number;
  KEY_MEDIA_TRACKNEXT_ALT: number;

  KEY_GAMEPAD_A: number;
  KEY_GAMEPAD_B: number;
  KEY_GAMEPAD_X: number;
  KEY_GAMEPAD_Y: number;
  KEY_GAMEPAD_RIGHT_SHOULDER: number;
  KEY_GAMEPAD_LEFT_SHOULDER: number;
  KEY_GAMEPAD_LEFT_TRIGGER: number;
  KEY_GAMEPAD_RIGHT_TRIGGER: number;
  KEY_GAMEPAD_DPAD_UP: number;
  KEY_GAMEPAD_DPAD_DOWN: number;
  KEY_GAMEPAD_DPAD_LEFT: number;
  KEY_GAMEPAD_DPAD_RIGHT: number;
  KEY_GAMEPAD_MENU: number;
  KEY_GAMEPAD_VIEW: number;
  KEY_GAMEPAD_LEFT_THUMBSTICK: number;
  KEY_GAMEPAD_RIGHT_THUMBSTICK: number;
  KEY_GAMEPAD_LEFT_THUMBSTICK_UP: number;
  KEY_GAMEPAD_LEFT_THUMBSTICK_DOWN: number;
  KEY_GAMEPAD_LEFT_THUMBSTICK_RIGHT: number;
  KEY_GAMEPAD_LEFT_THUMBSTICK_LEFT: number;
  KEY_GAMEPAD_RIGHT_THUMBSTICK_UP: number;
  KEY_GAMEPAD_RIGHT_THUMBSTICK_DOWN: number;
  KEY_GAMEPAD_RIGHT_THUMBSTICK_RIGHT: number;
  KEY_GAMEPAD_RIGHT_THUMBSTICK_LEFT: number;

  KEY_IGNORE: number;
}

export interface DeviceInfo extends CapabilitiesPlayer {
  clientId: string;
  manufacturer: string;
  os: string;
  type: DeviceType;
  model: string;
  friendlyModel?: string;
  sdk: string;
  stanName: string;
  stanVersion: string;
  stanProductVersion: string;
  dashFormat?: '' | 'timeline' | 'duration';
  // due to the way os is used inconsistently across the platforms,
  // we need a separate platformVersion param which is consistent.
  // TODO: split the current os key into firmware and os to replace platformVersion
  platformVersion?: string;
}

export interface Codec {
  name: string;
  codecs: string;
}

export interface CapabilitiesScreen {
  orientation: {
    type: OrientationType;
    angle: number;
  };
  width: number;
  height: number;
  availWidth: number;
  availHeight: number;
  colorDepth: number;
  pixelDepth: number;
  devicePixelRatio: number;
}

export interface CapabilitiesStatus {
  // Panasonic + Samsung Tizen
  freeMemory?: number;

  // Samsung Tizen
  network?: any;

  // PlayStation 4 WebMAF
  devSettings?: any;
  videoPlayer?: any;
}

export type TizenApplicationControlData = (
  key: string,
  value: ReadonlyArray<string>
) => void;

export type TizenApplicationControlLaunchMode = 'SINGLE' | 'GROUP';

export type TizenApplicationControl = (
  operation: string,
  uri?: string,
  mime?: string,
  category?: string,
  data?: ReadonlyArray<TizenApplicationControlData>,
  launchMode?: TizenApplicationControlLaunchMode
) => void;

export interface TizenMessagePortDataItem {
  key: Readonly<string>;
  value: Readonly<string> | ReadonlyArray<string>;
}

export interface TizenLocalMessagePort {
  messagePortName: Readonly<string>;
  isTrusted: Readonly<boolean>;
  addMessagePortListener: (
    callback: (data: ReadonlyArray<TizenMessagePortDataItem>) => void
  ) => void;
  removeMessagePortListener: (watchId: number) => void;
}

export interface TizenRemoteMessagePort {
  messagePortName: Readonly<string>;
  applicationId: Readonly<string>;
  isTrusted: Readonly<boolean>;
  sendMessage: (
    data: ReadonlyArray<TizenMessagePortDataItem>,
    localMessagePort?: TizenLocalMessagePort
  ) => void;
}

export interface TizenNetworkState {
  LAN_CABLE_ATTACHED: 1;
  LAN_CABLE_DETACHED: 2;
  LAN_CABLE_STATE_UNKNOWN: 3;
  GATEWAY_CONNECTED: 4;
  GATEWAY_DISCONNECTED: 5;
  WIFI_MODULE_STATE_ATTACHED: 6;
  WIFI_MODULE_STATE_DETACHED: 7;
  WIFI_MODULE_STATE_UNKNOWN: 8;
}

export interface Window {
  // 2019 LG TVs have launch parameters for deep-linking
  launchParams: string;

  tizen: {
    ApplicationControl: TizenApplicationControl;
    ApplicationControlData: TizenApplicationControlData;
    application: {
      getAppInfo: () => {
        version: string;
      };
      getCurrentApplication: () => {
        exit: () => void;
        getRequestedAppControl: () => {
          appControl: {
            data: ReadonlyArray<{ key: string; value: ReadonlyArray<string> }>;
          };
        };
      };
      launchAppControl: any;
    };
    messageport: {
      requestLocalMessagePort: (portName: string) => TizenLocalMessagePort;
      requestRemoteMessagePort: (
        appId: string,
        portName: string
      ) => TizenRemoteMessagePort;
    };
    systeminfo: {
      getTotalMemory: () => number;
      getAvailableMemory: () => number;
      getCapabilities: () => any;
      getPropertyValue: (property: string, successCB: any, errorCB: any) => any;
    };
    tvaudiocontrol: {
      getOutputMode: () => string;
      getVolume: () => number;
      isMute: () => boolean;
    };
    tvinputdevice: {
      getSupportedKeys: () => ReadonlyArray<{ name: string; code: string }>;
      registerKey: (key: string) => void;
      registerKeyBatch: (keys: ReadonlyArray<string>) => void;
    };
  };
  webapis: {
    appcommon: {
      AppCommonScreenSaverState: {
        SCREEN_SAVER_ON: string;
        SCREEN_SAVER_OFF: string;
      };
      setScreenSaver: (
        state: any,
        successCB: (result: any) => void,
        errorCB: (error: Error) => void
      ) => void;
    };
    avinfo: {
      getDolbyDigitalCompMode: () => any;
      isHdrTvSupport: () => boolean;
    };
    avplay: any;
    network: {
      addNetworkStateChangeListener: (
        callback: (networkState: number) => void
      ) => number;
      removeNetworkStateChangeListener: (listenerID: number) => void;
      getIpMode: () => number;
      getVersion: () => string;
      isConnectedToGateway: () => boolean;
      getSubnetMask: () => string;
      getGateway: () => string;
      getMac: () => string;
      getDns: () => string;
      getIp: () => string;
      getActiveConnectionType: () => number;
      getWiFiSsid: () => string;
      getWiFiSignalStrengthLevel: () => number;
      getWiFiSecurityMode: () => number;
      getWiFiEncryptionType: () => number;
      getSecondaryDns: () => string;
      getCurrentDhcpOption60Field: () => string;
      checkCurrentIpWith60Field: () => string;
      NetworkIpMode: {
        NONE: 0;
        STATIC: 1;
        DYNAMIC: 2;
        AUTO: 3;
        FIXED: 4;
        UNKNOWN: 5;
      };
      NetworkActiveConnectionType: {
        DISCONNECTED: 0;
        WIFI: 1;
        CELLULAR: 2;
        ETHERNET: 3;
      };
      NetworkState: TizenNetworkState;
      NetworkWiFiSecurityMode: {
        WEP: 1;
        WPA_PSK: 2;
        WPA2_PSK: 3;
        EAP: 4;
        NONE: 5;
        UNKNOWN: 6;
      };
      NetworkWiFiEncryptionType: {
        WEP: 1;
        TKIP: 2;
        AES: 3;
        TKIP_AES_MIXED: 4;
        NONE: 5;
        UNKNOWN: 6;
      };
    };
    productinfo: {
      getVersion: () => string;
      getFirmware: () => string;
      getDuid: () => string;
      getModelCode: () => string;
      getModel: () => string;
      getSmartTVServerType: () => string;
      getSmartTVServerVersion: () => string;
      getTunerEpop: () => string;
      isSoccerModeEnabled: () => boolean;
      isTtvSupported: () => boolean;
      isUdPanelSupported: () => boolean;
      getRealModel: () => string;
      getNoGlass3dSupport: () => string;
      getLocalSet: () => string;
      getSystemConfig: (key: number) => string;
      isUHDAModel: () => boolean;
      ProductInfoConfigKey: {
        CONFIG_KEY_DATA_SERVICE: number;
        CONFIG_KEY_SERVICE_COUNTRY: number;
      };
    };
    systeminfo: {
      getPropertyValue: (property: string) => string;
    };
    tvinfo: {
      isTvsPicSizeResized: () => boolean;
    };
  };
}

export interface PSAccountInfoResult {
  accountId: string;
  onlineId: string;
  countryCode: string;
  languageCode: string;
  dateOfBirth: string;
}

export interface WebMAFDeviceInfo {
  HDCP: HDCPVersion;
  colorSpace: 'Rec.709' | 'Rec.2020';
  connectionType: 'wired' | 'wireless' | 'phone' | 'unknown';
  portType: string;
  supportedColorSpace: string;
  neoMode: 'true' | 'false';
}

export interface PSNInfo {
  isPsnPlusAccount: boolean;
  psnUserAge: string;
  psnOnlineId: string;
}

export type StanHDCPVersion = '0' | '1' | '1.4' | '2' | '2.2';

export type HDCPVersion =
  | '0'
  | '1.0'
  | '1.1'
  | '1.2'
  | '1.3'
  | '1.4'
  | '2.0'
  | '2.1'
  | '2.2'
  | '2.3';

interface VendorCapability {
  vendor: string;
  platform: string;
}

interface DeviceInfo {
  screenConfig?: CapabilitiesScreen;
  clientId: string;
  sendErrorAnalytics?: (err: Error, fatal: boolean) => void;
  vendor?: string;
  platform?: string;
}

interface OtherInfo {
  sendErrorAnalytics: (err: Error, fatal: boolean) => void;
  vendor: string;
  platform: string;
}

export interface UserActivation {
  jwToken: string;
  profileId: string;
  userId?: string;
}

export interface UserProfile {
  id: string;
  name: string;
  expiry: number;
  iconIndex: number;
  iconImage: ProfileIconImage;
  isPrimary: boolean;
  isKidsProfile?: boolean;
  maxClassification?: ClassificationRating;
  locked?: boolean;
}
export interface UserSession {
  jwToken: string;
  userId: string;
  mpxToken: string;
  expiry?: number;
  now: number;
  renew: number;
  profile: UserProfile;
  subscription: UserSubscription;
  userCap: ReadonlyArray<UserCapabilities>;
  deviceCap: ReadonlyArray<DeviceCapabilities>;
  settings: Settings;
  isProfileExpired?: boolean;
}

export interface ProfileIconImage {
  height?: number;
  width?: number;
  url: string;
}

export type ClassificationRating =
  | ''
  | 'Unrestricted'
  | 'G'
  | 'PG'
  | 'M'
  | 'MA 15+'
  | 'R 18+';

export interface CapabilitiesPlayer {
  hdcpVersion: StanHDCPVersion;
  colorSpace: string;
  videoCodec: string;
  audioCodec: string;
  drm: DeviceDRM;
  eme: boolean;
  captions: string;
  screenSize: string;
  hdr: boolean;
  features: string;
}

export interface DeviceStorage extends StorageEstimate {
  persist: boolean;
}

export interface LocalUserCache {
  Key: string;
  Id: string;
  Item: {
    AudioTracksEnabled: boolean;
    VideoCarouselEnabled: boolean;
    ChaptersEnabled: boolean;
    deviceCap: DeviceCapabilities[];
    Expires: string;
    jwToken: string;
    mpxToken: string;
    now: number;
    profile: {
      id: string;
      userId: string;
      name: string;
      maxClassification: ClassificationRating;
      isPrimary: boolean;
      isKidsProfile: boolean;
      historyListId: any;
      watchListId: any;
      iconIndex: number;
      IsRestrictedProfile: boolean;
    };
    renew: number;
    subscription: {
      active: boolean;
      streams: UserSubscriptionStreams;
    };
    userCap: UserCapabilities[];
    userId: string;
    UserSubscribe4K: boolean;
    UserSubscribeHD: boolean;
    UserSubscribeHDR: boolean;
    errors: any;
    expirationDate: number;
    IsSuccess: boolean;
  };
  DateStamp: string;
  MaxAge: any;
}

export interface APIError extends Error {
  response: DiscoveryError;
  statusCode?: number;
}

interface DeviceNavigator extends Navigator {
  deviceMemory?: number;
  connection: any;
}

export interface DevicePerformance {
  onLine: boolean;
  deviceMemory: number;
  hardwareConcurrency: number;
  memory: {
    jsHeapSizeLimit: number;
    totalJSHeapSize: number;
    usedJSHeapSize: number;
    usedJSHeapPercentage: number;
  };
  connection: {
    downlink: number;
    downlinkMax: number;
    effectiveType: string;
    rtt: number;
    type: string;
  };
  timing?: any;
}

interface LegacyDevicePerformance extends Performance {
  memory: any;
  timing: any;
}

// User
export type UserCapabilities =
  | 'internal'
  | 'qa'
  | 'altcdn'
  | 'kids-feed'
  | 'offline'
  | 'hdr'
  | 'uhd'
  | 'dolbyatmos'
  | 'dolbyvision';

export type DeviceCapabilities =
  | 'internal'
  | 'offline'
  | 'hdr'
  | 'uhd'
  | 'dolbyatmos'
  | 'dolbyvision'
  | 'audiotracks'
  | 'videocarousel'
  | 'chapters'
  | 'drm=fairplay'
  | 'widevinel3'
  | 'badopl'
  | 'disablelive'
  | 'oldpostroll';

export interface Capabilities {
  features: CapabilitiesFeatures;
  userAgent: string;
  screen: CapabilitiesScreen;
  image: ImageConfig;
  language: string;
  platform: string;
  plugins: string;
  graphics: DeviceGraphics;
  device: DeviceInfo;
  other: any;
}

export interface CapabilitiesFeatures {
  isFetch: boolean;
  isPromise: boolean;
  isCookie: boolean;
  isLocalStorage: boolean;
  isScrollBased: boolean;
  isServiceWorker: boolean;
  isWorker: boolean;
  isPassiveEvent: boolean;
  isWebGL: boolean;
  isIntl: boolean;
}

export interface DeviceGraphics {
  vendor: string;
  renderer: string;
  vendor_unmasked: string;
  renderer_unmasked: string;
}

export interface ImageConfig {
  screenWidth: number;
  scale: number;
  quality: number;
  isWebP: boolean;
}

export interface UserSession {
  jwToken: string;
  userId: string;
  mpxToken: string;
  expiry?: number;
  now: number;
  renew: number;
  profile: UserProfile;
  subscription: UserSubscription;
  userCap: ReadonlyArray<UserCapabilities>;
  deviceCap: ReadonlyArray<DeviceCapabilities>;
  settings: Settings;
  isProfileExpired?: boolean;
}
export interface UserProfile {
  id: string;
  name: string;
  expiry: number;
  iconIndex: number;
  iconImage: ProfileIconImage;
  isPrimary: boolean;
  isKidsProfile?: boolean;
  maxClassification?: ClassificationRating;
  locked?: boolean;
}

export interface UserSubscription {
  active: boolean;
  biller: UserSubscriptionBiller;
  streams: UserSubscriptionStreams;
  email: string;
  status: 'active' | 'inactive';
}

export type UserSubscriptionBiller = 'stan' | 'itunes';

export type UserSubscriptionStreams = 'sd' | 'hd' | 'uhd';

export interface LocalUserCache {
  Key: string;
  Id: string;
  Item: {
    AudioTracksEnabled: boolean;
    VideoCarouselEnabled: boolean;
    ChaptersEnabled: boolean;
    deviceCap: DeviceCapabilities[];
    Expires: string;
    jwToken: string;
    mpxToken: string;
    now: number;
    profile: {
      id: string;
      userId: string;
      name: string;
      maxClassification: ClassificationRating;
      isPrimary: boolean;
      isKidsProfile: boolean;
      historyListId: any;
      watchListId: any;
      iconIndex: number;
      IsRestrictedProfile: boolean;
    };
    renew: number;
    subscription: {
      active: boolean;
      streams: UserSubscriptionStreams;
    };
    userCap: UserCapabilities[];
    userId: string;
    UserSubscribe4K: boolean;
    UserSubscribeHD: boolean;
    UserSubscribeHDR: boolean;
    errors: any;
    expirationDate: number;
    IsSuccess: boolean;
  };
  DateStamp: string;
  MaxAge: any;
}

interface DeviceHelpers {
  getDeviceInfo: (
    screenConfig: CapabilitiesScreen,
    clientId: string,
    sendErrorAnalytics: (err: Error, fatal: boolean) => void
  ) => Promise<Readonly<Partial<DeviceInfo>>>;
  getOtherInfo: (
    sendErrorAnalytics: (err: Error, fatal: boolean) => void
  ) => Promise<Readonly<any>>;
  getStatus: () => Promise<CapabilitiesStatus>;
  getKeyCodes: () => Promise<Readonly<DeviceKeycodes>>;
  getAppSecureSecret: (
    sendErrorAnalytics: (err: Error, fatal: boolean) => void
  ) => Promise<string>;
}

declare const Hisense_GetDeviceID: () => string;
declare const Hisense_GetBrand: () => string;
declare const Hisense_GetModelName: () => string;
declare const Hisense_GetFirmWareVersion: () => string;
declare const Hisense_Get4KSupportState: () => boolean;
declare const Hisense_GetCountryCode: () => string;
declare const Hisense_GetSupportForHDR: () => 'HDR10' | 'HLG' | 'HDR10+HLG';
declare const Hisense_GetParentalControlEnabled: () => string;
declare const Hisense_GetRatingEnable: () => string;
declare const Hisense_GetTvRating: () => string;
declare const Hisense_GetTvChildrenRating: () => string;
declare const Hisense_GetMovieRating: () => string;
declare const Hisense_GetCanEngRating: () => string;
declare const Hisense_GetCanFreRating: () => string;
declare const Hisense_GetParentControls: () => string;
declare const Hisense_GetPictureModeList: () => string;
declare const Hisense_GetPictureMode: () => string;
declare const Hisense_GetResolution: () => string;
declare const Hisense_GetNetStatus: () => string;
declare const Hisense_GetVolume: () => number;
declare const Hisense_GetMute: () => boolean;

export interface Program {
  url: string;
  seriesUrl?: string;
  id: string;
  guid: string;
  title: string;
  shortTitle: string;
  description: string;
  longDescription: string;
  shortDescription: string;
  shortMessage: string;
  added: number;
  updated: number;
  programType: ProgramType;
  images: { [imageType in ProgramImageType]?: ProgramImage }; // TODO
  classifications: ReadonlyArray<ProgramClassification>;
  tags: ReadonlyArray<ProgramTag>;
  runtime: number;
  releaseYear: number;
  languages: ReadonlyArray<string>;
  closedCaptions: ReadonlyArray<string> | null;
  audioLayout: string;
  new: boolean;
  exclusive: boolean;
  countries: any; // TODO
  seasons?: ReadonlyArray<ProgramSeason>; // TODO
  extras?: ProgramExtras;
  seriesPremiere?: string;
  seriesFinale?: string;
  seriesId?: string;
  seasonid?: string;
  tvSeasonEpisodeNumber?: number;
  tvSeasonNumber?: number;
  highDefinition: boolean;
  uhd?: boolean;
  hdr?: boolean;
  dolbyvision?: boolean;
  dolbyatmos?: boolean;
  expirationDate: number;
  totalSeasons?: number;
  availableOffline: boolean;
  startDate?: number;
  liveStartDate?: number;
  liveEndDate?: number;
  credits: ReadonlyArray<ProgramCredit>;
  related: string;
  prev?: string;
  next?: string;
  media?: ReadonlyArray<any>; // TODO
  streams?: {
    [stream in UserSubscriptionStreams]: {
      [format in StreamFormat]: { [quality in StreamQuality]: ProgramStream };
    };
  };
  airplay: boolean;
  airplayHd: boolean;
  chromecast: boolean;
  chromecastHd: boolean;
  chapters: ReadonlyArray<ProgramChapter> | null;
  theme?: any; // TODO
  audioTracks?: ReadonlyArray<ProgramAudioTrack>;
  audioDescription: boolean;
  bundle?: string;
  __expires?: number;
}

// Program
export type ProgramType =
  | 'episode'
  | 'series'
  | 'movie'
  | 'trailer'
  | 'post-roll';

export type ProgramImageType =
  | 'AltHero'
  | 'Banner-L0'
  | 'Banner-L1'
  | 'Banner-L2'
  | 'Box Art'
  | 'Cast in Character'
  | 'Landscape'
  | 'Hero'
  | 'Iconic'
  | 'Poster Art'
  | 'Scene Still'
  | 'Logo'
  | 'LandscapeCover';

export interface ProgramImage {
  url: string;
  height: number;
  width: number;
}

export interface ProgramClassification {
  scheme: string;
  rating: ClassificationRating;
  consumerAdvice: ReadonlyArray<string> | null;
}

export interface ProgramTag {
  url: string;
  scheme: ProgramTagType;
  title: string;
}

export type ProgramTagType = 'genre';

export interface ProgramSeason {
  audioLayout: string;
  availableOffline: boolean;
  closedCaptions: ReadonlyArray<string>;
  expirationDate: number;
  guid: string;
  highDefinition: boolean;
  id: number;
  releaseYear: number;
  seasonNumber: number;
  title: string;
  total: number;
  url: string;
  entries?: ReadonlyArray<Program>;
  bonusFeature?: boolean;
  __expires?: number;
}

export interface ProgramExtras {
  url: string;
  title: string;
  entries?: ReadonlyArray<Program>;
}

export interface ProgramCredit {
  url: string;
  characterName: string;
  creditType: ProgramCreditType;
  personId: string;
  personName: string;
}

export interface ProgramFeedEntry {
  readonly key?: string; // unique key
  readonly index?: number;
  readonly isLink?: boolean; // type FeedType = 'program' | 'link';
  readonly id?: string; // "history"
  readonly programId?: string; // "history" episode program ID
  readonly programType: ProgramType;
  readonly guid: string;
  readonly title: string;
  readonly previewTitle?: string; // custom title for "history"
  readonly imageURL?: string;
  readonly backgroundImageURL?: string;
  readonly path?: string; // "link"
  isMyListLoading?: boolean; // "watchlist"
  readonly deleteURL?: string; // "watchlist"
  readonly updated?: number;
  resumePoint?: ProgramFeedEntryResumePoint;
  readonly carousel?: ProgramFeedEntryCarousel;
  readonly cta?: CallToActionConfigs;
  readonly clips?: ProgramFeedEntryClips;
  readonly classifications?: ReadonlyArray<ProgramClassification>;
  readonly tvSeasonEpisodeNumber?: number;
  readonly tvSeasonNumber?: number;
  readonly bonusFeature?: boolean;
  readonly runtime?: number;
  readonly startDate?: number;
  readonly liveStartDate?: number;
  readonly liveEndDate?: number;
  readonly __refreshAt?: number;
}

export interface ProgramFeedEntryCarousel {
  readonly guid: string;
  readonly headline: string;
  readonly synopsis: string;
  readonly cta: CallToActionConfigs;
  readonly isPlayCTA: boolean;
  readonly isMyListCTA: boolean;
  readonly disabled?: boolean;
  readonly logoImageURL: string;
}

export interface ProgramFeedEntryClips {
  readonly guid: string;
  readonly title: string;
  readonly classifications: ReadonlyArray<ProgramClassification>;
  readonly tags: ReadonlyArray<ProgramTag>;
  readonly runtime: number;
  readonly videoURL: string;
  readonly originalVideoURL: string;
}

// Feed
export type ProgramFeedEntryType =
  | 'hero'
  | 'posters'
  | 'landscapes'
  | 'link'
  | 'continue'
  | 'single_list'
  | 'history';

export type ProgramFeedSource =
  | 'catalogue'
  | 'because-you-watched'
  | 'recommended-for-you'
  | 'my-list';

export interface ProgramFeedEntryResumePoint {
  readonly guid?: string;
  readonly label: string;
  readonly position?: number;
  readonly totalDuration?: number;
  readonly completed?: boolean;
  readonly tvSeasonNumber?: number;
  readonly tvSeasonEpisodeNumber?: number;
  readonly updatedText?: string; // "history"
  readonly runtime?: number;
  readonly __expires?: number;
}

export interface ProgramFeed {
  readonly id: string | number; // depends on the API, number or string...
  readonly type: ProgramFeedEntryType;
  readonly title: string;
  readonly total: number;
  readonly updated: number;
  readonly version?: string;
  readonly url?: string;
  readonly entries: ProgramFeedEntry[];
  readonly source?: ProgramFeedSource;
  readonly next: string;
  readonly prev?: string;
  readonly liveRefreshIndicies: number[];
  readonly __expires?: number;
}

export interface CallToActionConfigs {
  primary: CallToActionConfig;
  secondary?: CallToActionConfig;
}

export interface CallToActionConfig {
  path?: string;
  label: string;
  type: CallToActionType;
  disabled?: boolean;
  method?: string;
  params?: {
    [key: string]: string;
  };
}

export type CallToActionType =
  | 'info'
  | 'play'
  | 'my-list'
  | 'modal'
  | 'billing'
  | 'close';

// Stream
export type StreamFormat =
  | 'android.ttml'
  | 'dash'
  | 'hls'
  | 'smooth'
  | 'ttml'
  | 'vtt';

export type StreamQuality = 'auto' | 'ultra' | 'high' | 'low' | 'medium';

export interface ProgramStream {
  height: number;
  pid: string;
  protectionKey: string;
  url: string;
  width: number;
  disabled: boolean;
}

export interface PreviewProgramFeedEntry extends PreviewProgramEntry {
  deleteURL?: string;
  resumePoint?: ProgramFeedEntryResumePoint;
  isVOD: boolean;
  checkIsLivePlayable: () => boolean;
  path: string;
}

export interface PreviewProgramEntry
  extends SanitisedProgramEntry,
    PreviewProgramStatus {}

export interface ProgramHistoryEntry extends ProgramResumeEntry {
  programId?: string;
  completed: boolean;
  deleteUrl: string;
}

// Program
export type ProgramType =
  | 'episode'
  | 'series'
  | 'movie'
  | 'trailer'
  | 'post-roll';

export type ProgramImageType =
  | 'AltHero'
  | 'Banner-L0'
  | 'Banner-L1'
  | 'Banner-L2'
  | 'Box Art'
  | 'Cast in Character'
  | 'Landscape'
  | 'Hero'
  | 'Iconic'
  | 'Poster Art'
  | 'Scene Still'
  | 'Logo'
  | 'LandscapeCover';

export type ClassificationRating =
  | ''
  | 'Unrestricted'
  | 'G'
  | 'PG'
  | 'M'
  | 'MA 15+'
  | 'R 18+';

export type ProgramTagType = 'genre';

export type ProgramCreditType = 'cast' | 'directors';

export type ProgramAvailableStreams = {
  [key in StreamQuality]?: ProgramStream;
};

export interface ProgramSeason {
  audioLayout: string;
  availableOffline: boolean;
  closedCaptions: ReadonlyArray<string>;
  expirationDate: number;
  guid: string;
  highDefinition: boolean;
  id: number;
  releaseYear: number;
  seasonNumber: number;
  title: string;
  total: number;
  url: string;
  entries?: ReadonlyArray<Program>;
  bonusFeature?: boolean;
  __expires?: number;
}

export interface ProgramExtras {
  url: string;
  title: string;
  entries?: ReadonlyArray<Program>;
}

export interface ProgramChapter {
  name: string;
  start: number;
  end?: number;
  skippable: boolean;
  canSkipOnAutoplay: boolean;
  showUpNext: boolean;
}

export interface ProgramAudioTrack {
  indexId: number;
  id: StreamAudioType;
  label: string;
  active: boolean;
  roles: ReadonlyArray<StreamAudioType>;
  type: StreamAudioType;
  language: {
    iso: string;
    name: string;
  };
}

export interface Program {
  url: string;
  seriesUrl?: string;
  id: string;
  guid: string;
  title: string;
  shortTitle: string;
  description: string;
  longDescription: string;
  shortDescription: string;
  shortMessage: string;
  added: number;
  updated: number;
  programType: ProgramType;
  images: { [imageType in ProgramImageType]?: ProgramImage }; // TODO
  classifications: ReadonlyArray<ProgramClassification>;
  tags: ReadonlyArray<ProgramTag>;
  runtime: number;
  releaseYear: number;
  languages: ReadonlyArray<string>;
  closedCaptions: ReadonlyArray<string> | null;
  audioLayout: string;
  new: boolean;
  exclusive: boolean;
  countries: any; // TODO
  seasons?: ReadonlyArray<ProgramSeason>; // TODO
  extras?: ProgramExtras;
  seriesPremiere?: string;
  seriesFinale?: string;
  seriesId?: string;
  seasonid?: string;
  tvSeasonEpisodeNumber?: number;
  tvSeasonNumber?: number;
  highDefinition: boolean;
  uhd?: boolean;
  hdr?: boolean;
  dolbyvision?: boolean;
  dolbyatmos?: boolean;
  expirationDate: number;
  totalSeasons?: number;
  availableOffline: boolean;
  startDate?: number;
  liveStartDate?: number;
  liveEndDate?: number;
  credits: ReadonlyArray<ProgramCredit>;
  related: string;
  prev?: string;
  next?: string;
  media?: ReadonlyArray<any>; // TODO
  streams?: {
    [stream in UserSubscriptionStreams]: {
      [format in StreamFormat]: { [quality in StreamQuality]: ProgramStream };
    };
  };
  airplay: boolean;
  airplayHd: boolean;
  chromecast: boolean;
  chromecastHd: boolean;
  chapters: ReadonlyArray<ProgramChapter> | null;
  theme?: any; // TODO
  audioTracks?: ReadonlyArray<ProgramAudioTrack>;
  audioDescription: boolean;
  bundle?: string;
  __expires?: number;
}

export type ProgramFeature =
  | 'LIVE'
  | '4K Ultra HD'
  | 'HD'
  | 'Dolby Vision'
  | 'HDR'
  | 'Dolby Atmos'
  | '5.1'
  | 'AD';

// Sanitised
export interface SanitisedProgram {
  guid: string;
  title: string;
  description: string;
  programType: ProgramType;
  shortMessage: string;
  details: ReadonlyArray<string>;
  features: ReadonlyArray<ProgramFeature>;
  cast: ReadonlyArray<ProgramCredit>;
  directors: ReadonlyArray<ProgramCredit>;
  expiry: string;
  images: any; // TODO:
  extras: { title: string; url: string };
  totalSeasons: number;
}

export interface SanitisedProgramEntry {
  programId: string;
  key?: string;
  guid: string;
  title: string;
  description: string;
  programType: ProgramType;
  details: ReadonlyArray<string>;
  features: ReadonlyArray<ProgramFeature>;
  backgroundImageURL?: string;
  isVOD: boolean;
  checkIsPlayable: (isLiveDisabled: boolean) => boolean;
  tvSeasonNumber: number;
}

export interface PreviewProgramStatus {
  isLoaded: boolean;
  isResumePointLoading: boolean;
  isLink?: boolean;
  resumePoint?: ProgramFeedEntryResumePoint;
  isMyListLoading: boolean;
  deleteURL?: string;
  tvSeasonEpisodeNumber?: number;
  bonusFeature?: boolean;
}

export interface SanitisedProgramEntry {
  programId: string;
  key?: string;
  guid: string;
  title: string;
  description: string;
  programType: ProgramType;
  details: ReadonlyArray<string>;
  features: ReadonlyArray<ProgramFeature>;
  backgroundImageURL?: string;
  isVOD: boolean;
  checkIsPlayable: (isLiveDisabled: boolean) => boolean;
  tvSeasonNumber: number;
}

export interface SanitisedProgram {
  guid: string;
  title: string;
  description: string;
  programType: ProgramType;
  shortMessage: string;
  details: ReadonlyArray<string>;
  features: ReadonlyArray<ProgramFeature>;
  cast: ReadonlyArray<ProgramCredit>;
  directors: ReadonlyArray<ProgramCredit>;
  expiry: string;
  images: any; // TODO:
  extras: { title: string; url: string };
  totalSeasons: number;
}

// Resume
export interface ResumePoint {
  id?: number;
  title?: string;
  label?: string;
  guid: string;
  programId?: string;
  programType?: ProgramType;
  seriesId?: number;
  tvSeasonNumber?: number;
  tvSeasonEpisodeNumber?: number;
  classifications?: ReadonlyArray<ProgramClassification>;
  isFirstEpisode?: boolean;
  position: number;
  totalDuration?: number;
  programEnd?: number;
  completed?: boolean;
  resume?: string;
  runtime?: number;
  updateToken?: string;
  updateUrl?: string;
  updated?: number;
  bonusFeature?: boolean;
}

export type StreamAudioType =
  | 'all'
  | 'main'
  | 'audio-main'
  | 'audio-description';

export interface ImageParams {
  imageType: ReadonlyArray<ProgramImageType> | ProgramImageType;
  imageWidth: number;
  imageConfig: ImageConfig;
}

export interface ImageSizing {
  imageType: ReadonlyArray<ProgramImageType> | ProgramImageType;
  imageRatio: string;
  imageWidth: number;
  imageHeight: number;
}

export interface ImageResizeParams {
  resize?: string;
  quality?: number;
  preferredFormat?: string;
  blur?: number;
}

export interface ImageResizeOptions {
  url: string;
  width?: number;
  quality?: number;
  isWebP?: boolean;
  blur?: number;
  height?: number;
}

export interface HomeFeedEntry extends ProgramFeedEntry {
  deleteURL?: string;
}

export interface SitemapPageTheme {
  buttonColor: string;
  fontColor: string;
  pageBackgroundImage: string;
  pageWideBackgroundImage: string;
  pageBackgroundColor1: string;
  pageBackgroundColor2: string;
  logoImage?: {
    url: string;
    width: number;
    height: number;
  };
}

export interface SitemapPage {
  id: string;
  updated: number;
  type: SitemapPageType;
  title: string;
  subtitle?: string;
  theme?: SitemapPageTheme;
  entries?: ReadonlyArray<SitemapEntry>;
  config?: BundleConfig;
  path?: string;
  __expires: number;
}

export interface Sitemap {
  id: string;
  title: string;
  type: SitemapPageType;
  theme?: SitemapPageTheme;
  version: number;
  entries: ReadonlyArray<SitemapEntry>;
  navigation: ReadonlyArray<SitemapNavigationEntry>;
  __expires: number;
}

// Sitemap
export type SitemapPageType =
  | 'home'
  | 'section'
  | 'single_list'
  | 'multi_list'
  | 'watchlist'
  | 'history'
  | 'bundle';

export interface SitemapEntry {
  id: number | string;
  type: ProgramFeedEntryType;
  title: string;
  hideTitle?: boolean;
  url?: string;
  total: number;
  source?: ProgramFeedSource;
}

export interface HomeFeed extends ProgramFeed, ImageSizing {
  readonly index: number;
  readonly feedIndex?: number;
  entries: HomeFeedEntry[];
  next: string;
  isPreload: boolean;
  isVolatile: boolean;
  previousSelectedID?: string;
  nextTriggerIndex?: number;
  __updated: number;
}

export interface PlayerLoadOptions {
  quality?: StreamQuality;
  currentTime?: number;
  skipAskResume?: boolean;
  ignoreClassification?: boolean;
  isFromOrigin?: boolean;
  isFromStart?: boolean;
}

export type StreamQuality = 'auto' | 'ultra' | 'high' | 'low' | 'medium';

export interface AppContext {
  config: Capabilities;
  keycodes: DeviceKeycodes;
  userManager: User;
  themeManager: AppTheme;
  isPointerOn: () => boolean;
}

// * completed = video has been watched to the end
// * error = video ran into an error, mark as watched/skip
// * selected = user selected a different entry mid-stream
// * exit = exit and remove the video carousel
export type CarouselEndType =
  | 'completed'
  | 'error'
  | 'selected'
  | 'exit'
  | 'skip';

export interface CarouselVideoPlayerProps {
  parentElement: HTMLElement;
  videoURL: string;
  handleOnLoad: (currentTime: number) => void;
  handleOnPlay: () => void;
  handleOnPlaying: (currentTime: number, isInitial: boolean) => void;
  handleOnPause: (currentTime: number) => void;
  handleOnBuffer: (currentTime: number) => void;
  handleOnProgress: (currentTime: number) => void;
  handleOnEnded: (endType: CarouselEndType, currentTime: number) => void;
  handleOnError: (
    endType: CarouselEndType,
    videoURL: string,
    currentTime: number,
    err: MediaError | Error
  ) => void;
  sendPromoErrorAnalytics: (
    currentTime: number,
    videoURL: string,
    err: MediaError | Error,
    isWarning?: boolean
  ) => void;
  setYoubora: (
    element?: HTMLVideoElement
  ) => (eventDetail: string) => void | void;
  resetYoubora: () => void;
}

// Discovery
export interface DiscoveryError {
  readonly code?: Readonly<string>;
  readonly dialogTitle: Readonly<string>;
  readonly displayType?: Readonly<DiscoveryErrorDisplayType>;
  readonly messageTemplate?: Readonly<string>;
  readonly shortCode?: Readonly<string>;
}

export type DiscoveryErrorDisplayType = 'dialog' | 'full_page';

export interface DiscoveryWebMAFPlayerParameters {
  readonly availableBandwidth: Readonly<number>;
  readonly bandwidthHistoryMaxEntries: Readonly<number>;
  readonly bandwidthHistoryMinEntries: Readonly<number>;
  readonly bandwidthSafetyFactor: Readonly<number>;
  readonly bandwidthUtilisationPercentage: Readonly<number>;
  readonly minAvailableBandwidth: Readonly<number>;
  readonly segmentsBetweenSwitchUp: Readonly<number>;
  readonly videoStartingBandwidth: Readonly<number>;
}
export interface DiscoveryLunaPlayerParameters {
  readonly videoStartingBandwidth: Readonly<number>;
}
export interface DiscoveryTizenPlayerParameters {
  readonly adaptiveInfo: Readonly<string>;
  readonly bufferForPlay: Readonly<number>;
  readonly bufferForResume: Readonly<number>;
  readonly bufferingTimeout: Readonly<number>;
  readonly bufferForLivePlay: Readonly<number>;
  readonly bufferForLiveResume: Readonly<number>;
}

export type DiscoveryPlayerParameters =
  | DiscoveryWebMAFPlayerParameters
  | DiscoveryLunaPlayerParameters
  | DiscoveryTizenPlayerParameters
  | DiscoveryShakaPlayerParameters;

export interface DiscoveryPromoParameters {
  readonly minImageDuration: Readonly<number>;
  readonly minImageDurationWithClip: Readonly<number>;
  readonly __updated: Readonly<number>;
}
export interface DiscoveryStatus {
  readonly authenticationAvailable: Readonly<boolean>;
  readonly registrationAvailable: Readonly<boolean>;
}
export interface DiscoveryTerms {
  readonly currentVersion: Readonly<number>;
}

export interface DiscoveryErrors {
  readonly [key: string]: Readonly<DiscoveryError>;
}
export interface DiscoverySites {
  readonly [key: string]: Readonly<string>;
}

export interface DiscoveryVersion {
  readonly minimum: Readonly<string>;
  readonly text: Readonly<string>;
  readonly title: Readonly<string>;
}

export interface DiscoverySdkVersion {
  readonly minimum: Readonly<string>;
  readonly text: Readonly<string>;
  readonly title: Readonly<string>;
}

export interface DiscoveryFirmareVersions {
  readonly [year: string]: Readonly<DiscoveryVersion>;
}

export interface Discovery {
  readonly promoParameters: Readonly<DiscoveryPromoParameters>;
  readonly status: Readonly<DiscoveryStatus>;
  readonly terms: Readonly<DiscoveryTerms>;
  readonly faqs: Readonly<string>;
  readonly errors: Readonly<DiscoveryErrors>;
  readonly sites: Readonly<DiscoverySites>;
  readonly allowSignup: Readonly<boolean>;
  readonly appVersion: Readonly<DiscoveryVersion>;
  readonly firmwareVersions: Readonly<DiscoveryFirmareVersions>;
  readonly sdkVersion: Readonly<DiscoverySdkVersion>;
  readonly playerParameters: Readonly<DiscoveryPlayerParameters>;
  readonly __updated: Readonly<number>;
}

export type WebMAFCommands =
  | 'setPlayTime'
  | 'setVideoPortalSize'
  | 'pause'
  | 'play'
  | 'load'
  | 'stop'
  | 'getPlaybackTime'
  | 'setAdaptiveStreamingParameters'
  | 'setVideoStartingBandwidth';

export type WebMAFMetrics =
  | 'url'
  | 'bandwidth'
  | 'naturalWidth'
  | 'naturalHeight'
  | 'sampleRate'
  | 'frameworkVersion'
  | 'currentState'
  | 'duration'
  | 'elapsed'
  | 'load'
  | 'stop'
  | 'currentBitrate';

export type WebMAFErrorType =
  | 'http_connection'
  | 'http_status_code'
  | 'video_error'
  | 'drm_error';

export interface WebMAFError {
  name: string;
  message: WebMAFErrorType;
  stack: string;
  playerError: {
    command: 'playerError' | 'playerStreamingError';
    error: WebMAFErrorType;
    error_code: number;
    error_code_hexa: string;
    error_info: string;
    status: 'fail';
  };
}

export interface ImageConfig {
  screenWidth: number;
  scale: number;
  quality: number;
  isWebP: boolean;
}
 

Javascript Online Compiler

Write, Run & Share Javascript code online using OneCompiler's JS online compiler for free. It's one of the robust, feature-rich online compilers for Javascript language. Getting started with the OneCompiler's Javascript editor is easy and fast. The editor shows sample boilerplate code when you choose language as Javascript and start coding.

About Javascript

Javascript(JS) is a object-oriented programming language which adhere to ECMA Script Standards. Javascript is required to design the behaviour of the web pages.

Key Features

  • Open-source
  • Just-in-time compiled language
  • Embedded along with HTML and makes web pages alive
  • Originally named as LiveScript.
  • Executable in both browser and server which has Javascript engines like V8(chrome), SpiderMonkey(Firefox) etc.

Syntax help

STDIN Example

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function(line){
    console.log("Hello, " + line);
});

variable declaration

KeywordDescriptionScope
varVar is used to declare variables(old way of declaring variables)Function or global scope
letlet is also used to declare variables(new way)Global or block Scope
constconst is used to declare const values. Once the value is assigned, it can not be modifiedGlobal or block Scope

Backtick Strings

Interpolation

let greetings = `Hello ${name}`

Multi line Strings

const msg = `
hello
world!
`

Arrays

An array is a collection of items or values.

Syntax:

let arrayName = [value1, value2,..etc];
// or
let arrayName = new Array("value1","value2",..etc);

Example:

let mobiles = ["iPhone", "Samsung", "Pixel"];

// accessing an array
console.log(mobiles[0]);

// changing an array element
mobiles[3] = "Nokia";

Arrow functions

Arrow Functions helps developers to write code in concise way, it’s introduced in ES6.
Arrow functions can be written in multiple ways. Below are couple of ways to use arrow function but it can be written in many other ways as well.

Syntax:

() => expression

Example:

const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const squaresOfEvenNumbers = numbers.filter(ele => ele % 2 == 0)
                                    .map(ele => ele ** 2);
console.log(squaresOfEvenNumbers);

De-structuring

Arrays

let [firstName, lastName] = ['Foo', 'Bar']

Objects

let {firstName, lastName} = {
  firstName: 'Foo',
  lastName: 'Bar'
}

rest(...) operator

 const {
    title,
    firstName,
    lastName,
    ...rest
  } = record;

Spread(...) operator

//Object spread
const post = {
  ...options,
  type: "new"
}
//array spread
const users = [
  ...adminUsers,
  ...normalUsers
]

Functions

function greetings({ name = 'Foo' } = {}) { //Defaulting name to Foo
  console.log(`Hello ${name}!`);
}
 
greet() // Hello Foo
greet({ name: 'Bar' }) // Hi Bar

Loops

1. If:

IF is used to execute a block of code based on a condition.

Syntax

if(condition){
    // code
}

2. If-Else:

Else part is used to execute the block of code when the condition fails.

Syntax

if(condition){
    // code
} else {
    // code
}

3. Switch:

Switch is used to replace nested If-Else statements.

Syntax

switch(condition){
    case 'value1' :
        //code
        [break;]
    case 'value2' :
        //code
        [break;]
    .......
    default :
        //code
        [break;]
}

4. For

For loop is used to iterate a set of statements based on a condition.

for(Initialization; Condition; Increment/decrement){  
//code  
} 

5. While

While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.

while (condition) {  
  // code 
}  

6. Do-While

Do-while is also used to iterate a set of statements based on a condition. It is mostly used when you need to execute the statements atleast once.

do {  
  // code 
} while (condition); 

Classes

ES6 introduced classes along with OOPS concepts in JS. Class is similar to a function which you can think like kind of template which will get called when ever you initialize class.

Syntax:

class className {
  constructor() { ... } //Mandatory Class method
  method1() { ... }
  method2() { ... }
  ...
}

Example:

class Mobile {
  constructor(model) {
    this.name = model;
  }
}

mbl = new Mobile("iPhone");