Skip to content

Commit

Permalink
types: define an AudioTrackList, VideoTrackList, AudioTrack and Video…
Browse files Browse the repository at this point in the history
…Track replacement type

TypeScript v3.9.x seems to have removed its type definitions for those
DOM APIs (video tracks and audio tracks we can find on an
HTMLMediaElement).

The reason why are not clear but judging by the problematic commit
(microsoft/TypeScript@065a996#diff-46fd87925e4552c166ec188712741c3f)
- meaning of LKG being "last known good". My guess would be that it is
  automatically generated from a source which does not have those APIs
(why we had them before is more of a mystery but I guess that's what
this LKG thing was about).

What I did in this commit is to creates four new types:
  - ICompatAudioTrackList
  - ICompatVideoTrackList
  - ICompatAudioTrack
  - ICompatVideoTrack
Which are respectively the implementation of an AudioTrackList,
VideoTrackList, AudioTrack and VideoTrack as they are defined in the
web-idl of the corresponding APIs in the whatwg spec
(https://html.spec.whatwg.org/multipage/media.html#audiotracklist-and-videotracklist-objects).

I chose to name those ICompatThing and not directly extending Thing
because I find it allows to better see which types are "native"
TypeScript ones and which have been actually extended because they
missed some browser APIs. As TypeScript could evolve to add those APIs,
we could need to remove those. So making them stand out from the rest
is and advantage to me.
  • Loading branch information
peaBerberian committed Jun 3, 2020
1 parent 2344c74 commit 237a322
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 20 deletions.
72 changes: 72 additions & 0 deletions src/compat/browser_compatibility_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,79 @@ interface ICompatDocument extends Document { mozCancelFullScreen? : () => void;
/**
* HTMLMediaElement with added optional vendored functions used by "old"
* browsers.
* And TypeScript forgot to add assiociated AudioTrackList and VideoTrackList
* (and yes apparently a HTMLAudioElement can have an assiociated
* VideoTrackList).
*
* Note: I prefer to define my own `ICompatHTMLMediaElement` rather to extend
* the original definition to better detect which types have been extended and
* are not actually valid TypeScript types.
*/
interface ICompatHTMLMediaElement extends HTMLMediaElement {
mozRequestFullScreen? : () => void;
msRequestFullscreen? : () => void;
webkitRequestFullscreen : () => void;
readonly audioTracks? : ICompatAudioTrackList;
readonly videoTracks? : ICompatVideoTrackList;
}

/**
* AudioTrackList implementation (that TS forgot).
* Directly taken from the WHATG spec:
* https://html.spec.whatwg.org/multipage/media.html#audiotracklist
*/
interface ICompatAudioTrackList extends EventTarget {
readonly length : number;
getTrackById(id : string) : ICompatAudioTrack;
onchange? : ((n : Event) => void) | null;
onaddtrack? : ((n : Event) => void) | null;
onremovetrack? : ((n : Event) => void) | null;

// It can be indexed
[x : number] : ICompatAudioTrack;
}

/**
* AudioTrack implementation (that TS forgot).
* Directly taken from the WHATG spec:
* https://html.spec.whatwg.org/multipage/media.html#audiotracklist
*/
interface ICompatAudioTrack {
id : string;
kind : string;
label : string;
language : string;
enabled : boolean;
}

/**
* VideoTrackList implementation (that TS forgot).
* Directly taken from the WHATG spec:
* https://html.spec.whatwg.org/multipage/media.html#audiotracklist
*/
interface ICompatVideoTrackList extends EventTarget {
readonly length : number;
selectedIndex : number;
getTrackById(id : string) : ICompatVideoTrack;
onchange? : ((n : Event) => void) | null;
onaddtrack? : ((n : Event) => void) | null;
onremovetrack? : ((n : Event) => void) | null;

// It can be indexed
[x : number] : ICompatVideoTrack;
}

/**
* VideoTrack implementation (that TS forgot).
* Directly taken from the WHATG spec:
* https://html.spec.whatwg.org/multipage/media.html#audiotracklist
*/
interface ICompatVideoTrack {
id : string;
kind : string;
label : string;
language : string;
selected : boolean;
}

/**
Expand Down Expand Up @@ -190,6 +258,10 @@ export {
HTMLElement_,
ICompatDocument,
ICompatHTMLMediaElement,
ICompatAudioTrackList,
ICompatVideoTrackList,
ICompatAudioTrack,
ICompatVideoTrack,
ICompatMediaKeySystemAccess,
ICompatMediaKeySystemConfiguration,
ICompatMediaKeysConstructor,
Expand Down
47 changes: 27 additions & 20 deletions src/core/api/media_element_track_choice_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@
*/

import { BehaviorSubject } from "rxjs";
import { ICompatTextTrackList } from "../../compat/browser_compatibility_types";
import {
ICompatAudioTrack,
ICompatAudioTrackList,
ICompatHTMLMediaElement,
ICompatTextTrackList,
ICompatVideoTrack,
ICompatVideoTrackList,
} from "../../compat/browser_compatibility_types";
import EventEmitter from "../../utils/event_emitter";
import normalizeLanguage from "../../utils/languages";
import {
Expand Down Expand Up @@ -52,8 +59,8 @@ interface IMediaElementTrackChoiceManagerEvents {
* @returns {boolean}
*/
function areTrackArraysDifferent(
oldTrackArray: Array<{ nativeTrack: VideoTrack|AudioTrack|TextTrack }>,
newTrackArray: Array<{ nativeTrack: VideoTrack|AudioTrack|TextTrack }>
oldTrackArray: Array<{ nativeTrack: ICompatVideoTrack|ICompatAudioTrack|TextTrack }>,
newTrackArray: Array<{ nativeTrack: ICompatVideoTrack|ICompatAudioTrack|TextTrack }>
): boolean {
if (newTrackArray.length !== oldTrackArray.length) {
return true;
Expand All @@ -72,12 +79,12 @@ function areTrackArraysDifferent(
* @returns {Array.<Object>}
*/
function createAudioTracks(
audioTracks: AudioTrackList
audioTracks: ICompatAudioTrackList
): Array<{ track: { id: string;
normalized: string;
language: string;
audioDescription: boolean; };
nativeTrack: AudioTrack; }> {
nativeTrack: ICompatAudioTrack; }> {
const newAudioTracks = [];
const languagesOccurences: Partial<Record<string, number>> = {};
for (let i = 0; i < audioTracks.length; i++) {
Expand Down Expand Up @@ -140,10 +147,10 @@ function createTextTracks(
* @returns {Array.<Object>}
*/
function createVideoTracks(
videoTracks: VideoTrackList
videoTracks: ICompatVideoTrackList
): Array<{ track: { id: string;
representations: []; };
nativeTrack: VideoTrack; }> {
nativeTrack: ICompatVideoTrack; }> {
const newVideoTracks = [];
const languagesOccurences: Partial<Record<string, number>> = {};
for (let i = 0; i < videoTracks.length; i++) {
Expand Down Expand Up @@ -188,23 +195,23 @@ export default class MediaElementTrackChoiceManager
private _preferredVideoTracks : BehaviorSubject<IVideoTrackPreference[]>;

/** List every available audio tracks available on the media element. */
private _audioTracks : Array<{ track: ITMAudioTrack; nativeTrack: AudioTrack }>;
private _audioTracks : Array<{ track: ITMAudioTrack; nativeTrack: ICompatAudioTrack }>;
/** List every available text tracks available on the media element. */
private _textTracks : Array<{ track: ITMTextTrack; nativeTrack: TextTrack }>;
/** List every available video tracks available on the media element. */
private _videoTracks : Array<{ track: ITMVideoTrack; nativeTrack: VideoTrack }>;
private _videoTracks : Array<{ track: ITMVideoTrack; nativeTrack: ICompatVideoTrack }>;

/** Last audio track emitted as active. */
private _lastEmittedNativeAudioTrack : AudioTrack | null | undefined;
private _lastEmittedNativeAudioTrack : ICompatAudioTrack | null | undefined;
/** Last video track emitted as active. */
private _lastEmittedNativeVideoTrack : VideoTrack | null | undefined;
private _lastEmittedNativeVideoTrack : ICompatVideoTrack | null | undefined;
/** Last text track emitted as active. */
private _lastEmittedNativeTextTrack : TextTrack | null | undefined;

/** Native `AudioTrackList` implemented on the media element. */
private _nativeAudioTracks : AudioTrackList|undefined;
private _nativeAudioTracks : ICompatAudioTrackList | undefined;
/** Native `VideoTrackList` implemented on the media element. */
private _nativeVideoTracks : VideoTrackList|undefined;
private _nativeVideoTracks : ICompatVideoTrackList | undefined;
/** Native `TextTrackList` implemented on the media element. */
private _nativeTextTracks : ICompatTextTrackList|undefined;

Expand All @@ -215,7 +222,7 @@ export default class MediaElementTrackChoiceManager
* through audio track list updates, as long as it is still available.
* `undefined` if the audio track was not manually set.
*/
private _audioTrackLockedOn : AudioTrack | undefined;
private _audioTrackLockedOn : ICompatAudioTrack | undefined;

/**
* Last text track manually set active through the corresponding
Expand All @@ -235,7 +242,7 @@ export default class MediaElementTrackChoiceManager
* `null` if the video track was disabled.
* `undefined` if the video track was not manually set.
*/
private _videoTrackLockedOn : VideoTrack | undefined | null;
private _videoTrackLockedOn : ICompatVideoTrack | undefined | null;

constructor(
defaults : { preferredAudioTracks : BehaviorSubject<IAudioTrackPreference[]>;
Expand All @@ -252,8 +259,8 @@ export default class MediaElementTrackChoiceManager
// TODO In practice, the audio/video/text tracks API are not always implemented on
// the media element, although Typescript HTMLMediaElement types tend to mean
// that can't be undefined.
this._nativeAudioTracks = mediaElement.audioTracks as AudioTrackList|undefined;
this._nativeVideoTracks = mediaElement.videoTracks as VideoTrackList|undefined;
this._nativeAudioTracks = (mediaElement as ICompatHTMLMediaElement).audioTracks;
this._nativeVideoTracks = (mediaElement as ICompatHTMLMediaElement).videoTracks;
this._nativeTextTracks = mediaElement.textTracks as ICompatTextTrackList|undefined;

this._audioTracks =
Expand Down Expand Up @@ -462,7 +469,7 @@ export default class MediaElementTrackChoiceManager
* @returns {Object|undefined|null}
*/
private _getPrivateChosenAudioTrack(): { track: ITMAudioTrack;
nativeTrack: AudioTrack; } |
nativeTrack: ICompatAudioTrack; } |
undefined |
null {
if (this._nativeAudioTracks === undefined) {
Expand All @@ -484,7 +491,7 @@ export default class MediaElementTrackChoiceManager
* @returns {Object|undefined|null}
*/
private _getPrivateChosenVideoTrack(): { track: ITMVideoTrack;
nativeTrack: VideoTrack; } |
nativeTrack: ICompatVideoTrack; } |
undefined |
null {
if (this._nativeVideoTracks === undefined) {
Expand Down Expand Up @@ -863,7 +870,7 @@ function disableAllTextTracksBut(
* @param {Array.<Object>} videoTracks
*/
function disableVideoTracks(
videoTracks : Array<{ nativeTrack : VideoTrack }>
videoTracks : Array<{ nativeTrack : ICompatVideoTrack }>
) {
for (let i = 0; i < videoTracks.length; i++) {
const { nativeTrack } = videoTracks[i];
Expand Down

0 comments on commit 237a322

Please sign in to comment.